Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Munq IocContainer V2 - Overview

0.00/5 (No votes)
23 Nov 2010 1  
Table of Contents Downloading the code Overview What is Munq IocContainer Using Munq IocContainer Registering Factory Methods Obtaining an Instance from the IocContainer Initializing the IocContainer Lifetime Management Downloading the Code The code is maintained at CodePlex.

Table of Contents

  • Downloading the code
  • Overview
  • What is Munq IocContainer
  • Using Munq IocContainer
  • Registering Factory Methods
  • Obtaining an Instance from the IocContainer
  • Initializing the IocContainer
  • Lifetime Management

Downloading the Code

The code is maintained at CodePlex.  The latest release can be found here.

Overview

This post is the first of several heralding the release of the first beta of the second release Munq IocContainer. In previous posts, Introduction to Munq IOC Container for ASP.NET and Using Munq IOC with ASP.NET MVC 2 Preview 2, I introduced an IOC Container that is high performance and designed for ASP.NET.  This latest release fixes a number of small issues and adds functionality without sacrificing any of the performance.  Unit tests were created to ensure the quality and correctness of the implement.  I felt this was critical as several people have enquired about using Munq IocContainer in production code.

In addition, I will be releasing Munq FluentTest, a fluent interface to be used with MSTest or NUnit to allow the verification statements in a test to be written in an almost English syntax.  Examples can be seen in the unit tests for the IocContainer.

The first articles in this series will be:

  1. Munq IocContainer V2 – Overview (this article)
  2. Munq IocContainer V2 – Using Munq in an ASP.NET application (may be in two parts)
  3. Munq IocContainer V2 – Using Lifetime Management to Eliminate Unnecessary Instance Creation
  4. Munq IocContainer V2 – Using Munq to Implement an Automatic Plug-in or Modular Application
  5. Munq IocContainer V2 – Creating a custom Lifetime Manager.

The previous list will be updated as changes and wild ideas occur.

What is Munq IocContainer

If you have been, or trying to, program using the SOLID principles then you are implementing your classes with Dependency Inversion, the D in SOLID.  Simply stated, Dependency Inversion moves the responsibility of resolving dependencies to the caller of a method or constructor, instead the method or constructor.  This makes the called method easier to test as the dependencies can be substituted with a mock or stub by unit test code.  At its simplest, you can use the Poor Man’s Dependency Injection pattern.  This involve using two constructors, one which has the dependencies passed in, and a default (parameter-less constructor) that calls the other constructor with default dependency implementation.  This is shown in the code snippet below:

public</span /> class</span /> MyClass
{
    //</span /> fields to hold references to the concrete implementations of
</span />    //</span /> the class's dependencies.
</span />    IDependencyOne _dependencyOne;
    IDependencyTwo _depencencyTwo;

    //</span /> constructor which allows the dependencies to be passed in.
</span />    public</span /> MyClass(IDepenencyOne dependOne, IDependencyTwo dependTwo)
    {
        _dependencyOne = dependOne;
        _dependencyOne = dependTwo;
    }

    //</span /> default constructor which uses default implementations of
</span />    //</span /> the class's dependencies/
</span />    public</span /> MyClass() : this</span />(new</span /> ImplDependOne(), new</span /> ImplDependTwo())
    {
    }

    public</span /> void</span /> AMethod(int</span /> a)
    {
        var</span /> b = _dependencyOne.MethodA(a);
        _dependencyTwo.MethodB(b);
    }
    ...
}        

Production code calls the default constructor, and unit tests use the constructor with parameters. Unfortunately, this still couples MyClass to the concrete implementations ImplDependOne and ImplDependTwo.

This dependency can be removed by removing the default constructor.  However, this now means that every place that an instance of MyClass is created, the code must also create instances of classes implementing IDependencyOne and IDependencyTwo.  This is where an IOC Container comes in.  IOC Containers can be queried for an implementation of an interface or abstract class, and will return an instance with all of its dependencies resolved.

With Munq IocContainer, this requires two steps.  The first is the registration of a factory method with the container, and second, using the container to resolve the dependency.  Munq IocContainer registers methods that take an instance of IocContainer and returns an instance that implements the requested interface.  By using factory methods instead of classes, the developer has complete control over what is done to create the instance.  Furthermore, because there is no need to use Reflection or other CPU expensive techniques to select a constructor and resolve its dependencies, the resolution speed of the container is as fast as possible.  Thus the previous example would look like:

public</span /> class</span /> MyClass : IMyClass
{
    //</span /> fields to hold references to the concrete implementations of
</span />    //</span /> the class's dependencies.
</span />    IDependencyOne _dependencyOne;
    IDependencyTwo _depencencyTwo;



    //</span /> constructor which allows the dependencies to be passed in.
</span />    public</span /> MyClass(IDepenencyOne dependOne, IDependencyTwo dependTwo)
    {
        _dependencyOne = dependOne;
        _dependencyOne = dependTwo;
    }



    public</span /> void</span /> AMethod(int</span /> a)
    {
        var</span /> b = _dependencyOne.MethodA(a);
        _dependencyTwo.MethodB(b);
    }
    ...
}       
... 
///</span />//////////////////////////////////////////////////////////////////////////
</span />//</span /> Registration, probably in Global.asax for an ASP.NET application
</span />public</span /> static</span /> Container { get</span />; private</span /> set</span />;}



private</span /> void</span /> InitIocContainer()
{
    //</span /> create the container
</span />    Container = new</span /> IocContainer();



    //</span /> Register the interface implementations.
</span />    Container.Register<IMyClass>(c => new</span /> MyClass(c.Resolve<IDependencyOne>(), c.Resolve<IDependencyTwo>()) );
    Container.Register<IDependencyOne>(c => new</span /> ImplDependOne() );
    Container.Register<IDependencyTwo>(c => new</span /> ImplDependTwo() );
}

... 
///</span />//////////////////////////////////////////////////////////////////////////
</span />    //</span /> getting the class instance from the container
</span />    IMyClass instance = MyApp.Container.Resolve<IMyClass>();
    instance.MethodA(42</span />);

If you are familiar with any of the more well known IOC Containers such as Unity, NInject, AutoFac, Windsor, or StructureMap, you will be thinking, “This is just the same as all the others”.  And you would be mainly correct.  The difference is in the speed of the Resolve functionality and Web oriented lifetime management features.  For some details on the performance of Munq compared to the other IOC Containers see my previous article Introduction to Munq IOC Container for ASP.NET. You can also get someone else’s opinion at Munq is for web, Unity is for Enterprise by Omar AL Zabir, creator of DropThings.  But just to recap, the relative performances of the IOC containers is show below, smaller is better.

Using Munq IocContainer

imageVersion 2 of Munq has been refactored into two DLLs.  The first, Munq.Interfaces, contains only the interfaces required to use an instance of the IIocContainer, an IRegistration object (returned from one the IIocContainer.Register methods), or create your own lifetime manager.  By programming to the interfaces, only the main application requires a hard dependency on the Munq IOC Container.  Other DLLs can be passed the container instance as an IIocContainer.  This will allow the replacement of the IOC Container with another implementation if desired.

The second DLL, Munq.IocContainer contains the implementation of the Munq IOC Container, the standard Lifetime Managers, a configuration loader, and a Controller Factory for ASP.NET MVC.

Registering Factory Methods

Munq allows you to Register a named or unnamed factory method using Generic or Non-Generic methods, for a total of 4 different signatures for the Register method.  Similarly, there are 4 methods for registering a pre-constructed instance to always be returned on a Resolve request.

//</span />Register
</span /> IRegistration Register(string</span /> name, Type type, Func<</span />IIocContainer, object</span />></span /> func);
 IRegistration Register(Type type, Func<</span />IIocContainer, object</span />></span /> func);
 IRegistration Register<</span />TType></span />(Func<</span />IIocContainer, TType></span /> func) where TType : class</span />;
 IRegistration Register<</span />TType></span />(string</span /> name, Func<</span />IIocContainer, TType></span /> func) where TType : class</span />;

 //</span />Register Instance
</span /> IRegistration RegisterInstance(string</span /> name, Type type, object</span /> instance);
 IRegistration RegisterInstance(Type type, object</span /> instance);
 IRegistration RegisterInstance<</span />TType></span />(string</span /> name, TType instance) where TType : class</span />;
 IRegistration RegisterInstance<</span />TType></span />(TType instance) where TType : class</span />;

All the RegisterXXX methods return an object that implements the IRegistration interface.  This interface allows you to get information about the registration, specify a LifetimeManger, of invalidate any cached instances.

public</span /> interface</span /> IRegistration
{
    string</span /> Name         { get</span />; }
    string</span /> Key          { get</span />; }
    Type   ResolvesTo   { get</span />; }
    IRegistration WithLifetimeManager(ILifetimeManager manager);
    void</span /> InvalidateInstanceCache();
}

Obtaining an Instance from the IocContainer

The user asks the container to create, or serve up a cached instance, of an implementation of an interface by calling one of the Resolve methods.  For example:

//</span /> getting the class instance from the container    
</span />IMyClass instance = MyApp.Container.Resolve<</span />IMyClass></span />();    
instance.MethodA(42</span />);

The container returns a instance of the required type, with all its dependencies resolved and fully initialized.
//</span />Resolve
</span />object</span /> Resolve(string</span /> name, Type type);
object</span /> Resolve(Type type);
TType Resolve<</span />TType></span />() where TType : class</span />;
TType Resolve<</span />TType></span />(string</span /> name) where TType : class</span />;
 
In many cases, you may not wish to actually create the instance due to the cost of construction, or use of scarce resources.  Instead, you want to delay the construction until you need it.  For these cases, call one of the LazyResolve methods. 
//</span />Lazy Resolve
</span />Func<</span />object</span />></span /> LazyResolve(string</span /> name, Type type);
Func<</span />object</span />></span /> LazyResolve(Type type);
Func<</span />TType></span /> LazyResolve<</span />TType></span />() where TType : class</span />;
Func<</span />TType></span /> LazyResolve<</span />TType></span />(string</span /> name) where TType : class</span />;
These will return a delegate that will get the instance from the container, when and if you need it.
//</span /> Example of LazyResolve
</span />Func<</span />MyClass></span /> lazyLoader = Container.LazyResolve<</span />MyClass></span />();
//</span /> do stuff
</span />   ...
if</span /> (INeedMyClass)
{
   using</span />(MyClass myClass = lazyLoader())
   {
      //</span /> do stuff with myClass
</span />        ...
   }
}

Initializing the IOC Container

Initialization of the IOC container can be performed in a number of ways, but usually occurs in the Application_Start of the Global.asax file for ASP.NET applications.  Out of the box, Munq supports:

  • initialization by code
  • automatic discovery and registration

Typically, applications will use a combination of both.  For example, the code below has an InitializeIOC method which calls the ConfigurationLoader.FindAndRegisterDependencies method and also the Register<..> method.

The FindAndRegisterDependencies method is passed the IIocContainer and does:

  1. Searches the bin directory for any classes that implement the IMunqConfig interface.
  2. For each of these classes it creates an instance and calls the RegisterIn method, passing in the container.
  3. These methods allow the module to register its classes and dependencies with the container.

The explicit registration allows the factory method and its dependencies to be registered.  Examine the Register<IController>(“Account”, …) statement below.  The line translated into English says, “When asked for an instance of the IController interface, named “Account”, create an instance by calling the constructor.  Pass in to the constructor instances of IFormsAuthentication and IMembershipService, both of which are also resolved by the container, as are any of their dependencies.”

public</span /> class</span /> MvcApplication : System.Web.HttpApplication
 {
     public</span /> static</span /> Container IOC { get</span />; private</span /> set</span />; }

     public</span /> static</span /> void</span /> RegisterRoutes(RouteCollection routes)
     {
         routes.IgnoreRoute("</span />{resource}.axd/{*pathInfo}"</span />);

         routes.MapRoute(
             "</span />Default"</span />,                                              //</span /> Route name
</span />             "</span />{controller}/{action}/{id}"</span />,                           //</span /> URL with parameters
</span />             new</span /> { controller = "</span />Home"</span />, action = "</span />Index"</span />, id = "</span />"</span /> }  //</span /> Parameter defaults
</span />         );

     }

     protected</span /> void</span /> Application_Start()
     {
         InitializeIOC();
         RegisterRoutes(RouteTable.Routes);
     }

     private</span /> void</span /> InitializeIOC()
     {
         //</span /> Create the IOC container
</span />         IOC = new</span /> Container();

         //</span /> Create the Default Factory
</span />         var</span /> controllerFactory = new</span /> MunqControllerFactory(IOC);

         //</span /> set the controller factory
</span />         ControllerBuilder.Current.SetControllerFactory(controllerFactory);



         ConfigurationLoader.FindAndRegisterDependencies(IOC);



         //</span /> Register the Controllers
</span />         IOC.Register<IController>("</span />Home"</span />, ioc => new</span /> HomeController());
         IOC.Register<IController>("</span />Account"</span />,
                 ioc => new</span /> AccountController(ioc.Resolve<IFormsAuthentication>(),
                                              ioc.Resolve<IMembershipService>())
         );
     }
 }

Lifetime Management

Lifetime Management functionality has not changed since version 1, although the implementation has.

    Lifetime Managers allow you to modify the behaviour of the container as to how it resolves instances, and what is the lifetime of the instance.  Munq has a set of Lifetime Managers designed for web applications.  These are described below.

    Warning: if you used the RegisterInstance method, then the same instance will be returned regardless of which lifetime manager is used.

    AlwaysNewLifetime

    This lifetime manager’s behaviour is to always return a new instance when the Resolve method is called by executing the factory method.  This is the default behaviour.

    ContainerLifetime

    This lifetime manager’s behaviour is to always return a the same instance when the Resolve method is called by executing the factory method.  The instance is cached in the container itself.

    SessionLifetime

    This lifetime manager’s behaviour is to always return a attempt to retrieve the instance from Session when the Resolve method is called.  If the instance does not exist in Session, the a new instance is created by executing the factory method, and storing it in the Session.

    RequestLifetime

    This lifetime manager’s behaviour is to always return a attempt to retrieve the instance from Request.Items when the Resolve method is called.  If the instance does not exist in Request.Items, the a new instance is created by executing the factory method, and storing it in the Request.Items.

    CachedLifetime

    This lifetime manager’s behaviour is to always return a attempt to retrieve the instance from Cache when the Resolve method is called.  If the instance does not exist in Cache, the a new instance is created by executing the factory method, and storing it in the Cache. CacheDependencies and Sliding or Absolute Timeouts can be applied to the the CachedLifetimeManager.

    Conclusion

    This article was just a brief overview of the functionality and performance of the Munq IocContainer.  I will examine different aspects of using the Munq.IocContainer in other articles in this series, so stay tuned.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here