How to use DI with UserManager and UserStore

2019-06-27 05:17发布

问题:

Given a typical setup of a MVC controller constructor passing UserManager (which takes UserStore) to it's parent class, how would this be converted to be injected via IoC?

Starting with this:

public AccountController()
    : this(new UserManager<ApplicationUser>(
        new UserStore<ApplicationUser>(new ApplicationDbContext())))
{
}

I would think something like this:

public AccountController(IUserStore store)
    : this(new UserManager<ApplicationUser>(store)))
{
}

Though this does, of course, lose the IdentityDbContext.

How should the IoC be setup and how should the constructor be defined to allow injection of the UserManager, UserStore and IdentityDbContext?

回答1:

You would need to create some classes to allow for easier injection.

Let us start with the UserStore. Create the desired interface and have it inherit from IUserStore<ApplicationUser>

public IUserStore : IUserStore<ApplicationUser> { }

Create an implementation as follows.

public ApplicationUserStore : UserStore<ApplicationUser>, IUserSTore {
    public ApplicationUserStore(ApplicationDbContext dbContext)
        :base(dbContext) { }
}

The UserManager can then be done as desired in the OP.

public class ApplicationUserManager : UserManager<ApplicationUser> {

    public ApplicationUserManager(IUserSTore userStore) : base(userStore) { }

}

SO now all that is left is to make sure that which ever IoC container you decide to use registers the necessary classes.

ApplicationDbContext --> ApplicationDbContext 
IUserStore --> ApplicationUserStore 

If you want to go a step further and abstract the UserManager then just create an interface that exposes the functionality you want

public interface IUserManager<TUser, TKey> : IDisposable
    where TUser : class, Microsoft.AspNet.Identity.IUser<TKey>
    where TKey : System.IEquatable<TKey> {
    //...include all the properties and methods to be exposed
    IQueryable<TUser> Users { get; }
    Task<TUser> FindByEmailAsync(string email);
    Task<TUser> FindByIdAsync(TKey userId);
    //...other code removed for brevity
}

public IUserManager<TUser> : IUserManager<TUser, string>
    where TUser : class, Microsoft.AspNet.Identity.IUser<string> { }

public IApplicationUserManager : IUserManager<ApplicationUser> { }

and have you manager inherit from that.

public class ApplicationUserManager : UserManager<ApplicationUser>, IApplicationUserManager {

    public ApplicationUserManager(IUserSTore userStore) : base(userStore) { }

}

This now means that the Controller can now depend on an abstraction and not on implementation concerns

private readonly IApplicationUserManager userManager;

public AccountController(IApplicationUserManager userManager) {
    this.userManager = userManager;
}

And again you register the interface with the implementation in the IoC container.

IApplicationUserManager  --> ApplicationUserManager 

UPDATE:

If you are feeling adventurous and want to abstract identity framework itself take a look at the answer given here