Как выполнить проверку модели унаследованного класса, когда его базовый класс также имеет проверку?

Что ж, моя проблема в том, что я создаю 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;
    }
}
Другие вопросы по тегам