В FluentValidation есть расширение или какой-либо другой способ отложить выбор дочернего валидатора в зависимости от типа/значения проверяемого свойства?Отложить выбор дочернего валидатора в зависимости от типа или значения свойства
Моя ситуация в том, что у меня есть класс уведомлений, который я хочу проверить. Этот класс имеет свойство Payload, которое может быть одним из нескольких типов полезных нагрузок, например. SmsPayload, EmailPayload и т. Д. Каждый из этих подклассов Payload имеет свой собственный связанный валидатор, например. SmsPayloadValidator и EmailPayloadValidator соответственно. В дополнение к вышесказанному, нет ссылок от основной библиотеки (ов) на отдельных поставщиков уведомлений. По сути, это означает, что я могу добавить поставщиков по мере необходимости и подключить все, используя IoC.
Рассмотрим следующие классы:
public class Notification
{
public Payload Payload { get; set; }
public IEnumerable<string> Details { get; set; }
}
public abstract class Payload
{
public string Message { get; set; }
public abstract string Type { get; }
}
public class SmsPayload : Payload
{
public List<string> Numbers { get; set; }
public string Region { get; set; }
public string Provider { get; set; }
}
Существует уведомления валидатор и SmsPayloadValidator следующим образом:
public class NotificationValidator : AbstractValidator<Notification>
{
public NotificationValidator(IValidator<Payload> payloadValidator)
{
RuleFor(notification => notification.Payload).NotNull().WithMessage("Payload cannot be null.");
RuleFor(notification => notification.Payload).SetValidator(payloadValidator);
}
}
public class SmsPayloadValidator : AbstractValidator<SmsPayload>
{
public SmsPayloadValidator()
{
RuleFor(payload => payload.Provider)
.Must(s => !string.IsNullOrEmpty(s))
.WithMessage("Provider is required.");
RuleFor(payload => payload.Numbers)
.Must(list => list != null && list.Any())
.WithMessage("Sms has no phone numbers specified.");
RuleFor(payload => payload.Region)
.Must(s => !string.IsNullOrEmpty(s))
.WithMessage("Region is required.");
}
}
Как я уже упоминал узел, где NotificationValidator это не ссылки на узлы, где человек Классы валидатора полезной нагрузки живут. Все проводки позаботились Ioc (Simple-Injector для этого проекта).
В основном я хочу сделать что-то вроде следующего - первый регистрирующим заводского обратного вызова в простом Injector:
container.Register<Func<Payload, IValidator<Payload>>>(() => (payload =>
{
if (payload.GetType() == typeof(SmsPayload))
{
return container.GetInstance<ISmsPayloadValidator>();
}
else if (payload.GetType() == typeof(EmailPayload))
{
return container.GetInstance<IEmailPayloadValidator>();
}
else
{
//something else;
}
}));
Такое, что я могу выбрать подходящий валидатор следующим образом:
public class NotificationValidator : AbstractValidator<Notification>
{
public NotificationValidator(Func<Payload, IValidator<Payload>> factory)
{
RuleFor(notification => notification.Payload).NotNull().WithMessage("Payload cannot be null.");
RuleFor(notification => notification.Payload).SetValidator(payload => factory.Invoke(payload));
}
}
Любого предложения? или есть лучший способ сделать то, что я предлагаю? Если нет, я открою репозиторий FluentValidation и отправлю PR.
+1 для использования композитного материала. Однако я считаю, что ваша реализация является более сложной, чем необходимо. См. Мой ответ для альтернативного подхода. – Steven
Спасибо за upvote. Однако я бы сказал, что мой подход не более «сложный», чем ваш. Более утомительно, да. Возможно, непрактично, как вы говорите в своем ответе, но только в зависимости от намерений исполнителя. –
Хотя немного сложнее, он немного более явный, и я работал. Благодаря! –