In previous article(Dependency injection in ASP.NET MVC 3 action filters) we have seen how to inject dependencies in MVC Action Filters. Now let's try to inject dependency in Validation attribute. Validation attributes is used in MVC Model Validation. There are many useful built-in attributes, but often you need to write your own.
Let's imagine that we need attribute that validate that user's login(typed in registration form) is not occupied. So we write custom attribute:
Our attribute needs IRegistrator dependency to be injected. So, how to inject it? We have problem like one than we have discussed in previous article with Action Filters. And we have similar solution(as before, we use Castle Windsor IoC container):
All we need is to write custom Model Validator WindsorModelValidator that will inject dependencies in Validation attribute at some point. Example:
All magic is in this line:
We again use this extension method to inject dependencies in object. You can view code of this method in previous article about Action filters.
Let's imagine that we need attribute that validate that user's login(typed in registration form) is not occupied. So we write custom attribute:
public class LoginExistanceValidatorAttribute : ValidationAttribute
{
public IRegistrator Registrator { get ; set ; }
public override bool IsValid(object value)
{
var valueStr = value as string;
if (string.IsNullOrEmpty(valueStr))
return false;
return Registrator.CheckLoginExistance(valueStr);
}
}
Our attribute needs IRegistrator dependency to be injected. So, how to inject it? We have problem like one than we have discussed in previous article with Action Filters. And we have similar solution(as before, we use Castle Windsor IoC container):
var container = new WindsorContainer();
container.Register(Component.For<ModelValidator>().ImplementedBy<WindsorModelValidator>().LifeStyle.Transient);
DataAnnotationsModelValidatorProvider.RegisterDefaultAdapterFactory((metadata, context, attribute) => container.Resolve<ModelValidator>(new { metadata, context, attribute }));
All we need is to write custom Model Validator WindsorModelValidator that will inject dependencies in Validation attribute at some point. Example:
public class WindsorModelValidator : DataAnnotationsModelValidator
{
public IWindsorContainer Container { get ; set ; }
public WindsorModelValidator(ModelMetadata metadata, ControllerContext context, ValidationAttribute attribute): base(metadata, context, attribute)
{
}
public override IEnumerable<ModelValidationResult> Validate(object container)
{
// inject the services from the container that
// the Validation attribute requires
Container.Kernel.InjectProperties(Attribute);
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 WindsorServiceProvider(Container), null );
context.DisplayName = Metadata.GetDisplayName();
return context;
}
}
All magic is in this line:
Container.Kernel.InjectProperties(Attribute);
We again use this extension method to inject dependencies in object. You can view code of this method in previous article about Action filters.
So meny lines of code for so simple thing...
ReplyDeleteI Prefere to use public statis wrapper for IoC and resolve any interface wharever I need (If i can't do constructor injection).
You right. Your solution is much simpler. But i wanted the cleanest solution in this scenario.
ReplyDelete