当继承的类的基类也具有验证时,如何执行验证?

时间:2019-01-06 17:51:59

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

好吧,我的问题是我正在使用aspnetcore 2.1创建一个api,为避免代码重复,我创建了一个抽象类,该类具有共享dto的属性(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 个答案:

答案 0 :(得分:2)

向基类添加虚拟方法。

如果要在基类中执行一些通用的验证逻辑,并在每个具体实现中执行额外的验证逻辑,则可以在基类中添加一个虚拟验证方法,该方法在基类验证函数中调用。

添加到您的基类:

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,因此仅在需要执行其他自定义逻辑时才需要覆盖它。

答案 1 :(得分:0)

使用虚拟并使用收益率。 IValidatableObject是一个非常清晰的界面,带有非常清晰的意图-像这样在Controller中使用

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

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

答案 2 :(得分:0)

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