Fluent Validator WithMessage 和单例实例
Fluent Validator WithMessage and singleton instance
在我工作的 MVC 项目中,我使用 Fluent Validation 来实现验证逻辑和 unity 作为依赖注入容器
在我的验证器中 class 有一些复杂的业务验证规则
public class ServicerRequestViewModelValidator : AbstractValidator<ServiceRequestViewModel>
{
public ServiceRequestViewModelValidator(ILocalizationService localizationService)
{
RuleFor(x => x.IdStato).NotNull().WithMessage(string.Format(localizationService.GetMessage("Validation.General.MandataryField"), localizationService.GetMessage("ServiceRequestDetail.State")));
// other business validations rule with localized error message
}
}
该规则设置根据用户语言本地化的错误消息
杰里米斯金纳说:
Instantiation of validators is an expensive process due to the
expression tree compilation and parsing within the RuleFor
definitions. Because of this, it's recommended that you use validator
instances as singletons- once instantiated they should be cached and
reused, rather than being instantiated multiple times.
Validators do not contain any shared state, so it should also be safe
to reuse them in multithreaded scenarios too. The best approach to
caching the validator instances would be to use an IoC container (eg,
StructureMap) to manage the instance lifecycles.
所以我在容器中注册了带有 ContainerControlledLifetimeManager 的验证器(单例)
container.RegisterType<IValidator<ServiceRequestViewModel>, ServiceRequestViewModelValidator>(new ContainerControlledLifetimeManager());
但是这样做出现了一个问题:
第一次 ServiceRequestViewModelValidator
解析时,构造函数被执行,本地化的错误消息将根据用户的语言缓存,后续用户将根据实例化单例的用户的语言获得本地化的消息 class.
我创建了一个新的 WithMessage
扩展方法,它接收一个字符串的委托,利用 LazyStringSource
class
public static IRuleBuilderOptions<T, TProperty> WithMessage<T, TProperty>(this IRuleBuilderOptions<T, TProperty> rule, Func<string> errorMessage)
{
return rule.Configure((Action<PropertyRule>)(config =>
{
config.CurrentValidator.ErrorMessageSource = (IStringSource)new LazyStringSource(errorMessage);
}));
}
然后我像这样更改了我的 ServiceRequestValidator:
public class ServicerRequestViewModelValidator : AbstractValidator<ServiceRequestViewModel>
{
public ServiceRequestViewModelValidator(ILocalizationService localizationService)
{
RuleFor(x => x.IdStato).NotNull().WithMessage(()=>string.Format(localizationService.GetMessage("Validation.General.MandataryField"), localizationService.GetMessage("ServiceRequestDetail.State")));
}
}
在构造函数中这样做是设置将在验证过程中调用的委托,以根据用户语言解析本地化错误消息,而不是直接解析本地化错误消息字符串。
在我工作的 MVC 项目中,我使用 Fluent Validation 来实现验证逻辑和 unity 作为依赖注入容器
在我的验证器中 class 有一些复杂的业务验证规则
public class ServicerRequestViewModelValidator : AbstractValidator<ServiceRequestViewModel>
{
public ServiceRequestViewModelValidator(ILocalizationService localizationService)
{
RuleFor(x => x.IdStato).NotNull().WithMessage(string.Format(localizationService.GetMessage("Validation.General.MandataryField"), localizationService.GetMessage("ServiceRequestDetail.State")));
// other business validations rule with localized error message
}
}
该规则设置根据用户语言本地化的错误消息
杰里米斯金纳说:
Instantiation of validators is an expensive process due to the expression tree compilation and parsing within the RuleFor definitions. Because of this, it's recommended that you use validator instances as singletons- once instantiated they should be cached and reused, rather than being instantiated multiple times.
Validators do not contain any shared state, so it should also be safe to reuse them in multithreaded scenarios too. The best approach to caching the validator instances would be to use an IoC container (eg, StructureMap) to manage the instance lifecycles.
所以我在容器中注册了带有 ContainerControlledLifetimeManager 的验证器(单例)
container.RegisterType<IValidator<ServiceRequestViewModel>, ServiceRequestViewModelValidator>(new ContainerControlledLifetimeManager());
但是这样做出现了一个问题:
第一次 ServiceRequestViewModelValidator
解析时,构造函数被执行,本地化的错误消息将根据用户的语言缓存,后续用户将根据实例化单例的用户的语言获得本地化的消息 class.
我创建了一个新的 WithMessage
扩展方法,它接收一个字符串的委托,利用 LazyStringSource
class
public static IRuleBuilderOptions<T, TProperty> WithMessage<T, TProperty>(this IRuleBuilderOptions<T, TProperty> rule, Func<string> errorMessage)
{
return rule.Configure((Action<PropertyRule>)(config =>
{
config.CurrentValidator.ErrorMessageSource = (IStringSource)new LazyStringSource(errorMessage);
}));
}
然后我像这样更改了我的 ServiceRequestValidator:
public class ServicerRequestViewModelValidator : AbstractValidator<ServiceRequestViewModel>
{
public ServiceRequestViewModelValidator(ILocalizationService localizationService)
{
RuleFor(x => x.IdStato).NotNull().WithMessage(()=>string.Format(localizationService.GetMessage("Validation.General.MandataryField"), localizationService.GetMessage("ServiceRequestDetail.State")));
}
}
在构造函数中这样做是设置将在验证过程中调用的委托,以根据用户语言解析本地化错误消息,而不是直接解析本地化错误消息字符串。