I've tried reading the docs on ASP.NET Core Identity and Entity Framework. But I feel still non the wiser.
I don't want to have anything to do with IdentityDbContext
, IdentityUser
, IdentityRole
. I just want to use my own DbContext
implementation and work happily with UserManager
, UserStore
, RoleManager
, SignInManager
, and what other classes involved with signing in.
So that been said, having created a the default ASP.NET Core project that uses "Individual User Accounts". Now I want to figure out what DI wire up is required for that controller to "work".
Looking at the account controller constructor:
public AccountController(UserManager<ApplicationUser> userManager,
SignInManager<ApplicationUser> signInManager,
IOptions<IdentityCookieOptions> identityCookieOptions,
IEmailSender emailSender,
ISmsSender smsSender,
ILoggerFactory loggerFactory)
Along with that, the following related classes are generated:
public class ApplicationUser : IdentityUser
{
}
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
...
}
And some related DI configuration:
public void ConfigureServices(IServiceCollection services)
{
...
services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
services.AddIdentity<ApplicationUser, IdentityRole>()
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddDefaultTokenProviders();
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
...
app.UseIdentity();
}
Digging around in the source code for ASP.NET Core Identity, UserStore forces the constraint on IdentityUser
.
public class UserStore : UserStore<IdentityUser<string>>
For what I want to work is with something like the following - for starters:
public class AuthenticationDbContext : DbContext
{
public AuthenticationDbContext(DbContextOptions options) : base(options)
{
}
public DbSet<ApplicationUser> ApplicationUsers { get; set; }
public DbSet<ApplicationRole> ApplicationRoles { get; set; }
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
builder.Entity<ApplicationUser>(te =>
te.HasKey(user => user.Id));
builder.Entity<ApplicationRole>(te =>
te.HasKey(role => role.Id));
}
}
public class ApplicationRole
{
public int Id { get; set; }
public string UserName { get; set; }
}
public class ApplicationUser
{
public int Id { get; set; }
public Guid Guid { get; set; }
public string GivenName { get; set; }
public string FamilyName { get; set; }
public string MiddleName { get; set; }
public DateTime? DateOfBirth { get; set; }
public string UserName { get; set; }
public string EmailAddress { get; set; }
}
If I listed the issues I have had, exceptions et al, this post would be too long.
The question is, how do I wire this up with DI configuration?
OK. Following on from keeping my AuthenticationDbContext the same, I can provide an implementation of IUserStore
, IRoleStore
and configure the DI as follows.
public class ApplicationUserStore : IUserStore<ApplicationUser>, IUserPasswordStore<ApplicationUser>
{
private readonly AuthenticationDbContext _dbContext;
public ApplicationUserStore(AuthenticationDbContext dbContext)
{
_dbContext = dbContext;
}
...
OK, so I need to provide implementations of 10 or 11 methods here which needs a little figuring out. The same for ApplicationRoleStore.
public class ApplicationRoleStore : IRoleStore<ApplicationRole>
{
private AuthenticationDbContext _dbContext;
public ApplicationRoleStore(AuthenticationDbContext context, IdentityErrorDescriber describer = null)
{
_dbContext = context;
}
...
Then this can be wired up with:
public void ConfigureServices(IServiceCollection services)
{
services.AddIdentity<ApplicationUser, ApplicationRole>()
.AddUserStore<ApplicationUserStore>()
.AddRoleStore<ApplicationRoleStore>()
.AddDefaultTokenProviders();
services.AddTransient(c =>
new AuthenticationDbContext(
new DbContextOptionsBuilder()
.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")).Options));
So signing in via Account Controller/Login view now invokes my ApplicationUserStore.FindByNameAsync
when I click on Login.
Whether this is or isn't the "proper" remains to be seen. I just feel strongly that the ASP.NET Identity should work with "any" data store implementation, therefore you shouldn't have to be constrained to using IdentityUser
. The above should work for non-Entity Framework implementations I feel.