好吧,我的问题是我正在使用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;
}
}