Table of Contents
- Introduction
- What are IoC Containers
- Prequisites - Delegates
- Building an IoC Container
- Registering types in our Container
- Creating objects for the client
- Utility functions
- Using the Container
- Using our IoC Container with .NET MVC
- Benefits of using IoC Containers
- Conclusion
- Related Articles
Introduction
Hi,
In my humble opinion, web development got more and more serious in the last few years. More traditional development environment always had more attention for methodologies and design than web development. I've even heard from some game developers that doing web development wasn't even programming at all.
Well, in the past I've worked with real time applications, desktop applications and did web development and I can safety say that the level of seriousness a programming field have is in proportion to the level of seriousness you put into it.
Take those points (from what I've heard or observed):
- Web development is more accessible for newcomers and the less experienced (for me it's a false assumption) so that's why maybe it's seen as an easy development environment.
- Web development is still young compared to some other programming environment.
- Web development is in its own league with its stateless protocol between clients and servers (compared to desktop applications where everything is in memory on the user's computer).
I think they are the reasons why web development was left of the bench for so much time in terms of design, methodologies and best practices. It's like no one cared as long as it worked. Microsoft even tried to do some smoke and mirrors with Web Forms mimicking desktop application development.
Fortunately, the gap is closing as there is more and more attention driven to proper web development. You may be confused with the relatively new lingo on the Internet composed of terms like MVC, IoC, Factories, Ubiquity Language, and so on...
Today, I'll show you what's a IoC Container and how can you use it with .NET MVC.
What are IoC Containers
Assuming you already know what's the MVC design pattern and how it's applied to web development (.NET MVC), I'll show you today something that I use mainly with my .NET MVC Controllers: IoC Containers.
IoC stands for Inversion of Control and represents a design pattern. According to wikipedia: Inversion of control, or IoC, is an abstract principle describing an aspect of some software architecture designs in which the flow of control of a system is inverted in comparison to procedural programming.
To achieve Inversion of control in .NET MVC, we'll use something called a Container. We will use this Container to acquire instances of objects we would normally create ourselves here and there in the code. This is where the inversion occurs: Not only I'm not in control of the objects creation, lifetime and nature, but this is also something imposed to me (the Container is the one in control).
For example, instead of creating a repository object passing the connection string, we would ask the Container an instance of it (or we will automatically receive an instance of it by some other means, but more on that later). That way, we don't have to know the details of the object's creation and focus ourselves on the problems we have to solve.
This is something we use a lot with interfaces. You'll also see the benefits of using interfaces with IoC Containers in a couple of paragraphs.
before going any further and to give you a preview, let's look at an example of client code using a IoC Container:
// Example 1 // give me an instance of the User's Repository IUsersRepository r = Container.Resolve<IUsersRepository>(); // Example 2 // receive an instance of the repository via a // constructor parameter and keep it for later use public HomeController(IUsersRepository usersRepository) { _usersRepository = usersRepository; }
Don't worry if you're a little lost here, we'll go in details in the next sections. But before writing our own IoC Container, let's review some .NET stuff.
Prequisites - Delegates
Before beginning the creation of our IoC Container, let's review what's a delegate is in .NET. We will use them as a foundation for our Container so I devote a paragraph or two to them.
A delegate, if you're familiar with C/C++, is a pointer to a function.
In .NET, before using a delegate you must define what the delegate (the function pointed at) will look like in therms of its signature. The definition of the delegate is like the definition of any other type. It's like saying "a variable of type int" or "a variable of type string". With delegates you say "a variable of type function that takes a string as input and returns a boolean". Besides the time it takes to say it it's roughly the same thing. So here's a complete example for you to understand delegates. After that, we're good to go to create from the ground up our IoC Container:
public class MyDelegates { // Declare a new type: a type "pointer to // function that takes a string as input // and returns an int" delegate int myDel(string input); static int test3(string i) { return 3; } public static void Test() { // Instanciate a variable of our // new type (pointer to function): myDel func; int ret; // Assign our variable a new // function (created via delegate) func = delegate(string i) { return 5; }; ret = func("yo"); // =5 // Assign our variable a new // function (created via // a lambda expressions) func = (i => i.Length); ret = func("Hello World"); // =11 // Assign our variable an existing // function func = test3; ret = func("dsadsa"); // =3 } }
Simple isn't it? Ok let's go.
Building an IoC Container
Our IoC Container will be pretty simple. It will have a dictionary as a data structure to hold some informations. Since a IoC Container is responsible to give you instances of objects, you have to keep track of what object the Container is able to create (hence our dictionary). The dictionary will be indexed by type (Type in .NET) since the client will ask for instances of objects by their type.
Our dictionary:
The functions that will create objects will be provided by the client (usually on application start. We will see an example later). So let's see our preliminary version of our Container:
public class IOCContainer { // This is the delegate that we will // be used to create objects // (don't mind the reason of the input parameter for now) public delegate object CreateInstance(IOCContainer container); // Our dictionary that will hold the couples: // (type, function that creates "type") Dictionary<Type, CreateInstance> Factories; // Default constructor public IOCContainer() { Factories = new Dictionary<Type, CreateInstance>(); } }
Registering types in our Container
Ok next we will provide a way for the client to fill our dictionary so that later on he can ask us some instances of objects. It sounds weird and unnecessary told that way but there are many advantages of using Containers (yes you'll see later... it's always later :P).
Let's add entries to our dictionary via a public function of our IOCContainer class:
public void RegisterInterface<T>(CreateInstance ci) { if (ci == null) throw new ArgumentNullException("ci"); if (Factories.ContainsKey(typeof(T))) throw new ArgumentException("Type already registered"); Factories.Add(typeof(T), ci); }
The function uses generics as a way for the client to tell us the type of the object that "ci" returns. It could also be a second parameter to the function. Great! The client can provide us functions to create some objects. Let's see how can we return objects.
Creating objects for the client
Creating objects is only a matter of fetching an entry from our dictionary and call the function found. We can then return the newly object created to the client:
public T Resolve<T>() { if (!Factories.ContainsKey(typeof(T))) throw new ArgumentException("Type not registered"); // retrieve the function that creates // the object from the dictionary CreateInstance creator = (CreateInstance)Factories[typeof(T)]; // call it! return (T)creator(this); } // We provide an overload that doesn't use generics, to be more // flexible when the client doesn't know the type he wants to // retrieve at compile time. public object Resolve(Type type) { if (type == null) throw new ArgumentNullException("type"); if (!Factories.ContainsKey(type)) throw new ArgumentException("Type not registered"); CreateInstance creator = (CreateInstance)Factories[type]; return creator(this); }
Before looking at how the client can use our Container, let's declare some utility functions and after that we're done... that was quick huh!?
Utility functions
Finally, we add some utility functions to our class to determine if a type has already been registered (I called them "InterfaceRegistered" because I always use IoC Containers with interfaces):
public bool InterfaceRegistered<t>() { return Factories.ContainsKey(typeof(T)); } public bool InterfaceRegistered(Type type) { if (type == null) throw new ArgumentNullException("type"); return Factories.ContainsKey(type); }
Using the Container
Cool let's see it in action. The code is self documented so I won't hold you down:
// Remember, the delegate was of the form: // public delegate object CreateInstance(IOCContainer container); IOCContainer c = new IOCContainer(); string queryString = "..."; // Register Types using... // A static function that could be declared earlier: c.RegisterInterface<ISessionService>(createSessionService); // Lambda expressions: c.RegisterInterface<IUsersRepository>(c => new UsersRepository(queryString)); // Delegates: c.RegisterInterface<IUserFactory>(delegate(IOCContainer c) { return new UFMockUp(); }); // Remember the input parameter of type ICOContainer the delegate have? // Well we did this to be able to inject objects in other objects. // This is pretty powerful. Let's say we have an object that depends // on another. We could instantiate them like this: c.RegisterInterface<ICar>(delegate(IOCContainer c) { return new Car(c.Resolve<IEngine>()); }); // Each Car created will receive a IEngine as parameter! // Use our Container ISessionService s = c.Resolve<ISessionService>(); IUsersRepository r = c.Resolve<IUsersRepository>(); IUserFactory f = c.Resolve<IUserFactory>(); // do something with s, r, f // [...]
That completes our IoC Container! The benefits of using one may not be obvious with these examples, but coupled with .NET MVC it becomes pretty powerful. So let's move on to see how it can be of any help.
Using our IoC Container with .NET MVC
I'm assuming you're familiar with .NET MVC. If you don't you can still continue reading but it will be hard for you to follow.
I see Controllers in .NET MVC being the pivots between the User Interface and the Model. They're connecting objects together to make work individual things as a whole. Why not make our Controllers only do that and focus on that making abstraction of everything else. That's it, without worrying about object's lifetime and configuration. That's what the Container is trying to achieve in .NET MVC. Let's suppose our Home Controller looks like:
public class HomeController : Controller { public ActionResult Index() { string cs = ConfigurationManager .ConnectionStrings["default"].ConnectionString; IUsersRepository r = new UsersRepository(cs); IUser u = r.GetUser("some@email.com"); // Let's say the View is strongly typed of type IUser return View(u); } }
It's pretty small, but still. We're using a repository to fetch a user to display some of his informations with the view. But what if 5 or 6 controllers needs to do relatively the same thing? It would be much cleaner to achieve something like this:
public class HomeController : Controller { IUsersRepository _r; public HomeController(IUsersRepository r) { _r = r; } public ActionResult Index() { IUser u = _r.GetUser("some@email.com"); return View(u); } }
Much better Isn't it!? Our Index action can now focus on what to do rather than how to do it. Plus, every Action in the Controller will have access to the repository. But how the hell the controller constructor received the instance of the IUsersRepository object? You must know it, from our Container.
The built-in ControllerFactory (The class responsible to instantiate controllers in .NET MVC) is not able to achieve such a thing. So it won't work if you try it out right away.
I will give you the necessary code to create your own ControllerFactory for you to be able to inject objects in the controller's constructors using our Container.
I'm not explaining everything because I want to focus on Containers and not Controller Factories! But look at the GetControllerInstance function. .NET MVC will call this function every time it needs an instance of a specific Controller. We create the needed Controller according to its type. While creating it, we're looking into the constructor to see if we can pass instances of objects from our Container:
public class IOCControllerFactory : DefaultControllerFactory { private IOCContainer _container { get; set; } private IOCControllerFactory() { } public IOCControllerFactory(IOCContainer container) { if (container == null) throw new ArgumentNullException("container"); _container = container; } protected override IController GetControllerInstance(Type controllerType) { if (controllerType == null) throw new HttpException(404, "Page not found: " + RequestContext.HttpContext.Request.Path); if (!typeof(IController).IsAssignableFrom(controllerType)) throw new ArgumentException("Type does not subclass IController", "controllerType"); object[] parameters = null; ConstructorInfo constructor = controllerType.GetConstructors().FirstOrDefault(c => c.GetParameters().Length > 0); if (constructor != null) { ParameterInfo[] parametersInfo = constructor.GetParameters(); parameters = new object[parametersInfo.Length]; for (int i = 0; i < parametersInfo.Length; i++) { ParameterInfo p = parametersInfo[i]; if (!_container.InterfaceRegistered(p.ParameterType)) throw new ApplicationException("Can't instanciate controller '" + controllerType.Name + "', one of its parameters is unknown to the IoC Container"); parameters[i] = _container.Resolve(p.ParameterType); } } try { return (IController)Activator.CreateInstance(controllerType, parameters); } catch (Exception ex) { throw new InvalidOperationException(String.Format(CultureInfo.CurrentUICulture, "error creating controller", controllerType), ex); } } }
Now that we have our custom ControllerFactory using our Container. Let's bind everything together. Open up the Global.asax.cs file:
protected void Application_Start() { // Standard .NET MVC stuff RegisterRoutes(RouteTable.Routes); // Create our Container IOCContainer c = new IOCContainer(); string cs = ConfigurationManager.ConnectionStrings["default"].ConnectionString; // Register a Type c.RegisterInterface<IUsersRepository>(c => new UsersRepository(cs)); // Change the default ControllerFactory for ours, passing // the Container ControllerBuilder.Current.SetControllerFactory(new IOCControllerFactory(c)); }
We're done!
Now every time you put a parameter of type IUsersRepository in any Controller, you'll get an instance of UsersRepository properly initialized with its connection string. There are no limits on how many parameters a constructor can have as long as what you request is in the container.
There are many benefits using this technique. I did a partial list in the next section.
Benefits of using IoC Containers
- Your Controllers doesn't have to know how to construct you objects.
- Less code client side (much clearer, less error-prone, focus on what to do).
- You can change the implementation of a Type in the Container at any time without changing a single line of code anywhere else (Assuming you're using Interfaces). For example, if we have a new version of the UsersRepository, named UsersRepository2. We could only change the registration to be like: c.RegisterInterface<IUsersRepository>(c => new UsersRepository2(cs)); and then all the controllers will have an instance of the new version of the repository.
- Like above, you can revert to a previous version of an object if anything goes wrong with the new version.
- Still like above, you can create mock-ups of objects deriving from the same interface than the real one. You can then test your client code with some exact scenarios or write your client code even before writing the real object!
- There are even more advantages by using real Containers available on the Internet (see below)
Conclusion
Well, we've made it. We saw how to create our own IoC Container and how to use it with .NET MVC. In the beginning, I was warning you from using our Container in a production environment. This is because there are great Containers out there (and way more robust). Plus they contains tons of tested features like (excerpt from Autofac, the one I use):
- XML Configuration Support
- Flexible Component Instantiation
- Predictable Resource Management
- Zero Intrusion
- Flexible Module System
- Autowiring
- Multiple Services per Component
- No Implementation Type Dependencies
- etc...
I suggest you check out some of these Containers, this is not an exhaustive list but hey, you're ready to choose a solution on your own since you now know how they're working!
Autofac: http://code.google.com/p/autofac/
Windsor Container: http://www.castleproject.org/container/index.html
Ninject: http://ninject.org/
Unity: http://unity.codeplex.com/
I'm putting time and effort on this blog and having you here is my reward. Make me feel better and better everyday by spreading the love with the buttons below! Also, don't hesitate to leave a comment. Thanks for reading!
Related Articles
- ASP.NET Web Forms versus .NET MVC: Comparing apples with.. really old apples
- StackOverflow DevDays - (Talk 1 of 6) .NET MVC
See ya!
6 comments:
Mike,
Is IoC the same as Dependency Injection? If not, could you please explain the difference?
Thanks!
Ron
Hi Ron, thanks for being here.
IoC and Dependency Injection are two different things but highly related because Dependency Injection is a way of doing IoC!
Using a custom controller factory in .NET MVC that creates controllers and "inject" any "dependencies" found in the constructor is a form of Dependency Injection.
Since the controllers are receiving those dependencies (instances of objects), he is not the one managing their lifetime (creation/disposal/nature, etc). The process is inverted, we have IoC.
I think that's pretty it :)
Don't hesitate if there's anything else.
We just developed a open source Light Weight Ioc / Dependency Injection framework. Can you have a look?
http://blog.isilverlabs.com/2010/08/lightweight-ioc-dependency-injection-framework/
That was useful. I'm creating a MVC app and I'm looking for a IoC container. Thank you!
Hi Mike, thanks for this great example. It was quick for me to add an IoC on a MVC2 job project :-)
However, to make it work under MVC 2, I had to change the method signature of the "GetControllerInstance" since the params changed from MVC to MVC2.
So by changing it to :
protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
And replacing the "RequestContext" for "requestContext", everything worked fine !
Thanks man, the exact introduction that I was looking for.
Post a Comment