Dependency Injection Pattern In C# - Short Tutorial

This article will be a concise tutorial on Dependency Injection Pattern and other related topics: the Dependency inversion principle (DIP), Inversion of control (IoC) principle, and Dependency Injection Container (aka IoC container). While short, this tutorial will go into enough breadth and depth to provide a solid overview of the topics. This article is well suited for those who need to master basic concepts fast.

Interview survival tutorial

The goal of this article is to provide a short, concise tutorial about Dependency Injection Pattern and related topics. It can be used as “the first contact tutorial” for those who want to learn about the topic or as “refresher material” for those who want to refresh their knowledge. This article can be used as interview preparation material for those who need to master basic concepts fast, as well.

Topics covered in this tutorial are typically asked of a candidate interviewing for a Senior Software Engineer (.NET) position.

This is a basic tutorial and does not go into all of the finest details. People learn best when presented with knowledge in “concentric circles." In the first circle, they are taught the basis of everything; in the second concentric circle, they go over what they learned in the previous circle and extend that knowledge with more details; then, in the next circle, they do something similar again, etc. This article is meant to be the first pass on the topic for an interested reader.

Topics presented

The following topics are presented,

  • Dependency Injection Pattern – DI (*)
  • Dependency inversion principle – DIP (**)
  • Inversion of control – IoC (***)
  • Dependency Injection Container (****)

Dependency Injection Pattern – DI (*)

First of all, “Dependency Injection Pattern” is a SOFTWARE DESIGN PATTERN. It is called a "pattern" because it suggests low-level specific implementation to a specific problem.

The main problem this pattern aims to solve is how to create “loosely coupled” components. It does that by separating the creation of components from their dependencies.

There are four main roles (classes) in this pattern:

  1. Client. The client is a component/class that wants to use services provided by another component, called a Service.
  2. Service-Interface. The service interface is an abstraction describing what kind of services the Service component is providing.
  3. Service. The Service component/class is providing services according to Service-Interface description.
  4. Injector. Is a component/class that is tasked with creating Client and Service components and assembling them together.

The way it works is that the Client is dependent on Service-Interface IService. The client depends on the IService interface, but has no dependency on the Service itself. Service implements the IService interface and offers certain services that the Client needs. Injector creates both Client and Service objects and assembles them together. We say that Injector “injects” Service into Client.

Below is a class diagram of this pattern.

Here is a sample code of this pattern.

public interface IService {
    void UsefulMethod();
}
public class Service: IService {
    void IService.UsefulMethod() {
        //some usefull work
        Console.WriteLine("Service-UsefulMethod");
    }
}
public class Client {
    public Client(IService injectedService = null) {
        //Constructor Injection
        _iService1 = injectedService;
    }
    private IService _iService1 = null;
    public void UseService() {
        _iService1?.UsefulMethod();
    }
}
public class Injector {
    public Client ResolveClient() {
        Service s = new Service();
        Client c = new Client(s);
        return c;
    }
}
internal class Program {
    static void Main(string[] args) {
        Injector injector = new Injector();
        Client cli = injector.ResolveClient();
        cli.UseService();
        Console.ReadLine();
    }
}

Types of Dependency Injection based on a method of injecting

Often in literature [1] one can find mentioned different types of Dependency Injection, classified based on the method of injecting Service into Client. I think that is an unimportant distinction, since the effect is always the same. That is, reference to Service is being passed to Client, no matter how. But, for completeness, let us explain it.

So, the types of Dependency Injection, are:

  1. Constructor Injection – Injection is done in Client constructor
  2. Method Injection – Injection is done via a dedicated method
  3. Property Injection – Injection is done via public property

Here is the code that demos each type.

public interface IService {
    void UsefulMethod();
}
public class Service: IService {
    void IService.UsefulMethod() {
        //some usefull work
        Console.WriteLine("Service-UsefulMethod");
    }
}
public class Client {
    public Client(IService injectedService = null) {
        //1.Constructor Injection
        _iService1 = injectedService;
    }
    public void InjectService(IService injectedService) {
        //2.Method Injection 
        _iService1 = injectedService;
    }
    public IService Service {
        //3.Property Injection
        set {
            _iService1 = value;
        }
    }
    private IService _iService1 = null;
    public void UseService() {
        _iService1?.UsefulMethod();
    }
}
public class Injector {
    public Client ResolveClient() {
        Service S = new Service();
        //NOTE: This is tutorial/demo code, normally you
        //implement only one of these 3 methods
        //1.Constructor Injection
        Client C = new Client(S);
        //2.Method Injection 
        C.InjectService(S);
        //3.Property Injection
        C.Service = S;
        return C;
    }
}

Main point - Client unaware of the type of Service injected

Let us emphasize the main component in this design pattern. It is the fact that Client is completely ignorant of the type of Service injected. It just sees interface IService, and has no clue what version of Service is being injected. Let us look at the following class diagram:

The Client has no knowledge of which service is being injected, whether if it is Service1, Service2, or Service3. That is the desired result. We see that components/classes Client, Service1, Service2, and Service3 are “loosely coupled."

The Client class is now more reusable and testable. One typical usage of this feature is that in the production environment Client is injected with real service Service1, and in the test environment, the Client is injected Service2, which is a mock service created just for testing.

Benefits of this pattern

The benefits of this pattern are:

  • Creation of loosely coupled components/classes Client and Service
  • Client has no dependency nor knowledge of Service, which makes it more reusable and testable
  • Enables parallel development of components/classes Client and Service by different developers/teams, since the boundary between them is clearly defined by the IService interface
  • It eases the unit-testing of components.

Disadvantages that this pattern brings are:

  • More effort to plan, create, and maintain an interface
  • Dependency on Injector to assemble components/classes.

Similar patterns

This pattern is very similar to GoF book Strategy Pattern [2]. The class diagram is practically the same. The difference is in intent: 1) Dependency injection is more like Structural Patten that has the purpose to assemble loosely coupled components and once assembled they usually stay that way during Client lifetime; while 2) Strategy pattern is a Behavior Pattern whose purpose is to offer different algorithms to the problem. These are usually interchangeable during the Client lifetime.

Dependency inversion principle – DIP (**)

So, the “Dependency inversion principle (DIP)” is a SOFTWARE DESIGN PRINCIPLE. It is called “principle” because it provides high-level advice on how to design software products.

DIP is one of five design principles known under the acronym SOLID [3], promoted by Robert C. Martin [5]. The DIP principle states:

  1. High-level modules should not depend on low-level modules. Both should depend on the abstraction.
  2. Abstractions should not depend on details. Details should depend on abstractions.

My interpretation is:

While high-level principles talk about “abstraction," we need to translate that into terms in our specific programming environment. In this case, we'll use C#/.NET. Abstractions in C# are realized by interfaces and abstract classes. When talking about “details," the principle means “concrete implementations."

So, basically, that means that DIP promotes the usage of the interfaces in C# and concrete implementations (low-level modules) should depend on interfaces.

Traditional module dependencies look like this:

DIP proposes this new design:

As you can see, some dependencies (arrows) have inverted directions, so that is where the name “inversion” originated.

The goal of DIP is to create “loosely coupled” software modules. Traditionally, high-level modules depend on low-level modules. DIP's goal is to make high-level modules independent of low-level modules’ implementation details. DIP achieves that by introducing an “abstract layer” (in the form of an interface) between them.

Dependency Injection Pattern (*) follows this principle, and is often referred to as closely related to DIP realization. But, the DIP principle is a broader concept and has an influence on other design patterns. For example, when applied to the factory design pattern or Singleton design pattern, it suggests that those patterns should return a reference to an interface, not a reference to an object.

Inversion of control – IoC (***)

Again, “Inversion of control (IoC)” is a SOFTWARE DESIGN PRINCIPLE. It is called “principle” because it provides high-level advice on how to design software products.

In traditional programming, a custom code always has flow control and calls libraries to perform tasks.
The IoC principle proposes that (sometimes) the flow of control should be given to libraries (“framework”), which will call custom code to perform tasks.

When we say “framework,” we mean a specialized, arbitrary complex reusable module/library that is designed for a specific task. Custom code is written in a manner so it can work with that “framework.” We say that the “flow of control is inverted” since now “framework” calls into custom code.

The framework plays the role of the main program in controlling application activity. The main control of the program is inverted, moved away from you to the framework. Inversion of control is a key part of what makes a framework different from a library ([26]).

The IoC principle promotes the development and usage of reusable “software frameworks” that implement common scenarios. Then, problem-specific custom code is written and made to work together with the “framework” to solve a specific task.

While IoC principle is often mentioned in the context of Dependency Injection Pattern (*) which follows it, it is a much broader concept than DIP. For example, an “UI framework” based on event handlers/callback methods also follows IoC principle. See [26], [25], [8] for more explanation.

Dependency Injection Pattern (*) follows this principle, since the normal traditional approach is for the Client to create a Service and establish dependency. Here, control is inverted. That is, the creation of Service and the creation of dependency are delegated to the Injector, which in this case is the “framework."

Dependency Injection Container (****)

So, “Dependency Injection Container (DI Container)” is a SOFTWARE MODULE/LIBRARY that enables automatic Dependency Injection with many advanced options.

In the terminology of IoC principle (***), DI Container has the role of the “framework” so often you will see it referred to as “DI framework." My opinion is that word “framework” is overused, and this leads to confusion (you have ASP MVC framework, DI framework, Entity Framework, etc.).

Often in literature, this is referred to as “IoC Container." But, I think the IoC principle (***) is a broader concept than the DI pattern (*). Here we take the reality of the DI pattern implementation on a large scale. So, “DI Container” is a much better name, but the name “IoC Container” is very popular and is used broadly to mean the same thing.

What is DI Container

Remember DI pattern (*) and the role of the Injector? So, DI Container is an advanced module/library that serves as an Injector for many Services at the same time. It enables the implementation of DI pattern on a large scale, with many advanced functions. DI Containers are a very popular architectural mechanism and many popular frameworks such as ASP MVC plan for and enable the integration of DI Containers.

The most popular DI Containers are Autofac [10], Unity [15], Ninject [16], Castle Windsor [17], etc.

DI Container functions

Typical functions that one DI Container will offer are:

Register Mappings

You need to tell the DI Container mappings between abstraction (interfaces) to concrete implementations (classes) so that it can properly inject proper types. Here you feed the container with the basic info it needs to work.

Mange objects Scope and Lifetime

You need to tell the container what scope and lifetime the object it creates will have.

Typical “lifestyle” patterns are:

  1. Singleton. A single instance of the object is always used.
  2. Transient. Every time a new instance of an object is created.
  3. Scoped. That is typically a singleton pattern per an implicitly or explicitly defined scope.

You need to tell the container. For example, if you want it so that every time it resolves dependency a new object will be created, or if you want a singleton pattern applied. A singleton can be, for example, per process, per thread, or per “user-defined scope”. Also, you need to specify the desired lifetime of your object. You can configure, for example, object lifetime per process, or object lifetime to be per “user-defined scope." This means that the object will be disposed of at the end of the scope that the user defines. The container can enforce all that; it just needs to be precisely configured.

Resolve Method

Here the actual work of creating and assembling the required object/type are done. The container creates an object of a specific type, resolves all the dependencies, and injects them into the created object. This method works recursively into depth until all dependencies are resolved. DI Container does the work of resolving dependencies using technologies like reflection and other similar technologies.

DI Container: Autofac – Simple example

One of the most used DI Containers in C# world is Autofac [10]. We will show a simple example of how it works.

Here is our class diagram:

Here is our example code:

public interface IService {}
public class Service: IService {
    public Service() {}
}
public class Client {
    public IService iService;
    public Client(IService injectedService) {
        iService = injectedService;
    }
}
internal class Program {
    public static string MySerialize(object obj) {
        string serialized = null;
        var indented = Newtonsoft.Json.Formatting.Indented;
        var settings = new JsonSerializerSettings() {
            TypeNameHandling = TypeNameHandling.All
        };
        serialized = JsonConvert.SerializeObject(obj, indented, settings); //(5)
        return serialized;
    }
    static void Main(string[] args) {
        // Register mappings
        var builder = new Autofac.ContainerBuilder();
        builder.RegisterType < Service > ().As < IService > (); //(1)
        builder.RegisterType < Client > ().AsSelf(); //(2)
        Autofac.IContainer Container = builder.Build(); //(3)
        //Resolve object
        var client = Container.Resolve < Client > (); //(4)  
        // Json serialize object to see what we got
        Console.WriteLine(MySerialize(client)); //(6)
        Console.ReadLine();
    }
}

Here is the execution result:

As you can see, Autofac has its own API that we need to follow. At (1) we registered the mapping IService->Service. Then in (2) we registered the Client itself. At (3) we build the container, and it is ready for use. At (4) we do resolution, and that is where the resolution of dependencies and injection is done.

In order to verify that we got an object we wanted, we serialize it at (5) and print it out at (6).

If you look again at (*) and the terminology there, you will see that our class Client has the role of “Client” from (*), cour lass Service has the role of “Service” from (*), and our object Container has the role of “Injector” from (*).

A short note: this is a tutorial – a demo of a concept code. The manner in which we used DI Container in the above example, explicitly requesting dependencies resolution on the top level, makes it a bit resemble Service Locator Pattern [29]. The proper usage of DI Container is as a framework, not to explicitly request resolution of dependencies.

DI Container: Autofac – Deep dependency example

Now we will show a more complicated example with a deep dependency tree. 

Here is the new class diagram:

Here is our example code:

public class C {
    public IS obj_is = null;
    public IT obj_it = null;
    public C(IS injectIs, IT injectIT) {
        obj_is = injectIs;
        obj_it = injectIT;
    }
}
public interface IS {}
public class S: IS {
    public IU obj_iu = null;
    public IV obj_iv = null;
    public S(IU injectIU, IV injectIV) {
        obj_iu = injectIU;
        obj_iv = injectIV;
    }
}
public interface IT {}
public class T: IT {
    public IZ obj_iz = null;
    public T(IZ injectIZ) {
        obj_iz = injectIZ;
    }
}
public interface IU {}
public class U: IU {}
public interface IV {}
public class V: IV {
    public IX obj_ix = null;
    public V(IX injectIX) {
        obj_ix = injectIX;
    }
}
public interface IZ {}
public class Z: IZ {}
public interface IX {}
public class X: IX {}
internal class Program {
    public static string MySerialize(object obj) {
        string serialized = null;
        var indented = Newtonsoft.Json.Formatting.Indented;
        var settings = new JsonSerializerSettings() {
            TypeNameHandling = TypeNameHandling.All
        };
        serialized = JsonConvert.SerializeObject(obj, indented, settings);
        return serialized;
    }
    static void Main(string[] args) {
        // Register mappings
        var builder = new Autofac.ContainerBuilder();
        builder.RegisterType < S > ().As < IS > ();
        builder.RegisterType < T > ().As < IT > ();
        builder.RegisterType < U > ().As < IU > ();
        builder.RegisterType < V > ().As < IV > ();
        builder.RegisterType < Z > ().As < IZ > ();
        builder.RegisterType < X > ().As < IX > ();
        builder.RegisterType < C > ().AsSelf();
        Autofac.IContainer Container = builder.Build();
        //Resolve object
        var c = Container.Resolve < C > ();
        // Json serialize object to see what we got
        Console.WriteLine(MySerialize(c));
        Console.ReadLine();
    }
}

Here is the execution result:

As can be seen from the execution result, Autofac DI Container did its work again. For example, class S is, in terms of the terminology of DI Pattern (*), at the same time “Client” and “Service”. It is a “Service” for class C, and a “Client” for classes U and V. The Object Container plays the role of the “Injector”, in the terminology of (*).

Another note: this is a tutorial – a demo of concept code. The manner in which we used DI Container in the above example, explicitly requesting dependencies resolution on the top level, makes it a bit resemble Service Locator Pattern [29]. The proper usage of DI Container is to use it as a framework, and not to explicitly request resolution of dependencies.

DI Container: Autofac – Configuring Object Scope and Lifetime

If we look at the above example, one question occurs to us. If we have two objects of class C, objects c1 and c2, that were generated by DI Container through resolution, are these objects different or the same? And what about dependent objects, like objects of class T - let’s call them t1 and t2 - are they all different or the same?

//let us assume we created two objects, c1 and c1
var c1 = Container.Resolve<C>();
var c2 = Container.Resolve<C>();

//are they same or different?
//what will this give us as result?
bool sameObjects=(c1 == c2);    

The answer is: it is configurable. But, since we didn’t configure that in the previous example, we will get the default behavior. That is, every time a new object is created. In this case objects, c1 and c1 are different as are all dependent objects of classes S, T, U, V, Z, and X.

Typical options for configuring object scope and lifetime for DI Container Autofac ([11], [12]) are:

  1. Instance Per Dependency
    Also frequently called in literature, “transient”. This is an “every time new object” pattern. Basically, it means every time an object is requested, a new instance is created. That is the default behavior.
     
  2. Single Instance
    Also known as “singleton”. That is basically, “singleton per process”. Every time you request resolution in the process, you will get the same instance of the object.
     
  3. Instance Per Lifetime Scope
    That is a “singleton per user-defined scope”. The user needs to specify the scope, and inside it, he will get the same instance all the time.
     
  4. Instance Per Matching Lifetime Scope
    This is again singleton, but this time “singleton per user-defined named scope." The user needs to use defined named scope, and every time inside it will get the same instance of the object.
     
  5. Instance Per Request
    In ASP type of applications, it results in “singleton per request”. This is really the same as #4. It is just named scope is created per each request. More explanation at [12].
     
  6. Instance Per Owned
    This is a bit complicated, so we will not go into details here. More explanation at [12].
     
  7. Thread Scope
    This does not exist as a separate configuration option, but instead relies on #3 to create a named scope in your thread method and implement it as an “Instance Per Lifetime Scope” solution.

Here are some examples of how the configuration options listed above look in the code:

//1. Instance Per Dependency
builder.RegisterType < Worker > (); // using defualt behaviour
//explicit definition
builder.RegisterType < Worker > ().InstancePerDependency();
//2. Single Instance
builder.RegisterType < Worker > ().SingleInstance();
//3. Instance Per Lifetime Scope
builder.RegisterType < Worker > ().InstancePerLifetimeScope();
using(var scope1 = container.BeginLifetimeScope()) {
    var w1 = scope1.Resolve < Worker > ();
    var w2 = scope1.Resolve < Worker > ();
    // w1 and w2 are the same instance 
}
//4. Instance Per Matching Lifetime Scope
builder.RegisterType < Worker > ().InstancePerMatchingLifetimeScope("MyScope");
using(var scope1 = container.BeginLifetimeScope("MyScope")) {
    var w1 = scope1.Resolve < Worker > ();
}
using(var scope2 = container.BeginLifetimeScope("MyScope")) {
    var w2 = scope2.Resolve < Worker > ();
    // w1 and w2 are the same instance 
}
//5. Instance Per Request
builder.RegisterType < Worker > ().InstancePerRequest();
//6. Instance Per Owned
builder.RegisterType < OwnerClass > ();
builder.RegisterType < SlaveClass > ().InstancePerOwned < OwnerClass > ();
//7. Thread Scope
//similar to 3.

We will not go into more detail or provide code samples because that would be too much for this article.

DI Container – Integration with application frameworks

DI Containers are a very popular architectural mechanism and many application frameworks plan for and enable integration with DI Containers.

For example, ASP.NET MVC framework is exposing the interface IDependencyResolver [13] that the prospect DI Container needs to implement. An example of integration with Autofac looks like this:

// ASP.NET MVC and Autofac integration
// Context:
// - build Autofac DI Container
//      var builder = new Autofac.ContainerBuilder();
//      Autofac.IContainer container = builder.Build();
// - container implements interfaces Autofac.IContainer  
//      and Autofac.IComponentContext
// - new AutofacDependencyResolver(container) implements 
//      System.Web.Mvc.IDependencyResolver
// - System.Web.Mvc provides a registration point for 
//      dependency resolvers with method
//      public static void System.Web.Mvc.DependencyResolver
//      .SetResolver(System.Web.Mvc.IDependencyResolver resolver)
DependencyResolver.SetResolver(new AutofacDependencyResolver(container));

So, the point is ASP.NET MVC framework provides a registration point for dependency resolver. If you do not want to use DI, that is fine too. But, if you want to use DI in your ASP.NET MVC application, you can register your DI Container of choice with the application framework as shown above. Magically, DI resolution will start to work in your application. 

For info on how to integrate the Autofac DI container with other applications, see [27]. This is enough material for this tutorial article.

Conclusion

In this article, we focused on the Dependency Injection Pattern (DI) and its industrial application Dependency Injection Container (aka IoC Container). We also explained related principles for software design, the Dependency Inversion Principle (DIP) and Inversion of Control (IoC) principle. We showed some sample code using Autofac container. Emphasize is on the reader’s ability to understand and appreciate the practical usage of DI Container in modern applications.

DI Pattern and DI Container are mainstream technologies of Software Architecture today and are here to stay.

In this tutorial, we gave a concise and brief overview of the material suitable for the reader that needs to master concepts fast. Further reading on the topics is recommended. Some beginner’s tutorials are [14], [18] – [23]. Serious literature is [9], [8], [26].

References