Unity – Named Resolution with a Parameter Attribute

I try to limit dependencies in my code as much as possible. The less knowledge components have of each other the better. One such dependency I try to avoid is on a specific dependency injection container. Though the most popular containers generally feature the same general functionality I avoid having assembly references to a particular library in each project. I accomplish this by wrapping each external library in an abstracted library. This abstracted library has a tendency to expose only the main, core functionality leaving some of the edge cases out.

This is one such case in which I want to specify a custom attribute on a constructor parameter that specifies the name the container should use to resolve the dependency. Unity, the underlying container, already provides this through the DependencyAttribute class. The problem is that using this attribute requires a reference to the Unity assembly. In order to utilize this feature but not take another dependency on Unity I’m going to add an extension to allow me to specify my own attribute from which to pull the name used to resolve the dependency.

Step 1: Inherit from the DefaultUnityConstructorSelectorPolicy

This policy is used when resolving dependencies from the ctor. Override the CreateResolver method which takes a ParameterInfo object as a parameter. This method is called on each parameter to the ctor and with it we’ll look for the custom attribute and pull the name of the dependency being resolved. First is the custom attribute itself, it contains a single string field for the name of the dependency.

[AttributeUsage(AttributeTargets.Parameter)]
public class NamedAttribute : Attribute
{
    private readonly string name;

    public NamedAttribute(string name)
    {
        this.name = name;
    }

    public string Name { get { return name; } }
}

public class NamedAttributeConstructorSelectorPolicy
    : DefaultUnityConstructorSelectorPolicy
{
    protected override IDependencyResolverPolicy
        CreateResolver(ParameterInfo parameter)
    {
        List<NamedAttribute> attributes = 
            parameter.GetCustomAttributes(false)
                .OfType<NamedAttribute>()
                .ToList();
        if (attributes.Count > 0)
        {
            string name = attributes[0].Name;
            return new NamedTypeDependencyResolverPolicy(
                parameter.ParameterType, name);
        }

        return base.CreateResolver(parameter);
    }
}

Step 2: Add the custom policy to the context.

This is a simple class that simply adds the custom policy created in the first step to the collection of policies that default with Unity in the current context.

public class CustomUnityExtension : UnityContainerExtension
{
    protected override void Initialize()
    {
        Context.Policies
            .SetDefault<IConstructorSelectorPolicy>(
                new NamedAttributeConstructorSelectorPolicy());
    }
}

Step 3: Add the extension to the container.

Another simple snippet that adds the custom extension to the container. This taken from the ctor of the wrapper class around Unity.

public UnityApplicationBlockContainer(IUnityContainer container)
{
    this.container = container;

    container.AddNewExtension<CustomUnityExtension>();
}

With these 3 steps in place you can start marking up the parameters within the ctors of services.

public class Service
{
    private readonly IDatabase sqlDatabase;
    private readonly IDatabase couchDatabase;

    public Service([Named("SqlDb")]IDatabase sqlDatabase, 
        [Named("CouchDb")]IDatabase couchDatabase)
    {
        this.sqlDatabase = sqlDatabase;
        this.couchDatabase = couchDatabase;
    }
}
Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s