基于反射与表达式树调用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 }); }
问题原因
- 表达式树参数类型不匹配:当前验证器继承自
AbstractValidator<T>,表达式参数应固定为typeof(T),而非model.GetType()(若T为基类,model可能是派生类,会导致类型不兼容)。 - RuleFor方法获取错误:不需要动态构建
AbstractValidator<>的泛型类型,直接从当前类AbstractValidator<T>中获取方法即可。 - 委托类型错误: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
相关产品推荐
相关产品推荐

