如何在不破坏DI注册的情况下向FluentValidation验证器传参?
问题:FluentValidation子验证器带参数时DI注册失败,父验证器通过SetValidator传参
需要通过.SetValidator()方法从父验证器向子验证器传递参数,但子验证器带有参数时,使用FluentValidation的自动注册功能将其注册到DI容器时遇到构造错误。
父验证器代码
public class FooValidator: AbstractValidator<Foo> { public FooValidator() { RuleFor(foo => foo.Bar) .SetValidator(foo => new BarValidator(foo.SomeStringValue)) .When(foo => foo.Bar != null); } }
子验证器代码
public class BarValidator: AbstractValidator<Bar> { public BarValidator(string someStringValue) { RuleFor(bar => bar.Baz) .Must(BeValid(bar.Baz, someStringValue) .When(bar => bar.Baz != null); } private static bool BeValid(Baz baz, string someStringValue) { return baz == someStringValue; } }
DI注册代码
services.AddValidatorsFromAssembly(Assembly.GetExecutingAssembly(), ServiceLifetime.Transient);
错误信息
System.AggregateException: Some services are not able to be constructed (Error while validating the service descriptor 'ServiceType: FluentValidation.IValidator`1[Domain.ValueObjects.Bar] Lifetime: Transient ImplementationType: Application.Common.Validators.BarValidator': Unable to resolve service for type 'System.String' while attempting to activate 'Application.Common.Validators.BarValidator'.)
---> System.InvalidOperationException: Unable to resolve service for type 'System.String' while attempting to activate 'Application.Common.Validators.BarValidator'. at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteFactory.CreateArgumentCallSites(Type implementationType, CallSiteChain callSiteChain, ParameterInfo[] parameters, Boolean throwIfCallSiteNotFound) at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteFactory.CreateConstructorCallSite(ResultCache lifetime, Type serviceType, Type implementationType, CallSiteChain callSiteChain) at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteFactory.TryCreateExact(ServiceDescriptor descriptor, Type serviceType, CallSiteChain callSiteChain, Int32 slot) at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteFactory.GetCallSite(ServiceDescriptor serviceDescriptor, CallSiteChain callSiteChain) at Microsoft.Extensions.DependencyInjection.ServiceProvider.ValidateService(ServiceDescriptor descriptor) --- End of inner exception stack trace --- at Microsoft.Extensions.DependencyInjection.ServiceProvider.ValidateService(ServiceDescriptor descriptor) at Microsoft.Extensions.DependencyInjection.ServiceProvider..ctor(ICollection`1 serviceDescriptors, ServiceProviderOptions options) --- End of inner exception stack trace --- at Microsoft.Extensions.DependencyInjection.ServiceProvider..ctor(ICollection`1 serviceDescriptors, ServiceProviderOptions options) at Microsoft.Extensions.DependencyInjection.ServiceCollectionContainerBuilderExtensions.BuildServiceProvider(IServiceCollection services, ServiceProviderOptions options) at Microsoft.Extensions.Hosting.HostApplicationBuilder.Build() at Microsoft.AspNetCore.Builder.WebApplicationBuilder.Build() at Program.<Main>$(String[] args) in C:\Program.cs:line 9
环境信息
- .NET 7
- FluentValidation v11.2.2
- Microsoft.Extensions.DependencyInjection
已尝试的无效方法
_ = services.AddValidatorsFromAssembly(Assembly.GetExecutingAssembly(), ServiceLifetime.Transient, filter => filter.ValidatorType != typeof(BarValidator)); _ = services.AddTransient<IValidator<Bar>>(_ => new BarValidator(""));
解决方案
1. 修正子验证器的语法错误
子验证器中Must方法的调用存在语法错误,需改为Lambda表达式传递参数:
public class BarValidator: AbstractValidator<Bar> { public BarValidator(string someStringValue) { RuleFor(bar => bar.Baz) .Must(baz => BeValid(baz, someStringValue)) // 修正为Lambda表达式 .When(bar => bar.Baz != null); } private static bool BeValid(Baz baz, string someStringValue) { // 增加空判断,避免空引用异常 return baz?.ToString() == someStringValue; } }
2. 排除子验证器的自动注册
由于父验证器是手动实例化子验证器,不需要DI容器提供BarValidator的实例,因此需要阻止FluentValidation自动注册它,有两种方式:
方式一:使用过滤条件排除
在自动注册时通过filter排除BarValidator,且不需要手动注册它:
services.AddValidatorsFromAssembly( Assembly.GetExecutingAssembly(), ServiceLifetime.Transient, // 确保这里的类型是正确的BarValidator类型(注意命名空间) filter => filter.ValidatorType != typeof(BarValidator) );
方式二:使用[ValidatorIgnore]特性
给BarValidator添加FluentValidation提供的[ValidatorIgnore]特性,自动注册时会忽略该验证器:
using FluentValidation; [ValidatorIgnore] public class BarValidator: AbstractValidator<Bar> { // 代码不变 }
此时DI注册代码可简化为:
services.AddValidatorsFromAssembly(Assembly.GetExecutingAssembly(), ServiceLifetime.Transient);
关键说明
错误的核心原因是AddValidatorsFromAssembly会自动扫描并注册所有继承AbstractValidator<T>的类型,但BarValidator的构造函数需要字符串参数,DI容器无法自动解析该参数。而父验证器中是直接手动创建BarValidator实例,完全不需要DI容器介入,因此只需阻止BarValidator被注册到DI即可解决问题。
内容的提问来源于stack exchange,提问作者trymv
相关产品推荐
相关产品推荐

