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)