ASP NET Core Identity with Entity Framework custom DbContext

asp.net-core asp.net-core-identity entity-framework-core

Question

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?

1
0
3/28/2017 10:01:41 PM

Popular Answer

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.

0
3/30/2017 9:38:53 PM


Related Questions





Related

Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow
Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow