Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Data Annotations Constructor Injection

With ASP.NET MVC, is it possible to use constructor injection with the data annotation attributes (specifically, I'm using validation attributes)?

What I would like to be able to do is:

public class NoEmailTakenAttribute : ValidationAttribute
{
    public NoEmailTakenAttribute(IService service) { .. }
}

Is that possible?

Thanks.

like image 970
Brian Mains Avatar asked Apr 14 '26 19:04

Brian Mains


2 Answers

You can't use Controller injection from what I see using Reflector, but it does appear possible to use property injection. By creating a class that inherits from DataAnnotationsModelValidatorProvider, and by overriding the method GetValidators, it seems plausible that the attributes can be property injected into before the validation happens... this is from an initial analysis, yet to be fully determined.

like image 167
Brian Mains Avatar answered Apr 16 '26 09:04

Brian Mains


The solution proposed by Brian Mains should work fine. I don't think constructor injection is an option here but property injection will do the job. You can derive from ModelValidatorProvider and your implementation might look similiar to this:

public class MyModelValidatorProvider : ModelValidatorProvider
{
    private IDiContainer _container;

    public MyModelValidatorProvider(IDiContainer container)
    {
        _container = container;
    }

    public override IEnumerable<ModelValidator> GetValidators(ModelMetadata metadata, ControllerContext context)
    {
        List<ModelValidator> validators = new List<ModelValidator>();

        PropertyInfo targetProperty = metadata.ContainerType.GetProperty(metadata.PropertyName);
        if (targetProperty.GetCustomAttributes(false).Any(attr => attr.GetType() == typeof(NoEmailTakenAttribute)))
        {
            DataAnnotationsModelValidator<NoEmailTakenAttribute> validator = new DataAnnotationsModelValidator<NoEmailTakenAttribute>(
                metadata, context, _container.Resolve<NoEmailTakenAttribute>());

            validators.Add(validator);
        }

        return validators;
    }
}

I did not look closely into the ModelMetadata and just used refelction to decide whether to return the validator or not but it probably can be done better.

Then in the Global.asax add the following:

ModelValidatorProviders.Providers.Add(new MyModelValidatorProvider(InstanceOfContainer));

and you should be good to go. The only problem here is that your validator will get created by the default mechanism as well. Obviously this will result in your validator not having the proper dependencies injected. I have no idea how to exclude a validator from default creation but if you properly check against null values inside your validator it should work fine (a bit of a workaround i must say but maybe you'll find a better way).

like image 24
dmusial Avatar answered Apr 16 '26 07:04

dmusial