Dependency Injection in ASP.NET vNext

Dependency Injection in ASP.NET vNext

Rate This
  • Comments 21

Dependency Injection (DI) is a software design pattern, a particular case of the Inversion of Control pattern, in which one or more dependencies are injected into dependent objects. The pattern is used to create program designs that are loosely coupled and testable.

This article assumes that you are already familiar with DI. If not, you can read this article for a brief introduction.

DI in ASP.NET vNext

In ASP.NET vNext, dependency injection is a first class citizen. While in the previous versions of the framework, DI was partially supported, in ASP.NET vNext it is available throughout the entire stack. A minimalistic DI container is provided out of the box but we are leaving the door open to BYOC (Bring Your Own Container). The default container is useful in the cases when you don’t need any advanced injection capabilities (see the known limitations section at the end of the post).

BYOC is possible because of an abstraction over the actual DI container implementation. The abstraction is the IServiceProvider interface and it represents the least set of container behavior our components are limited to assuming are present. All the framework components (MVC, Routing, SignalR, Entity Framework, etc.) rely only on the capabilities of IServiceProvider, but your own application code can use any feature that your chosen DI container has. When you BYOC, you can replace the default implementation of IServiceProvider with a wrapper around your own container.  Once that happens, all the dependency resolution calls will be routed to your own container. In the case when you want to use your own container strictly for your own custom types, we support fallback to our default container.

Because all framework components use the same container to register services, we can now flow dependencies across the stack. This opens the door to new scenarios that were not possible before, like injecting a SignalR broadcaster into an MVC controller action. As we walk up the stack, there are different layers of dependency resolvers. All dependencies are added to a single container and everyone can see everybody else’s services. The single container also addresses the cross-cutting concern customization story. It is trivial in the new stack to change cross-cutting concerns (e.g. logging) via a single entry point.

The out of the box container supports the following lifestyles:

Lifestyle Description
Instance A specific instance is given all the time. You are responsible for its initial creation
Transient A new instance is created every time
Singleton A single instance is created and it acts like a singleton
Scoped A single instance is created inside the current scope. It is equivalent to Singleton in the current scope

Per Request Scope

A popular feature for DI in web applications is to create objects that have a single instance per web request. This means that the objects acts as a singleton inside that request but two distinct requests will have different instances of the objects.

In ASP.NET vNext, the Per Request Scope is achieved using a middleware and a scoped lifestyle. The middleware, when invoked, will create a new scoped container which will replace the container for the current request. All the subsequent middleware in the pipeline will then utilize the scoped container. After the request flows through the pipeline and the container middleware is signaled to complete, the scope is destroyed and all the objects created inside it are disposed.

 

The source code for the ContainerMiddleware, is available on GitHub.

In the rare case in which you need to create your own container scope, you can use the IServiceScopeFactory to do this. When the default implementation of the IServiceProvider is created, the IServiceScopeFactory is one of the services that are registered by default.

New vs Old

For the purpose of showing the differences between DI in the old and new stack, we are going to use an MVC controller that writes a string provided through an injected dependency:

Code Snippet
  1. public interface IMessageGenerator
  2. {
  3.     string GenerateMessage();
  4. }
  5.  
  6. public class HelloMessageGenerator : IMessageGenerator
  7. {
  8.     public string GenerateMessage()
  9.     {
  10.         return"Hello DI!";
  11.     }
  12. }
  13.  
  14. public class MessageController : Controller
  15. {
  16.     private readonly IMessageGenerator messageGenerator;
  17.  
  18.     public MessageController(IMessageGenerator generator)
  19.     {
  20.         if (generator == null)
  21.         {
  22.             throw new ArgumentNullException("generator", "The generator dependecy is mandatory");
  23.         }
  24.  
  25.         this.messageGenerator = generator;
  26.     }
  27.  
  28.     public string GetMessage()
  29.     {
  30.         return this.messageGenerator.GenerateMessage();
  31.     }
  32. }

None of the code above is changed between the old and the new stack. The only difference is where and how the dependency are registered and resolved.

In the old MVC stack, controller dependencies are resolved through a custom controller factory. For the purpose of this demo, we are going to implement the Poor Man’s DI and manually compose the dependencies:

Code Snippet
  1. public class DIControllerFactory : DefaultControllerFactory
  2. {
  3.     public override IController CreateController(RequestContext requestContext, string controllerName)
  4.     {
  5.         // If a message controller is requested...
  6.         if (controllerName == "Message")
  7.         {
  8.             // ... then create a new controller and set up the dependency
  9.             return new MessageController(new HelloMessageGenerator());
  10.         }
  11.  
  12.         // Otherwise, fallback to the default implementation
  13.         return base.CreateController(requestContext, controllerName);
  14.     }
  15. }
  16.  
  17. public class MvcApplication : HttpApplication
  18. {
  19.     protected void Application_Start()
  20.     {
  21.         ...
  22.  
  23.         // Register the controller factor
  24.         ControllerBuilder.Current.SetControllerFactory(new DIControllerFactory());
  25.  
  26.         ...
  27.     }
  28. }

The controller factory will inject a concrete implementation (HelloMessageGenerator) of the interface (IMessageGenerator) in the Message controller.

The same code, ported to ASP.NET vNext, doesn’t need a Poor Man’s DI implementation. As mentioned before, in this new stack, a DI container is available out of the box. Thus, all we need to do is tell it what is the mapping between the interface and the concrete implementation.

Code Snippet
  1. public class Startup
  2. {
  3.     public void Configure(IBuilder app)
  4.     {
  5.         ...
  6.         app.UseServices(services =>
  7.         {
  8.             ...
  9.             // Set up the dependencies
  10.             services.AddTransient<IMessageGenerator, HelloMessageGenerator>();
  11.             ...
  12.         });
  13.         ...
  14.     }
  15. }

The default implementation of the controller factory in ASP.NET vNext uses the IServiceProvider to resolve the dependencies. If you BYOC, none of the code above will change. You would only have to tell the application to use a different implementation of IServiceProvider.

Replacing the default DI container

The code below shows how the previous sample can be rewritten to use Autofac instead of the out of the box container.

The code uses compiler directives to use Autofac code only for the full .NET 4.5 Framework because Autofac is not available for the .NET Core Framework 4.5. In Visual Studio “14” CTP you can change the target framework of a project by right clicking on it and going to Properties -> Active Target Framework.

Code Snippet
  1. using System;
  2. using Microsoft.AspNet.Builder;
  3. using Microsoft.AspNet.Routing;
  4. using Microsoft.Framework.DependencyInjection;
  5. using Microsoft.Framework.DependencyInjection.Fallback;
  6. using Microsoft.AspNet.Mvc;
  7. using Microsoft.AspNet.RequestContainer;
  8. using DISample.Models;
  9.  
  10. #if NET45
  11. using Autofac;
  12. using Microsoft.Framework.DependencyInjection.Autofac;
  13. using Microsoft.Framework.OptionsModel;
  14. #endif
  15.  
  16. namespace DISample
  17. {
  18.     public class Startup
  19.     {
  20.         public void Configure(IBuilder app)
  21.         {
  22.             // Add the MVC services
  23.             ServiceCollection services = new ServiceCollection();
  24.             services.AddMvc();
  25.             services.Add(OptionsServices.GetDefaultServices());
  26.  
  27. // The NET45 symbol is defined when the project targets .NET Framework 4.5
  28. #if NET45
  29.             // Create the autofac container
  30.             ContainerBuilder builder = new ContainerBuilder();
  31.  
  32.             // Register the message generator through autofac
  33.             builder.RegisterType<HelloMessageGenerator>().As<IMessageGenerator>();
  34.  
  35.             // Create the container and use the default application services as a fallback
  36.             AutofacRegistration.Populate(
  37.                 builder,
  38.                 services,
  39.                 fallbackServiceProvider: app.ApplicationServices);
  40.  
  41.             IContainer container = builder.Build();
  42.  
  43.             // Replace the default container
  44.             app.ApplicationServices = container.Resolve<IServiceProvider>();
  45. #else
  46.             // Here we are running on .NET Core Framework 4.5 so we cannot use Autofac
  47.  
  48.             services.AddTransient<IMessageGenerator, HelloMessageGenerator>();
  49.             app.ApplicationServices = services.BuildServiceProvider(app.ApplicationServices);
  50. #endif
  51.             // MVC requires the container middleware
  52.             app.UseMiddleware(typeof(ContainerMiddleware));
  53.  
  54.             app.UseMvc(routes =>
  55.             {
  56.                 routes.MapRoute(
  57.                     name: "default",
  58.                     template: "{controller}/{action}/{id?}",
  59.                     defaults: new { controller = "Message", action = "GetMessage" });
  60.             });
  61.         }
  62.     }
  63. }

In order to compile the code above, you must add the dependency injection dependencies to project.json. Since Autofac is not available for .NET Core Framework 4.5, the Autofac dependency is only defined for in the net45 section:

Code Snippet
  1. {
  2.     "dependencies": {
  3.         "Helios": "0.1-alpha-build-*",
  4.         "Microsoft.AspNet.Mvc": "0.1-alpha-build-*",
  5.         "Microsoft.AspNet.Identity.Entity": "0.1-alpha-build-*",
  6.         "Microsoft.AspNet.Identity.Security": "0.1-alpha-build-*",
  7.         "Microsoft.AspNet.Security.Cookies": "0.1-alpha-build-*",
  8.         "Microsoft.AspNet.Server.WebListener": "0.1-alpha-build-*",
  9.         "Microsoft.AspNet.StaticFiles": "0.1-alpha-build-*",
  10.         "Microsoft.Data.Entity": "0.1-alpha-build-*",
  11.         "Microsoft.Data.Entity.SqlServer": "0.1-alpha-build-*",
  12.         "Microsoft.Framework.ConfigurationModel.Json": "0.1-alpha-build-*",
  13.         "Microsoft.VisualStudio.Web.BrowserLink.Loader": "14.0-alpha",
  14.         "Microsoft.Framework.DependencyInjection": "0.1-alpha-build-*"
  15.     },
  16.     "commands": {
  17.         "web": "Microsoft.AspNet.Hosting --server Microsoft.AspNet.Server.WebListener --server.urls http://localhost:5000"
  18.     },
  19.     "configurations": {
  20.         "net45": {
  21.             "dependencies": {
  22.                 "System.Data": "",
  23.                 "System.ComponentModel.DataAnnotations": "",
  24.                 "Microsoft.Framework.DependencyInjection.Autofac": "0.1-alpha-build-*",
  25.                 "Autofac": "3.3.0"
  26.             }
  27.         },
  28.         "k10": {
  29.         }
  30.     }
  31. }

Best Practices for DI in ASP.NET vNext

When using DI, we recommend the following practices:

  1. Register all dependencies in the application startup method, before doing anything else.
  2. Avoid consuming the IServiceProvider interface directly. Instead, add explicit dependencies as constructor parameters and let the callers resolve them. Use abstract factories when the number of dependencies becomes hard to manage.
  3. Code against contracts rather than actual implementations.

Known Limitations

The out of the box container for ASP.NET vNext Alpha has a few known limitations:

  • It only supports constructor injection
  • It can only resolve types with one and only one public constructor
  • It doesn’t support advanced features (like per thread scope or auto discovery)

Summary

ASP.NET vNext is DI-friendly and it uses dependency injection throughout the stack. While there is a default minimalistic DI container out of the box, you can Bring Your Own Container and use advanced DI capabilities. The source code for Dependency Injection is available on GitHub.

We’d love to hear your feedback. Please provide it in Github, comments on this blog, or the ASP.NET vNext forum. If you ask a question in Stack Overflow, use asp.net-vnexttag.

ASP.NET vNext is an open source project released under Apache License Version 2.0 by Microsoft Open Technologies, Inc. You can follow its progress and find instructions on how to contribute on https://github.com/aspnet.

Leave a Comment
  • Please add 5 and 6 and type the answer here:
  • Post
  • Why not bundle in Unity?

  • "It can only resolve types with one and only one public constructor"

    Ouch.. that's the biggest limitation to using the OOTB DI. If I'm building libraries to be consumed by other I prefer the true "poor man's DI" which is to have a default constructor (no params) and one with all of the dependencies listed. That way you have a default behavior (the default constructor initiates a set of default dependencies if it's called and chains to the second constructor) and you can also override that by registering the dependencies in DI or use the DI constructor for testing.

    Is this a pure OOTB perf decision? Seems like something that should be solvable.

  • @Caleb I think it's a great decision, "poor man's dependency injection" as it's called is considered by many as an anti-pattern: www.cuttingedge.it/.../entry.php

    Sounds like you can still do it by providing your own implementation but the default is the "safe" option, which I like :)

  • This is incredibly wonderful news! This has been a very sore spot for a long, long time. Your recommendations at the end are also spot on. I'm very much looking forward to how this improves cross-cutting concerns.

    @Franklin Roosevelt: Please, no bundling of any full-fledged container by default. For those of us that much prefer other containers, we'd have the Unity bundled dependency for absolutely no reason.

  • FWIW, I left some feedback about this feature over on the official feedback forum for the project: forums.asp.net/.../1989008.aspx

  • I agreee with Franklin Roosevelt, use Unity.

  • You guys already have a great container. DI is so ubiquitous that Unity should be rolled into .NET proper and you should use that instead of wasting cycles on a useless feature. Of course, leave everything open for BYOC.

  • And what about MEF? Why do we need another IoC container?

  • Hi thanks for this post.

    I don't see how this is an improvement compared to using Unity or Structure Map with MVC or Web API today.

    The poor mans DI example is a bad example of how it's done now.

    The property injection is required, this is a big loss compared to DI now.

    I would much prefer to see vNext use Unity as the default container as this already has no dependencies and is open source.

    Why invent the wheel again?

    Greetings Damien

  • What do you guys think about Seemann's opinion on ASP.NET vNext Dependency Injection:

    blog.ploeh.dk/.../feedback-on-aspnet-vnext-dependency-injection

  • 'Code against contracts rather than actual implementations.'

    This is quite correct and is a really common turn of phrase - treating interfaces as 'contracts'. But isn't it inherently confusing - if I look in an ASP.net book I won't find any reference to contracts in the index, I'll find interfaces. I may find it discussed as a 'contract' in the body of the text, but it isn't... really. It's an interface, and I'm wondering if references like (not just in this article but in general) could be confusing to students / new programmers?

  • @morcs - I agree that having a large number of constructors is an anti-pattern - and in most situations I end up with exactly 1 (with all of my non-optional dependencies in it).

    However - for distributing consumable libraries, I almost always have 2. One default constructor (no prams) that chains to the "DI" constructor with the default implementation dependencies "newed" up.  This way I can nuget the the library and start using it right OOTB without any additional set up, and I still have the flexibility of injecting my own own implementations anytime I want to - even if they are the same ones that are the defaults.

    IMHO - I like "greedy" DI containers - they should use the constructor that has the most dependencies that they can fulfill - or throw an exception if one can't be found (no default constructor for example).

    SO here's a question for the team - (@ Victor) - If I only have 1 constructor (with dependencies) in my class, the .net compiler will create a default constructor (no params) "for me" if I don't specificity a private default constructor.  - So will ASP.NET VNext use the constructor with dependencies instead of the default constructor (that the compiler creates for me) or will I need to create a private default constructor to resolve that issue?

    I would be happy with a single constructor with params, as long as a default constructor was supported as well (and VNext was greedy and would use the one with the params)

  • I still don't see why you needed to bake in a container. There's already enough people out there that think DI requires a container and this decision is a massive set back for people trying to get the message out that you don't.

    I think your example illustrates that the container is an implementation detail perfectly. Your extensibility point is the controller factory; you even mention that the default implementation just delegates to the IServiceLocator - sorry I mean "IServiceProvider" implementation to resolve the dependencies.

    I do use a container for larger projects but I know that it's absolutely a design decision not a necessity. I would absolutely love to see an in depth post about why having a default implementation using a service locator is better than what we have now.

  • So I for one really do not want to see Unity bundled in and am glad you didn't consider it.   I realize that the default IOC needs to be extremely small and fast.  

    One of your recommendations is to not consume the IServiceProvider which I understand but I often find as MVC apps age more and more actions are added to a controller and more dependencies are injected into the controller with each action only using half or a third of the injected dependencies but still paying the overhead tax to create and inject all of the dependencies.   I don't know the best solution to this but it would be nice to have properties that are lazy loaded via the container.

  • Remember that for many smaller or medium sized projects, dependency injection may be more trouble than it is worth.  If you are new to it, and think it is something you need everywhere, please take what people say about dependency injection with a grain of salt.

Page 1 of 2 (21 items) 12