Dependency Resolution and Hosting: Dependency Injection
Excerpt by Phil Ledgerwood | March 19, 2013
There's a sense in which the entire idea behind having consumable services such as the ASP.NET Web API provides is a decoupled, reusable architecture. You want to have a body of data and business logic decoupled from a client application so it can be used by a variety of client applications. Testability is also an important architectural issue. A unit of code must be decoupled from its dependencies in order to write automated tests against it.
Practices like Test Driven Development (TDD) virtually force writing code this way. In software development, all code should be written with these principles in mind, including our client applications and the services, themselves.
Dependency Injection is one way to help achieve decoupling, reusability, and testability in Web API services. With this in mind, the Web API framework was designed to facilitate this technique.
Inversion of Control
In a broad sense, Inversion of Control is the concept of allowing something outside of a system to control it. In other words, the system doesn't run itself but something or someone on the outside invokes it when it is needed. In a very broad sense, things like a web-based interface over a database could be an example of Inversion of Control. In object-oriented software development, Inversion of Control means that, instead of tightly coupling objects to other objects in code, objects are pulled together at runtime by some outside mechanism.
If you go to a pizza restaurant, you'll see people making pizzas. What you probably won't see are people growing tomatoes for the sauce, milking cows to make the cheese, or processing cardboard to make the pizza boxes. The pizza shop needs all those things, but they are not responsible for creating them. Someone else creates them and gives them to the pizza shop to use to get their job done. This is roughly analogous to Inversion of Control in software development.
For instance, if you are writing a repository class that will save products to the database, it will need some kind of data access object to perform the database operations. One way to do this is simply to instantiate a data access object in your repository code. However, this will tightly couple your repository code to that data access object.
With Inversion of Control, you code the repository to an abstraction (usually an interface) that represents the data access object and you rely on some outside mechanism to instantiate a data access object and give it to your repository at runtime. By doing this, your repository is now decoupled from any particular data access object. This allows you to reuse the repository code with various data access components as well as making it testable (by giving the repository a "mock" data access object).
There are various techniques that give you Inversion of Control in software development and one of the more common and prominent ones is Dependency Injection.
Using Dependency Injection
Many classes that you write need other objects to do their jobs, and nowhere is this more true than in a services layer. Many services need objects to access data, process business rules, perform validation, handle logging, and a wide array of functionality that the services layer tends to coordinate.
With Dependency Injection, programmers code their classes assuming that these objects will be instantiated from an entity outside their class and passed into it. Sometimes, this is done by exposing properties that can be set from the outside. A very common way to set up for Dependency Injection, however, is to handle it in the constructor.
public Logger(IFileWriter fileWriter)
_fileWriter = fileWriter;
In the previous example, there is a Logger class that needs a file writer object. In tightly-coupled code, the Logger would just instantiate its own file writer:
_fileWriter = new FileWriter();
However, this would bind the Logger class to a specific FileWriter object. By allowing an object that implements IFileWriter to be passed into the constructor, you can now reuse this class for various kinds of file writers. Also, this code is very testable because you can pass in a fake file writer for testing purposes-perhaps one that writes to a test file or even does nothing at all.
If code is written to receive its dependencies from the outside, these objects have to come from somewhere. Something on the outside needs to instantiate these objects and pass them into the objects that require them. Some mechanism needs to inject these dependencies. There's more than one way to do this, but this is such a common issue in software development that common libraries have been written to solve this problem. They are often called IoC (Inversion of Control) Containers or DI (Dependency Injection) frameworks.