You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于反射与表达式树调用AbstractValidator的RuleFor方法报错求助

动态调用FluentValidation的RuleFor方法问题

我实现了一个继承自AbstractValidator<T>的验证类,场景如下:

假设存在实体类:

public class Users{
    [Required]
    public string Name {get;set;}
} 

我希望调用RuleFor(x => x.Name).NotEmpty();这类验证规则,但属性名称仅在运行时可知,因此想通过反射和表达式树实现动态调用。

但调用RuleFor方法时失败,以下是我的实现代码(注释部分为已尝试的方案):

GenValidator<T> : AbstractValidator<T> 
{
   // constructor etc..

    public async Task<IEnumerable<string>> ValidateValueAsync(T model, string propertyName) 
    {
        try
        {
            var loType = model.GetType();
            var property = loType.GetProperty(propertyName);
            var param = Expression.Parameter(loType);
            var propertyExpression = Expression.Property(param, property);
            var lambdaExpressionType = typeof(Expression);
        
            var lambda = Expression.Lambda(typeof(Func<,>).MakeGenericType(model.GetType(), typeof(object)), propertyExpression, param);

            var lambdaMethod = typeof(Expression).GetMethod("Lambda", new Type[] { typeof(Type), typeof(Expression), typeof(ParameterExpression[]) });
            var lambda2 = lambdaMethod.MakeGenericMethod(loType, typeof(object)).Invoke(null, new object[] { propertyExpression, new ParameterExpression[] { param } });
            var compileMethod = lambda.GetType().GetMethod("Compile");
            var func = compileMethod.Invoke(lambda2, null);
            var ruleForMethod = typeof(AbstractValidator<T>).GetMethod("RuleFor");
            var genericRuleForMethod = ruleForMethod.MakeGenericMethod(loType);
            genericRuleForMethod.Invoke(this, new object[] { func });

            //RuleFor<object>(lambda).NotEmpty().WithMessage("The property is required.");
            //RuleFor(lambda.Compile()).NotEmpty().WithMessage("The property is required.");
        }
        catch (Exception ex)
        {
        }
        //var lambda = Expression.Lambda<Func<T, object>>(property, param);
        //RuleFor(lambda).NotEmpty().WithMessage("The property is required.");

        var valContext = ValidationContext<T>.CreateWithOptions(model, x => x.IncludeProperties(_propertyName));

        var result = await ValidateAsync(valContext);

        if (result.IsValid)
        {
            return Array.Empty<string>();
        }
        return result.Errors.Select(e => e.ErrorMessage);
    }
}

编辑补充

public class MyValidator
{
    public MyValidator(string propertyname)
    {
       // 硬编码调用FluentValidation的方式
       RuleFor(x => x.Name).NotEmpty();

      // 我不想硬编码
      // 我想在这里做一些处理,调用RuleFor方法,此时我只有propertyname字符串、模型类型和模型实例
       
    }
}

编辑补充2
修改后的代码仍无法运行,调用genericRuleForMethod时抛出异常:object type does not match Target type,代码如下:

private void GenericRuleFor(T Model, string propertyName)
{
    var property = Model.GetType().GetProperty(propertyName);
    var param = Expression.Parameter(Model.GetType());
    var propertyExpression = Expression.Property(param, property);

    var lambda = Expression.Lambda(typeof(Func<,>).MakeGenericType(Model.GetType(), property.PropertyType), propertyExpression, param);

    var abstractValidatorType = typeof(AbstractValidator<>).MakeGenericType(Model.GetType());
    var ruleForMethod = abstractValidatorType.GetMethods().First(m => m.Name == "RuleFor" && m.IsGenericMethodDefinition);
    var genericRuleForMethod = ruleForMethod.MakeGenericMethod(property.PropertyType);
    genericRuleForMethod.Invoke(this, new object[] { lambda });
}

问题原因

  1. 表达式树参数类型不匹配:当前验证器继承自AbstractValidator<T>,表达式参数应固定为typeof(T),而非model.GetType()(若T为基类,model可能是派生类,会导致类型不兼容)。
  2. RuleFor方法获取错误:不需要动态构建AbstractValidator<>的泛型类型,直接从当前类AbstractValidator<T>中获取方法即可。
  3. 委托类型错误:RuleFor接收的是Expression<Func<T, TProperty>>,而非编译后的委托实例,也不是基于model.GetType()构建的委托。

正确实现代码

public class GenValidator<T> : AbstractValidator<T>
{
    public async Task<IEnumerable<string>> ValidateValueAsync(T model, string propertyName)
    {
        var propertyInfo = typeof(T).GetProperty(propertyName) 
                           ?? throw new ArgumentException($"属性 {propertyName} 在类型 {typeof(T)} 中不存在");

        // 构建表达式树:x => x.PropertyName
        var parameter = Expression.Parameter(typeof(T), "x");
        var propertyAccess = Expression.Property(parameter, propertyInfo);
        var lambda = Expression.Lambda(typeof(Func<,>).MakeGenericType(typeof(T), propertyInfo.PropertyType), propertyAccess, parameter);

        // 获取RuleFor方法:RuleFor<TProperty>(Expression<Func<T, TProperty>> expression)
        var ruleForMethod = typeof(AbstractValidator<T>)
            .GetMethods()
            .First(m => m.Name == "RuleFor" && m.GetParameters()[0].ParameterType.GetGenericTypeDefinition() == typeof(Expression<>));

        // 构造泛型方法,传入属性类型作为泛型参数
        var genericRuleFor = ruleForMethod.MakeGenericMethod(propertyInfo.PropertyType);

        // 调用RuleFor并链式调用NotEmpty规则
        var ruleBuilder = genericRuleFor.Invoke(this, new object[] { lambda });
        typeof(IRuleBuilder<T, object>)
            .GetMethod("NotEmpty")
            .Invoke(ruleBuilder, null);

        // 执行验证
        var validationContext = ValidationContext<T>.CreateWithOptions(model, opts => opts.IncludeProperties(propertyName));
        var validationResult = await ValidateAsync(validationContext);

        return validationResult.IsValid 
            ? Array.Empty<string>() 
            : validationResult.Errors.Select(e => e.ErrorMessage);
    }

    // 适配构造函数场景的GenericRuleFor方法
    private void GenericRuleFor(string propertyName)
    {
        var propertyInfo = typeof(T).GetProperty(propertyName) 
                           ?? throw new ArgumentException($"属性 {propertyName} 在类型 {typeof(T)} 中不存在");

        var parameter = Expression.Parameter(typeof(T), "x");
        var propertyAccess = Expression.Property(parameter, propertyInfo);
        var lambda = Expression.Lambda(typeof(Func<,>).MakeGenericType(typeof(T), propertyInfo.PropertyType), propertyAccess, parameter);

        var ruleForMethod = typeof(AbstractValidator<T>)
            .GetMethods()
            .First(m => m.Name == "RuleFor" && m.GetParameters()[0].ParameterType.GetGenericTypeDefinition() == typeof(Expression<>));

        var genericRuleFor = ruleForMethod.MakeGenericMethod(propertyInfo.PropertyType);
        var ruleBuilder = genericRuleFor.Invoke(this, new object[] { lambda });

        // 添加NotEmpty规则,其他规则可同理反射调用
        typeof(IRuleBuilder<T, object>)
            .GetMethod("NotEmpty")
            .Invoke(ruleBuilder, null);
    }
}

关键说明

  • 表达式树参数固定为typeof(T),确保与当前验证器的泛型类型一致,避免类型不匹配问题。
  • 通过判断参数类型为Expression<>泛型定义,精准匹配RuleFor的正确重载。
  • 调用RuleFor后,通过反射链式调用验证规则方法,支持扩展其他规则(如Length、EmailAddress等)。

内容的提问来源于stack exchange,提问作者Asım Gündüz

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.04 08:20:22