8

I have a class that requires ApplicationUser (from ASP.NET Identity). The instance should be the current user.

public class SomeClass
{
    public SomeClass(ApplicationUser user)
    {

Currently, what I'm doing is I inject the current user from the Controller:

var currentUser = await _userManager.GetUserAsync(User);
var instance = new SomeClass(currentUser);

Now I want to use Dependency Injection provided by Microsoft. I can't figure out how am I going to add ApplicationUser to the services. It requires User which is a property of the Controller.

So how do you inject ApplicationUser (instance of the current user) via DI provided by Microsoft?

kazinix
  • 28,987
  • 33
  • 107
  • 157
  • 2
    Generally, you're supposed to inject *dependencies*, not data. It's better to provide the desired context (the invoking user, in your case) upon calling each method (or explicitly by calling some `Init(context)`) so that the consumers of the type are more aware of the actual needs. Injecting the context automatically behind the scenes by the DI will advance your DI skills but may lead to inconsistencies in the future. – haim770 Apr 10 '17 at 12:05
  • @haim770 thanks. I think I'm just being lazy by not wanting to provide it in every method call. The `Init(context)` you said may be an alternative, I wonder how do I enforce it to be called. – kazinix Apr 10 '17 at 12:16
  • As @haim770 stated, prevent injecting runtime data into your components. For more details, please read [this](https://www.cuttingedge.it/blogs/steven/pivot/entry.php?id=99). – Steven Apr 10 '17 at 13:41

1 Answers1

6

You can inject both UserManager<ApplicationUser> and IHttpContextAccessor to the constructor of your class, then:

public class SomeClass
{
    private readonly UserManager<ApplicationUser> _userManager;
    private readonly IHttpContextAccessor _context;
    public SomeClass(UserManager<ApplicationUser> userManager,IHttpContextAccessor context)
    {
        _userManager = userManager;
        _context = context;
    }

    public async Task DoSomethingWithUser() {
        var user = await _userManager.GetUserAsync(_context.HttpContext.User);
        // do stuff
    }
}

If you don't want to take direct dependency on IHttpContextAccessor but still want to use DI, you can create interface to access your user:

public interface IApplicationUserAccessor {
    Task<ApplicationUser> GetUser();
}

public class ApplicationUserAccessor : IApplicationUserAccessor {
    private readonly UserManager<ApplicationUser> _userManager;
    private readonly IHttpContextAccessor _context;
    public ApplicationUserAccessor(UserManager<ApplicationUser> userManager, IHttpContextAccessor context) {
        _userManager = userManager;
        _context = context;
    }

    public Task<ApplicationUser> GetUser() {
        return _userManager.GetUserAsync(_context.HttpContext.User);
    }
}

Then register it in DI container and inject into SomeClass:

public class SomeClass
{
    private readonly IApplicationUserAccessor _userAccessor;
    public SomeClass(IApplicationUserAccessor userAccessor)
    {
        _userAcccessor = userAccessor;
    }

    public async Task DoSomethingWithUser() {
        var user = await _userAccessor.GetUser();
        // do stuff
    }
}

Other options include (as mentioned in comments) not inject anything but require passing ApplicationUser as argument to the methods which require it (good option) and require initialization before using any methods with special Initialize(user) method (not so good, because you cannot be sure this method is called before using other methods).

Evk
  • 98,527
  • 8
  • 141
  • 191
  • 1
    In case `SomeClass` is declared in a different assembly, this will enforce it to depend on `Microsoft.AspNetCore.Http` – haim770 Apr 10 '17 at 12:09
  • @haim770 you are right, it's better to not inject ApplicationUser at all but just accept it as a parameter to the method. However if OP wants to get it via DI still - this is a possible option. – Evk Apr 10 '17 at 12:13
  • My thoughts exactly, this is an alternative but don't want to be tied to HttpContext – kazinix Apr 10 '17 at 12:18
  • @dpp you can avoid that, and you can even inject `ApplicationUser` itself - but I would advice against doing that. Doing heavy things that might throw during dependency resolution is not a good idea. If you want to go via injection route - create some `IApplicationUserAccessor` with `Task GetUser()` method and inject this one into your class. – Evk Apr 10 '17 at 12:32
  • @dpp I've updated answer with an example with IApplicationUserAccessor. – Evk Apr 10 '17 at 12:39
  • Thanks for your advice. – kazinix Apr 10 '17 at 12:40
  • That's great! :) `IApplicationUserAccessor` is replaceable so I'm not dependent on `HttpContext`. – kazinix Apr 10 '17 at 12:44
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/141360/discussion-between-dpp-and-evk). – kazinix Apr 10 '17 at 12:45
  • A problem with this approach is if the injectee (the controller) is in a separate assembly where ApplicationUser is NOT defined. For example consider that that separate assembly needs access to the UserManagerbut it cannot reference the main web application because it is actually the web application that references this class library. – Lord of Scripts Jul 04 '17 at 21:32