使用Fluent验证的模型T的通用验证器?

问题描述 投票:2回答:1

我昨天刚刚介绍了Fluent Validation,我认为这很酷。我试过了它,它的工作原理。但我的应用程序目前有几个模型,我必须承认为每个模型编写Validators是很紧张的。是否有可能用Generics编写并找到一种方法来验证每个模型?

这就是我的Validator当前编写的方式。但我不知道如何在泛型中写它。

EmployeeValidator.cs

public class EmployeeValidator : AbstractValidator<EmployeeViewModel>
{
    private readonly ValidationEntitySettingServices _validationEntitySettingService;

    public EmployeeValidator(ValidationEntitySettingServices validationEntitySettingService)
    {
        _validationEntitySettingService = validationEntitySettingService;

        RuleFor(x => x.LastName).NotEmpty().When(x => IsPropertyRequired(x.LastName)).WithMessage("Last Name is a required field!");
        RuleFor(x => x.FirstName).NotEmpty().When(x => IsPropertyRequired(x.FirstName)).WithMessage("First Name is a required field!");
        RuleFor(x => x.MiddleName).NotEmpty().When(x => IsPropertyRequired(x.MiddleName)).WithMessage("Middle Name is a required field!");
    }

    public bool IsPropertyRequired(string propertyName)
    {
        var empValidationSetting = _validationEntitySettingService.GetIncluding("Employee");
        if (empValidationSetting != null)
        {
            return empValidationSetting.ValidationEntityProperties.Any(p => p.PropertyName.Equals(propertyName) && p.IsRequired);
        }
        else
            return false;
    }
}

提前致谢。

c# generics fluentvalidation
1个回答
2
投票

我不认为使验证通用是有意义的,因为所有模型都可能具有需要以不同方式进行验证的不同属性和属性类型。但是,您可以通过添加基本验证类来使您在此处拥有的内容更通用,例如:

public abstract class BaseValidator<T> : AbstractValidator<T>
{
    private readonly ValidationEntitySettingServices _validationEntitySettingService;

    public BaseValidator(ValidationEntitySettingServices validationEntitySettingService)
    {
        _validationEntitySettingService = validationEntitySettingService;
        AutoApplyEmptyRules();
    }

    private string ViewModelName
    {
        get { return GetType().Name.Replace("Validator", string.Empty); }
    }

    // no longer needed
    //public bool IsPropertyRequired(string propertyName)
    //{
    //    var validationSetting = _validationEntitySettingService.GetIncluding(ViewModelName);
    //    if (validationSetting != null)
    //    {
    //        return validationSetting.ValidationEntityProperties.Any(p => p.PropertyName.Equals(propertyName) && p.IsRequired);
    //    }
    //    else
    //        return false;
    //}

    protected void AddEmptyRuleFor<TProperty>(Expression<Func<T, TProperty>> expression, string message)
    {
        //RuleFor(expression).NotEmpty().When(x => IsPropertyRequired(((MemberExpression)expression.Body).Name)).WithMessage(message);
        RuleFor(expression).NotEmpty().WithMessage(message);
    }

    private void AddEmptyRuleForProperty(PropertyInfo property)
    {
        MethodInfo methodInfo = GetType().GetMethod("AddEmptyRuleFor");
        Type[] argumentTypes = new Type[] { typeof(T), property.PropertyType };
        MethodInfo genericMethod = methodInfo.MakeGenericMethod(argumentTypes);
        object propertyExpression = ExpressionHelper.CreateMemberExpressionForProperty<T>(property);
        genericMethod.Invoke(this, new object[] { propertyExpression, $"{propertyInfo.Name} is a required field!" });
    }

    private PropertyInfo[] GetRequiredProperties()
    {
        var validationSetting = _validationEntitySettingService.GetIncluding(ViewModelName);
        if (validationSetting != null)
        {
            return validationSetting.ValidationEntityProperties.Where(p => p.IsRequired);
        }
        else
            return null;
    }

    private void AutoApplyEmptyRules()
    {
        PropertyInfo[] properties = GetRequiredProperties();
        if (properties == null)
            return;
        foreach (PropertyInfo propertyInfo in properties)
        {
            AddEmptyRuleForProperty(property);
        }
    }
}

这里的主要要求是AddEmptyRuleForProperty方法,它将通过构造基于PropertyType的方法来调用泛型AddEmptyRuleFor方法。

然后,您可以继承此类,并使用泛型方法应用规则:

public class EmployeeValidator : BaseValidator<EmployeeViewModel>
{
    public EmployeeValidator(ValidationEntitySettingServices validationEntitySettingService) : base(validationEntitySettingService)
    {
        // no longer needed
        //AddEmptyRuleFor(x => x.LastName, "Last Name is a required field!");
        //AddEmptyRuleFor(x => x.FirstName, "First Name is a required field!");
        //AddEmptyRuleFor(x => x.MiddleName, "Middle Name is a required field!");
    }
}

这是ExpressionHelper类,它提供了一种创建泛型成员表达式的方法,该方法可以在调用上面的AddEmptyRuleFor方法时传入:

public static class ExpressionHelper
{
    public static Expression<Func<TModel, TProperty>> CreateMemberExpression<TModel, TProperty>(PropertyInfo propertyInfo)
    {
        if (propertyInfo == null)
            throw new ArgumentException("Argument cannot be null", "propertyInfo");

        ParameterExpression entityParam = Expression.Parameter(typeof(TModel), "x");
        Expression columnExpr = Expression.Property(entityParam, propertyInfo);

        if (propertyInfo.PropertyType != typeof(T))
            columnExpr = Expression.Convert(columnExpr, typeof(T));

        return Expression.Lambda<Func<TModel, TProperty>>(columnExpr, entityParam);
    }

    public static object CreateMemberExpressionForProperty<TModel>(PropertyInfo property)
    {
        MethodInfo methodInfo = typeof(ExpressionHelper).GetMethod("CreateMemberExpression", BindingFlags.Static | BindingFlags.Public);
        Type[] argumentTypes = new Type[] { typeof(TModel), property.PropertyType };
        MethodInfo genericMethod = methodInfo.MakeGenericMethod(argumentTypes);
        return genericMethod.Invoke(null, new object[] { property });
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.