Search code examples
c#asp.net-mvcasp.net-mvc-5asp.net-identity

Extending ASP.NET Identity Roles: IdentityRole is not part of the model for the current context


I'm trying to use the new ASP.NET Identity in my MVC5 application, specifically I'm trying to integrate ASP.NET Identity into an existing database. I've already read the questions/answers on SO pertaining to DB First and ASP.NET Identity, and having followed all the recommendations I still can't add roles to my database, although I have no problems adding users. Here's my code:

var context = new PayrollDBEntities();
var roleManager = new RoleManager<AspNetRole>(new RoleStore<AspNetRole>(context));

bool roleExists = roleManager.RoleExists(roleDto.Name);
if (roleExists){
    return false;
}

var role = new AspNetRole(roleDto.Name){
    Name = roleDto.Name,
};

IdentityResult result = roleManager.Create(role);//Getting exception here

At the last line of code I get an exception of type 'System.InvalidOperationException': The entity type IdentityRole is not part of the model for the current context.

Here is my context:

public partial class PayrollDBEntities : IdentityDbContext
{
        public PayrollDBEntities()
            : base("name=PayrollDBEntities")
        {
        }

        public virtual DbSet<AspNetRole> AspNetRoles { get; set; }
        public virtual DbSet<AspNetUserClaim> AspNetUserClaims { get; set; }
        public virtual DbSet<AspNetUserLogin> AspNetUserLogins { get; set; }
        public virtual DbSet<AspNetUser> AspNetUsers { get; set; }
......
}

My AspNetUser and AspNetRole classes derive from IdentityUser and IdentityRole respectively, but I'm still getting that exception. Here is my database diagram:

enter image description here

Any help would be greatly appreciated.


Solution

  • After a few days of trying to get this to work in a clean manner, I've come to the conclusion that if you're using Database first and want to integrate ASP.NET Identity into your app, by far the easiest and cleanest solution is to create your own membership provider by overriding ASP.NET Identity. It's actually pretty easy, so far I've implemented UserStore and RoleStore to my liking. I've added columns/relations specific to my domain in my database, and whenever I create a user or a role, I take care of my database commits by adding the required relations. My UserStore implementation is quite similar to this. My RoleStore implementation is something like this:

    public class ApplicationRoleStore : IRoleStore<ApplicationRoleDTO>
    {
        private PayrollDBEntities _context;
        public ApplicationRoleStore() { }
    
        public ApplicationRoleStore(PayrollDBEntities database)
        {
            _context = database;
        }
    
        public Task CreateAsync(ApplicationRoleDTO role)
        {
            if (role == null)
            {
                throw new ArgumentNullException("RoleIsRequired");
            }
            var roleEntity = ConvertApplicationRoleDTOToAspNetRole(role);
            _context.AspNetRoles.Add(roleEntity);
            return _context.SaveChangesAsync();
    
        }
    
        public Task DeleteAsync(ApplicationRoleDTO role)
        {
            var roleEntity = _context.AspNetRoles.FirstOrDefault(x => x.Id == role.Id);
            if (roleEntity == null) throw new InvalidOperationException("No such role exists!");
            _context.AspNetRoles.Remove(roleEntity);
            return _context.SaveChangesAsync();
        }
    
        public Task<ApplicationRoleDTO> FindByIdAsync(string roleId)
        {
            var role = _context.AspNetRoles.FirstOrDefault(x => x.Id == roleId);
    
            var result = role == null
                ? null
                : ConvertAspNetRoleToApplicationRoleDTO(role);
    
            return Task.FromResult(result);
        }
    
        public Task<ApplicationRoleDTO> FindByNameAsync(string roleName)
        {
    
            var role = _context.AspNetRoles.FirstOrDefault(x => x.Name == roleName);
    
            var result = role == null
                ? null
                : ConvertAspNetRoleToApplicationRoleDTO(role);
    
            return Task.FromResult(result);
        }
    
        public Task UpdateAsync(ApplicationRoleDTO role)
        {
    
            return _context.SaveChangesAsync();
        }
    
        public void Dispose()
        {
            _context.Dispose();
        }
        private ApplicationRoleDTO ConvertAspNetRoleToApplicationRoleDTO(AspNetRole aspRole)
        {
            return new ApplicationRoleDTO{
                Id = aspRole.Id,
                EnterpriseId = aspRole.EnterpriseId,
                Name = aspRole.Name
            };
        }
    
        private AspNetRole ConvertApplicationRoleDTOToAspNetRole(ApplicationRoleDTO appRole)
        {
            return new AspNetRole{
                Id = appRole.Id,
                EnterpriseId = appRole.EnterpriseId,
                Name = appRole.Name,
            };
        }
    }
    

    And my ApplicationRoleDTO:

    public class ApplicationRoleDTO : IRole
    {
        public ApplicationRoleDTO()
        {
            Id = Guid.NewGuid().ToString();
        }
    
        public ApplicationRoleDTO(string roleName)
            : this()
        {
            Name = roleName;
        }
        public string Id { get; set; }
        public string Name { get; set; }
        public Guid EnterpriseId { get; set; }
    }
    

    I also found these 2 articles pretty helpful:

    Overview of Custom Storage Providers for ASP.NET Identity

    Implementing a Custom MySQL ASP.NET Identity Storage Provider