Dependency Injection
There are two terms related to object composition in .NET that are thrown around quite a bit these days: “Dependency Injection” (DI) and “Inversion of Control” (IoC). I’d like to use this article to provide some basic definitions, implement a basic DI Container, and make you aware of the popular related frameworks.
The Basics
Let’s start with “Inversion of Control”. What does that mean? To understand, let’s look at a fake authentication service. Here’s some demo code:
public class AuthenticationService { readonly IUserRepository users; public AuthenticationService() { users = new UserRepository(new Database()); } public bool Login(string username, string password) { var user = users.GetByUsernameAndPassword(username, password); if(user != null) { //set up some stuff } return user != null; } }
Here’s what I want you to notice. The authentication service uses (or is dependent on) a second class: the UserRepository, which is itself dependent on the database. The really important detail here is that the authentication service is “controlling” the creation of the UserRepository and the Database. Let’s “invert” that control:
public class AuthenticationService2 { readonly IUserRepository users; public AuthenticationService2(IUserRepository users) { this.users = users; } public bool Login(string username, string password) { var user = users.GetByUsernameAndPassword(username, password); if(user != null) { //set up some stuff } return user != null; } }
The dependencies are still the same. But, now we’ve taken control of the creation of the UserRepository out of the equation. We’ve inverted the control. We’ve also exercised the Single Responsibility Principle. The responsibility of creating the service (and its dependencies) is removed and allows the authentication service to focus on using the repository. Something else will take on the responsibility of creating and providing the UserRepository.
So, “Inversion of Control” is the act of removing the control of dependency creation from the consuming object. But what is “Dependency Injection”? Well, once we’ve disallowed the consumer to create its own dependencies, we then need to provide pre-constructed dependencies to it in order for it to function properly. The act of providing created and configured dependencies to a consumer is “Dependency Injection.” In our example above, the dependencies would be injected through the constructor. This is called “Constructor Injection” and it is the most common mechanism to accomplish this. However, you can also inject dependencies through properties, or even fields.
The Reasoning
Why would you bother with this? Well, I mentioned the Single Responsibility Principle above as it pertains to object creation. But, it’s even more important as it pertains to your entire architecture.
Here’s a recap of the principle:
… every class should have a single responsibility, and that responsibility should be entirely encapsulated by the class. All its services should be narrowly aligned with that responsibility.
When we build complex systems, we can more easily tackle the problem by de-composing it into a series of small, simple problems. This plays out in object oriented programming through the creation of small, simple classes or objects. These objects are easy to understand because they do one thing and one thing only. (They follow the Single Responsibility Principle.) But, this causes a new technical problem. Now, instead of having a few large objects, we’ve got many small objects … which all have dependencies and need to coordinate in order to perform the complex tasks. So, how do we do that? Well, there are several different ways. In this article, we’re talking about Dependency Injection though. So, let’s try it out.
Build It Yourself
Let’s build a class with a single responsibility. Its job is to instantiate other classes. It should be able to understand their dependencies and assemble things correctly. Here’s some code:
public class NaiveContainer { readonly Dictionary<Type, Func<object>> factories = new Dictionary<Type, Func<object>>(); public void Register(Type service, Func<object> factory) { factories[service] = factory; } public void Register(Type service, Type implementation) { Register(service, () => { return CreateInstance(implementation); }); } public object GetInstance(Type service) { var factory = factories[service]; return factory(); } object CreateInstance(Type type) { var args = DetermineConstructorArgs(type); return args.Length > 0 ? Activator.CreateInstance(type, args) : Activator.CreateInstance(type); } object[] DetermineConstructorArgs(Type type) { var args = new List<object>(); var constructor = SelectEligibleConstructor(type); if (constructor != null) { args.AddRange(constructor.GetParameters().Select(info => GetInstance(info.ParameterType))); } return args.ToArray(); } static ConstructorInfo SelectEligibleConstructor(Type type) { return (from c in type.GetConstructors() orderby c.GetParameters().Length descending select c).FirstOrDefault(); } }
This is called a Dependency Injection Container. Now this is a very naïve implementation, but it’s still fairly powerful. Notice that we can use the Register method to associate a function with a Type. The function returns an instance of some object … presumably of the Type that it was registered for. Also notice, GetInstance, which looks up a function by type and calls it, returning the object. Now, the really interesting part is the overload of Register that takes a service Type and an implementation Type. This method registers a generic function capable of using reflection to inspect the implementation’s constructor parameters and recursively call GetInstance to obtain their values. Now we can create objects with any number of constructor dependencies, register them with the container and easily call GetInstance to pull back a usable instance of the type with all its dependencies satisfied.
Production Quality
The implementation above is not suitable for real-world use, but it is fun code to study and think through. For a slightly more realistic implementation, have a look at Caliburn.Micro’s SimpleContainer on which the above example was based. It was designed for simple client composition scenarios. However, if you need a more robust solution or more features, you are going to want to investigate some other options. Here’s a few that I’ve used in production applications at one time or another: MEF, Ninject, Windsor and StructureMap. Autofac is also a pretty popular option. There are many to choose from.
If you are using Cocktail, you’re in luck. We’ve already worked this out for you. Cocktail uses MEF internally to configure itself and provide developers with a standard model for changing or extending its behavior. It’s built from the beginning with IoC and Separation of Concerns at its core.
Wrapping Up
In order to tackle complex problems, we need to break them down into small, simple pieces. However, having many small pieces instead of a few large ones introduces a new technical challenge. To ease the composition and coordination of many small objects, one solution is to adopt an Inversion of Control Container, using it to declare the relationships between the objects (via constructor signatures) and allowing it to do the heavy lifting of complex object construction for us.
Recent comments ( 2 )
Good article! So with IOC since you take away the “has a”
object created in the class, how does the container differentiate one instance from another of the same type?
@Bob Buchanan, the container doesn’t differentiate if you register multiple types with the same contract. It’s up to your code to differentiate when the container gives you all intstances matching a certain contract.
The trick is in what contract you chose to register your type. In MEF for example a contract can be an interface or a class. In addition you can specify a contract name.
For example:
public interface IDependency
{
}
[Export(“A”, typeof(IDependency))]
public class DependencyA : IDependency
{}
[Export(“B”, typeof(IDependency))]
public class DependencyB : IDependency
{
}
[Export]
public class SomeClassWithDependencyToA
{
[ImportingContstructor]
public SomeClassWithDependencyA([Import(“A”)] IDependency dependency)
{
}
}
Follow