【发布时间】:2020-08-15 15:00:07
【问题描述】:
我正在尝试使用访问者模式在树结构上应用 FluentValidation (v 9.1.1)。它的特殊之处在于,几个不同的树元素都实现了一个接口,并且元素的子属性属于这个接口类型。换句话说,子属性不是强类型的。简化模型见下文。每个验证器都进行特定的实现,我不明白如何为 interface 子级附加子验证器。
这是一个演示模型(工作代码):
public interface IElement
{
Type ResultType { get; }
TResult Accept<TResult>(IElementVisitor<TResult> visitor);
}
public class ConstElement : IElement
{
public object Value { get; set; }
public Type ResultType => Value?.GetType();
public TResult Accept<TResult>(IElementVisitor<TResult> visitor)
{
return visitor.VisitElement(this);
}
}
public class BinaryElement : IElement
{
// Child properties are not strongly typed.
public IElement Left { get; set; }
public IElement Right { get; set; }
public Operand Operand { get; set; }
public Type ResultType => Operand switch
{
Operand.Equal => typeof(bool),
Operand.GreaterThan => typeof(bool),
Operand.Plus => Left.GetType(),
Operand.Multiply => Left.GetType(),
_ => throw new NotImplementedException(),
};
public TResult Accept<TResult>(IElementVisitor<TResult> visitor)
{
return visitor.VisitElement(this);
}
}
public enum Operand { Equal, GreaterThan, Plus, Multiply }
public class ConstElementValidator : AbstractValidator<ConstElement>
{
public ConstElementValidator()
{
RuleFor(ele => ele.Value).NotNull().Must(value => (value is double) || (value is TimeSpan));
}
}
public class BinaryElementValidator : AbstractValidator<BinaryElement>
{
public BinaryElementValidator()
{
// Rules for the element itself
RuleFor(ele => ele.Left).NotNull();
RuleFor(ele => ele.Right).NotNull();
RuleFor(ele => ele).Must(ele => IsValidResultTypeCombination(ele.Left.ResultType, ele.Right.ResultType, ele.Operand));
// Add rules for child elements here? How?
}
private bool IsValidResultTypeCombination(Type left, Type right, Operand operand)
{
if (left == typeof(bool) && right != typeof(bool))
return false;
// other result type validations...
return true;
}
}
public interface IElementVisitor<TResult>
{
TResult VisitElement(ConstElement element);
TResult VisitElement(BinaryElement element);
}
public class ValidationVisitor : IElementVisitor<ValidationResult>
{
public ValidationResult VisitElement(ConstElement element)
{
return new ConstElementValidator().Validate(element);
}
public ValidationResult VisitElement(BinaryElement element)
{
// How to add validation of element.Left and element.Right,
// taking into account, that their type is IElement, while Validators are bound to the implementation type?
var result = new BinaryElementValidator().Validate(element);
var leftResult = element.Left.Accept(this);
var rightResult = element.Right.Accept(this);
// merge leftResult and rightResult with result
return result;
}
}
一般来说,添加子验证有两种方法。要么直接在验证器中调用子验证器,这将使 ValidationVisitor 过时,要么让验证器专注于自己的逻辑并在 ValidationVisitor 中添加子验证,如代码所示。
我现在能够继续的唯一方法是使用访问者并合并元素及其子元素的验证结果。
在这种情况下,有没有办法将子验证器添加到 BinaryElement?在访问者中或直接在 BinaryElementValidator 中。
【问题讨论】:
标签: fluentvalidation