DEV Community

Mauro Petrini ๐Ÿ‘จโ€๐Ÿ’ป
Mauro Petrini ๐Ÿ‘จโ€๐Ÿ’ป

Posted on • Updated on

Injecting and Resolving dependencies in AspNet Core


Welcome! Spanish articles on LinkedIn. You can follow me on Twitter for news.


Table of Contents

  1. Benefits of Dependency Injection
  2. Components of Microsoft Dependency Injection
  3. Example
  4. Constructor injection
  5. 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!

Oldest comments (0)