7

I'm using ASP.NET Identity Provider and EF 6 Code First and I have created a custom IdentityUserRole table which has an extra column OrganisationId. The custom table is named UserRole.

The table currently has the default primary key of UserId and RoleId.

I would like to have the OrganisationId column included in the Primary Key of the IdentityUserRole table.

I can see the UserRole table in my database, and it's the one that's being used for the userroles (there's no aspnet_userrole table and the table has data when I assign a role to a user)

I tried this:

modelBuilder.Entity<IdentityUserRole>().ToTable("UserRole").HasKey(x => new { x.UserId, x.RoleId }); 

But it doesn't show me the OrganisationId property.

So I tried:

modelBuilder.Entity<UserRole>().HasKey(x => new { x.OrganisationId, x.RoleId, x.UserId });

But when I add a migration, both the Up and Down methods are empty.

Update (some code)

The IdentityUserRole class:

public class UserRole : IdentityUserRole, IOrganisationEntity
{
    [Key]
    public int OrganisationId { get; set; }

    [ForeignKey("OrganisationId")]
    public Organisation Organisation { get; set; }
}

DbContext inherits from IdentityDbContext<User>

And User inherits from IdentityRebootUser

Update 2

This is the new DbContext:

public class MyDbContext : IdentityDbContext<User, Role, string, 
                             IdentityUserLogin, UserRole, IdentityUserClaim>

and this is my new User class:

public class User : IdentityRebootUser<string, IdentityUserLogin, 
                        Role, IdentityUserClaim>

And the Role class:

public class Role : IdentityRole<string, UserRole>

However, this gives me the following error:

The type 'MyNamespace.Model.Entities.User' cannot be used as type parameter 'TUser' in the generic type or method 'Microsoft.AspNet.Identity.EntityFramework.IdentityDbContext<TUser, TRole, TKey, TUserLogin, TUserRole, TUserClaim>'. There is no implicit reference conversion from 'MyNamespace.Model.Entities.User' to 'Microsoft.AspNet.Identity.EntityFramework.IdentityUser<string, Microsoft.AspNet.Identity.EntityFramework.IdentityUserLogin, MyNamespace.Model.Entities.UserRole, Microsoft.AspNet.Identity.EntityFramework.IdentityUserClaim>'

Update 3

I got rid of IdentityReboot. I created my own User object which has the following signature:

public class User : IdentityUser<string, IdentityUserLogin, 
                        UserRole, IdentityUserClaim>

I can now modify the UserRole table to add an extra PK, which is what I wanted.

The problem I have now is, that I cannot use the default UserStore, because when I tell it to use my custom 'User' table, I get the following error:

There is no implicit reference conversion from 'MyDB.Model.Entities.User' to 'Microsoft.AspNet.Identity.EntityFramework.IdentityUser'.

So I implemented my own version of IUserStore, which is generating the async errors.

If only there was a way to use UserStore with my custom User object, I would be more than happy.

jao
  • 18,273
  • 15
  • 63
  • 96
  • What does your `IdentityUserRole` class look like? Also, what does your context inherit from? – DavidG Jul 31 '14 at 12:10
  • I added the relevant code – jao Jul 31 '14 at 12:14
  • OK, so `UserRole` is the object to use though I assume it now makes `IdentityUserRole` redundant? Your context needs to inherit from the full `IdentityDbContext` instead of the basic one. – DavidG Jul 31 '14 at 12:20
  • Make sure your User class inherits from the right place too: `IdentityUser` – DavidG Aug 01 '14 at 01:06
  • I fixed it by getting rid of IdentityReboot, changing the appropriate classes and implementing my own UserStore – jao Aug 01 '14 at 05:14
  • So it's all working now? – DavidG Aug 01 '14 at 08:12
  • no :) it's off to the next error: A second operation started on this context before a previous asynchronous operation completed. Use 'await' to ensure that any asynchronous operations have completed before calling another method on this context. Any instance members are not guaranteed to be thread safe. Maybe i'll just skip the identity provider completely untill there's a stable version – jao Aug 01 '14 at 08:32
  • Identity *is* stable, I've been using it in production for some time now. – DavidG Aug 01 '14 at 08:44
  • Thanks for your help so far. Please see my update above. – jao Aug 01 '14 at 09:01
  • Have you looked at this: http://www.asp.net/identity/overview/extensibility/change-primary-key-for-users-in-aspnet-identity – DavidG Aug 01 '14 at 09:27
  • Thanks! That was the information I needed. Basically, I had to customize all Identity objects. Can you add is as an answer? – jao Aug 01 '14 at 10:26
  • Great, glad to hear it's working. Very handy for a multi tenanted environment for sure. – DavidG Aug 01 '14 at 10:46

1 Answers1

9

You need to customise all of your identity objects to specify different keys. There are various tutorials online but the best I've found is from www.asp.net.

Essentially you need to make your own version of each object but inherit from the more complex versions. For example, instead of:

public class ApplicationUser : IdentityUser
{
    ...
}

You would do this:

public class ApplicationUser : IdentityUser<int, CustomUserLogin, CustomUserRole, 
CustomUserClaim> 
{ 
    ...
}

And repeat for all object including UserStore, UserManager, RoleManager etc.

DavidG
  • 113,891
  • 12
  • 217
  • 223