ASP.NET Core has dependcy injection available at framework level and ASP.NET Core is heavy user of it. Most of things surrounding controllers, views and other MVC components are implemented as services that web applications consume. This post is intensive overview of dependency injection in ASP.NET Core.

Registering types for dependency injection

Types are registered when application starts. It happens in Startup class. There is method called ConfigureServices() and in the end of this method I usually define service mappings.

public virtual void ConfigureServices ( IServiceCollection services )

{

// configure services

var settings = new Settings ();



// add MVC and other services



// register dependencies

services . AddSingleton ( settings );



services .AddScoped< IFileClient , AzureFileClient >( client =>

{

var connStr = Configuration[ "StorageConnectionString" ];



return new AzureFileClient ( connStr );

});



services . AddScoped < IMapperSession , MapperSession >();

services . AddScoped < IProductService , ProductService >();

services . AddTransient < MyClass >();

}

Available scopes for dependencies are:

Singleton – always return same instance.

– always return same instance. Transient – return new instance every time.

– return new instance every time. Scoped – return same instance in current scope (most popular scope is request).

– return same instance in current scope (most popular scope is request). Instance – specific instance is returned every time and it’s up to you how it is actually ceated.

Types registered during application start-up are available to all classes invoked through dependency injection.

Although we can inject types to whatever classes, there are some things made very convenient for us. Let’s see now how injection works with controllers and views. Yes, views too.

Constructor injection

We don’t need custom controller factories anymore if we don’t use some other dependency injection container. Also we don’t have to dig around in system variables and classes to find settings we need. We can do it all through dependency injection.

Of ASP.NET Core UI components constructor injection is supported with controllers, view components and tag helpers.

Here is the example how to provide environment information and some custom services to controller.

public class HomeController : Controller

{

private readonly IApplicationEnvironment _appEnvironment;

private readonly ShopContext _shopContext;

private readonly IProductService _productService;



public HomeController ( ShopContext shopContext ,

IProductService productService ,

IApplicationEnvironment appEnvironment )

{

_appEnvironment = appEnvironment ;

_shopContext = shopContext ;

_productService = productService ;

}



public IActionResult Index ()

{

return View ();

}



// ... more methods follow ...

}

The code here uses constructor injection – the only injecton mode supported by ASP.NET right now. We don’t have to do anything special for dependency injection to happen. We just make sure we register our custom services at application startup.

Username tag helper

Here’s the simple tag helper I have in one web application. Based on claims of authenticated user it writes out name of user.

[ HtmlTargetElement ( "username" , TagStructure = TagStructure .NormalOrSelfClosing)]

public class UserNameTagHelper : TagHelper

{

private readonly HttpContext _httpContext;



public UserNameTagHelper ( IHttpContextAccessor accessor )

{

_httpContext = accessor .HttpContext;

}



public override void Process ( TagHelperContext context , TagHelperOutput output )

{

output .TagName = "" ;



if (_httpContext.User == null )

{

return ;

}



var user = _httpContext.User;

var name = user .Claims. FirstOrDefault ( c => c .Type == ClaimTypes .Name)?.Value;

var firstName = user .Claims. FirstOrDefault ( c => c .Type == ClaimTypes .GivenName)?.Value;

var lastName = user .Claims. FirstOrDefault ( c => c .Type == ClaimTypes .Surname)?.Value;

var id = user .Claims. FirstOrDefault ( c => c .Type == ClaimTypes .NameIdentifier)?.Value;



if (! string . IsNullOrEmpty ( name ))

{

output .Content. Append ( name );



return ;

}

else if (! string . IsNullOrEmpty ( firstName ) && ! string . IsNullOrEmpty ( lastName ))

{

output .Content. Append ( firstName );

output .Content. Append ( " " );

output .Content. Append ( lastName );



return ;

}

else if (! string . IsNullOrEmpty ( firstName ))

{

output .Content. Append ( firstName );



return ;

}



output .Content. Append ( id );

}

}

This tag helper uses ASP.NET Core dependency injection to get instance of IHttpContextAccessor.

Injecting all instances of same type

We can inject also multiple instances of same type to controller. For this we must use IEnumerable of given type. Here’s the controller to send hello to current user through all alerting services in web application.

public class HomeController : Controller

{

private readonly IEnumerable < IAlertService > _alertServices;



public HomeController ( IEnumerable < IAlertService > alertServices )

{

_alertServices = alertServices ;

}



public async Task SendAlerts ()

{

foreach ( var service in _alertServices)

{

await service .SendAlert(User.Identity.Name, "Hello" );

}

}

}

Controller action injection

Constructors of controllers built as kind of business facade may grow big as there can be many instances injected to controller. But every action uses only one or two of these instances. Usually API controllers like these need instances of service classes.

Here’s the example how to inject instance to controller action.

public async Task < IActionResult > UpdateNewsFeeds ([ FromServices ] IUserService userService )

{

if (! await userService .IsAllowedUser(User.Identity.Name))

{

return Forbid ();

}



await userService .UpdateFeeds();



return NoContent ();

}

Notice FromServices attribute that is needed for ASP.NET Core to understand that userService must be asked from dependency injection.

View injection: Injecting instances to views

It’s also possible now to inject instances to views. There’s new syntax for this.

@model ProductCategoryMenuModel

@inject ShopContext ShopContext





< h1 > @ ShopContext.PageTitle </ h1 >



< ul >

<!-- write out categories here -->

</ ul >

@inject tells to view engine that we want instance of ShopContext to be injected to view and we name variable holding it as ShopContext.

What about StructureMap, Autofac and others? ASP.NET Core dependency injection can be replaced by other DI/IoC containers that support ASP.NET Core. There are few tricks to know and I have described these in my blog post ASP.NET Core: Using third-party DI/IoC containers.

Conclusion

Framework level dependency injection in ASP.NET 5 is very transparent and configuration is simple. First register type mappings in application start-up and then we use constructor injection to get instances to our classes. This way we can use classes by interfaces and we don’t have to create instances in our own code. On MVC side we can use dependency injection for controller, views and view components.

References

Liked this post? Empower your friends by sharing it! share

share

share

share

share

share