In this post we are going to look at the concept of dependency injection in general and in asp dotnet core in particular.

We will cover a lot of ground in the terms of why it is useful where it came from and how it is baked into asp dotnet core.
In subsequent posts we will be looking at how to extend this with third party frameworks, especially with autofac.

So what is the concept dependency injection, where does it come from and what does it give us?

Usually I would start with the reason for this being so useful, but in this particular case I want to start with the what and get to the why later on. This is for the simple reason that I think it helps the understanding.

Should you already be familiar with the concept of Dependency Injection or Dependency Inversion principles in general, then you can simply proceed to the asp dotnet core implementation details.

The point of having Dependency injection is to ease the maintenance of your software by decoupling your components from one another.  This is true for dependency injection for following reasons (and probably others I could not think of):

  1. you can easily swap an implementation against another one, which helps to minimize bugs in the system
  2. unit testing becomes easier by supplying mocks
  3. you design your system upfront to use DI and therefore decouple the system more and introduce more abstraction that will aid you in the later stages of the system
  4. increased development speed

How this helps in practical terms we look at the common way to create dependencies in OO – software systems:

namespace my_dependencies
{
    class Dependent
    {
         private readonly Dependency _dependency;
         public Dependent()
         {
             _dependency = new Dependency();
         }
    }
}

So this is easy enough, but it tightly couples the Dependent class to the Dependency class. So if the constructor of the Dependency changes we need to change the Dependent class, too.
This obviously has a ripple over effect if there is more than one of this dependencies involved in either of those classes.

The solution to this is the dependency inversion principle which is complemented by Dependency Injection.

 

By programming against an interface the dependency is inverted, and the class now only depends on the interface. The implementation does not matter anymore.
To make the best use of this we now inject the Interface into the class. What this means is, that the class is supplied with an instance of the dependency without constructing it directly itself. This can be done by constructor or property (setter) injection:

1.) constructor injection:

...
public Dependent(IDependency dependency)
{
    _dependency = dependency;
}
...

2.) and property injection
for this wee need to adjust the dependent class a little bit:

class Dependent
{
    public IDependency Dependency { get; set; }
}
{
...
    private Dependent Factory(IDependency dependency)
    {
        var dependent = new Dependent();
        dependent.Dependency = dependency;
    }
}
...
// or simply for demonstration purposes
...
var dependent = new Dependent();
var dependency = new Dependency();

dependent.Dependency = dependency;
...

Both lead to the same result, the Dependent class is now not directly dependent on the Dependency but on the interface.

Evolution to IOC Containers

But from this explanation it is not totally clear how we came to sophisticated containers to supply us with dependencies. Why this came about lets think of an alternative solution:
To inject the dependencies to the constructor or the setter, we could supply a static factory class/method so we do not need to instantiate the class in the same class.

But this would still be a tight coupling to the factory method then, although we could use this as a sort of bootstrapping class that encapsulates all instantiation. This could actually make sense for some projects or even for some smaller libraries or components where this is only used internally.

The next step would be to register all services/classes with a central repository and resolve them on request. This is called the service locator and looks in general something like this:

...
var locator = new MyServiceLocator();
locator.Register<IDependency>(new Dependency());
...
var dependent = new Dependent(locator.Resolve<IDependency>());

This is often considered an anti pattern, because the classes often request the dependency by itself, which couples them tightly to the locator. Also the locator is often implemented as a static class/ singleton which has its own problems in and of itself.

Just as a side note: Combined with the factory/bootstrapping approach from above this can still be feasible for small projects.

But from this approach of the register/resolve pattern we get to the Dependency injection (or inversion of control) container.
What those do is essentially resolving the requested dependencies automatically for you. For this you still have to register the types with their corresponding interfaces (as with the service locator).
When a dependency is being requested the container then instantiates your classes based on this configuration and registration.

Besides resolving the dependencies, you can also manage the lifecycle and handling of resources to which those dependencies hold on to.

Now with a simple understanding of dependency injection we want to look at the benefits and drawbacks of this approach as the usefulness might not be clear for some who have not worked with this type of framework before.

Benefits of Dependency injection containers

Extend and change application. Most containers support type scanning and allow you to only implement an interface and have it be drawn into the application without wiring it up somewhere.
Also if you want to simply exchange a dependency like a cross cutting one for all of the application you only need to change the registration. This also helps with the Open/Closed principle of software system design from the SOLID principles  or get this book and see for yourself.

Modular approach helps decoupling and cohesiveness. If your application is build in a modular way it is really easy to concentrate your dependencies in one place.

Avoids boilerplate code. The instantiation of objects in a  static factory method, bootstrapping class or whatever it is that you are using is always very similar code. But with IOC containers and some of their approaches (like type/assembly scanning) it is easy to avoid the boilerplate if the container is already configured.

Lifetime management. You can define that each dependency is instantiated in a special way. For example you can instantiate a new instance per dependency, one instance for every requesting the dependency (singleton) or  for Asp dotnet core you can have a dependency for every request and much more.
This helps you to avoid the complicated issue of handling the lifetime yourself.

Writing code to create instances adds no business value. We should focus on the domain we are working in and not on the technology. With the container configured we can then concentrate more efforts to create value for the users of our software.

Unit testing with mocks becomes a breeze. Not every test class needs to create its own mocks, even though this is fairly easy with frameworks like Moq, RhinoMock or Nsubstitute.

Drawbacks of using an IOC container/DI framework

Tied to the implementation of the container. Often the containers have some concepts you inadvertently get tightly coupled to for some reason or another. So your application will then depend on this framework and the concepts on which it is build. This can have severe implications for the overall maintainability of your application.

Performance considerations. Most of the time it will be negligible in contrast to network requests, I/O completion or intensive computing, still the automated resolution of dependencies is an indirection and adds a performance penalty (which is more than compensated with the productivity you will gain, especially upfront).

The code and the object graph get more obstructed. The dependencies are obviously not as clear cut as before and in a big application this can get quite confusing at times.

Weakened compile time safety for constructing objects. You need to have integration tests (which you should have in any case) to discover errors in the configuration of your IOC container.

One of the challenges with DI is that it solves a problem that isn’t always obvious when writing code and that appears only later in the development cycle.
Also DI offers limited benefit if you are not doing unit testing or if you are working on a small, self-contained and stable project. It is still helpful to understand how DI works because DI is used to access some important MVC features. On the other hand you are not required to embrace DI in the controllers and other classes you write.
Interestingly enough you should always consider DI a tool and check if it is the right tool for the job.

If the concept is unfamiliar to some developers in the team it has a steep learning curve.

As you can tell it has a lot of benefits and some drawbacks also. With every decision in software it depends on the domain and on the project you are creating if you should use it or not.
But whatever path you take, in enterprise systems you absolutely positively have to know about this pattern at the very least.

From my point of view there is probably no case in an asp dotnet core application where you will be better off when you do not use this pattern. This is because of the nature of asp dotnet core, its middleware approach and the handling of requests in general. Also this pattern is strongly encouraged by the framework itself, because it is built into it already.
And we all know how working against the framework does no good for you.

With the general overview we will now look at how this is built into asp dotnet core itself. For further infos look at this book about dependency injection in .net core.

Dependency Injection in Asp dotnet core

In asp dotnet core the principle of dependency injection is build into the framework and supported for all the code you create. The reason for this is, that asp dotnet core highly encourages unit testing, decoupling and component oriented design. For all of this, dependency injection is very helpful to increase developer productivity.

Therefore you can use this in an easy way. You simply declare a dependency in a constructor of a class you want to use. With this registration MVC then matches those with the interface type of the dependency supplied to the constructor. For this to work MVC uses its services component.

When a class/controller is requested and initiated during an incoming http request, the services component requests all the declared dependencies by their interface.
Because this is the core concept I want to clearly show the performed steps:

  1. ASP receives an incoming request to an action method on a controller.
  2. ASP asks the ASP.NET service provider component for a new instance of the requested Controller class.
  3. The service provider inspects the Controller constructor and discovers that
    it has a dependency on say the IRepository interface.
  4. The service provider consults its mappings to find the implementation class it
    has been told to use for dependencies on the IRepository interface.
  5. The service provider creates a new instance of the implementation class.
  6. The service provider creates a new Controller object, using the implementation object as a constructor argument.
  7. The service provider returns the newly created Controller object to MVC, which uses it to handle the incoming HTTP request.

Because as I said this is baked into ASP.NET core, the service provider component will be used on every request and whenever a component of yours is requested.

The good thing is, that it allows controllers, and other components for that matter, to declare dependencies without the need to have any knowledge about the way they are going to be resolved. This is the beauty of an IOC container, the register and resolve are decoupled and invert the flow of control and dependency. (as discussed above).

Register phase in ASP

Still where you want to resolve some dependencies you need to configure/register them first with the service provider. This can be done in the Startup class.

For example if we have an IRepository interface that encapsulates the data access layer, we can add it to the service provider like so:

namespace DependencyInjection
{
    public class Startup
    {
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddTransient<IRepository, MemoryRepository>();
            services.AddMvc();
        }

        public void Configure(IApplicationBuilder app, IHostingEnvironment env)
        {
           app.UseStatusCodePages();
           app.UseDeveloperExceptionPage();
           app.UseStaticFiles();
           app.UseMvcWithDefaultRoute();
        }
    }
}

The registering process is done by calling extension methods on the IServiceCollection object which is received by the ConfigureServices method of the Startup class.
And we will now look at the semantics of the Add method.

You register a component by specifying the type via a generic argument, where the service is the first type parameter and the implementation is the second parameter. The suffix of the Add method tells MVC how to handle the dependency if it is requested. AddTransient declares that for each requesting component a new instance should be created. We will later in this post look at all option we have with this regard.

This immediately raises the question what to do if the IRepository implementation also has a dependency?
This is easily handled by the service provider, because it recursively handles dependencies. If it should create a dependency, it is like any type it is requested to instantiate. It simply looks again if this class has dependencies and so on and so forth.
This again leads to the problem of circular dependencies, where one of the recursive dependencies relies on another dependency that itself relies on the dependency it should be injected in:
A –> B
B –> C
C –> A
This would result in an exception with the service provider. There are solutions to this problem that we will tackle in the post about advanced dependency injection with autofac.

But again to handle dependencies of dependencies they also have to be registered with the service provider first. So in essence in the register/configuration part you create a chain of configurations.

Concrete Types

Dependency Injection can also be used for concrete types and not only for interfaces. This is true for most DI frameworks as well as the service provider of ASP.NET core.
It can be used like so:

services.AddTransient<MyConcreteType>();

You can see that you do not need the interface in this case, because it may have none.
While this doesn’t provide the loose-coupling advantages of using an interface, it is a useful technique in its own right because it allows objects to be accessed anywhere in an application and puts concrete types under life-cycle management, which I describe later in this post.

Also the service provider will provide this concrete type with all its dependencies. This can actually make a lot of sense if this class is only some form of adapter or delegator that only invokes calls on dependencies, so you do not have to deal with the overhead of creating an interface and so on. Yet this couples the concrete type to your instance and can make it harder to test all of this.

Service life cycles

As we have now talked to some extend about lifecycle management of components which is handled by the container we now want to look at the different service lifecycles that exist in asp net core.

 

 

Each of those lifecycles offers different trade offs of its own. The transient lifecycle has the following benefits:

  • most simple one
  • new instance of implementation type for each time it resolves this dependency, so no need for concurrency management or such

The drawback is:

  •  cost of new object creation each time (generally negligible, but with a lot of dependencies in dependencies it can add up over time)

You can also supply it with a factory function:

services.AddTransient<IRepository>(provider => 
{
     if (env.IsDevelopment()) 
     {
           var x = provider.GetService<MemoryRepository>();
           return x;
     }
     else 
     {
          return new AlternateRepository();
     }
}
});

The scoped lifecycle is part of the whole request and therefore needs to be carefully used with included state, if any.

For the singleton lifecycle you must ensure that the implementation classes used to resolve dependencies are safe for concurrent access.

Action injection

Beides the controller injection in constructors you can also utilize this on Action methods directly. This can make sense if those actions are seldom called and you want to avoid supplying all of the expensive dependencies to each controller instantiation.

The action injection feature is actually implemented from model binder, for which you can see the details in my posts about model binding and validation

public ActionResult DoSomeExpensiveStuff([FromServices]IRepository repository)
{
    return Ok(repository.GetAll());
}

Property injection Attributes

In my post about Controllers we looked at a way to receive context data in a POCO controller by declaring a property and decorating it with the ControllerContext attribute.
Now that we know how dependency injection works in asp dotnet core we can see, that this attribute actually indicates how the services get injected into the Controller.
This way is known as property injection (see explanation at beginning of the post).

MVC provides a set of specialized attributes that can be used to receive specific types via property injection in controllers and in view components.
You won’t need these attributes if you derive your controllers from the Controller base class, which I highly encourage you to do.
This is because the context information is exposed through convenience properties on the base class.

But for completeness sake here are the attributes you can utilize:

ControllerContext
This attribute sets a ControllerContext property, which provides a superset of the functionality of the ActionContext class, as described in Chapter 31

ActionContext
This attribute sets an ActionContext property to provide context information to action methods. The Controller classes expose the context information through an ActionContext property.

Manually requesting an implementation object

The main ASP.NET dependency injection feature and the additional attributes that MVC provides for property and action injection provide all the support that most applications will require for creating loosely coupled components.
However there can be occasions when it can be useful to create an implementation for an interface without relying on injection. In these situations, you can work directly with the service provider:

IRepository repository =
HttpContext.RequestServices.GetService<IRepository>();

This is the service locator anti pattern, which I described already in the section about evolution to IOC containers.

Summary

In this post we looked at what is dependency injection, where it came from and why it is useful.

We also looked at the basic ideas on how to use dependency injection in asp net core.

Categories: AspNetCore

0 Comments

Leave a Reply

Avatar placeholder