Enterprise Library 6, Unity 3 and MVC 4, Registration by Convention Part 3

This post continues on from the previous post. Part 2 Enterprise Library 6, Unity 3 and MVC 4, LifetimeManagers Part 2

The MVC application with unity is starting to take shape. It would be better if the registration by convention method could select which interfaces with which lifetime manager should be registered and only the required interfaces.

The application could use 3 different lifetime managers:

  1. singleton
  2. per request
  3. always new

An attribute will be used to register all Types to be registered by convention. To do this, a new attribute has to be created.

[System.AttributeUsage(System.AttributeTargets.Class | System.AttributeTargets.Struct)]
public class UnityIoCPerRequestLifetimeAttribute : System.Attribute
{
   public double version;

   public UnityIoCPerRequestLifetimeAttribute()
   {
        version = 1.0;
   }
}

Now this attribute can be applied to the business classes

[UnityIoCPerRequestLifetimeAttribute]
public class BusinessClass : IBusinessClass
{
}

A function is also required to add to the unity RegisterTypes extension method. This method checks if the class has a UnityIoCPerRequestLifetimeAttribute attribute and when it does, it returns all implemented interfaces types for each class.

    public static class UnityHelpers
    {
        private static readonly Type[] EmptyTypes = new Type[0];
   
        public static IEnumerable<Type> FromAllInterfacesWith_PerRequestLifetimeAttribute(Type implementationType)
        {
            Guard.ArgumentNotNull(implementationType, "implementationType");

            if (implementationType.GetCustomAttributes(typeof(UnityIoCPerRequestLifetimeAttribute), true).Length > 0)
            {
                var implementationTypeAssembly = implementationType.GetTypeInfo().Assembly;
                var typeInfo = implementationType.GetTypeInfo();
                return typeInfo.ImplementedInterfaces;
            }
            else
            {
                return EmptyTypes;
            }
        }

        public static IEnumerable<Type> None(Type implementationType)
        {
            return EmptyTypes;
        }
    }

The Func can then be used and all classes with the custom attribute will be registered with a per request lifetime manager .

public static void RegisterTypes(IUnityContainer container)
{
    container.RegisterTypes(AllClasses.FromLoadedAssemblies(),
                 UnityHelpers.FromAllInterfacesWith_PerRequestLifetimeAttribute,
                 WithName.Default,
                 PerRequest
                 )
              .RegisterType<IUnitOfWorkExample, UnitOfWorkExampleTest>(new TransientLifetimeManager());
 
}

Our log output shows the construction and destruction of the different classes.

LogsUnityOfWorkUnityExample

code: https://github.com/damienbod/MvcUnityBootstrapperTestPart3

The above implementation checks for classes from Loaded Assemblies and creates a per request lifetime object for classes with the custom attribute.

Now we can simplify it again. This time the Types parameter of the RegisterTypes method will be used and all classes with the attribute will be registered when it has a matching interface.

Firstly a helper method is required to get class types which have the UnityIoCPerRequestLifetimeAttribute attribute

public static IEnumerable<Type> GetTypesWithPerRequestLifetimeAttribute(Assembly[] assemblies)
{
  foreach (var assembly in assemblies)
  {
    foreach (Type type in assembly.GetTypes())
    {
       if (type.GetCustomAttributes(typeof(UnityIoCPerRequestLifetimeAttribute), true).Length > 0)
       {
         yield return type;
       }
    }
  }
}

Now we use the Types parameter to define our types and unity auto maps for us using the optional WithMappings.FromMatchingInterface helper.

public static void RegisterTypes(IUnityContainer container)
{
  container.RegisterTypes( UnityHelpers.GetTypesWithPerRequestLifetimeAttribute(AppDomain.CurrentDomain.GetAssemblies()),
           WithMappings.FromMatchingInterface,
           WithName.Default,
           PerRequest
         )
       .RegisterType<IUnitOfWorkExample, UnitOfWorkExampleTest>(new TransientLifetimeManager());
 
}

This setups unity just as before.

Again we can optimise the TransientLifetimeManager registrations. Lets create a new attribute and apply it to all classes as required.

Define a new attribute.

[System.AttributeUsage(System.AttributeTargets.Class | System.AttributeTargets.Struct)]
public class UnityIoCTransientLifetimeAttribute : System.Attribute
{
        public double version;

        public UnityIoCTransientLifetimeAttribute()
        {
            version = 1.0;
        }
}

Then the reflection helper method needs to be changed:

public static IEnumerable<Type> GetTypesWithCustomAttribute<T>( Assembly[] assemblies)
{
    foreach (var assembly in assemblies)
    {
        foreach (Type type in assembly.GetTypes())
        {
          if (type.GetCustomAttributes(typeof(T), true).Length > 0)
          {
              yield return type;
          }
        }
     }
}

And then use it in the RegisterTypes method

public static void RegisterTypes(IUnityContainer container)
{
            container.RegisterTypes(UnityHelpers.GetTypesWithCustomAttribute<UnityIoCPerRequestLifetimeAttribute>(AppDomain.CurrentDomain.GetAssemblies()),
                                    WithMappings.FromMatchingInterface,
                                    WithName.Default,
                                    PerRequest
                                )
                     .RegisterTypes(UnityHelpers.GetTypesWithCustomAttribute<UnityIoCTransientLifetimeAttribute>(AppDomain.CurrentDomain.GetAssemblies()),
                                    WithMappings.FromMatchingInterface,
                                    WithName.Default,
                                    WithLifetime.Transient
                                );
}

The classes are constructed again in the same way except we have a much better registration by convention implementation. The same could be done for all singleton classes.

Now who requires the complex unity configuration?

Links:

Part 1 Enterprise Library 6, Unity 3 with ASP.NET MVC 4
Part 2 Enterprise Library 6, Unity 3 and MVC 4, LifetimeManagers
Part 4 Enterprise Library 6, Unity 3 InterfaceInterceptor with MVC 4
Part 5 Enterprise Library 6, Unity 3, MVC, Validation with Interception ValidationCallHandler

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 )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.

%d bloggers like this: