How to get an instance of an injected service without constructor injection in ASP.NET Core?

Viewed 2258

I have a three layered architecture.

I can't use constructor injection and I need to get access to a service in my business code, in which I don't have access to HttpContext.

For example, in action methods, or in filters or middleware I can get a service using:

 HttpContext.RequestServices.GetRequiredService<ITranslator>();

But in my business code, I don't have access to HttpContext.

How can I get an instance of my service?

Update:

Here's my business code:

public class InvoiceBusiness
{
   // for some reasons, I can't use constructor injection here

   public void CalculateTranslationsInvoice(long customerId) 
   {
       // I need to get an instance of ITranslator here, and a couple of other services. 
       // If this method was in a controller, I could use HttpContext.RequestServices. 
       // But here what should I do?
   }
}
1 Answers

If you're needing to access HTTP concerns in the inner layers, you should abstract it to an interface.

Assume you need to access the current user. Normally, you'd use HttpContext.User. But you can't access it in the domain layer.

The solution is to define an interface in your domain layer that encapsulates what your ITranslator implementation actually needs from the HTTP context.

public interface IUserAccessor {
    ClaimsPrincipal CurrentUser { get; }
}

public class Translator: ITranslator {
    // inject the interface
    private readonly IUserAccessor _userAccessor;
    public Translator(IUserAccessor userAccessor) {
        _userAccessor = userAccessor;   
    }
    // ...
}

Keep this interface as focused as possible. Here, I'm OK with using ClaimsPrincipal and having a dependency on the standard library, but if you're not, you can just extract the user id claim if that makes sense in your application.

Then implement this interface in the application/HTTP layer.

internal class HttpUserAccessor: IUserAccessor {
    IHttpContextAccessor _httpAccessor;

    public HttpUserAccessor(IHttpContextAccessor httpAccessor) {
        _httpAccessor = httpAccessor;
    }

    public ClaimsPrincipal CurrentUser => _httpAccessor.HttpContext?.User;
}

Then register this implementation:

services.AddHttpContextAccessor();
services.AddScoped<IUserAccessor, HttpUserAccessor>();

Now you can access HTTP concerns in any layer without that layer knowing where the data actually comes from.

The bottom line is: you don't have to forego dependency injection. You can define & implement interfaces in different layers.

Related