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

Introduction to Munq IOC Container for ASP.NET

0.00/5 (No votes)
23 Nov 2010 1  
This is the first in a series of articles about using the Munq DI IOC Container and IOC containers in general.

Introduction

This is the first in a series of articles about using the Munq DI IOC Container and IOC containers in general.

Inversion of Control (IOC) is a pattern which decouples the use of an interface from the concrete implementation of that interface. By eliminating this coupling:

  • the code can be tested by using special test implementations of the interface.
  • fewer DLLs need to be deployed on each tier of a multi-tiered deployment.
  • project compilation may be sped up as there are fewer project dependencies.
  • alternate implementations of the interface can be configured for use without requiring changes to the main application code. This could mean changing from MSSQL to Oracle with a simple configuration file change.

An IOC Container is a pattern which allows the application to an instance of the interface without knowing the specific concrete class that will be used.

Munq DI IOC Container is the smallest, fastest IOC container that I know of. It is faster than any of the big players, Unity, Windsor Castle, Ninject, StructureMap, etc. See the graph below. Additionally, it has been designed for the web developer, with lifetime management that includes Request, Session, and Cached lifetime managers.

This article will provide you with a detailed understanding of the Munq IOC Container with:

  • a performance graph to grab your attention
  • API documentation

Additional articles will demonstrate the usage of the Munq DI IOC Container with real world examples including using Munq with ASP.NET MVC, a tour of the Munq code, and things to watch out for when using IOC Containers.

Background

Earlier this year, I watched the very interesting Funq Screencast Series by Daniel Cazzulino (Kzu). This nine part series detail the TDD development of a small, fast IOC Container called Funq which was destined to evolve into the ContainerModel for the Patterns & Practices: Mobile Application Blocks.

After downloading the code, I discovered a number of improvements which both simplified the code and significantly improved the performance. After several discussions, Kzu made me a contributor and several of my ideas and code were incorporated into the codebase.

Unfortunately, Kzu’s target for Funq was the mobile development space, while I am more involved in ASP.NET Webform and MVC application development. I didn’t need some of the features of Funq such as Container Hierarchies and Initializers, and I did need lifetime management to include web oriented styles including Request, Session, and Cached lifetime management. Thus Munq was conceived.

The goals for this development were:

  1. Speed. High volume websites need to minimize the amount of work each page request needs to execute. Even 1/10th of a second can make a difference of a user’s perception of the site.
  2. Simplicity. Munq does one thing, but does it well. It resolves requests for instances of Types by executing Factories that have been previously registered in the container.
  3. Provide Web Lifetime Management. Windows applications can get by with a container that has the option of creating a new instance each request, or re-use the same instance. In the web application world, objects can have lifetimes that only span the current Request, the user’s Session, or be Cached reduce database load and access delays.

The latest release of Munq is available on CodePlex at http://munq.codeplex.com.

Performance

While developing Funq, Kzu and I created a small performance measuring application to compare the relative overhead of creating new instances form various IOC containers.

This test used the IOC Containers to build up a “WebApp” which implemented the IWebService interface which has the dependencies shown in the following diagram:

Sample App Dependencies

All Containers were configured to return new instances for all interfaces except ILogger which was a shared instance. Running 10000 iterations for each use case had the following results.

Container Ticks/Iteration
None 5.1746
Munq 68.3385
Funq 76.2779
Unity 613.0442
Autofac 877.035
StructureMap 280.9433
Ninject 4122.1138
Ninject2 5059.9001
Windsor 4206.1035

This is shown in the graph below, smaller is better.

IOC Container Performance

The code to register the Factories is as follows:

ILifetimeManager lifetime = new ContainerLifetime();

	Container container = new Container();

	container.Register<IWebservice>(
		c => new WebService(
			c.Resolve<IAuthenticator>(),
			c.Resolve<IStockquote>()));

	container.Register<IAuthenticator>(
		c => new Authenticator(
			c.Resolve<ILogger>(),
			c.Resolve<IErrorhandler>(),
			c.Resolve<IDatabase>()));

	container.Register<IStockquote>(
		c => new StockQuote(
			c.Resolve<ILogger>(),
			c.Resolve<IErrorhandler>(),
			c.Resolve<IDatabase>()));

	container.Register<IDatabase>(
		c => new Database(
			c.Resolve<ILogger>(),
			c.Resolve<IErorhandler>()));

	container.Register<IErrorhandler>(
		c => new ErrorHandler(c.Resolve<ILogger>()));

	container.RegisterInstance<ILlogger>(new Logger())
		.WithLifetimeManager(lifetime);

Resolving the instance:

var webApp = container.Resolve<IWebservice>();
webApp.Execute();

Using the Code

Using the container is relatively simple. The basic steps are:

  1. Create the Container.
  2. Register the factory delegates for the Interfaces and/or Classes.
  3. Resolve instances by calling Resolve methods of the container.

Creating the IOC Container

The container is usually created when the application first starts. In a web application, the container would typically be created in the Application_Start and stored in a field of the derived Application class or a static variable.

Container container = new Container();

Registering Type Factories

Registering the Type Factories associates a type to be resolved with a function that will return an instance of that type. This method is called when the container is asked to return an instance of the type.

Munq has four ways of registering type factory functions. These functions can be anything which has the correct signature and are not limited to delegates of the form

c=> new MyType()

but could be

c => CreateAndInitialzeMyType(c.Resolve<IOne>(), c.Resolve<ITwo>)

The first is using the type-safe generic Register methods. There are versions for both named and un-named registrations. Both take as a parameter, a delegate which takes a Container as its single parameter and returns an instance of the type.

public IRegistration Register<TType>(Func<Container, TType> func)
public IRegistration Register<TType>(string name, Func<Container, TType>func)

The second is a set of methods which allows the factory method to be registered by passing the type of be resolved, and a delegate which takes a Container as its single parameter and returns an Object. There are versions for named and un-named registrations. These methods would typically be used if the registration information was read from an external store such as a database, XML file, or the web.config file.

public IRegistration Register(Type type, Func<Container, object> func)
public IRegistration Register(string name, Type type, Func<Container, object> func)

The third method is using the type-safe generic RegisterInstance methods. There are versions for both named and un-named registrations. Both take as a parameter, an instance of the type.

public IRegistration RegisterInstance<TType>(TType instance)
public IRegistration RegisterInstance<TType>(string name, TType instance)

The fourth is a set of methods which allows the factory method to be registered by passing the type of be resolved, and an object to be returned when the type is resolved. There are versions for named and un-named registrations. These methods would typically be used if the registration information was read from an external store such as a database, XML file, or the web.config file.

public IRegistration RegisterInstance(Type type, object instance)
public IRegistration RegisterInstance(string name, Type type, object instance)

Getting an Instance from the Container

Once an interface and function have been registered in the container, the application can retrieve an instance by asking the container to resolve the interface to the concrete implementation that was registered. Munq has two forms of the Resolve method, a type-safe version using Generics, and a version that takes a Type as a parameter and returns an Object. Both versions have named and un-named overloads.

public TType Resolve<TType>()
public TType Resolve<TType>(string name)

public object Resolve(Type type)
public object Resolve(string name, Type type)

Lazy Resolution

There are situations where you may not wish to create the instance immediately. This may be because the instance uses a scarce resource or due to logic that may not require instantiation. For these cases, you can use the LazyResolve methods which returns a function that, when executed, performs the resolution and returns the instance.

Like the Resolve methods, the LazyResolve has two forms, a type-safe version using Generics, and a version that takes a Type as a parameter. Both versions have named and un-named overloads.

Func LazyResolve<ttype>() 
Func<ttype> LazyResolve<ttype<(string name)
 
Func<Object> LazyResolve(Type type) 
Func<Object> LazyResolve(string name, Type type)

What is this IRegistration Thing?

You may have noticed that the Registration methods all return an object that implements the IRegistration interface. This interface allows the user to retrieve the internally generated ID for the registration and to specify the LifetimeManager to be used by the instance when it is resolved.

For example, if you need an IShoppingCart object to be stored in the users session, you would tell the registration to use the SessionLifetime manager:

ILifetimeManager lifetime = new SessionLifetime();
container.Register<IShoppingCart>(c => new MyShoppingCart())
         .WithLifetimeManager(lifetime);

Notice that this has been implemented in a fluent interface manner to allow changing of method calls. While there is currently only one method on this interface, any future methods will follow this pattern.

 public interface IRegistration
{
    string Id { get; }
    IRegistration WithLifetimeManager(ILifetimeManager manager);
}

Specifying the Default Lifetime Manager

When the Container is first created, the default behaviour is to return a new instance for each Resolve method call. As shown above, this can be modified on a registration by registration basis. Additionally, you can specify which lifetime manager is to be used for any registration. For example, to always return the same instance for each call to Resolve, use the ContainerLifetime manager as shown below:

Container container = new Container();
ILifetimeManager lifetime = new ContainerLifetime();

container.UsesDefaultLifetimeManagerOf(lifetime);

The method definition is:

public Container UsesDefaultLifetimeManagerOf(ILifetimeManager lifetimeManager)

Available Lifetime Managers

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 the same instance when the Resolve method is called by executing the factory method.

SessionLifetime

This lifetime manager’s behaviour is to always return an attempt to retrieve the instance from Session when the Resolve method is called. If the instance does not exist in Session, 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 an attempt to retrieve the instance from Request.Items when the Resolve method is called. If the instance does not exist in Request.Items, 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 an attempt to retrieve the instance from Cache when the Resolve method is called. If the instance does not exist in Cache, a new instance is created by executing the factory method, and storing it in the Cache.

Conclusion

I have given you a brief overview of the Munq DI IOC and its API. I will be following this with additional articles with examples of using Munq and dissecting the Munq code.

The full source for Munq is available on CodePlex at http://munq.codeplex.com.

del.icio.us Tags: , , , , ,

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