Using ApplicationUser in a Generic Repository pattern

asp.net-core c# entity-framework-6 generics

Question

Is it possible to use asp net core's default authentication in a generic repository pattern and if so, how?

I am able to create a project that uses a generic repository pattern for all of my code, except the authentication side of the project which uses the default way.

My repository looks like:

using System.Linq;
using DemoWebsite.Interfaces;
using Microsoft.EntityFrameworkCore;

namespace DemoWebsite.Data
{
 public class Repository<T> : IRepository<T> where T : class
{
    protected readonly DbContext Context;
    protected DbSet<T> DbSet;

    public Repository(ApplicationDbContext context)
    {
        Context = context;
        DbSet = context.Set<T>();
    }

    public void Add(T entity)
    {
        Context.Set<T>().Add(entity);

        Save();
    }

    public T Get<TKey>(TKey id)
    {
        return DbSet.Find(id);
    }

    public IQueryable<T> GetAll()
    {
        return DbSet;
    }

    public void Update(T entity)
    {
        Save();
    }

    private void Save()
    {
        Context.SaveChanges();
    }
}
}

My ApplicationDbContext class:

public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
    public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
        : base(options)
    {
    }

    protected override void OnModelCreating(ModelBuilder builder)
    {
        base.OnModelCreating(builder);
        // Customize the ASP.NET Identity model and override the defaults if needed.
        // For example, you can rename the ASP.NET Identity table names and more.
        // Add your customizations after calling base.OnModelCreating(builder);
    }
}
1
0
2/13/2017 3:10:54 PM

Accepted Answer

Yes it's possible, but it's quite a bit of effort to do so. You'll have to Implement IUserStore and possibly IRoleStore (interfaces here and here).

When implementing this interfaces, you also have to implement all of the feature interfaces (for getting passwords, two-factor authentication (here), security stamp, etc.).

For an example implementation you can always look at the source of the EF Core Identity provider.

Like this

public abstract class UserStore<TUser, TRole, TContext, TKey, TUserClaim, TUserRole, TUserLogin, TUserToken, TRoleClaim> :
    IUserLoginStore<TUser>,
    IUserRoleStore<TUser>,
    IUserClaimStore<TUser>,
    IUserPasswordStore<TUser>,
    IUserSecurityStampStore<TUser>,
    IUserEmailStore<TUser>,
    IUserLockoutStore<TUser>,
    IUserPhoneNumberStore<TUser>,
    IQueryableUserStore<TUser>,
    IUserTwoFactorStore<TUser>,
    IUserAuthenticationTokenStore<TUser>
    where TUser : IdentityUser<TKey, TUserClaim, TUserRole, TUserLogin>
    where TRole : IdentityRole<TKey, TUserRole, TRoleClaim>
    where TContext : DbContext
    where TKey : IEquatable<TKey>
    where TUserClaim : IdentityUserClaim<TKey>
    where TUserRole : IdentityUserRole<TKey>
    where TUserLogin : IdentityUserLogin<TKey>
    where TUserToken : IdentityUserToken<TKey>
    where TRoleClaim : IdentityRoleClaim<TKey>
{
    ...
}

Your user store could look like:

public class GenericUserStore<TUser> : IUserStore<TUser>,
    IUserPasswordStore<TUser> where TUser : ApplicationUser
{
    public GenericUserStore(IRepository<TUser> userRepo) { }

    public Task<TUser> FindByIdAsync(string userId, CancellationToken cancellationToken)
    {
        return userRepo.Get(userId);
    }

    ...
}

and same for RoleStore. Then finally replace the EF Provider registration with your own ones (source of EF here).

userStoreType = typeof(GenericUserStore<,,,>).MakeGenericType(userType, roleType, contextType, keyType);
roleStoreType = typeof(GenericRoleStore<,,>).MakeGenericType(roleType, contextType, keyType);

var services = new ServiceCollection();
services.AddScoped(
    typeof(IUserStore<>).MakeGenericType(userType),
    userStoreType);
services.AddScoped(
    typeof(IRoleStore<>).MakeGenericType(roleType),
    roleStoreType);

Then UserManager and other Identity classes will use your User/Role store. But it's more trouble then it's worth, unless you have an existing DB structure which can't be mapped to EF/known providers.

3
2/13/2017 7:46:29 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