Dependency Injection In ASP.NET 5 – One Step Deeper

By | June 15, 2015

Dependency Injection has always been an integral part of all the web frameworks under the umbrella of the ASP.NET: Web API, SignalR, and MVC. But historically, these frameworks evolved separately from each other, hence each of these frameworks had its own way of supporting Dependency Injection, even with Katana‘s trial to bring these frameworks together through OWIN, you still needed to do some hackery to have a unified container that supports them all at once. Well, things have changed!

In this post I will dive a little bit deeper than this MSDN post; here we will examine the main interfaces involved, have a small peek inside on how things are running, and explain what it means really to switch to your IoC container of choice.

Abstractions

The decision the ASP.NET team made was to provide the dependency injection functionality through abstracting the most common features of the most popular IoC containers out there, and then allowing the different Middlewares to interact with these interfaces to achieve dependency injection.
ASPNET5 supplies a basic IoC container that implements these interfaces, but also allows the developer to swap this default implementation with their own implementation, through which they can use the IoC container of choice. Usually this is something that is not going to be implemented by the application developer himself rather than something to be implemented by the IoC container maintainers; people behind Autofac, or Ninject…etc.
Having said that, the ASPNET team has provided a basic implementations for the most common IoC containers, but these implementations are most likely to be revised by the IoC maintainers themselves.

Let’s examine the interfaces, shall we?

IServiceProvider

This is the main interface, through which the developer will be able to retrieve the implementation of a service he/she previously registered with the container (we will come to registration later). This interface has one method only: GetService(Type), think of container.Resolve<Service>() in Autofac, or kernel.Get<Service>() in Ninject.

All Middlewares will have access to two IServiceProvider instances:

  • Application-level: made available to the Middleware through HttpContext.ApplicationServices property
  • Request-level: made available to the Middleware through the HttpContext.RequestServices property. This scoped ServiceProvider is created for each request at the very beginning of the request pipeline by an implicit Middleware, and of course this request-level Service Provider will be disposed by the same Middleware at the end of the request just before sending the response back.

Note: I agree, the naming of the ApplicationServices and RequestServices properties might be little bit confusing, but just take it as is for now; these are IServiceProvider.

All the Middlewares will use these properties (hopefully the RequestServices only!) to resolve their services, e.g. the ASP.NET MVC Middleware will create the controllers and their dependencies through the RequestServices (if you don’t believe me check the code, it’s open source ;)), the same goes for creating controllers in Web API …etc.

IServiceScope

Alright, so we said that the RequestServices Service Provider is a scoped container that will be disposed by the end of the request, but how is this managed? You guessed right, by an IServiceScope.

This interface should be a wrapper around a scoped container, whose role is to dispose the container at the end of the request. So naturally it has:

  • IServiceProvider property: the scoped container
  • Dispose() method: by inheriting the IDisposable interface

The question is, who creates the IServiceScope? This brings us to the 3rd interface.

IServiceScopeFactory

Very simple interface as well, it has one method CreateServiceScope() which of course returns a IServiceScope.

So if you maintain an IoC container and you want to use it in place of the default one served by default, you have to implement the above mentioned interfaces.
“But Emad, you didn’t talk about registration of services with the container! And how does it fit all together?!”. Patience my friend, let me just finish this section with the last two classes and then we will jump to the registration.

ServiceLifetime

Enum with 3 keys to define the lifetime of services (objects really):

  • Singleton: single instance throughout the whole application
  • Scoped: single instance within the scoped container
  • Transient: a new instance every time the service is requested

ServiceDescriptor

Finally, the last class! This class is the construct that will hold all the information the container will use in order to register a service correctly; imagine it saying: “hey you, whichever container you are, when you want to register this service make sure it’s a singleton, and take the implementation from this type”. Fancy? Let’s check the members of interest:

  • ServiceType: a property of type Type, this will be the interface for which you will want to substitute with a concrete implementation, e.g. ISchoolTeacher
  • ImplementationType: a property of type Type, this will be the implementation type of the ServiceType above, e.g. SchoolTeacher
  • Lifetime: The lifetime desired for this service: Singleton, Scoped, or Transient.
  • ImplementationFactory: a Func<IServiceProvider, Object>. In some scenarios, the app developer wishes to provide a factory method to instantiate the concrete implementation of the service; maybe there are factors that are outside of the service’s control that mandates how the service should be created, this property will hold this factory method. And yes, it’s mutually exclusive; if you provide an ImplementationType you don’t provide an ImplementationFactory, and vice versa.
  • ImplementationInstance: so you can provide a type as an implementation, and you can provide a factory method to create the object. You also can provide a specific instance, this property of type Object is to hold this instance. Also should be mutually exclusive with the ImplementationType and ImplementationFactory.

Great, now for your application to run as expected, you will have a list of these ServiceDescriptors that you will hand to your container, and tell it to register these services according to how they are described. So let’s look at how this runs together including the registration part.

Registering Services

Now to register your services, ASPNET5 expects that your Startup class has a method called ConfigureServices, it takes a list of ServiceDescriptors, wrapped in IServiceCollection, and returns nothing (there is another form of this method that we will discuss shortly). All what you have to do is to create ServiceDescriptors for the services you want to register and add them to the list. The web app will be pick this list later and then register it with the container.

public void ConfigureServices(IServiceCollection services)
{
var serviceDescriptor = new ServiceDescriptor(typeof(IBankManager), typeof(BankManager), ServiceLifetime.Transient);
services.Add(serviceDescriptor);

// Add MVC services to the services container.
services.AddMvc();
}

Note: Create ServiceDescriptors can be little bit verbose, this is why you see Middleware using Extension methods to create these ServiceDescriptors, like “service.AddMvc()”

So how will this be orchestrated with the application start?

The following pseudo statements explain the server startup and how the Service Provider is created, the corresponding code can be found in the HostingEngine.Start method:

Note: that this post is based on the beta4 version, things have changed since then but the main behavior is the same. So adding the code here won’t add much value; pseudo code should be good enough

  1. Hosting engine will create an IServiceCollection, which is a collection of ServiceDescriptors
  2. Hosting engine will add all the services it needs to the list
  3. Hosting engine will ensure that there is a Startup class in your assembly and that it has a method called ConfigureServices
  4. Hosting engine will load this method and call it passing the IServiceCollection
  5. ConfigureServices in the Startup class will add the apps services to the list
  6. Hosting engine will create a DefaultServiceProvider (the container) and use the information in IServiceCollection to register the services to the DefaultServiceProvider
  7. Hosting engine will create the Application Builder (IApplicationBuilder) and assign the new Service Provider to the property IApplicationBuilder.ApplicationServices so it can use it further down
  8. Hosting engine will add a Middleware before giving the chance for the Startup.Configure to run, placing it to be the first Middleware in the pipeline. The Middleware is RequestServicesContainerMiddleware, which will be discussed shortly.
  9. Hosting engine will call Configure method in Startup class passing the Application Builder to build the Middleware pipeline where the Service Provider can be used through the ApplicationServices property to build the Middleware if needed

Great, the server is configured, started, and ready to receive requests. What happens now in a request? how is the dependency injection is run?

Running a Request

When the request first comes, an HttpContext will be created to be handed to the Invoke method of the first Middleware, and subsequently to all the Middlewares. But just before it’s handed to the first Middleware, the Application Builder’s Service Provider is assigned to the property HttpContext.ApplicationServices, making the application-level Service Provider available through the HttpContext for all the Middleware to use it up to their needs. Though, it should be kept in mind that this is the application-level Service Provider, and depending on the IoC container of choice, your objects might stay alive through the whole lifetime of the application if you use it.

Note: in theory, as an application developer, you should not use the Service Provider directly; if you do then you are doing a Service Locator pattern, which is advised against.

Ok then, that was an application-level Service Provider, isn’t there a Service Provider that is scoped for the lifetime of the request? Yes, there is.

In step 8 in the list above, we mentioned that the hosting engine adds the RequestServicesContaienrMiddleware Middleware at the beginning of the pipeline, giving it the chance to run first.
The code hasn’t change much for this Middleware for a long time, so I think it’s safe to put the code here :)

public async Task Invoke(HttpContext httpContext)
{
    using (var container = RequestServicesContainer.EnsureRequestServices(httpContext, _services))
    {
        await _next.Invoke(httpContext);
    }
}

Going back to the request execution, the server creates the HttpContext, assigns the application-level Service Provider to the HttpConext.ApplicationServices, and then invokes the first Middleware, which is the RequestServicesContainerMiddleware. Can you see that using statement in the Invoke method? There where the magic lies; all what it does is that it creates a Scoped Service Provider that will be disposed at the end of the request. The pseudo will be:

  1. Request is handed by RequestServicesContainerMiddleware
  2. Invoke will retrieve an IServiceScopeFactory from the application-level Service Provider via HttpContext.ApplicationServices.
  3. IServiceScopeFactory will create a scoped container (think of ILifetimeScope in Autofac)
  4. The scoped container will be assigned to the property HttpContext.RequestServices
  5. The Invoke method calls the subsequent Middlwares allowing the request to go through
  6. When all the Middlewares are invoked and the call return is back to the RequestServicesContainerMiddleware, the scoped Service Provider will be disposed by the “using” statement.

Note: RequestServicesContainerMiddleware uses a wrapper/helper class RequestServicesContainer to manage the creation and disposition of the scoped Service Provider, which is the object used in the “using” statement really

The HttpContext.RequestServices is the scoped container for the request lifetime, all the subsequent Middleware will have access to it. For example, If you check the MvcRouteHandler.InvokeActionAsync you will see that it’s using it to create the controllers:

private async Task InvokeActionAsync(RouteContext context, ActionDescriptor actionDescriptor)
{
    var services = context.HttpContext.RequestServices;
    Debug.Assert(services != null);

    var actionContext = new ActionContext(context.HttpContext, context.RouteData, actionDescriptor);

    var optionsAccessor = services.GetRequiredService<IOptions<MvcOptions>>();
    actionContext.ModelState.MaxAllowedErrors = optionsAccessor.Options.MaxModelValidationErrors;

    var contextAccessor = services.GetRequiredService<IScopedInstance<ActionContext>>();
    contextAccessor.Value = actionContext;
    var invokerFactory = services.GetRequiredService<IActionInvokerFactory>();
    var invoker = invokerFactory.CreateInvoker(actionContext);
    if (invoker == null)
    {
        LogActionSelection(actionSelected: true, actionInvoked: false, handled: context.IsHandled);

        throw new InvalidOperationException(
            Resources.FormatActionInvokerFactory_CouldNotCreateInvoker(
                actionDescriptor.DisplayName));
    }

    await invoker.InvokeAsync();
}

Note: a reminder, again, you shouldn’t need to use the Service Provider directly; try to manifest your dependencies through constructors, avoid the Service Locator pattern.

Awesome, now what if you want to substitute the default container with something like Autofac? Glad you asked, let’s see how.

Bring Your Own IoC Container

Before we start, this is a reminder that this is something to be implemented by the IoC container maintainers, not by the application developer.

To use your own container you have to implement the interfaces: IServiceProvider, IServiceScope, and IServiceScopeFactory. Implementing the interfaces should be straight forward because the interface itself is mandating what you need to do, the Autofac implementation can be used as an example.

But the subtle thing that needs to be explained that the ConfigureServices method in the Startup class has another form that the hosting engine expects, this form is expected in case the developer wants to use his own IoC container. In this form the method should return an IServiceProvider; once all the desired ServiceDescriptors are added to the IServiceCollection, the developer should create his container, register the services the way the container expects it, and then returns the container’s implementation of the IServiceProvider. The following is the code to use Autofac:

public IServiceProvider ConfigureServices(IServiceCollection services)
{
    // Add MVC services to the services container.
    services.AddMvc();

    var builder = new ContainerBuilder();

    // Create the container and use the default application services as a fallback
    AutofacRegistration.Populate(
        builder,
        services);

    var container = builder.Build();

    return container.Resolve<IServiceProvider>();
}

The AutofacRegistration.Populate registers the services the way Autofac likes, and registers the IServiceScope and IServiceScopeFactory implementations (this is only a part, check the complete code on the link):

private static void Register(
ContainerBuilder builder,
IEnumerable descriptors)
{
foreach (var descriptor in descriptors)
{
if (descriptor.ImplementationType != null)
{
// Test if the an open generic type is being registered
var serviceTypeInfo = descriptor.ServiceType.GetTypeInfo();
if (serviceTypeInfo.IsGenericTypeDefinition)
{
builder
.RegisterGeneric(descriptor.ImplementationType)
.As(descriptor.ServiceType)
.ConfigureLifecycle(descriptor.Lifetime);
}
else
{
builder
.RegisterType(descriptor.ImplementationType)
.As(descriptor.ServiceType)
.ConfigureLifecycle(descriptor.Lifetime);
}
}
else if (descriptor.ImplementationFactory != null)
{
var registration = RegistrationBuilder.ForDelegate(descriptor.ServiceType, (context, parameters) =&gt;
{
var serviceProvider = context.Resolve();
return descriptor.ImplementationFactory(serviceProvider);
})
.ConfigureLifecycle(descriptor.Lifetime)
.CreateRegistration();

builder.RegisterComponent(registration);
}
else
{
builder
.RegisterInstance(descriptor.ImplementationInstance)
.As(descriptor.ServiceType)
.ConfigureLifecycle(descriptor.Lifetime);
}
}
}

But then how will this fit with the 9 steps above in Registering Services? Well, it changes a little bit to become like this (red and strike-through’s are the changes):

  1. Hosting engine will create an IServiceCollection, which is a collection of ServiceDescriptors
  2. Hosting engine will add all the services it needs to the list
  3. Hosting engine will ensure that there is a Startup class in your assembly and that it has a method called ConfigureServices. First it will look for the form that returns an IServiceProvider, if not found then it uses the one that returns nothing
  4. Hosting engine will load this method and call it passing the IServiceCollection
  5. ConfigureServices in the Startup class will add the apps services to the list
  6. ConfigureServices will create the IoC container of choice
  7. ConfigureServices will register all the services in the IServiceCollection to the new container
  8. ConfigureService will make sure to register the IServiceScope and IServiceScopeFactory with the services (remember step 2 in Running a Request above?)
  9. ConfigureServices will create an instance of the container’s implementation of the IServiceProvider and return it
  10. Hosting engine will create a DefaultServiceProvider and use the information in IServiceCollection to register the services to the DefaultServiceProvider The Hosting engine will retrieve the IServiceProvider supplied
  11. Hosting engine will create the Application Builder (IApplicationBuilder) and assign the new ServiceProvider to the property IApplicationBuilder.ApplicationServices so it can use it further down
  12. Hosting engine will add a Middleware before giving the chance for the Startup.Configure to run, placing it to be the first Middleware in the pipeline. The Middleware is called RequestServicesContainerMiddleware, which will be discussed shortly.
  13. Hosting engine will call Configure method in Startup class passing the Application Builder to build the Middleware pipeline where the Service Provider can be used through the ApplicationServices property to build the Middleware if needed

Voila! all is ready

Conclusion

I hope by now there is no magic in how dependency injection really works in ASP.NET 5, if you have questions or comments feel free leave it in the comments section.

Tips’n Tricks

  • In order for you debug this whole process and step in the code you need to do two things:
    • Get the code by checking out the repositories from GitHub and make sure you are on one release tag (like beta4)
    • Create a web app in Visual Studio
    • Alter the “global.json” file so you add the paths to the repositories source to the “projects” key like this *
    • Now you have the code in your hands and can step through it
  • Code of interest:

14 thoughts on “Dependency Injection In ASP.NET 5 – One Step Deeper

  1. Pingback: ASP.NET Community Standup - June 30, 2015 - .NET Web Development and Tools Blog - Site Home - MSDN Blogs

  2. Pingback: Links of the month (June Edition) | Jan @ Development

  3. Daniel

    Will IOC automatically dispose services at end Mvc request? (Scoped, Transient ones)

  4. Pingback: Готовим ASP.NET5, выпуск №3 — внедрение зависимостей по-новому | FNIT.RU

  5. Arun

    Hi Emad Alashi

    Would you please help me with the following?
    A DI problem in ASP.NET 5 WebAPI
    I havce an Interface IABC and it’s implementation class ABC. The constructor of ABC takes only a string parameter.
    ABC(string str). How may I initialize it on Startup.CS so that it will be availabel in the controller(s).

    I tried Services.AddSingleton(). But it failes with the obvious error System.InvalidOperationException, Unable to resolve service for type System.String while attempting to activate ABC.

    How may I pass the string argument while doing initialization in Startup.CS?

    In Unity DI, we used to give the param name and value in unity.config and that is how we used to Initilaize the Classes with the construtor that have String (or other primitive type) parameter in constructor. Is there any way to do the same in DI of ASP.NET5 WebAPI?

  6. Emad Alashi Post author

    Hi Arun,

    Sorry for the late reply, my blog wasn’t feeling good.

    For your case, the simplest way is to use the factory injection; something like this:

    services.AddTransient(x => new Abc(“MyString”));

    This line means that every time an implementation if IAbc is needed, invoke this Action and return a new instance of Abc constructed with the string “MyString”.

    AddTransient or AddSingleton determines if the same instance is going to be used for all the dependencies or not.

    Let me know how things go.

  7. Adrien

    Hi,

    Thank you for this post, so interesting, and your response. I had the same question :)

  8. Pratik

    Thanks for the detailed post. I have a special requirement thought. I want to inject a service based on the HTTP header value. So I have 2 classes – DbDataProvider and InMemDataProvider, both are implemented from IDataProvider. Whenever an API call is made, a header is passed by the client which determines whether DbDataProvider is required or InMemDataProvider is required. How do I achieve that? So in short I need to inject service in the ServiceCollection in one of the middlewares. Is that possible?

  9. Vivek

    Hi Emad Alashi,
    Is there a way to use NInject similar to Autofac. I am searching for sometime now.
    Thanks and Regards
    Vivek

  10. Emad Alashi Post author

    Hi Vivek,

    Initially, the asp.net team created the adapters as samples, then left it for the Containers’ maintainers to complete the task and build a real implementation for the adapter. While Autofact agreed on embracing this model it seems that Ninject team hasn’t, check the following StackOverflow question:

    http://stackoverflow.com/questions/32788637/continued-ninject-support-in-asp-net-mvc-6

    and this conversation that has been taking place on the Ninject GitHub repo (also from the question):

    https://github.com/ninject/Ninject/issues/177

Leave a Reply

Your email address will not be published. Required fields are marked *