Click here to Skip to main content
11,492,291 members (64,391 online)
Click here to Skip to main content

Get injected into the world of inverted dependencies

, 17 May 2012 CPOL 27.9K 73
Rate this:
Please Sign up or sign in to vote.
An introduction to Inversion Of Control containers and their purpose
This is an old version of the currently published article.

Introduction

You have probably heard of inversion of control containers (or IoC containers as they are refered to) by now. If you have not you should probably lock the door and continue to read really slow to understand all the goodies. This article aims to tell you the secrets to introduce you to the new world where injected dependencies will make your applications more mean and lean.

But before we begin we have to understand the problem that IoC containers solves for us. But it all began with dependency injection, or as Mr SOLID says: "Let's invert those dependencies". Look at the following simple example:

public clas Car
{
  Engine _engine = new V5();
  
  public void Start()
  {
      _engine.Start();
  }
}

public class RaceProgram
{
    public static void Main(string[] args)
    {
        Car car = new Car();
        car.Start();
    }
}

Pretty neet, huh. Well. I don't think that Cole Trickle would agree with you. He want to be able to switch engine at any time. So he said, "We don't need no stinking V5".

Invert that thang! 

So let's invert that dependency:

public class Car
{
  Engine _engine;
  
  public Car(Engine engine)
  {
    _engine = engine;
  }
  
  public void Start()
  {
      _engine.Start();
  }
}

public class RaceProgram
{
    public static void Main(string[] args)
    {
        var engine = new BigBadV10();
        Car car = new Car(engine);
        car.Start();
    }
}

There you have it. We have moved the dependency management from the class itself to the calling code. By doing so we promote code reuse since we can now control how the car is built and which parts that it should use. The CEOs in the vehicle industry would hold their heads screaming and crying at the same time if it were that easy for them. So dear fellow programmer: Take advantage of that power.

Now you are probably asking yourself, "Why the shell do I need an IoC container for that? I can just create those dependencies by myself." True. Absolutely correct. I should probably go to bed right now...

Let's take another example. We have a fairly large application which uses the repository pattern. And we have used a database within our repositories as a data source. Since we use dependency inversion without a container we have the following code:

public class UserRepository
{
    public UserRepository(IDbConnection connection)
    {
    }
}

public class MessageController : MyAspMvcController
{
    UserRepository _userRepos;
    MessageRepository _messageRepos;
    
    public MessageController()
    {
        var connection = new SqlClient(ConfigurationManager.ConnectionStrings["SomeString"].ConnectionString);
        _userRepos = new UserRepository(connection)
        _messageRepos = new UserRepository(connection)
    }
}

We have to go through every 20 or so controllers to switch implementation. And since we code against classes, and not abstractions as dependency inversion principle says, we have to replace the implementations with new ones too. Thus destroying binary compability (which means that we can't switch the DLL for another one, we have to recompile the entire application).

With an IoC (and dependencies against abstrations) we would instead have written:

public class UserRepository : IUserRepository
{
    public UserRepository(IDbConnection connection)
    {
    }
}

public class MessageController : MyAspMvcController
{
    IUserRepository _userRepos;
    IMessageRepository _messageRepos;
    
    public MessageController(IUserRepository userRepository, IMessageRepository messageRepository)
    {
        _userRepos = userRepository;
        _messageRepos = messageRepository;
    }
}

The magic would have been the following two configuration statements (syntax depends on the container implementation):

container.RegisterType<IUserRepository, DbUserRepository>();
container.RegisterType<IMessageRepository, DbMessageRepository>();

which will be replaced with:

container.RegisterType<IUserRepository, UserRepository>();
container.RegisterType<IMessageRepository, MessageRepository>();

See? We have to change two lines instead of 20 or so (i.e. in each place where the dependencies were used previously).

Chained dependencies 

The whole thing gets awesome when we start to really take advantage of IoC containers. Let's say that we have the following dependency chain:

Which gives us the following constructors:

TodoService(IUserRepository, ITodoRepository, IDomainEventDispatcher)
UserRepository(IEntityValidator, ISession)
TodoRepository(ISession)
DomainEventDispatcher(IServiceLocator)

which in turn means that we have to use the following code in each place that we want to use a TodoService:

var sessionFactory = new SessionFactory();
// config nhibernate bla bla
var session = sessionFactory.OpenSession();
var userRepository = new UserRepository(new EntityValidator(), session);
var todoRepository = new TodoRepository(session);
var dispatcher = new DomainEventDispatcher(Globals.ServiceLocator);
var service = new TodoService();

We have to repeat that code bloat in every place where we want to use the TodoService. We could of course use the Abstract factory pattern, the Builder pattern or Singleton pattern. All of those would work for parts of the problem, but the solutions would be a more complex than just telling the container which classes we have.

With our container we'll just map each class to the service(s) that it implement:

container.RegisterType<IUserRepository,UserRepository>();
container.RegisterType<IMessageRepository, MessageRepository>();

//google "lambda expresions" if you don't understand the line below
container.RegisterFactory<IDomainEventDispatcher>(container => new DomainEventDispatcher(container)); 
container.RegisterType<IEntityValidator, EntityValidator>();
container.RegisterType<ITodoService, TodoService>();

// user the service location pattern to get the service
var service = container.Resolve<ITodoService>();

See the difference? We don't have to know which depenencies each class have. We only have to know which classes we have created. That means that we never have to do anything when a class changes it's dependencies (other than adding a new class to the container).

So let's see how we look at dependencies when we are using an IoC container: 

 

We know that the class has some dependencies, but we really do not care since the class is created for us. We have gone from having to know all dependencies and how they are being created to only knowing which class we want to use. 

Scoping

We introduced a new thing in the previous section. And that was a class with a limited lifetime. The nhibernate session (could have been an ADO.NET connection, a TCP connection or something similar). Those classes stop working when the connection is lost, and continue to fail on all following calls.

We also have classes which are not thread safe.

The obvious approach to those two problems is to handle them internally in the classes. That will unfortunally increase the complexity in those classes. And with complexity comes bugs.

There is however another solution: Scoped objects.

Inversion of control containers can create scopes, which really means that the objects have a limited lifetime and are stored in a thread specific storage (which makes the objects thread safe). A typical scope is the one of a HTTP Request/Response cycle.

Wierd words 

There are some terms that you need to know when speaking IoCly with someone, and these are:

Term Description
Service The thing which is requested from the container. Typically an interface like IUserRepository, but can also be a class
Concrete  The object which is returned when a service is requested. Would be the DbUserRepository object if a IUserRepository is requested
Transient A new object will be returned each time a service is requested from the container
Scope A subset of a container which contains objects with a limited lifetime. The objects are disposed when the scope is closed. 
Child container The most typical implementation of a scope. (A container which only stores scoped objects and uses the parent container for everything else)

Introducing the container

So let's start playing with a container then. I've been around the world and back. I've sailed the seven seas. All of that just to be able to give you the perfect IoC container. Believe it or not, I FOUND  IT! It's named Griffin.Container and is made by me Wink | <img src= " />

Register those classes  

I showed you in the previous section the traditional approach used to register classes in the container. That is to use RegisterType and similar methods. But that's inefficient and time consuming. I've created an alternative and more conventional way of registering types. 

What you do is to decorate each class which should be registered in the container with an attribute named [Component]

[Component] //<-- lookie lookie
public class UserRepository : IUserRepository
{
}

Do that for all classes in all assemblies that you have your implementations in. Then use a single line to register all those classes. 

var registrar = new ContainerRegistrar();
registrar.RegisterComponents(Lifetime.Scoped, Environment.CurrentDirectory, "MyApp.*.dll");
var container = registrar.Build();

The first parameter to RegisterComponents tells the container that all classes which uses just [Component] (without a lifetime specified) should be registered as scoped components.

The main project (application entry point) doesn't even have to have references to those DLL's. They will be loaded into your application by the container. An application can't get more loosely coupled than that.

That's a powerful approach since it allows you to build a plugin system without effort. Simply create a new class library where you place all your extension points (interfaces). Then reference that class library from all plugins and implement the extension points. I've described that approach (using ASP.NET MVC3) in detail in my blog

You can also specify the lifetime of an object by using a property for the attribute: 

[Component(Lifetime = Lifetime.Singleton)]
public class UserRepository : IUserRepository
{
}

The [Component] attribute should work in 99% of the cases (for your code). I strongly discourage you from specifying which services a class implement. If you have to specify the services, it's likely that you are breaking one of the SOLID principles. I suggest that you try to refactor your class (break it into smaller classes) and register each class with the [Component] attribute.

Modules

Sometimes it's impossible to use the [Component] attribute. For instance when you want to inject external dependencies in the container. For that you can either use the container directly to register the dependencies. But a much better solution is to let each module/section/namespace of your code manage it's own dependencies. It's quite easy. Simply create a new class and let it implement the interface IContainerModule like this:

public class UserDependencyRegistrations : IContainerModule
{
    public void Register(IContainerRegistrar registrar)
    {
        registrar.AddService<ISession>(container => container.Resolve<ISessionFactory>().OpenSession());
    }
}

Then you have to load the modules from your application root (like Program.cs):

public class Program
{
    public static Main(string[] args)
    {
        var registrar = new ContainerRegistrar();
        registrar.RegisterModules(Environment.CurrentDirectory, "MyApp.*.dll");
        var container = registrar.Build();
        
        // rest of your code here.
    }
}

You have now loaded all services which have been defined in modules which exists in any of the matching DLLs in the current directory.

Domain events 

Griffin.Container contains another feature which aids you greatly in keeping the coupling low between classes. And that's domain events.

Domain events are a weak events, which means that the subscriber cannot prevent the publisher from being garbage collected. Here is a great cartoon illustrating the problem with the .NET event model:


(click on the image for a larger one) 

A sample event would look like this:

public class UserCreated
{
	User _user;
	
	public UserCreated(User user)
	{
		_user = user;
	}
	
	public User User { get { return _user; } }
} 

Which is generated by our UserService

[Component]
public class UserService : IUserService
{
	IUserRepository _repos;
	
	public UserService(IUserRepository repos)
	{
		_repos = repos;
	}
	
	public void Register(string username, string password, string email)
	{
		var user = _repos.Create(username, password);
		user.Email = email;
		_repos.Save(user);
		
		DomainEvent.Publish(new UserCreated(user));
	}
}  

The event is caught by two different handlers. 

[Component]
public class UserWelcomeEmailSender : ISubscriberOf<UserCreated>
{
	ITemplateGenerator _templateGenerator
	public UserWelcomeEmailSender(ITemplateGenerator templateGenerator)
	{
		_templateGenerator = templateGenerator;
	}
	
	public void Handle(UserCreated e)
	{
		var emailBody = _templateGenerator.Generate("UserCreatedTemplate", e.UserName);
		var msg = new MailMessage("mysuper@cool.site", e.Email);
		
		// It's possible to configure the SmtpClient in app.Config
		// which means that we can just created it and send the email.
		var client = new SmtpClient();
		client.Send(msg);
	}
}  
[Component]
public class NotifyAdminsOfNewUser : ISubscriberOf<UserCreated>
{
	ITemplateGenerator _templateGenerator
	public NotifyAdminsOfNewUser(ITemplateGenerator templateGenerator)
	{
		_templateGenerator = templateGenerator;
	}
	
	public void Handle(UserCreated e)
	{
		var emailBody = _templateGenerator.Generate("ValidateNewUser", e.UserName);
		var msg = new MailMessage("mysuper@cool.site", e.Email);
		var client = new SmtpClient();
		client.Send(msg);
	}
}

See? The UserService have no knowledge of the subscribers or what they does. Nor do the subscribers know or care where the event came from. That means that you can switch UserService for something else or add another subscriber without affecting the other parts. 

 Let's just look at the constructors of each class: 

public UserCreated(User user) 
public UserService(IUserRepository repos)
public UserWelcomeEmailSender(ITemplateGenerator templateGenerator)
public NotifyAdminsOfNewUser(ITemplateGenerator templateGenerator) 

Can you tell what the classes are responsible of by just looking at the constructors?  Of course. Small classes are a lot easier to understand and follow. Having full understanding of what a class does make it less likely that you introduce a bug when you fix/improve it. 

This is very important. The container takes care of all dependency management and object creation for you, which means that it doesn't matter how many classes you create or how small they are.  

Summary

I hope that I've at least made you understand a bit more about containers and why you should use them.  

I could go on and on about code quality and why an inversion of control container aids you in increasing the quality. But I don't know if you would appreciate that. Wink | <img src=  That's why I'm going to stop right here. 

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

jgauffin
Founder Gauffin Interactive AB
Sweden Sweden
Founder of OneTrueError, a .NET service which captures, analyzes and provide possible solutions for exceptions.

blog | twitter
Follow on   Twitter   LinkedIn

Comments and Discussions

Discussions on this specific version of this article. Add your comments on how to improve this article here. These comments will not be visible on the final published version of this article.
 
-- There are no messages in this forum --

Discussions posted for the Published version of this article. Posting a message here will take you to the publicly available article in order to continue your conversation in public.
 
Questionnow i feel i can understand this someday i'd avoid hard coding ,thank you so mucn for this. Pin
varun15015-Apr-15 17:53
membervarun15015-Apr-15 17:53 
QuestionGreat Article Pin
Fabio Franco18-Apr-13 1:12
memberFabio Franco18-Apr-13 1:12 
QuestionMessage Automatically Removed Pin
11-Jun-12 6:58
memberMember 852840911-Jun-12 6:58 
AnswerMessage Automatically Removed Pin
11-Jun-12 6:59
memberMember 852840911-Jun-12 6:59 
GeneralMy vote of 5 Pin
Kanasz Robert11-Jun-12 0:38
mvpKanasz Robert11-Jun-12 0:38 
QuestionVery nice Pin
CIDev31-May-12 11:13
memberCIDev31-May-12 11:13 
QuestionNice Pin
P.Salini29-May-12 1:59
memberP.Salini29-May-12 1:59 
GeneralMy vote of 5 Pin
Fernando "Poorlyte" Girotto27-May-12 17:26
memberFernando "Poorlyte" Girotto27-May-12 17:26 
GeneralMy vote of 5 Pin
burndavid9327-May-12 6:21
memberburndavid9327-May-12 6:21 
QuestionWonderful article Pin
Marc Clifton26-May-12 6:54
protectorMarc Clifton26-May-12 6:54 
AnswerRe: Wonderful article [modified] Pin
jgauffin26-May-12 7:24
memberjgauffin26-May-12 7:24 
GeneralMy vote of 5 Pin
xabikos19-May-12 3:12
memberxabikos19-May-12 3:12 
Generalmy 5 Pin
Shahriar Iqbal Chowdhury18-May-12 4:08
memberShahriar Iqbal Chowdhury18-May-12 4:08 
GeneralMy vote of 5 Pin
Ankush Bansal17-May-12 19:54
memberAnkush Bansal17-May-12 19:54 
GeneralMy vote of 5 Pin
HoyaSaxa9317-May-12 9:52
memberHoyaSaxa9317-May-12 9:52 
GeneralMy vote of 5 Pin
Eugene Sadovoi17-May-12 9:00
memberEugene Sadovoi17-May-12 9:00 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Terms of Use | Mobile
Web04 | 2.8.150520.1 | Last Updated 17 May 2012
Article Copyright 2012 by jgauffin
Everything else Copyright © CodeProject, 1999-2015
Layout: fixed | fluid