Как выполнить проверку модели унаследованного класса, когда его базовый класс также имеет проверку?
Что ж, моя проблема в том, что я создаю API, используя aspnetcore 2.1, чтобы избежать дублирования кода, я создал абстрактный класс со свойствами, которые разделяют dtos (board, boardforcreation, boardforupdate и т. Д.). Я добавил к абстрактному классу персонализированную проверку с использованием ivalidatableobject, теперь я хочу добавить персонализированную проверку к классам, производным от абстрактного класса, но он говорит мне, что расширение интерфейса ivalidatableobject является избыточным, поскольку оно уже было объявлено в базовом классе, а также Когда я добавляю метод Validate в производный класс, он говорит мне, что он уже объявлен и реализован, тогда как я могу добавить проверку в абстрактном классе и в производном классе, используя ivalidatableobject? или есть другой способ добиться этого. Заранее спасибо.
public class Board : BoardAbstractBase, IValidatableObject
{
public Guid BoardId { get; set; }
public DateTimeOffset StartDate { get; set; }
public DateTimeOffset EndDate { get; set; }
}
public abstract class BoardAbstractBase : AbstractBasicEntity, IValidatableObject
{
public DateTimeOffset EstimatedStartDate { get; set; }
public DateTimeOffset EstimatedEndDate { get; set; }
public decimal EstimatedBudget { get; set; }
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
if (!(EstimatedStartDate < EstimatedEndDate))
yield return new ValidationResult(
"StartDateBeforeEndDate|The estimated start date should be smaller than the end date.",
new[] {"BoardAbstractBase"});
}
}
3 ответа
Добавьте виртуальный метод в ваш базовый класс.
Если вы хотите выполнить некоторую общую логику проверки в базовом классе, а также выполнить дополнительную логику проверки в каждой из конкретных реализаций, то добавьте метод виртуальной проверки в ваш базовый класс, который вызывается в функции проверки базового класса.
Добавьте к своему базовому классу:
public abstract class BoardAbstractBase {
...
protected virtual bool RepoValidate() {
return true;
}
...
}
Затем в каждой конкретной реализации реализуем RepoValidate
с любой пользовательской логикой проверки, которая вам нужна protected override bool RepoValidate() {...}
,
Например
public class Board : BoardAbstractBase, IValidatableObject
{
...
protected override bool RepoValidate() {
return this.BoardId == "";
}
...
}
Затем в BoardAbstractBase.Validate
:
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
if (!(EstimatedStartDate < EstimatedEndDate))
yield return new ValidationResult(
"StartDateBeforeEndDate|The estimated start date should be smaller than the end date.",
new[] {"BoardAbstractBase"});
if (!this.RepoValidate()){ ... }
}
Теперь вы всегда можете изменить RepoValidate
чтобы вернуть результат проверки, если он потерпит неудачу, или принять какой-либо аргумент, но просто для примера, этот просто возвращает false. Кроме того, потому что это virtual
и не abstract
вам нужно только переопределить его, когда у вас есть дополнительная пользовательская логика для выполнения.
Используйте виртуальный и используйте доходность возврата.
IValidatableObject
- очень понятный интерфейс с очень ясным намерением - он используется в контроллерах вроде этого
if (ModelState.IsValid) {....}
Итак, если ваше действие выглядит примерно так (красиво и чисто)
public Task<IActionResult> DoSomeAction(SomeClass model)
{
if (ModelState.IsValid) {
...
}
else return View(model);
}
тогда ваш код будет примерно таким
public BaseClass : IValidatableObject
{
// and implements
public virtual IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
if (...)
yield return new ValidationResult($"...error - ",
new string[] { "variable1 name" });
if (...)
yield return new ValidationResult($"...error2 - ",
new string[] { "variable1", "variable2" });
}
public class SomeClass : SomeBaseClass, IValidatableObject
{
// and implements
public override IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
var baseErrors = base.Validate(validationContext);
if (baseErrors != null && baseErrors.Any())
{
foreach (var item in baseErrors)
{
yield return item;
}
}
if (...some error condition...)
yield return new ValidationResult($"Validation error - condition 1",
new string[] { "variable1 });
}
Я хочу сказать, что это итеративный процесс - каждый подкласс должен «наследовать» все проверки своих родителей и добавлять к ним новые проверки.
Как и в принятом ответе, вы могли бы сделать реализацию базового класса метода Validate виртуальной, а затем в своем дочернем классе переопределить метод Validate, сначала вернуть унаследованный результат, а затем добавить настраиваемую логику проверки для дочернего класса. См. Упрощенный пример ниже
public abstract class BoardBase: IValidatableObject
{
public int Id { get; set; }
public virtual IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
var results = new List<ValidationResult>();
if (Id != 1)
{
results.Add(new ValidationResult("Id must be 1"));
}
return results;
}
}
public class Board: BoardBase
{
public string Name { get; set; }
public override IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
var results = base.Validate(validationContext).ToList();
if (Name.Length > 4)
{
results.Add(new ValidationResult("Name must be shorter than 5"));
}
return results;
}
}