W wersji FluentValidation istnieje rozszerzenie lub inny sposób odroczenia wyboru walidatora podrzędnego w zależności od typu/wartości sprawdzanej właściwości?Odroczyć wybór walidatora podrzędnego w zależności od typu/wartości właściwości
Moja sytuacja polega na tym, że mam klasę powiadomień, którą chcę sprawdzić. Ta klasa ma właściwość Ładunek, który może być jednym z wielu typów Ładunków, np. SmsPayload, EmailPayload itp. Każda z tych podklas ładunków ma swój powiązany walidator np. SmsPayloadValidator i EmailPayloadValidator odpowiednio. Oprócz powyższego, nie ma odniesień z głównej biblioteki (bibliotek podstawowych) do poszczególnych dostawców powiadomień. Zasadniczo oznacza to, że mogę dodawać dostawców w miarę potrzeb i wszystko podłączać za pomocą IoC.
Rozważmy następujące klasy:
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; }
}
Jest walidator Powiadamianie i SmsPayloadValidator następująco:
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.");
}
}
Jak wspomniałem zespół gdzie NotificationValidator jest nie odwoływać zespoły gdzie poszczególne Klasy walidatorów Payload na żywo. Wszystkie okablowanie jest obsługiwane przez Ioc (Simple-Injector dla tego projektu).
Zasadniczo chcę zrobić coś jak następuje - pierwszy rejestrując zwrotnego fabryki w prosty 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;
}
}));
taka, że mogę wybrać odpowiedni walidator następująco:
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));
}
}
Any propozycje? czy jest lepszy sposób na robienie tego, co proponuję? Jeśli nie, rozwiążę repozytorium FluentValidation i prześlę PR.
+1 za użycie kompozytu. Uważam jednak, że twoja implementacja jest bardziej złożona niż potrzebna. Zobacz moją odpowiedź na alternatywne podejście. – Steven
Dzięki za przegraną. Twierdzę jednak, że moje podejście nie jest bardziej "złożone" niż twoje. Bardziej nużący, tak. Być może niepraktyczne, jak wspominasz w swojej odpowiedzi, ale tylko w zależności od intencji realizatora. –
Choć nieco bardziej skomplikowany, jest trochę bardziej wyraźny i zadziałało. Dzięki! –