6

I have extended my AspNetRoles that was created by Entity Framework to look like this:

public class AspNetRoles:IdentityRole
{
        public AspNetRoles() : base() { }
        public String Label { get; set; }
        public String ApplicationId { get; set; }
        public AspNetApplications Application { get; set; }

        public static readonly String SystemAdministrator = "SystemAdministrator";
}

I understood that because I have extended the identityrole table, I had to make changes to my usermanager. This is what I have done:

public class ApplicationUserManager : UserManager<ApplicationUser>
{
    public ApplicationUserManager(IUserStore<ApplicationUser> store)
        : base(store)
    {
    }

    public static ApplicationUserManager Create(IdentityFactoryOptions<ApplicationUserManager> options, IOwinContext context) 
    {
        var manager = new ApplicationUserManager(new UserStore<ApplicationUser>(context.Get<ApplicationDbContext>()));
        // Configure validation logic for usernames
        manager.UserValidator = new UserValidator<ApplicationUser>(manager)
        {
            AllowOnlyAlphanumericUserNames = false,
            RequireUniqueEmail = true
        };

        // Configure validation logic for passwords
        manager.PasswordValidator = new PasswordValidator
        {
            RequiredLength = 6,
            RequireNonLetterOrDigit = true,
            RequireDigit = true,
            RequireLowercase = true,
            RequireUppercase = true,
        };

        // Configure user lockout defaults
        manager.UserLockoutEnabledByDefault = true;
        manager.DefaultAccountLockoutTimeSpan = TimeSpan.FromMinutes(5);
        manager.MaxFailedAccessAttemptsBeforeLockout = 5;

        // Register two factor authentication providers. This application uses Phone and Emails as a step of receiving a code for verifying the user
        // You can write your own provider and plug it in here.
        manager.RegisterTwoFactorProvider("Phone Code", new PhoneNumberTokenProvider<ApplicationUser>
        {
            MessageFormat = "Your security code is {0}"
        });
        manager.RegisterTwoFactorProvider("Email Code", new EmailTokenProvider<ApplicationUser>
        {
            Subject = "Security Code",
            BodyFormat = "Your security code is {0}"
        });
        manager.EmailService = new EmailService();
        manager.SmsService = new SmsService();
        var dataProtectionProvider = options.DataProtectionProvider;
        if (dataProtectionProvider != null)
        {
            manager.UserTokenProvider = 
                new DataProtectorTokenProvider<ApplicationUser>(dataProtectionProvider.Create("ASP.NET Identity"));
        }
        return manager;
    }
}

// Configure the application sign-in manager which is used in this application.
public class ApplicationSignInManager : SignInManager<ApplicationUser, string>
{
    public ApplicationSignInManager(ApplicationUserManager userManager, IAuthenticationManager authenticationManager)
        : base(userManager, authenticationManager)
    {
    }

    public override Task<ClaimsIdentity> CreateUserIdentityAsync(ApplicationUser user)
    {
        return user.GenerateUserIdentityAsync((ApplicationUserManager)UserManager);
    }

    public static ApplicationSignInManager Create(IdentityFactoryOptions<ApplicationSignInManager> options, IOwinContext context)
    {
        return new ApplicationSignInManager(context.GetUserManager<ApplicationUserManager>(), context.Authentication);
    }
}

public class ApplicationRoleManager : RoleManager<AspNetRoles>, IDisposable
{
    public ApplicationRoleManager(RoleStore<AspNetRoles> store) : base(store)
    { }


    public static ApplicationRoleManager Create(IdentityFactoryOptions<ApplicationRoleManager> options, IOwinContext context)
    {
        //AppIdentityDbContext db = context.Get<AppIdentityDbContext>();
        //AppRoleManager manager = new AppRoleManager(new RoleStore<AppRole>(db));
        return new ApplicationRoleManager(new RoleStore<AspNetRoles>(context.Get<ApplicationDbContext>()));

        //return manager;
    }
}

public class ApplicationUserStore<TUser> : UserStore<TUser, AspNetRoles, string, IdentityUserLogin, IdentityUserRole, IdentityUserClaim>, IUserStore<TUser>, IUserStore<TUser, string>, IDisposable where TUser : IdentityUser
{
    public ApplicationUserStore(DbContext context) : base(context) { }
}

This is my DBContext:

public class ApplicationDbContext : IdentityDbContext<ApplicationUser, AspNetRoles, string, IdentityUserLogin, IdentityUserRole, IdentityUserClaim>
    {
        public virtual DbSet<AspNetUsersExtendedDetails> AspNetUsersExtendedDetails { get; set; }
        public virtual DbSet<AspNetApplications> AspNetApplications { get; set; }
        public virtual DbSet<AspNetEventLogs> AspNetEventLogs { get; set; }
        public ApplicationDbContext() : base("AppStudio")
        {

        }
        public static ApplicationDbContext Create()
        {
            return new ApplicationDbContext();
        }
    }

However, I get this error when I start my application:

The entity type IdentityRole is not part of the model for the current context.

I'm not sure why this is happening. Have I missed something that needs to be changed after extending my roles table?

JianYA
  • 2,750
  • 8
  • 60
  • 136
  • Not sure if you're using Code First or Database First, but customizing roles so far requires modification of `UserStore` and/or `UserManager` (see [here](https://stackoverflow.com/questions/22796804/why-am-i-getting-an-identityrole-error) and [here](https://stackoverflow.com/questions/28116426/addtorole-and-identityrole-is-not-part-of-the-model-for-the-current-context) for further explanation). – Tetsuya Yamamoto Aug 23 '18 at 12:46
  • Hi @TetsuyaYamamoto, I'm using Code First. I have updated my question with some changes I made. Can you check if I'm missing something? – JianYA Aug 23 '18 at 12:50
  • @TetsuyaYamamoto I tried to follow your examples but no change. – JianYA Aug 23 '18 at 13:21

1 Answers1

15

Short answer

The main problem in above code is in Create method of UserManager. In that method, you should create a UserManager using a UserStore which is aware of the new role class which you created. To do so, you can use an instance of ApplicationUserStore class which you have or create a new user store this way:

new UserStore<ApplicationUser, [YourRoleClass], string, 
    IdentityUserLogin, IdentityUserRole, IdentityUserClaim(
        context.Get<ApplicationDbContext>())

How to Add a custom Property to IdentityRole?

To add a new property to IdentityRole, you can follow the following steps:

  1. Create an ASP.NET Web Application
  2. Make sure you select MVC and the Authentication is Individual User Accounts
  3. Go to Models folder → Open IdentityModels.cs and Create ApplicationRole class containing the custom property that you want to add:

    public class ApplicationRole : IdentityRole   //My custom role class
    {
        public string ApplicationId { get; set; } //My custom property
    }
    
  4. Change GenerateUserIdentityAsync method of ApplicationUser to accept parameter of type of UserManager<ApplicationUser, string>:

    public class ApplicationUser : IdentityUser
    {
        public async Task<ClaimsIdentity> GenerateUserIdentityAsync(UserManager<ApplicationUser, string> manager)
        {
    
  5. Change ApplicationDbContext base class and introduce all the generic parameters:

    public class ApplicationDbContext : IdentityDbContext<ApplicationUser, ApplicationRole, string, IdentityUserLogin, IdentityUserRole, IdentityUserClaim>
    {
        public ApplicationDbContext()
            : base("DefaultConnection")
        {
    
  6. Build the project.

  7. Go to TOOLS menu → Nuget Package Manager → click Package Manager Console
  8. Type Enable-Migrations and press Enter and wait until the task get completed.
  9. Type Add-Migration "ApplicationRole" and press Enter and wait until the task get completed.
  10. Type Update-Database and press Enter and wait until the task get completed.
  11. Go to App_Start folder → Open IdentityConfig.cs and Change the ApplicationUserManager class to derive from UserManager<ApplicationUser, string> and also change its Create method to return a UserManage aware of ApplicationRole:

    public class ApplicationUserManager : UserManager<ApplicationUser, string>
    {
        public ApplicationUserManager(IUserStore<ApplicationUser, string> store)
            : base(store)
        {
        }
    
        public static ApplicationUserManager Create(IdentityFactoryOptions<ApplicationUserManager> options, IOwinContext context)
        {
            var manager = new ApplicationUserManager(new UserStore<ApplicationUser, ApplicationRole, string, IdentityUserLogin, IdentityUserRole, IdentityUserClaim>(context.Get<ApplicationDbContext>()));
    
  12. To manage roles, create ApplicationRoleManager class in the same file:

    public class ApplicationRoleManager : RoleManager<ApplicationRole>
    {
        public ApplicationRoleManager(IRoleStore<ApplicationRole, string> store) : base(store) { }
    
        public static ApplicationRoleManager Create(
            IdentityFactoryOptions<ApplicationRoleManager> options,
            IOwinContext context)
        {
            return new ApplicationRoleManager(new RoleStore<ApplicationRole>(context.Get<ApplicationDbContext>()));
        }
    }
    
  13. Go to App_Start folder → Open Startup.Auth.cs and add the following code to the ConfigureAuth method:

    ConfigureAuthapp.CreatePerOwinContext<ApplicationRoleManager>(ApplicationRoleManager.Create);
    

Now the project is ready to take advantage of the new ApplicationRole.

Reza Aghaei
  • 120,393
  • 18
  • 203
  • 398
  • 1
    I'll try this. Thank you so much. At the moment I just inherited the IdentityRole with a discriminator field in my table. Seems to work fine as well? What is the difference between this method and mine? – JianYA Aug 27 '18 at 04:51
  • 1
    You're welcome, The main difference is in step 11 (and 4). – Reza Aghaei Aug 27 '18 at 05:32
  • 2
    @El7or You're welcome, thanks for the feedback. In case you are customizing identity user as well, you may want to take a look at [this post](https://stackoverflow.com/a/40579369/3110834). – Reza Aghaei Feb 22 '20 at 15:09
  • @Örvar Great! Thanks for the feedback :) – Reza Aghaei May 05 '21 at 15:27