ОК, я применил предложенное выше для контейнера Unity DI.
Вот новая модель Validator
public class UnityModelValidator : DataAnnotationsModelValidator
{
private readonly IUnityContainer unityContainer;
public UnityModelValidator(ModelMetadata metadata,
ControllerContext context,
ValidationAttribute attribute)
: base(metadata, context, attribute)
{
this.unityContainer = DependencyResolver.Current.GetService<IUnityContainer>();
}
public override IEnumerable<ModelValidationResult> Validate(object container)
{
try
{
unityContainer.BuildUp(Attribute.GetType(), Attribute);
}
catch (ResolutionFailedException ex)
{
//Don't understand why it sometimes tries to use Unity to create an attribute rather than just build up an existing object. If this happens it can fail but we want to ignore it.
}
ValidationContext context = CreateValidationContext(container);
ValidationResult result = Attribute.GetValidationResult(Metadata.Model, context);
if (result != ValidationResult.Success)
{
yield return new ModelValidationResult
{
Message = result.ErrorMessage
};
}
}
protected virtual ValidationContext CreateValidationContext(object container)
{
var context = new ValidationContext(container ?? Metadata.Model, new UnityServiceLocator(unityContainer), null);
context.DisplayName = Metadata.GetDisplayName();
return context;
}
}
Как и выполнение Buildup на атрибуте он также добавляет контейнер в качестве ServiceProvider к контексту, поэтому он может быть использован непосредственно в атрибуте. Я знаю, что это анти-шаблон, и я не хочу использовать его сам, но он включен здесь для полноты.
Вот код единства конфигурации
container.RegisterType<ModelValidator, UnityModelValidator>(new TransientLifetimeManager());
DataAnnotationsModelValidatorProvider.RegisterDefaultAdapterFactory(
(metadata, context, attribute) => container.Resolve<ModelValidator>(new ParameterOverrides() { { "metadata", metadata }, { "context", context }, { "attribute", attribute } }));
Наконец это означает, что мы можем строить наши проверки атрибутов как этот
public class InitialsMustBeAvailableAttribute : ValidationAttribute
{
[Dependency]
public IUserService UserService { get; set; }
public override bool IsValid(object value)
{
return UserService.AreInitialsAvailable((string)value);
}
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
var userService = (IUserService)validationContext.GetService(typeof(IUserService));
if (userService != null)
{
if (userService.AreInitialsAvailable((string) value))
{
return null;
}
}
return new ValidationResult("Initals must be unique!");
}
}
Пожалуйста, обратите внимание, что вы на самом деле только использовать один из этих методов. Первый использует UserService, который был введен, второй использует ServiceLocator в контексте, чтобы захватить его сам.
Я предпочитаю первый.
Приветствия Майк
Посмотрите на [этот ответ на CodePlex] (https://simpleinjector.codeplex.com/discussions/472187#post1137461). В нем говорится об извлечении логики из атрибута и размещении его внутри службы. Хотя речь идет о веб-API и Simple Injector, я думаю, вы найдете это полезным, поскольку решение для Unity будет почти таким же. – Steven