【问题标题】:How to perform validation of a model of an inherited class when its base class also has validation?当继承类的模型也有验证时,如何对其进行验证?
【发布时间】:2019-06-01 12:32:59
【问题描述】:

好吧,我的问题是我正在使用 aspnetcore 2.1 创建一个 api,为了避免代码重复,我创建了一个抽象类,其属性共享 dto(板、板创建、板更新等)。我使用 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"});
    }
}

【问题讨论】:

    标签: c# inheritance asp.net-core ivalidatableobject


    【解决方案1】:

    向您的基类添加一个虚拟方法。

    如果您想在基类中执行一些常见的验证逻辑,并在每个具体实现中执行额外的验证逻辑,请向您的基类添加一个虚拟验证方法,该方法在基类验证函数中调用。

    添加到您的基类:

    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,所以只有在需要执行额外的自定义逻辑时才需要覆盖它。

    【讨论】:

    • 是的,很有帮助,我已经把它标记为正确答案了。
    【解决方案2】:

    使用 virtual 并使用 return yield。 IValidatableObject 是一个非常清晰的界面,具有非常清晰的意图 - 它在这样的控制器中使用

    if (ModelState.IsValid) {....}
    

    所以如果你的 Action 看起来像这样(又好又干净)

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

    我的意思是这是一个迭代过程 - 每个子类必须“继承”其父类的所有验证检查,并在它们之上添加新的验证检查。

    【讨论】:

    • 这应该是公认的答案。
    【解决方案3】:

    与接受的答案类似,您可以将 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;
        }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2023-02-15
      • 1970-01-01
      • 1970-01-01
      • 2015-09-24
      • 2014-03-17
      • 2014-08-01
      • 1970-01-01
      • 2013-01-21
      相关资源
      最近更新 更多