I was recently working on an MVC application and I ran into a problem.  I was creating a ValidationAttribute and I needed to have access to my repository in this attribute.  The use case is that I want to add a release of an application, and I want to make sure that the ID being passed in is actually a valid application.  My first pass at this was to try and use property injection, like this:

public class ApplicationIDValidAttribute : ValidationAttribute
{
    [Inject]
    public IRepository<Application> AppRepo { set; private get; }

    public override bool IsValid(object value)
    {
        // Don't force required here, they can use the required attribute.
        if (value == null)
        {
            return true;
        }

        int appID;
        if (!Int32.TryParse(value.ToString(), out appID))
        {
            return false;
        }

        // This did not work, AppRepo was not injected
        var app = AppRepo.LoadApplicationById(appID);

        return (app != null);
    }
}

The problem here is that because of the way attributes are instantiated, this injection method does not work.  In the above code sample, when this gets executed my AppRepo is null.

Service Locator


The solution to my problem ended up being the use of a service locator to handle my injection.  I could have went with the Common Service Locator library, but that isn’t exactly what I want.  I already know what DI container I want to use, so completely abstracting that layer away isn’t what I need.  I ended up creating a library to handle the wiring up of Ninject, and to basically act as a wrapper to access the Ninject kernel. 

public static class Container
{
    private static IKernel _kernel;

    public static void Initialize(IKernel kernel)
    {
        _kernel = kernel;
    }

    public static T Get<T>()
    {
        return _kernel.Get<T>();
    }

    public static object Get(Type type)
    {
        return _kernel.Get(type);
    }
}


Then, I could wire up my service locator in my global.asax file, like this:

// This is in my global.asax.cs file
// Required because I inherited from NinjectHttpApplication
protected override IKernel CreateKernel()
{
    var kernel =  new StandardKernel(new DataModule());

    // Gives my wrapper class access to the kernel instance
    Container.Initialize(kernel);

    return kernel;
}

// This is my data module for reference.
public class DataModule : NinjectModule
{
    public override void Load()
    {
        Bind(typeof (IRepository<>)).To(typeof (Repository<>));
    }
}

Then, to close the loop, I can wire up my attribute like this:

public class ApplicationIDValidAttribute : ValidationAttribute
{
    public override bool IsValid(object value)
    {
        // Don't force required here, they can use the required attribute.
        if (value == null)
        {
            return true;
        }

        int appID;
        if (!Int32.TryParse(value.ToString(), out appID))
        {
            return false;
        }

        var repo = Container.Get<IRepository<Application>>();
        var app = repo.LoadApplicationById(appID);

        return (app != null);
    }
}

Don’t Abuse It


I try to only use my service locator container when I have to.  I use standard property or constructor injection whenever possible, but this does provide a nice way to be able to inject into my ValidationAttribute classes.

kick it on DotNetKicks.com