Click here to Skip to main content
15,860,972 members
Articles / Web Development / ASP.NET
Alternative
Article

Get injected into the world of inverted dependencies

Rate me:
Please Sign up or sign in to vote.
4.92/5 (31 votes)
21 May 2012CPOL13 min read 68.2K   75   16
An introduction to Inversion Of Control containers and their purpose

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 this article really slowly to understand all the goodies. This article aims to reveal the secrets, and 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:

C#
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 neat, huh. Well. I don't think that Cole Trickle would agree with you. He would want to be able to switch the engine at any time. So he said, "We don't need no stinking V5".

Invert that thang!

So let's invert that dependency:

C#
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:

C#
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:

C#
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):

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

which would 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:

Image 1

Which gives us the following constructors:

C#
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:

C#
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(userRepository, todoRepository, dispatcher);

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 different 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 implements:

C#
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:

Image 2

We know that the class has 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 should be created to only knowing which service we want to use. 

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)

 

Lifetimes

So you have seen that the container can help you assemble/create all your objects for you. You just tell it which service that you want to get and you'll get the proper concrete(s). It's great, isn't it? However, that's just half of what the container can do for you.

You have probably heard of the singleton pattern and that it prevent you from using one of the fundamental principles of object oriented programming: Extension through inheritance. You can of course combine the singleton pattern with the proxy pattern, which would take you a bit further.  

With a container you do not have that limitation. The container do not just handle the dependencies, or do also control the lifetime of each object. It does that since it holds a reference to each created object and only removes that reference (and disposes all classes which implement IDisposable) upon certain conditions.  

The typical usage of a container is that you generate it when your application starts and dispose it when your application ends. That enables you to be able to provide to kind of lifetimes for all of your objects: Singletons and transient.

That's quite interesting, since you do not have to do anything special in your classes to make them singletons (other than making sure that they are thread-safe and/or manage their state fields correctly). In other words: Extension through inheritance is still possible. You just create a new concrete and register the new implementation instead.

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. 

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 called 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 a 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 decorate each class which should be registered in the container with an attribute named [Component].

C#
[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.

C#
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 each plugin and implement some/all 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:

C#
[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:

C#
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):

C#
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.

You might wonder how you use the container later on in your application. There are nuget   packages available for WCF, ASP.NET MVC3 which takes care of the integration for you. For other application types you need to manually create scopes and invoke the root classes (which in turn get their dependencies injected). 

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:

Image 4
(click on the image for a larger one)

A sample event would look like this:

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

Which is generated by our UserService:

C#
[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.

C#
[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. Hence the container promotes you to create small reusable classes which follows the SOLID principles (since you without effort can add them to the container). 

Best practices

There are some practices which I'm using. Note that they are my very own subjective practices. They are based on my on experience (I've been using containers for about four years).

Avoid service Location 

The container itself is a Service Locator. Which means that you can use service location, instead of constructors to find the services that you depend on. Something like this:

public class UserService
{
    public void Register(string userName)
    {
        var repository = Container.Instance.Resolve<IUserRepository>();
        repository.Create(new User(userName));
    }
}  

That's naughty. You can't tell that the Register method has a dependency without looking at the method code (or by invoking the method).

That becomes a maintenance nightmare when applications grow since the dependencies are hidden. You either have to inspect every method or invoke every execution path in every method to be sure that you have discovered and configured all dependencies correctly.

Simply put: Don't use service location if you can avoid it.

Depend on interfaces, not concretes

That's part of the D principle in SOLID (as in Dependency Inversion). Depending on interfaces (abstractions) instead of concretes (classes) makes it easier to refactor your code. You can for instance create a facade for the interface if you've decided to split the concrete into smaller classes (and break down the original interface into smaller interfaces). You would still have binary compability (and therefore only having to recompile and update the class library assembly). 

There is nothing that says that there are a 1-1 mapping between a class and the interface that it implements. In fact, it's much better that you try to break down a class into smaller interfaces.

It will make code maintenance and refactoring a lot easier in the future.

Don’t choose a longer lifetime to get better performance

Complexity is tied to the lifetime. Long living objects have to care about thread safety and state management.

Let's take database transactions as an example. When having scoped objects you can simply register a Unit Of Work implementation as scoped. The UoW will automatically be used by all scoped objects (and therefore chare the transaction). The transaction will rolledback if SaveChanges() has not been called when the scope is disposed (and therefore the Unit Of Work). You can't do that with singletons since they live as long as the application.

Hence you need to use code like this:

C#
public class UserService : IUserService
{
    public void Add()
    {
        var (var uow = UnitOfWork.GetOrCreate())
        {
            // do stuff.
            uow.Save();
        }
    }
} 

Instead of:  

C#
public class UserService : IUserService
{
    public UserService(IUnitOfWork uow)
    {
    }
     public void Add()
    {
        // do stuff.
    }
} 

Don't mix lifetimes

Mixing lifetimes can lead to undesired effects if you are not careful. Especially when projects have begun to grow and getting more complex dependency graphs.

Example:

C#
// EF4 context, scoped lifetime
class Dbcontext : IDbContext
{
}
 
// short lifetime, takes db context as a dependency
class Service1 : IService1
{
}
 
// Single instance, keeps a cache etc.
class Service2 : IService2
{
} 

Service2 takes service1 as a dependency which in turn requires a dbcontext. Let’s say that the dbcontext has an idle timeout which closes the connection after a while. That would make service1 fail and also service2 which depends on it.

Simply be careful when using different lifetimes.

Try to avoid primitives as constructor parameters

Try to avoid using primitives as constructor parameters (for instance a string). It’s better to take in object since it makes extension (subclassing) easier.

Hey, I need my primitives you say. Take this example:

C#
public class MyRepository
{
    public MyRepository(string connectionString)
    {
    }
}

Well. You’re class breaks SRP. It acts as a connection factory and a repository. Break the factory part out:

C#
public class MyRepository
{
    public MyRepository(IConnectionFactory factory)
    {
    }
}  

The factory itself can be registered as an instance in the container:

C#
container.RegisterInstance<IConnectionFactory>(new AppConfigConnectionFactory("MyConStr") 

Avoid named dependencies 

Some containers allow you to register services with names, so that you can get a specific implementation from the container. 

C#
container.Register<IDataSource, DataSource>("CustomerDb", new ConstructorParameter("theConString"))  

Don't do that. Named dependencies suggest that you want to use a factory. Using factories makes the intent more clear and it's easier to tell that a specific implementation is required.

Using a factory: 

C#
public class AccountantRepository : IAccountantRepository
{
	IDbConnection _connection;
	
	public class AccountantRepository(IConnectionFactory factory)
	{
		_connection = factory.Create("EconomicsDb");
	}
} 

Using named dependency: 

C#
public class AccountantRepository : IAccountantRepository
{
	IDbConnection _connection;
	
	public class AccountantRepository(IDbConnection economicsDb)
	{
		_connection = economicsDb;
	}
} 

The change might look trivial. But the change is for two reasons:

  • Using named dependencies are as fragile as using magic strings instead of constants.
  • A variable name doesn't really say that you are using different implementations for different classes, a factory do.   

Avoid fat constructors

A fat constructor take several dependencies in it. It most often indicate a violation of Single Responsibility Principle. Refactor the class into smaller ones. Use domain events if you have to take actions when different things happen (as in the email example above).

I try to use a maximum of three parameters in my constructors. 

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.

Article History 

  • 2012-05-17 First version
  • 2012-05-18 Added best practices  
  • 2012-05-20 Added fat constructors, change some wordings.  
  • 2012-05-26 Added lifetimes 

License

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


Written By
Founder 1TCompany AB
Sweden Sweden

Comments and Discussions

 
Questionnow i feel i can understand this someday i'd avoid hard coding ,thank you so mucn for this. Pin
varun15015-Apr-15 16:53
varun15015-Apr-15 16:53 
QuestionGreat Article Pin
Fabio Franco18-Apr-13 0:12
professionalFabio Franco18-Apr-13 0:12 
GeneralMy vote of 5 Pin
Kanasz Robert10-Jun-12 23:38
professionalKanasz Robert10-Jun-12 23:38 
QuestionVery nice Pin
BillW3331-May-12 10:13
professionalBillW3331-May-12 10:13 
QuestionNice Pin
P.Salini29-May-12 0:59
P.Salini29-May-12 0:59 
GeneralMy vote of 5 Pin
Fernando "Poorlyte" Girotto27-May-12 16:26
Fernando "Poorlyte" Girotto27-May-12 16:26 
GeneralMy vote of 5 Pin
burndavid9327-May-12 5:21
burndavid9327-May-12 5:21 
QuestionWonderful article Pin
Marc Clifton26-May-12 5:54
mvaMarc Clifton26-May-12 5:54 
AnswerRe: Wonderful article Pin
jgauffin26-May-12 6:24
jgauffin26-May-12 6:24 
GeneralMy vote of 5 Pin
xabikos19-May-12 2:12
xabikos19-May-12 2:12 
Generalmy 5 Pin
Shahriar Iqbal Chowdhury/Galib18-May-12 3:08
professionalShahriar Iqbal Chowdhury/Galib18-May-12 3:08 
GeneralMy vote of 5 Pin
Ankush Bansal17-May-12 18:54
Ankush Bansal17-May-12 18:54 
GeneralMy vote of 5 Pin
HoyaSaxa9317-May-12 8:52
HoyaSaxa9317-May-12 8:52 
GeneralMy vote of 5 Pin
Eugene Sadovoi17-May-12 8:00
Eugene Sadovoi17-May-12 8:00 

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

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