使用Fluent验证的T型通用验证器? [英] Generic Validator for Model T Using Fluent Validation?

查看:53
本文介绍了使用Fluent验证的T型通用验证器?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

昨天我刚刚被介绍给Fluent Validation,我觉得它很酷.我已经尝试过了,而且效果很好.但是我的应用程序当前有多个模型,我必须承认为每个模型编写Validators都很麻烦.是否可以用泛型编写它,并找到一种方法来验证每个模型?

I just got introduced to Fluent Validation yesterday and I think it's pretty cool. I have tried it and it works. But my application currently has several models and I must admit it is stressful to write Validators for each model. Is it possible to have it written in Generics and find a way to get each model validated with it?

这是我的验证器当前的编写方式.但是我不知道如何用泛型编写它.

This is how my Validator is currently written. But I don't know how to write it in generics.

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

谢谢.

推荐答案

我认为使验证通用没有任何意义,因为所有模型都可能具有不同的属性和需要的属性类型验证不同.但是,您可以通过添加诸如以下内容的基本验证类来使此处的内容更加通用:

I don't think it would really make sense to make the validation generic as all of your models are likely to have different properties and property types that need to be validated differently. However, you could make what you have here more generic by adding a base validation class such as:

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方法.

The main requirement here is the AddEmptyRuleForProperty method which will call the generic AddEmptyRuleFor method by constructing the method based on the PropertyType.

然后,您可以改为继承此类,并使用通用方法应用规则:

Then you can inherit this class instead and apply rules using the generic method:

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方法时可以将其传递给该对象:

This is the ExpressionHelper class which provides a method to create a generic member expression as an object that can be passed in when invoking the AddEmptyRuleFor method above:

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

这篇关于使用Fluent验证的T型通用验证器?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆