asp.net core DI - can I make a service dependent on other services

Viewed 3886

In my asp.net core 3.1 app I have a RequestInfo class that contains information about the current request, and I'd like to make an instance of RequestInfo availale to middleware and controllers by dependency injection. Obviously the RequestInfo must be constructed for every request, and needs the HttpContext to construct it. I'm registering a transient service like this:

services.AddTransient<RequestInfo>(sp =>
  {
    var context = sp.GetService<HttpContext>();
    return new RequestInfo(context)
  });

The problem is that when my factory is called, there is no HttpContext in the service provider.

I have tried addScoped, and I've also tried putting services.AddHttpContextAccessor(); in the top of my ConfigureServices and then calling sp.GetService<HttpContextAccessor>().HttpContext in the factory, but neither of those worked.

So what I'm asking is, is there a way to specify that my service factory isn't called until the service provider has already injected the HttpContext.

I realise there are other approaches: I could inject a RequestInfoFactory and then have the consumer call GetRequestInfo on that, or that I could use a middleware to construct the RequestInfo and add it into context.Items. I just want to find out if there's a way to inject the RequestInfo directly because that seems cleaner to me.

3 Answers

Instead of getting HttpContext, get IHtttpContextAccessor service from DI container (it does not make any sense to pass the HttpContext from the startup class):

services.AddHttpContextAccessor();
services.AddTransient<RequestInfo>(sp =>
{
    var context = sp.GetService<IHttpContextAccessor>();
    return new RequestInfo(context)
});

You can alternatively simplify this problem by introducing an interface for RequestInfo:

public interface IRequestInfo
{
}

public class RequestInfo
{
    private readonly IHttpContextAccessor _httpContextAccessor;
    
    public RequestInfo(IHttpContextAccessor httpContextAccessor)
    {
        _httpContextAccessor = httpContextAccessor;
    }
}

and in ConfigureServices method:

services.AddHttpContextAccessor();
services.AddScoped<IRequestInfo, RequestInfo>();

I've got this working by following Microsoft Docs - use HttpContext from custom components to inject directly without creating the instance in ConfigureServices.

The dependency injection container supplies the IHttpContextAccessor to any classes that declare it as a dependency in their constructors.

  • Add IHttpContextAccessor httpContextAccessor to the class
    constructor.
  • Add services.AddHttpContextAccessor(); to
    ConfigureServices.

You just need to register all the dependencies before they are needed as shown below:

services.AddHttpContextAccessor();
services.AddTransient<RequestInfo>();

Inside the RequestInfo class you can ask for IHttpContextAccessor

public class RequestInfo
{
    private readonly IHttpContextAccessor _httpContext;

    public RequestInfo(IHttpContextAccessor httpContext)
    {
        _httpContext = httpContext;
    }
}
Related