Introducing Action Injection with Autofac ASP.NET MVC Integration

by Alex Meyer-Gleaves 16 May 2010 - 2:33 AM

There are currently two main approaches to performing dependency injection, Constructor Injection and Setter Injection. The more popular of the two approaches is Constructor Injection. The dependencies that a type has are made obvious because they must be supplied in order to construct an instance. This also makes it easier for you to ensure that a newly instantiated object is in a valid state. When working with a type the constructor is usually the first thing that you come into contact with.

With Setter Injection, also known as Property Injection, it is much more difficult to tell what the dependencies are when looking at the type from the outside. Setter Injection is most useful when you have no control over the instantiation of the type that requires the dependencies to be injected. This is a common scenario for ASP.NET WebForms where the activation of a Page instance is performed by the runtime. You do not have an opportunity to take over the activation process, and the first chance you have to perform dependency injection is when you are provided with an existing instance of Page. In this case you have no choice but to inject the dependencies into the type via its properties.

ASP.NET MVC has many extensibility points and is very flexible. It provides you with the opportunity to take over the creation of your Controller instances by creating your own factory that implements IControllerFactory, or more commonly by deriving from the DefaultControllerFactory and overriding the GetControllerInstance method. This makes it possible for your controllers to take advantage of Constructor Injection, and is exactly what the Autofac ASP.NET MVC Integration does. When it comes to unit testing your controller classes, it becomes very easy to see what dependencies it has, and to provide mock implementations for those dependencies.

An issue that is often raised in regards to Constructor Injection is what some people like to call Constructor Bloat. This may indicate that you are not following the Single Responsibility Principle and that some refactoring may be in order. The number of constructor parameters that would be considered too many would no doubt vary depending on who you ask. In the case of ASP.NET MVC controllers the number of constructor dependencies is more likely to be higher than for other classes. The level of responsibility for a controller is usual greater than what you would expect for an ordinary internal component. This is the result of mapping an external view of the application (URL based) onto an internal representation (controller based).

It turns out that both Nicholas Blumhardt and I found ourselves shifting some of these dependencies out of the controller’s constructor and into the action methods that actually require them. We were both fairly surprised to find out that the other had independently been doing exactly the same thing, and at this point discussed if there was something wrong with the approach because it seemed that no one else was doing it. Surely all good ideas have already been done so this one must be bad. I personally feel that having dependencies injected into your action method should not feel like a foreign concept because that is exactly what MVC is already doing for you with your existing parameters.

For lack of any official term that I am aware of, Action Injection is what I am calling this particular approach to dependency injection in ASP.NET MVC. The more I play around with this approach the more I like it. Your constructor is provided the dependencies that are shared by all actions in your controller, and each individual action can request any additional dependencies that it needs. Now when writing unit tests for your actions there is no need to provide mock implementations for dependencies that your action will not be interacting with. The end result is less mocks in your unit tests and a clear indication of the action’s actual dependencies.

Nick and I have decided to test out the idea of Action Injection in the Autofac ASP.NET MVC Integration. The changes are only in the source code at the moment and have not yet been included in a release. I mentioned earlier that MVC is very extensible and the process for invoking your action methods is no different. It is possible to replace the default behaviour by creating your own IActionInvoker. The easiest way to do this is by deriving from the AsyncControllerActionInvoker class and overriding the appropriate methods. A controller can be requested to use your custom action invoker by assigning an instance to the controller's ActionInvoker property. The current source includes a registration extension that allows you to register an IActionInvoker instance that will be assigned to a controller as it is activated. There is a default IActionInvoker implementation called ExtensibleActionInvoker that allows dependencies to be injected into your action methods. It can also do Setter Injection on your filters but that is a topic for another post. As the name suggests, you can extend this class and add any additional behaviour that you require. Registering controllers in the HttpApplication start would look something like this.

ContainerBuilder builder = new ContainerBuilder();

builder.RegisterType<ExtensibleActionInvoker>().As<IActionInvoker>();
builder.RegisterControllers(Assembly.GetExecutingAssembly()).InjectActionInvoker();

// Register other services.

IContainer container = builder.Build();
_containerProvider = new ContainerProvider(container);

ControllerBuilder.Current.SetControllerFactory(new AutofacControllerFactory(_containerProvider));

I will not go into further detail on the implementation at this point because it may be tweaked a little before being released. Instead, let us look at an example of how we could make our action dependencies clearer using Action Injection. The NotifyController class below has action methods that send the current user a message using different delivery methods.

public class NotifyController
{
    public NotifyController(ILogger logger, 
        IEmailNotifier emailNotifier, 
        ISmsNotifier smsNotifier, 
        IMessengerNotifier messengerNotifier)
    {
        // Implementation.
    }
    
    public ActionResult Email(string message)
    {
        // Implementation.
    }
    
    public ActionResult Sms(string message)
    {
        // Implementation.
    }

    public ActionResult Messenger(string message)
    {
        // Implementation.
    }
}

There are three action methods on this controller and four dependencies that must be provided through the constructor. To unit test any of the action methods all four of the dependencies will need to be mocked. In this controller the ILogger instance is required by all action methods, but the remaining notifier dependencies are each required only by one action method. The controller could be refactored so that it takes the one ILogger dependency through its constructor, and each action could take its particular notifier dependency through a method parameter. Here is an example of how the refactored code would look.

public class NotifyController
{
    public NotifyController(ILogger logger)
    {
        // Implementation.
    }
    
    public ActionResult Email(string message, IEmailNotifier emailNotifier)
    {
        // Implementation.
    }
    
    public ActionResult Sms(string message, ISmsNotifier smsNotifier)
    {
        // Implementation.
    }

    public ActionResult Messenger(string message, IMessengerNotifier messengerNotifier)
    {
        // Implementation.
    }
}

Now when testing the action methods we only ever need to provide two mock services. There is no need to provide additional mock services that will never be used. Assuming we only had one unit test per action and setup our mocks inside each unit test, we would have halved the number of mocks required, taking the total from twelve down to six. That certainly seems like an improvement to me.

I would be interested to know what you think about this idea. Is it totally crazy or could there be something to it? Maybe you too have already been doing this and could share how it has been working out for you.

Tags: ,

Categories: Autofac | Web Development

Making Self-Hosting with Autofac WCF Integration easier

by Alex Meyer-Gleaves 16 May 2010 - 1:48 AM

Thinking about the sample I recently posted for shelf-hosting WCF Services with the Autofac WCF Integration, I decided that the boilerplate code for configuring the Service Behavior could be moved into an extension method on the ServiceHost instead. I have checked in some code that will extend ServiceHostBase with a new method called AddDependencyInjectionBehavior. There are two overloads of the method, one that takes a generic argument for the service contract type, and another that allows you to provide a Type for the service contract in case you are configuring your WCF Services in some sort of latebound manner. Both overloads of the method require an IContainer instance.

host.AddDependencyInjectionBehavior<IEchoService>(container);
host.AddDependencyInjectionBehavior(typeof(IEchoService), container);

As you can see from the updated sample below, the extension method makes the code considerably more concise, and will save you from having to write your own helper method that can be reused with your different WCF Services.

ContainerBuilder builder = new ContainerBuilder();
builder.Register(c => new Logger()).As<ILogger>();
builder.Register(c => new EchoService(c.Resolve<ILogger>())).As<IEchoService>();

using (IContainer container = builder.Build())
{
    Uri address = new Uri("http://localhost:8080/EchoService");
    ServiceHost host = new ServiceHost(typeof(EchoService), address);

    host.AddServiceEndpoint(typeof(IEchoService), new BasicHttpBinding(), string.Empty);

    host.AddDependencyInjectionBehavior<IEchoService>(container);

    host.Description.Behaviors.Add(new ServiceMetadataBehavior {HttpGetEnabled = true, HttpGetUrl = address});
    host.Open();
    
    Console.WriteLine("The host has been opened.");
    Console.ReadLine();

    host.Close();
    Environment.Exit(0);
}

The extension method will be available in the next release of Autofac. If you want to try it out without grabbing the latest Autofac source, the code below should give you a feel for how it works. You do not need to provide the service implementation type to the extension method because it can be retrieved from the ServiceHost instance. The Type instance that you provide in the ServiceHost constructor surfaces as the ServiceHost.Description.ServiceType property and can be used directly in the extension method.

public static class ServiceHostExtensions
{
    public static void AddDependencyInjectionBehavior<T>(this ServiceHostBase serviceHost, IContainer container)
    {
        AddDependencyInjectionBehavior(serviceHost, typeof(T), container);
    }

    public static void AddDependencyInjectionBehavior(this ServiceHostBase serviceHost, Type contractType, IContainer container)
    {
        IComponentRegistration registration;
        if (!container.ComponentRegistry.TryGetRegistration(new TypedService(contractType), out registration))
        {
            string message = string.Format("The service contract type '{0}' has not been registered in the container.", contractType.FullName);
            throw new ArgumentException(message);
        }

        AutofacDependencyInjectionServiceBehavior behavior = new AutofacDependencyInjectionServiceBehavior(
            container, serviceHost.Description.ServiceType, registration);
        serviceHost.Description.Behaviors.Add(behavior);
    }
}

Nothing fancy here but that should make self-hosting a little easier.

Tags: ,

Categories: Autofac | Web Services

Self-Hosting WCF Services with the Autofac WCF Integration

by Alex Meyer-Gleaves 7 May 2010 - 12:14 AM

A question came up recently in the Autofac group about how to use the WCF Integration when self-hosting WCF Services. This post provides a quick demonstration of how to handle the self-hosting scenario and should be enough to get you started. The example is a rather unimaginative web service that echoes back a message.

First declare the interface and implementation for a logger that the WCF Service will take as a dependency in its constructor. This is a simple logger that will log the message sent to the WCF Service out to the console.

public interface ILogger
{
    void Write(string message);
}

public class Logger : ILogger
{
    public void Write(string message)
    {
        Console.WriteLine(message);
    }
}

Next you need to define the contract and implementation for the WCF Service. Nothing interesting here other than our dependency being requested through the constructor of the WCF Service implementation.

[ServiceContract]
public interface IEchoService
{
    [OperationContract]
    string Echo(string message);
}

public class EchoService : IEchoService
{
    private readonly ILogger _logger;

    public EchoService(ILogger logger)
    {
        _logger = logger;
    }

    public string Echo(string message)
    {
        _logger.Write(message);
        return message;
    }
}

Now we can create a Console Application that will configure the container and host our WCF Service.

ContainerBuilder builder = new ContainerBuilder();
builder.Register(c => new Logger()).As<ILogger>();
builder.Register(c => new EchoService(c.Resolve<ILogger>())).As<IEchoService>();

using (IContainer container = builder.Build())
{
    Uri address = new Uri("http://localhost:8080/EchoService");
    ServiceHost host = new ServiceHost(typeof(EchoService), address);
    host.AddServiceEndpoint(typeof(IEchoService), new BasicHttpBinding(), string.Empty);

    IComponentRegistration registration;
    if (!container.ComponentRegistry.TryGetRegistration(new TypedService(typeof(IEchoService)), out registration))
    {
        Console.WriteLine("The service contract has not been registered in the container.");
        Console.ReadLine();
        Environment.Exit(-1);
    }

    host.Description.Behaviors.Add(new AutofacDependencyInjectionServiceBehavior(container, typeof(EchoService), registration));
    host.Description.Behaviors.Add(new ServiceMetadataBehavior {HttpGetEnabled = true, HttpGetUrl = address});
    host.Open();
    
    Console.WriteLine("The host has been opened.");
    Console.ReadLine();

    host.Close();
    Environment.Exit(0);
}

Here we have added an IServiceBehavior called AutofacDependencyInjectionServiceBehavior that will add a custom IInstanceProvider to the DispatchRuntime of each endpoint’s EndpointDispatcher. That is a bit of a mouth full but basically means that Autofac will use the WCF extensibility points to provide WCF Service instances that have their dependencies injected.

In this example the WCF Service is exposed on an endpoint that uses the BasicHttpBinding but you can use any type of endpoint that you like. I have added a ServiceMetadataBehavior in this example that exposes the WSDL for the WCF Service at http://localhost:8080/EchoService?wsdl. You can use this address to create a client proxy and send test messages that are hopefully more creative than my example.

Tags: ,

Categories: Autofac | Web Services

Autofac 2.0 Podcast

by Alex Meyer-Gleaves 6 April 2010 - 12:47 AM

Make sure you have a listen to Nicholas Blumhardt talking about Autofac 2 on the Talking Shop Down Under podcast.

It's Inversion of Control Time! This week Nick Blumhardt talks about IoC containers in general, the problems with the service locator pattern and why constructor injection is considered a better choice, a little about how MEF and Autofac differ in purpose and a lot about Autofac 2's new features.

Nick was also a guest on .NET Rocks! last year, and that episode is definitely worth listening to as well.

The .NET dudes talk to Nicholas Blumhardt about Autofac, an IoC container that uses lambda expressions in C# 3.0 to create components.

Keep spreading the good word Nick!

Tags:

Categories: Autofac

Registering open generic types in Autofac 1.4

by Alex Meyer-Gleaves 3 January 2010 - 12:34 AM

UPDATE (5 January 2010): This feature has now been added to the Autofac 1.4 codebase. I had intended to get this one directly into the codebase but Nick and I got our wires crossed, and I ended up posting it as an extension instead. Regardless, this post remains a valid example of extending Autofac 1.4. The RegisterClosedTypesOf method will appear on the ContainerBuilder in the next 1.4 maintenance release. Until then you can use the extension below to register your open generic types.

This is a follow up to my recent post about writing an extension for registering open generic interface types in Autofac 2. The feature described in the first post, along with support for open generic classes, has since been added to the Autofac 2 codebase. You can grab the current 2.1 preview release on the download page and test it out.

I personally feel that the latest preview version of Autofac 2 is stable enough to start using, but I know that even when released not everyone will be able to adopt the new version as soon as they would like. For that reason I have decided to write a similar extension for Autofac 1.4 that supports both open generic interfaces and classes.

Before I move onto the code I would like to draw a distinction between this feature and the RegisterGeneric method found on instances of the the ContainerBuilder class in Autofac 1.4. The RegisterGeneric method allows you to register an open generic type and have a closed generic type created for you when requested. For example, registering List<> and then resolving List<string> will cause Autofac to create a new List<string> instance for you. The difference is that the RegisterGeneric feature does not locate existing types that close the open generic type being registered.

In the unit test below we are ensuring that a closing type is provided for an open generic interface. It is similar to that from the original post, except the extension method is called RegisterClosedTypesOf and extends ContainerBuilder instances instead of RegistrationBuilder instances. The extension method also has a parameter for the assembly that will be scanned to find the closing types. This is different from the Autofac 2 implementation were the assembly is provided to the RegisterAssemblyTypes method, and the containing types are filtered using a delegate provided to the RegistrationBuilder.

[Test]
public void RegisterClosedTypesOf_OpenGenericInterfaceTypeProvided_ClosingGenericTypesRegistered()
{
    ContainerBuilder builder = new ContainerBuilder();
    Assembly assembly = typeof(ICommand<>).Assembly;
    builder.RegisterClosedTypesOf(typeof(ICommand<>), assembly);
    IContainer container = builder.Build();

    Assert.That(container.Resolve<ICommand<SaveCommandData>>(), Is.InstanceOf<SaveCommand>());
    Assert.That(container.Resolve<ICommand<DeleteCommandData>>(), Is.InstanceOf<DeleteCommand>());
}

In addition to the types I used for unit testing in the previous post, there are two new types used for the testing of open generic classes.

/// <summary>
/// An abstract open generic base class.
/// </summary>
public abstract class Message<T>
{
}

/// <summary>
/// A class that closed the open generic type.
/// </summary>
public class StringMessage : Message<string>
{
}

The new message types are used in the next unit test to make sure that support for open generic classes is working.

[Test]
public void RegisterClosedTypesOf_OpenGenericAbstractClassTypeProvided_ClosingGenericTypesRegistered()
{
    ContainerBuilder builder = new ContainerBuilder();
    Assembly assembly = typeof(Message<>).Assembly;
    builder.RegisterClosedTypesOf(typeof(Message<>), assembly);
    IContainer container = builder.Build();

    Assert.That(container.Resolve<Message<string>>(), Is.InstanceOf<StringMessage>());
}

Now that we know how the extension method is used we can move onto the implementation.

/// <summary>
/// Extension methods for the <see cref="ContainerBuilder"/> class.
/// </summary>
public static class ContainerBuilderExtensions
{
    /// <summary>
    /// Scans the types in an assembly and registers those that support any base or interface that closes the 
    /// provided open generic service type.
    /// </summary>
    /// <param name="builder">The container builder being extended.</param>
    /// <param name="openGenericServiceType">The open generic interface or base class type for which implementations will be found.</param>
    /// <param name="assembly">The assembly to scan for the matching types.</param>
    public static void RegisterClosedTypesOf(this ContainerBuilder builder, Type openGenericServiceType, Assembly assembly)
    {
        if (openGenericServiceType == null) throw new ArgumentNullException("openGenericServiceType");

        if (!(openGenericServiceType.IsGenericTypeDefinition || openGenericServiceType.ContainsGenericParameters))
        {
            throw new ArgumentException(
                string.Format("The type '{0}' is not an open generic class or interface type.",
                              openGenericServiceType.FullName));
        }

        foreach (Type candidateType in assembly.GetTypes())
        {
            Type closedServiceType;
            if (findAssignableTypeThatCloses(candidateType, openGenericServiceType, out closedServiceType))
            {
                builder.Register(candidateType).As(closedServiceType);
            }
        }
    }

    /// <summary>
    /// Looks for an interface on the candidate type that closes the provided open generic interface type.
    /// </summary>
    /// <param name="candidateType">The type that is being checked for the interface.</param>
    /// <param name="openGenericServiceType">The open generic service type to locate.</param>
    /// <param name="closedServiceType">The type of the closed service if found.</param>
    /// <returns>True if a closed implementation was found; otherwise false.</returns>
    private static bool findAssignableTypeThatCloses(Type candidateType, Type openGenericServiceType, out Type closedServiceType)
    {
        closedServiceType = null;

        if (candidateType.IsAbstract) return false;

        foreach (Type interfaceType in getTypesAssignableFrom(candidateType))
        {
            if (interfaceType.IsGenericType && interfaceType.GetGenericTypeDefinition() == openGenericServiceType)
            {
                closedServiceType = interfaceType;
                return true;
            }
        }

        return false;
    }

    /// <summary>
    /// Returns the interface and base types that given a type is assignable from.
    /// </summary>
    /// <param name="candidateType">The type to find assignable types for.</param>
    /// <returns>A list of the assignable interface and base types.</returns>
    private static IEnumerable<Type> getTypesAssignableFrom(Type candidateType)
    {
        foreach (Type interfaceType in candidateType.GetInterfaces())
        {
            yield return interfaceType;
        }

        Type nextType = candidateType;
        while (nextType != typeof(object))
        {
            yield return nextType;
            nextType = nextType.BaseType;
        }
    }
}

After the usual sort of argument checking the types in the assembly are enumerated and tested to see if they close the open generic type. The findAssignableTypeThatCloses method does the work of locating a possible match and returns a value indicating if a match was found. When a match is found the out parameter is assigned the closing type that was located, and the registration is added to the ContainerBuilder. The getTypesAssignableFrom method helps out by returning all the interface and base types assignable from the type it is provided.

That is all that is needed to add support for open generic types in Autofac 1.4.

ContainerBuilderExtensions.cs (3.71 kb)

ContainerBuilderExtensionsTests.cs (3.93 kb)

Tags:

Categories: Garage Sale Code | Microsoft .NET | Autofac

Registering open generic interface types in Autofac

by Alex Meyer-Gleaves 19 December 2009 - 7:22 PM

UPDATE (22 December 2009): I have submitted a patch to Nick and this feature has now been added to the Autofac V2 codebase. While including the patch Nick added support for open generic classes and renamed the extension method to AsClosedTypesOf. This post will be left in its current form and remains a valid example of extending Autofac.

There has been some discussion lately around connecting an open generic type to its implementation types in a number of different dependency injection containers including StructureMap and Unity:

It looks like this is not the first time this scenario has been discussed:

I also recently noticed a posting on the Autofac Google Group asking if it was possible for Autofac to automatically register an open generic interface type against its implementations. Nicholas Blumhardt (the creator of Autofac and all round nice guy) suggested that this would be easy to implement using the new RegisterAssemblyTypes method found on the ContainerBuilder in the upcoming V2 release. Since I have been meaning to take a closer look at the preview of Autofac V2, I decided that having a look into solving this would be a good way for me to dip my toes into the water. I decided to limit the scope to supporting only automatic registrations for open generic interface types. There is no doubt you could use the extensibility provided by the RegisterAssemblyTypes method to take things much further and add features like those supported in the Unity Auto Registration library.

Time to introduce some types that will be used in the unit tests. First is the open generic interface type.

/// <summary>
/// An open generic interface type.
/// </summary>
public interface ICommand<T>
{
    void Execute(T data);
}

Next we have a couple of simple types that will be used as the generic type parameters.

/// <summary>
/// A type to use as a generic parameter.
/// </summary>
public class SaveCommandData
{
}

/// <summary>
/// A type to use as a generic parameter.
/// </summary>
public class DeleteCommandData
{
}

To keep things interesting I decided to include an abstract base class that implements the ICommand interface.

/// <summary>
/// An abstract base class that implements the open generic 
/// interface type.
/// </summary>
public abstract class CommandBase<T> : ICommand<T>
{
    public abstract void Execute(T data);
}

There will be two command implementations. The first will directly implement the ICommand interface.

/// <summary>
/// A command class that directly implements the open 
/// generic interface type.
/// </summary>
public class SaveCommand : ICommand<SaveCommandData>
{
    public void Execute(SaveCommandData data)
    {
    }
}

The second will implement the ICommand interface by inheriting from the CommandBase<T> abstract class.

/// <summary>
/// A command class that implements the open generic interface 
/// type by inheriting from the abstract base class.
/// </summary>
public class DeleteCommand : CommandBase<DeleteCommandData>
{
    public override void Execute(DeleteCommandData data)
    {
    }
}

I will use the first unit test to define the name and signature of the extension method that be will added to the RegistrationBuilder. The extension method will be named WhereTypeClosesOpenGenericInterface and will be available on the RegistrationBuilder returned from the RegisterAssemblyTypes method. It will take a single parameter for the Type that represents the open generic interface type that automatic registrations will be created for.

This first unit test will actually ensure that passing a null value as the method parameter will result in an ArgumentNullException being thrown.

[Test]
public void WhereTypeClosesOpenGenericInterface_NullTypeProvided_ThrowsException()
{
    ContainerBuilder builder = new ContainerBuilder();
    Assert.Throws<ArgumentNullException>(() => builder.RegisterAssemblyTypes(typeof(ICommand<>).Assembly).
        WhereTypeClosesOpenGenericInterface(null));
}

The next unit test will ensure that passing a non-generic type into the method will result in an ArgumentException being thrown.

[Test]
public void WhereTypeClosesOpenGenericInterface_NonGenericTypeProvided_ThrowsException()
{
    ContainerBuilder builder = new ContainerBuilder();
    Assert.Throws<ArgumentException>(() => builder.RegisterAssemblyTypes(typeof(ICommand<>).Assembly).
        WhereTypeClosesOpenGenericInterface(typeof(SaveCommandData)));
}

Another simple unit test will ensure that passing in a closed generic type will also result in an ArgumentException being thrown.

[Test]
public void WhereTypeClosesOpenGenericInterface_ClosedGenericTypeProvided_ThrowsException()
{
    ContainerBuilder builder = new ContainerBuilder();
    Assert.Throws<ArgumentException>(() => builder.RegisterAssemblyTypes(typeof(ICommand<>).Assembly).
        WhereTypeClosesOpenGenericInterface(typeof(ICommand<SaveCommandData>)));
}

The last of the boring unit tests ensures that passing in an open generic type that is not an interface will again result in an ArgumentException being thrown.

[Test]
public void WhereTypeClosesOpenGenericInterface_NonInterfaceOpenGenericTypeProvided_ThrowsException()
{
    ContainerBuilder builder = new ContainerBuilder();
    Assert.Throws<ArgumentException>(() => builder.RegisterAssemblyTypes(typeof(ICommand<>).Assembly).
        WhereTypeClosesOpenGenericInterface(typeof(List<>)));
}

Now onto the interesting unit test that will ensure our registrations are wired up correctly. We use our new WhereTypeClosesOpenGenericInterface extension method and pass it the ICommand<> open generic interface type. Obviously when we resolve a closed generic interface type we except the returned instance to be the type that implements it. In the case of the SaveCommand the implementation of the interface is direct, and with the DeleteCommand the implementation of the interface is through its inheritance of CommandBase<T>.

[Test]
public void WhereTypeClosesOpenGenericInterface_OpenGenericInterfaceTypeProvided_ClosingGenericTypesRegistered()
{
    ContainerBuilder builder = new ContainerBuilder();
    builder.RegisterAssemblyTypes(typeof(ICommand<>).Assembly)
        .WhereTypeClosesOpenGenericInterface(typeof(ICommand<>));
    IContainer container = builder.Build();

    Assert.That(container.Resolve<ICommand<SaveCommandData>>(), Is.TypeOf<SaveCommand>());
    Assert.That(container.Resolve<ICommand<DeleteCommandData>>(), Is.TypeOf<DeleteCommand>());
}

Finally we arrive at the implementation code. I looked at the StructureMap implementation before writing this to keep an eye out for details that I might have otherwise forgotten. Doing so seemed like a good idea considering their code has already been put through its paces. Take a quick look over the code and I will explain what is going on below.

/// <summary>
/// Extension methods for the <see cref="RegistrationBuilder{TLimit,TActivatorData,TRegistrationStyle}"/> class.
/// </summary>
public static class RegistrationBuilderExtensions
{
    /// <summary>
    /// Specifies that a type from a scanned assembly is registered if it implements an interface
    /// that closes the provided open generic interface type.
    /// </summary>
    /// <typeparam name="TLimit">Registration limit type.</typeparam>
    /// <typeparam name="TRegistrationStyle">Registration style.</typeparam>
    /// <typeparam name="TScanningActivatorData">Activator data type.</typeparam>
    /// <param name="registration">Registration to set service mapping on.</param>
    /// <param name="openGenericInterfaceType">The open generic interface type for which implementations will be found.</param>
    /// <returns>Registration builder allowing the registration to be configured.</returns>
    public static RegistrationBuilder<TLimit, TScanningActivatorData, TRegistrationStyle>
        WhereTypeClosesOpenGenericInterface<TLimit, TScanningActivatorData, TRegistrationStyle>(
            this RegistrationBuilder<TLimit, TScanningActivatorData, TRegistrationStyle> registration, Type openGenericInterfaceType)
        where TScanningActivatorData : ScanningActivatorData
    {
        if (openGenericInterfaceType == null)
        {
            throw new ArgumentNullException("openGenericInterfaceType");
        }

        if (!(openGenericInterfaceType.IsGenericTypeDefinition || openGenericInterfaceType.ContainsGenericParameters) || !openGenericInterfaceType.IsInterface)
        {
            throw new ArgumentException("The type '" + openGenericInterfaceType.FullName + "' is not an open generic interface type.");
        }

        return registration.Where(candidateType => findInterfaceThatCloses(candidateType, openGenericInterfaceType) != null)
            .As(candidateType => findInterfaceThatCloses(candidateType, openGenericInterfaceType));
    }

    /// <summary>
    /// Looks for an interface on the candidate type that closes the provided open generic interface type.
    /// </summary>
    /// <param name="candidateType">The type that is being checked for the interface.</param>
    /// <param name="openGenericInterfaceType">The open generic interface type to locate.</param>
    /// <returns>The type of the interface if found; otherwise, <c>null</c>.</returns>
    private static Type findInterfaceThatCloses(Type candidateType, Type openGenericInterfaceType)
    {
        if (candidateType.IsAbstract) return null;

        foreach (Type interfaceType in candidateType.GetInterfaces())
        {
            if (interfaceType.IsGenericType && interfaceType.GetGenericTypeDefinition() == openGenericInterfaceType)
            {
                return interfaceType;
            }
        }

        return (candidateType.BaseType == typeof(object)) 
            ? null
            : findInterfaceThatCloses(candidateType.BaseType, openGenericInterfaceType);
    }
}

First the parameter for the open generic interface type is checked to ensure that it is not null and that it is indeed an open generic interface type. Next we use the RegistrationBuilder instance that is being extended to determine what types we want registered and what their service mappings will be. The important methods on the RegistrationBuilder that enable this are the Where and As methods. The Where method takes a predicate that is used to filter the list of scanned types down to only those you are interested in registering. The As method is used to provide the service mappings for the types that got included for registration after the filter was applied.

The Func<Type,bool> predicate provided to the Where method on the RegistrationBuilder instance utilizes a private method named findInterfaceThatCloses. When the findInterfaceThatCloses method is called it will look for an interface on the type provided as the first parameter, that matches the type provided as the second parameter. In our case we are passing in the candidate type that was provided by the assembly scanning process, and the open generic interface type we are interested in matching. When no matching interface is found null is returned. When used in the delegate parameter provided to the Where method for filtering we check for the return from findInterfaceThatCloses being not null, and use the actual type returned from the method for the delegate parameter provided to the As method for mapping the services. We know that when the As method is called it will only be provided with types that were included by the filter, so we need not worry about receiving a type that does not implement the interface at this point.

The implementation of the findInterfaceThatCloses method ensures that only types which are not abstract are checked for matching interfaces. It then iterates through the available interfaces and checks if any match the open generic interface type provided. If no matching interface is found we recursively check if the type’s base class implements the interface until we reach a type that inherits directly from object.

As you can see Nick has done a great job making Autofac extensible, allowing additional requirements for your container to be met with very little effort on your part. I think the next version of Autofac is shaping up nicely and I look forward to posting more about it in the future.

Tags:

Categories: Garage Sale Code | Microsoft .NET | Autofac

About the author

Alex Meyer-Gleaves I'm a software developer living in Australia (that island like continent in the southern hemisphere). I love Microsoft .NET and C#. I hate early mornings, slow drivers and Lotus Notes.

Google Reader Clips

SpringWidgets
RSS Reader
This widget is the staple of our platform. Read all your feeds right here with thisone widget - Supported feeds are OPML, RSS, RDF, ATOM. Watch your favorite Podcastin the embedded Video Player on the Desktop or publish your own video playlist toyour site for others to view!

Recent Comments

Comment RSS

Links

Disclaimer

The opinions expressed herein are my own personal opinions and do not represent my employer's view in  anyway.

© Copyright 2008