Welcome! Spanish articles on LinkedIn. You can follow me on Twitter for news.
Table of Contents
- Benefits of Dependency Injection
- Components of Microsoft Dependency Injection
- Example
- Constructor injection
- Action Injection
We are not going to dig into the main concepts of Dependency Injection, but I want to remember the main benefits.
Benefits of Dependency Injection
- Loose coupling of components
- Logical abstraction of components
- Supports unit testing
- Cleaner, more readable code
If you want to learn about Dependency Injection, I'd recommend Steve Gordon's Pluralsight course. It's a perfect way to getting started.
Components of Microsoft Dependency Injection
IServiceCollection
defines a contract for register and configure a collection of service descriptors.
IServiceProvider
provides a mechanism to retrieve a required service at runtime.
In this post, we are going to take a look at which elements of AspNet Core could retrieve and resolve dependencies.
Example
We are going to use the following interface and concrete class as an example.
public interface IExampleDependency
{
Guid Code { get; }
}
public class ExampleDependency : IExampleDependency
{
public Guid Code { get; } = Guid.NewGuid();
}
Then, we register the service in our ConfigureServices
method in the Startup class
as scoped.
public void ConfigureServices(IServiceCollection services)
{
services.AddScoped<IExampleDependency, ExampleDependency>();
//Code
}
Constructor injection
Dependencies can easy be injected via constructors. Remember that a public
constructor is required. We can inject a dependency into the constructor of:
- Controllers
- Middleware
- Filters
- Tag Helpers
- View Components
- Razor page models
- Razor Views
Controllers
[ApiController]
[Route("[controller]")]
public class ValuesController : ControllerBase
{
private readonly ILogger<ValuesController> _logger;
private readonly IExampleDependency _exampleDependency;
public ValuesController(ILogger<ValuesController> logger, IExampleDependency exampleDependency)
{
_logger = logger;
_exampleDependency = exampleDependency;
}
[HttpGet]
public string Get()
{
return _exampleDependency.Code.ToString();
}
}
Middleware
Middleware components are constructed once during the life of the application. We shouldn't inject short-lived scopes or transients services via the constructor. Our service is scope, so we need another solution. Middleware supports a second point of injection via the InvokeAsync
method. This method is invoked on each request.
public class CustomMiddleware
{
private readonly RequestDelegate _next;
public CustomMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task InvokeAsync(HttpContext httpContext, IExampleDependency exampleDependency)
{
httpContext.Response.Headers.Add("X-Code", exampleDependency.Code.ToString());
await _next(httpContext);
}
}
Remember to configure the middleware in the Configure
method in the Startup
class.
app.UseMiddleware<CustomMiddleware>();
Filters
We can inject a service in the constructor of a filter. Let's take a look at this example.
public class CustomAsyncFilter : IAsyncActionFilter
{
private readonly IExampleDependency _exampleDependency;
public CustomAsyncFilter(IExampleDependency exampleDependency)
{
_exampleDependency = exampleDependency;
}
public async Task OnActionExecutionAsync(
ActionExecutingContext context,
ActionExecutionDelegate next)
{
// Do something before the action executes.
context.HttpContext.Response.Headers.Add("X-Code-Filter", _exampleDependency.Code.ToString());
// next() calls the action method.
var resultContext = await next();
// resultContext.Result is set.
// Do something after the action executes.
}
}
Remember to configure the service in the ConfigureServices
method in the Startup
class.
services.AddControllers(opt => { opt.Filters.Add(typeof(CustomAsyncFilter)); });
We are adding the CustomAsyncFilter
by type and not by instance.
Tag Helpers
Tag helpers enable server-side code to participate in creating and rendering HTML elements in Razor files.
public class TitleTagHelper : TagHelper
{
private readonly IExampleDependency _exampleDependency;
public TitleTagHelper(IExampleDependency exampleDependency)
{
_exampleDependency = exampleDependency;
}
public override void Process(TagHelperContext context, TagHelperOutput output)
{
output.TagName = "h1";
output.AddClass("display-4", HtmlEncoder.Default);
output.Content.SetContent($"Welcome {_exampleDependency.Code.ToString()}");
}
}
And then, on your Razor page.
@page
@model IndexModel
@{
ViewData["Title"] = "Home page";
}
<div class="text-center">
<title></title>
<p>Learn about <a href="https://docs.microsoft.com/aspnet/core">building Web apps with ASP.NET Core</a>.</p>
</div>
View Components
View components are similar to partial views, but they are much more powerful.
public class CustomViewComponent : ViewComponent
{
private readonly IExampleDependency _exampleDependency;
public CustomViewComponent(IExampleDependency exampleDependency)
{
_exampleDependency = exampleDependency;
}
public Task<IViewComponentResult> InvokeAsync()
{
return Task.FromResult((IViewComponentResult)View("Default", _exampleDependency.Code.ToString()));
}
}
Then, create the Razor page for the custom view component under Pages/Components/Custom/Default.cshtml.
@model string
<h1 class="display-4">@Model</h1>
Finally, use the custom view component in the Razor page.
@page
@model IndexModel
@{
ViewData["Title"] = "Home page";
}
<div class="text-center">
@await Component.InvokeAsync("Custom");
<p>Learn about <a href="https://docs.microsoft.com/aspnet/core">building Web apps with ASP.NET Core</a>.</p>
</div>
Razor page models
Razor page models is the same that controller injection
public class IndexModel : PageModel
{
private readonly ILogger<IndexModel> _logger;
private readonly IExampleDependency _exampleDependency;
public IndexModel(ILogger<IndexModel> logger, IExampleDependency exampleDependency)
{
_logger = logger;
_exampleDependency = exampleDependency;
}
public void OnGet()
{
ViewData["code"] = _exampleDependency.Code;
}
}
Razor views
In razor views, we use the @inject
directive to inject a service in a view.
@page
@using WebApplication2.Demo
@model IndexModel
//Here
@inject IExampleDependency _exampleDependency
@{
ViewData["Title"] = "Home page";
}
<div class="text-center">
<h1 class="display-4">Welcome @_exampleDependency.Code.ToString()</h1>
<p>Learn about <a href="https://docs.microsoft.com/aspnet/core">building Web apps with ASP.NET Core</a>.</p>
</div>
Action Injection
AspNet Core controllers supports a second point of injection called action injection.
It's a common practice to use action injection over constructor injection when you need to use a service only once time on an action.
We have to apply [FromServices]
attribute to tell that this parameter is coming from dependency injection.
[ApiController]
[Route("[controller]")]
public class ValuesController : ControllerBase
{
private readonly ILogger<ValuesController> _logger;
public ValuesController(ILogger<ValuesController> logger)
{
_logger = logger;
}
[HttpGet]
public string Get([FromServices] IExampleDependency exampleDependency)
{
return exampleDependency.Code.ToString();
}
}
I think that there are common ways to inject and resolve dependencies in AspNet Core.
Am I forgetting some? Leave your comment below!
Top comments (0)