当继承类的基类也具有验证时,如何对其模型执行验证



好吧,我的问题是我正在使用aspnetcore 2.1创建一个api,为了避免代码重复,我创建了一个抽象类,其中包含共享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"});
    }
}

虚拟方法添加到基类。

如果要在基类中执行一些常见的验证逻辑,

并在每个具体实现中执行额外的验证逻辑,请将虚拟验证方法添加到基类验证函数中调用的方法。

添加到基类:

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;
    }
}

最新更新