Dependency injection is a design pattern used to handle the problem of tight coupling between classes. Although it helps a lot, it’s not perfect. There are situations in which the pattern becomes cumbersome – the best example is probably nested constructor injected classes.
Let’s look at a code sample
1: public class Repository
3: private readonly Database db;
4: public Repository(Database db)
6: this.db = db;
Nothing special here, but let’s also assume the following:
1: public class Database
3: private SqlConnection conn;
4: public Database(SqlConnection conn)
6: this.conn = conn;
Now the problem begins to appear. The client code for the repository class needs to supply the Database object as well as the SqlConnection object. So we need to write:
2: var conn = new SqlConnection();
3: var db = new Database(conn);
4: var repo = new Repository(db);
This isn’t so bad when there are only 2 nested classes, but it’s easy to imagine a situation with more, and handling that by hand is not very comfortable. So what can do about it?
Come forth DI Container
These kind of problems are addressed by DI containers. The concept is simple
2: var repo = container.Resolve<Repository>();
So what happened to the database and SqlConnection instantiation? Well, DI container happened. The container knows the dependencies between classes, and so, it can create an object for us.
Just to be clear, the injection isn’t gone, it’s just handled by the container.
How does the container know how to construct a class?
You have to tell him.
2: container.Register<SqlConnection>(new SqlConnection());
3: container.Register<Database>(new Database(container.Resolve<SqlConnection>()));
4: container.Register<Repository>(new Repository(container.Resolve<Database>()));
Yes, we have to perform that work, but it’s only required once, and there will probably be a ‘special’ place for that, but that’s highly dependent on the application itself.
Additionally, most containers have methods to make this process less painful – e.g registering all types within an assembly.
What else can a DI container do?
Depending on the container, there are several more things that it can handle.
One example would be to register a singleton, so a container will return the same instance each time you do a Resolve().
Another would be to be able to control the lifetime of the registered instance (e.g a single instance within a web request)
There are also several platform dependent cases like handling constructor injection for controllers in asp.net mvc and many other.
Where do I get one?
So what have we learned?
DI containers are classes that help to cope with problems that arise form applying the Dependency Injection pattern. They make the code cleaner and easier to maintain and we all love them, just can’t decide which one…