ASP.NET Core Inversion of Control and Dependency Injection

Introduction

There are quite a few good posts out there on Inversion of Control (IoC) and Dependency Injection (DI) in the ASP.NET Core world, but I felt there was still something to be said, hence this post! Mind you, this is going to be a long one! I once wrote another post on the history of dependency resolution in .NET, you may want to have a look at it. Always keep in mind that this is based on the latest bits, and may still change when it gets to the final version.

Services Registration

At bootstrap, ASP.NET Core will either call the ConfigureServices method, or one with a name following the convention Configure<environment>Services, where <environment> comes from the Hosting:Environment environment variable, and you can also set it in Visual Studio:

image

It is commonly set to “Development”, “Production”, “Staging”, etc. Keep in mind that if the environment-specific method exists (e.g., ConfigureDevelopmentServices), then the generic one (ConfigureServices) is not called.

The Configure*Services method is passed an instance of IServiceCollection, which is where we get to store our service registrations, so that they can be used by ASP.NET Core further along the pipeline. A service registration needs three things:

  • A key type, normally an interface or an abstract base class;
  • A concrete implementation of the key type;
  • A lifetime.

The lifetime determines how many times the concrete implementation is going to be built. The ASP.NET Core service provider implementation knows about three different lifetimes:

  • Scoped: an instance is created the first time it is requested during the same HTTP request, and during that request, this same instance is always returned;
  • Singleton: an instance is created the first time it is requested, and this same instance is always returned in all subsequent calls;
  • Transient: a new instance is always created, whenever it is requested.

A registration is an instance of the ServiceDescriptor class that is added to the IServiceCollection instance:

services.Add(new ServiceDescriptor(typeof(IMyService), typeof(MyService), ServiceLifetime.Singleton));

There are several extension methods that make this registration even easier.

What if you want to have custom processing when a service is returned? Simple:

services.AddTransient<IMyService>(sp =>

{

    var something = sp.GetService(typeof(ISomething)) as ISomething;

    var other = new OtherService();

    //do something with it

    return new MyService(other);

});

Keep in mind that the concrete service must either implement or inherit from the key service. Also, for the Scoped lifetime, if the concrete class implements IDisposable, the service provider will honor it and call Dispose at the end of the HTTP request.

After Configure*Services, ASP.NET Core calls Configure, also in the Startup class. This is where the core initialization occurs, like, adding all middleware, such as the MVC one, that makes the application works the way we expect it. The Configure method can either have no parameters or it can receive one or more parameters with types that match the registered services. For example, we will always have IApplicationBuilder, IHostingEnvironment, ILoggerFactory, so we can add them as parameters together with our own:

public void Configure(

    IApplicationBuilder app,

    IHostingEnvironment env,

    ILoggerFactory loggerFactory,

    IMyService service)

{

    //do something with the services

}

It gives us a good chance to initialize them before the actual fun begins. If we want, we can also pass it just an instance of the dependency resolution service, represented by the IServiceProvider interface, which has been around since the early days of .NET. The ASP.NET Core service provider implements this interface, so having it here just means: “get me the service provider implementation so that I can retrieve services from it”:

public void Configure(IServiceProvider serviceProvider)

{

    var env = serviceProvider.GetService(typeof(IHostingEnvironment)) as IHostingEnvironment;

    var service = serviceProvider.GetService(typeof(IMyService)) as IMyService;

}

In the latest version of ASP.NET Core, there is no globally accessible reference to the built-in service provider, something known as the service locator, of bad reputation. If, for any reason you feel you need it, here is the place to store it. By the way, the Microsoft.Extensions.DependencyInjection package has some good strongly typed extension methods for IServiceProvider, make sure you include it.

Services

Each concrete service class itself can be dependency-injected: by default, it should be a concrete class and have a public parameter-less constructor; if, however, we want to inject dependencies into it, which also need to be registered in the service provider, we can have a constructor that takes these dependencies:

public class MyService : IMyService

{

    public MyService(IServiceProvider serviceProvider)

    {

        //get something from the service provider

    }

}

Notice the usage of IServiceProvider again. Another option is to have the constructor contain more concrete dependencies:

public class MyService : IMyService

{

    public MyService(ILoggerFactory loggerFactory)

    {

        //do something with the logger factory

    }

}

You can pass any number of parameters, of any type that is registered in the service provider, normally in one of the Configure*Services methods. If the specified service type is not registered, you get an exception at runtime.

Controllers

An MVC controller can have dependencies injected through its constructor, like I’ve shown for services:

public class HomeController : Controller

{

    public HomeController(IMyService service)

    {

        //do something with the service

    }

}

The same pattern applies: we can either declare a parameter as IServiceProvider or as a more specific type. It doesn’t matter if our controller inherits from Controller or is a POCO controller.

There was another dependency injection possibility with MVC controllers, which consisted in decorating public properties with a [FromServices] attribute: if a registration exists that matched the property’s type, it would be injected into the property just after the controller is constructed:

public class HomeController

{

    [FromServices]

    public IService Service { get; set; }

}

This is no longer possible, and the functionality was removed in DNX RC2, although it will still work in RC1. Forget about it.

By default, controller classes themselves do not come from the dependency resolution mechanism. You can achieve that, if, in Configure*Services, you call AddControllersAsServices, passing it either a Type or an Assembly collection:

services

    .AddMvc()

    .AddControllersAsServices(new[] { typeof(HomeController).GetTypeInfo().Assembly });

This allows you to do this:

services.AddSingleton<HomeController, SingletonHomeController>();

Each time MVC tries to build an HomeController, it will instead get the same SingletonHomeController. Note that I am not saying that you should do this, but I think you get the idea!

Filters

Filters in MVC allow you to intercept action method calls, results and exceptions, and do stuff before, after of instead of the real actions. You have global filters and attribute filters: global filters apply always, and attribute filters only apply in the scope where they are declared – a controller’s class or an action method. Filters themselves can have dependencies injected into them.

In order to declare global filters, you use one of the overloads of the AddMvc method, normally in the Configure*Services method, and registering the filter as a service:

services.AddMvc(mvc => mvc.Filters.AddService(typeof(GlobalFilter)));

MVC will try to resolve the filter type, if necessary, injecting it any dependencies, in the usual way:

public class GlobalFilter : IActionFilter

{

    public GlobalFilter(IMyService service)

    {

        //do something with the service

    }

 

    public void OnActionExecuted(ActionExecutedContext context)

    {            

    }

 

    public void OnActionExecuting(ActionExecutingContext context)

    {

    }

}

The other option for filters is attributes, but attributes require a public parameter-less constructor. The MVC team came up with an alternative for that, in the form of the ServiceFilterAttribute and TypeFilterAttribute attributes. Both receive a Type and the difference is that the first will try to resolve that type from the services registration and the last one will just instantiate it. Let’s see an example:

[ServiceFilter(typeof(GlobalFilter))]

public class HomeController : Controller

{

    [TypeFilter(typeof(SomeFilter))]

    public IActionResult Index()

    {

        //...

    }

}

So, GlobalFilter is retrieved from the services registration, and, if it is a filter, it will behave accordingly to the context where it is located, in this case, it is the whole controller. SomeFilter, on the other hand, is just instantiated and used, no need to have it registered. Don’t forget that types passed to ServiceFilterAttribute and TypeFilterAttribute have to implement one of the filter interfaces in namespace Microsoft.AspNet.Mvc.Filters otherwise an exception is thrown.

Models

Model classes are declared as parameters to action methods in an MVC controller. There’s a binding mechanism that fills their properties automatically, but we can also use dependency injection here.

First, the whole model can come from dependency injection:

public IActionResult Index([FromServices] IMyService service)

{

    //...

}

Or, at least, some properties of the model:

public IActionResult Update(Model model)

{

    //...

}

 

public class Model

{

    [FromServices]

    public IMyService Service { get; set; }

 

    //...

}

Views

MVC views can also take injected services, through the @inject directive:

@inject IMyService service;

 

<p>@service.Serve()</p>

View Components

View components, introduced in ASP.NET MVC Core, can also be injected in pretty much the same way as controllers:

public class MyServiceViewComponent : ViewComponent

{

    public MyServiceViewComponent(IMyService service)

    {

        //do something with the service

    }

    

    public string Invoke()

    {

        //...

    }

}

Tag Helpers

Tag helpers, like view components and controllers, also support constructor injection:

[HtmlTargetElement("service")]

public class ServiceTagHelper : TagHelper

{

    public ServiceTagHelper(IMyService service)

    {

        //do something with the service

    }

 

    public override void Process(TagHelperContext context, TagHelperOutput output)

    {

        //...

    }

}

Middleware

OWIN-style middleware are likewise injectable through the constructor:

public class ServiceMiddleware

{

    private readonly RequestDelegate _next;

    

    public ServiceMiddleware(RequestDelegate next, IMyService service)

    {

        this._next = next;

        //do something with the service

    }

    

    public Task Invoke(HttpContext httpContext)

    {

        //...

    }

}

Using a Custom IoC/DI Container

Now, you may not want to use the built-in service provider and use your own (Unity, Autofac, Ninject, TinyIoC, etc). That is certainly possible!

You need to change the signature of the Configure*Services method so as to return an IServiceProvider:

public IServiceProvider ConfigureServices(IServiceCollection services)

{

    services.AddMvc();

 

    services.AddTransient<IMyService, MyService>();

 

    return new MyIoCContainer(services.BuildServiceProvider());

}

The key here is to create an instance of the service provider we want to use, maybe leveraging on the one produced from the service collection (BuildServiceProvider method), and return it here. Of course, if it has more features and lifetimes than the default one, you are free to use them all. The only contract it has to comply to is that of IServiceProvider.

Dependency Resolution

While running your MVC application, you can explicitly ask for services registered in the global (the default or your own) service provider. The HttpContext class exposes a RequestServices property; here you will find the custom service provider that you returned in Configure*Services, or ASP.NET Core’s built-in one. Whenever you have a reference to the current HttpContext (controllers, view components, tag helpers, views, middleware, filters), you get it as well. The “old” ApplicationServices was removed in recent commits, so it won’t make it to the final version of ASP.NET Core and you shouldn’t be using it.

Conclusion

The dependency injection mechanism was substantially changed in ASP.NET Core. The only identical feature seems to be constructor injection, and it is understandable, since it’s what most people should be using anyway. Now every moving part of ASP.NET Core is injectable through the same mechanism, which I think is a good thing. The problems so far have been the relative instability in the ASP.NET Core, things have been changing a lot, and there is still no light at the end of the tunnel.

Encrypted JavaScript in ASP.NET MVC Core

To complete the Data URI saga, here is another technique that you may find interesting: this time, it’s about encrypting JavaScript contents, so as to make them more difficult to tamper with.

Data URIs can be used for serving any content that would normally come from an external resource, such as a JavaScript/CSS file or an image. What we will do here is, using ASP.NET MVC Core’s Tag Helpers, we get hold of the JavaScript that is defined inside a SCRIPT tag and we turn into a Data URI. Pretty simple, yet the result is also pretty cool!

Show me the code, I hear you say:

[HtmlTargetElement("script")]

[HtmlTargetElement("script", Attributes = "asp-encrypt")]

[HtmlTargetElement("script", Attributes = "asp-src-include")]

[HtmlTargetElement("script", Attributes = "asp-src-exclude")]

[HtmlTargetElement("script", Attributes = "asp-fallback-src")]

[HtmlTargetElement("script", Attributes = "asp-fallback-src-include")]

[HtmlTargetElement("script", Attributes = "asp-fallback-src-exclude")]

[HtmlTargetElement("script", Attributes = "asp-fallback-test")]

[HtmlTargetElement("script", Attributes = "asp-append-version")]

public class InlineScriptTagHelper : ScriptTagHelper

{

    public InlineScriptTagHelper(ILogger<ScriptTagHelper> logger, IHostingEnvironment hostingEnvironment,

        IMemoryCache cache, IHtmlEncoder htmlEncoder, IJavaScriptStringEncoder javaScriptEncoder,

        IUrlHelper urlHelper) : base(logger, hostingEnvironment, cache, htmlEncoder, javaScriptEncoder, urlHelper)

    {

    }

 

    [HtmlAttributeName("asp-encrypt")]

    public bool Encrypt { get; set; }

 

    public override void Process(TagHelperContext context, TagHelperOutput output)

    {

        if ((this.Encrypt == true) && (string.IsNullOrWhiteSpace(this.Src) == true))

        {

            var content = output.GetChildContentAsync().GetAwaiter().GetResult().GetContent();

            var encryptedContent = Convert.ToBase64String(Encoding.ASCII.GetBytes(content));

            var script = $"data:text/javascript;base64,{encryptedContent}";

 

            output.Attributes.Add("src", script);

            output.Content.Clear();

        }

 

        base.Process(context, output);

    }

}

You can see that this tag helper inherits from ScriptTagHelper, which is the OOTB class that handles SCRIPT tags. Because of this inheritance, we need to add all the HtmlTargetElementAttributes, so that all the rules get applied properly. For this one, we need to add an extra rule, which forces the SCRIPT tag to have an asp-encrypt attribute.

So, say you have this in your Razor view:

<script asp-encrypt="true">
   1:  

   2:     window.alert('hello, world, from an encrypted script!');

   3:  

</script>

What you’ll end up with in the rendered page is this:

<script src="data:text/javascript;base64,DQoNCiAgICB3aW5kb3cuYWxlcnQoJ2hlbGxvLCB3b3JsZCwgZnJvbSBhbiBlbmNyeXB0ZWQgc2NyaXB0IScpOw0KDQo="></script>

Cool, wouldn’t you say? Of course, no JavaScript that runs on the client can ever be 100% secure, and you should always keep that in mind. But for some purposes, it does pretty well! Winking smile

Using ASP.NET 5 Tag Helpers and the Bing Translator API

Update: In the latest version of ASP.NET 5, the GetChildContextAsync, that was previously available in the TagHelperContext class, is now in TagHelperOutput.

Introduction

If you’ve been following my posts you probably know that I have been a critic of MVC because of its poor support of reuse. Until now, the two main mechanisms for reuse – partial views and helper extensions – had some problems:

  • Partial views cannot be reused across projects/assemblies;
  • Helper methods cannot be easily extended.

Fortunately, ASP.NET 5 offers (good) solutions for these problems, in the form of Tag Helpers and View Components! This time, I’ll focus on tag helpers.

Tag Helpers

A tag helper behaves somewhat like a server-side control in ASP.NET Web Forms, without the event lifecycle. It sits on a view and can take parameters from it, resulting on the generation of HTML (of course, can have other side effects as well).

Tag helper can either be declared as new tags, ones that do not exist in HTML, such as <animation>, <drop-panel>, etc, or can intercept any tag, matching some conditions:

  • Having some pre-defined tag;
  • Being declared inside a specific tag;
  • Having some attributes defined;
  • Having a well-known structure, like, self-closing or without ending tag.

A number of these conditions can be specified, for example:

  • All IMG tags;
  • All A tags having an attribute of action-name;
  • All SPAN tags having both translate and from-language attributes;
  • All COMPONENT tags declared inside a COMPONENTS tag.

ASP.NET 5 includes a number of them:

  • AnchorTagHelper: generates links to action methods in controllers;
  • CacheTagHelper: caches its content for a number of seconds;
  • EnvironmentTagHelper: renders its content conditionally, depending on the current execution environment;
  • FormTagHelper: posts to an action method of a controller;
  • ImageTagHelper: adds a suffix to an image URL, to control caching of the file;
  • InputTagHelper
  • LabelTagHelper
  • LinkTagHelper
  • OptionTagHelper
  • ScriptTagHelper
  • SelectTagHelper
  • TextAreaTagHelper
  • ValidationMessageTagHelper: outputs model validation messages;
  • ValidationSummaryTagHelper: outputs a validation summary.

In order to use tag helpers, we need to add a reference to the Microsoft.AspNet.Mvc.TagHelpers Nuget package in Project.json (at the time this post was written, the last version was 6.0.0-beta8):

"dependencies": {

    ...

    "Microsoft.AspNet.Mvc.TagHelpers": "6.0.0-beta8"

}

Enough talk, let’s see an example of the TagHelper class with some HtmlTargetElement attributes (no API documentation available yet):

[HtmlTargetElement("p", Attributes = "translate, to-language")]

[HtmlTargetElement("span", Attributes = "translate, to-language")]

public sealed class TranslateTagHelper : TagHelper

{

}

This tag helper will intercept the following tag declarations, inside a view:

<p translate="true" to-language="pt">This is some text meant for translation</p>

<span translate="true" to-language="pt">Same here</span>

Tag helpers need to be declared in a view before they can be used. They can come from any assembly:

@addTagHelper "*, MyNamespace"

Properties declared on the markup will be automatically mapped to properties on the TagHelper class. Special names, such as those having , will require an HtmlAttributeName attribute:

[HtmlAttributeName("to-language")]

public string ToLanguage { get; set; }

If, on the other hand, we do not want such a mapping, all we have to do is apply an HtmlAttributeNotBound attribute:

[HtmlAttributeNotBound]

public string SomeParameter { get; set; }

The TagHelper class defines two overridable methods:

  • Process: where the logic is declared, executes synchronously;
  • ProcessAsync: same as above, but executes asynchronously.

We only need to override one. In it, we have access to the passed attributes, the content declared inside the tag and its execution context:

public override async Task ProcessAsync(TagHelperContext context, TagHelperOutput output)

{

    var content = await context.GetChildContentAsync();

    var text = content.GetContent();


    //do something with content


    content.TagName = "DIV";

    content = output.Content.Clear();

    content = output.Content.Append("HTML content");

}

You can see that I am redefining the output tag, in this case, to DIV, and, also, I am clearing all content and replacing it with my own.

Now, let’s see a full example!

Content Translation

Microsoft makes available for developers the Bing Translator API. It allows, at no cost (for a reasonable number of requests) to perform translations of text through a REST interface (several API bindings exist that encapsulate it). Let’s implement a tag helper that will automatically translate any text contained inside of it.

In order to use this API, you first need to register at the Microsoft Azure Marketplace. Then, we need to create a Marketplace application. After we do that, we need to generate an authorization key. There are several tools that can do this, in my case, I use Postmap, a Google Chrome extension that allows the crafting and execution of REST requests.

The request for generating an authorization key goes like this:

POST https://datamarket.accesscontrol.windows.net/v2/OAuth2-13

grant_type: client_credentials

client_id: <client id>

client_secret: <client secret>

scope: http://api.microsofttranslator.com

The parameters <client id> and <client secret> are obtained from our registered applications’ page and grant_type, client_id, client_secret and scope are request headers. The response should look like this:

{
“token_type”: “
http://schemas.xmlsoap.org/ws/2009/11/swt-token-profile-1.0″,
“access_token”: “<access token>“,
“expires_in”: “600”,
“scope”: “
http://api.microsofttranslator.com”

}

Here, what interests us is the <access token> value. This is what we’ll use to authorize a translation request. The value for expires_in is also important, because it contains the amount of time the access token is valid.

A translation request should look like this:

GET http://api.microsofttranslator.com/V2/Ajax.svc/Translate?to=<target language>&text=<text to translate>&from=<source language>

Authorization: Bearer <access token>

The <target language> and <text> parameters are mandatory, but the <source language> one isn’t; if it isn’t supplied, Bing Translator will do its best to infer the language of the <text>. Authorization should be sent as a request header.

Let’s define an interface for representing translation services:

public interface ITranslationService

{

    Task<string> TranslateAsync(string text, string to, string @from = null);

}

Based on what I said, a Bing Translator implementation could look like this:

public sealed class BingTranslationService : ITranslationService

{

    public const string Url = "http://api.microsofttranslator.com/V2/Ajax.svc/Translate?text={0}&to={1}&from={2}";


    public string AuthKey { get; set; }


    public async Task<string> TranslateAsync(string text, string to, string @from = null)

    {

        using (var client = new HttpClient())

        {

            client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", this.AuthKey);


            var result = await client.GetStringAsync(new Uri(string.Format(Url, WebUtility.UrlEncode(text), to, from)));


            return result;

        }

    }

}

Let’s register this service in ConfigureServices:

services.AddSingleton<ITranslationService>((sp) => new BingTranslationService { AuthKey = "<access token>" });

Of course, do replace <access token> by a proper one, otherwise your calls will always fail.

Caching

In order to make our solution more scalable, we will cache the translated results, this way, we avoid unnecessary – and costly – network calls. ASP.NET 5 offers two caching contracts, IMemoryCache and IDistributedCache. For the sake of simplicity, let’s focus on IMemoryCache, On the ConfigureServices method of the Startup class let’s add the caching services:

services.AddCaching();

And now let’s see it all together.

Putting it All Together

The final tag helper looks like this:

[HtmlTargetElement("p", Attributes = "translate, to-language")]

[HtmlTargetElement("span", Attributes = "translate, to-language")]

public sealed class TranslateTagHelper : TagHelper

{

    private readonly ITranslationService svc;

    private readonly IMemoryCache cache;


    public TranslateTagHelper(ITranslationService svc, IMemoryCache cache)

    {

        if (svc == null)

        {

            throw new ArgumentNullException(nameof(svc));

        }


        this.svc = svc;

        this.cache = cache;

    }


    [HtmlAttributeName("from-language")]

    public string FromLanguage { get; set; }


    [HtmlAttributeName("to-language")]

    public string ToLanguage { get; set; }


    public override async Task ProcessAsync(TagHelperContext context, TagHelperOutput output)

    {

        if (string.IsNullOrWhiteSpace(this.ToLanguage) == true)

        {

            throw new InvalidOperationException("Missing target language.");

        }


        var content = await context.GetChildContentAsync();

        var text = content.GetContent();


        if (string.IsNullOrWhiteSpace(text) == true)

        {

            return;

        }


        var cacheKey = $"{this.FromLanguage}:{text.ToLowerInvariant().Trim()}:{this.ToLanguage}";

        var response = string.Empty;


        this.cache?.TryGetValue(cacheKey, out response);


        if (string.IsNullOrWhiteSpace(response) == true)

        {

            response = await svc.TranslateAsync(text, this.ToLanguage, this.FromLanguage);

            response = response.Trim('"');


            cache?.Set(cacheKey, response);

        }


        content = output.Content.Clear();

        content = output.Content.Append(response);

    }

}

It tries to obtain the translated context from the cache – if it is registered – otherwise, it will issue a translation request. If it succeeds, it stores the translation in the cache. The cache key is a combination of the text (in lowercase), the source and destination languages. The only strictly required service is an implementation of our ITranslationService, the cache is optional. In order use the tag in a view, all we need is:

@addTagHelper "*, MyNamespace"


<p translate="yes" to-language="pt">This is some translatable content</p>

Conclusion

I hope I managed to convince you of the power of tag helpers. They are a powerful mechanism and a welcome addition to ASP.NET MVC. Looking forward to hearing what you can do with them!