locked
Customisation of ASP.Net Identity UserRoles RRS feed

  • Question

  • User-395982222 posted

    Hi, 

    I am creating a system that where a user can have roles optionally assigned against an object.

    So, User1 can be a user for most of the system therefore has the "User" Role however i'd like to be able to allow User1 to have an "Admin" role for some entities on the system (these are basically represented by another EF entity called Account). 

    To do this we have customised the UserRole class by creating our own Class derived from IdentityUserRole and we use this in the IdentityDbContext generic template parameters. This works fine and the additional ObjectId member is present in the EF Core Migrations. 

     public class ConsoleUserRole : IdentityUserRole<Int64>
        {       
            public String ObjectType { get; set; }
     
            public String ObjectID { get; set; }
        }

    The problem is that the composite primary key on the UserId and RoleId is incorrect as now this should include the ObjectId. I tried adding HasKey() using the FluentApi and this changed the Primary key however left a table.UniqueConstraint("AK_AspNetUserRoles_UserId_RoleId", x => new { x.UserId, x.RoleId }); in the Migrations file which i assume must be the previous Primary key demoted to a constraint.

    What happens when i try and create the second entry on the DbSet.

    userRoles.Add(new ConsoleUserRole() { UserId = consoleUser.Id, RoleId = customerAdminRole.Id, ObjectID = "1", ObjectType = "Account" });
    userRoles.Add(new ConsoleUserRole() { UserId = consoleUser.Id, RoleId = customerAdminRole.Id, ObjectID = "2", ObjectType = "Account" });

    System.InvalidOperationException: 'The instance of entity type 'ConsoleUserRole' cannot be tracked because another instance with the same key value for {'UserId', 'RoleId'} is already being tracked. When attaching existing entities, ensure that only one entity instance with a given key value is attached. Consider using 'DbContextOptionsBuilder.EnableSensitiveDataLogging' to see the conflicting key values.'

    This must be the UniqueConstraint in the migration.

    The Migrations Builder includes the following...

    migrationBuilder.CreateTable(
    name: "AspNetUserRoles",
    columns: table => new
    {
    UserId = table.Column<long>(nullable: false),
    RoleId = table.Column<long>(nullable: false),
    ObjectID = table.Column<string>(nullable: false),
    ObjectType = table.Column<string>(nullable: true)
    },
    constraints: table =>
    {
    table.PrimaryKey("PK_AspNetUserRoles", x => new { x.UserId, x.RoleId, x.ObjectID });
    table.UniqueConstraint("AK_AspNetUserRoles_UserId_RoleId", x => new { x.UserId, x.RoleId });
    table.ForeignKey(
    name: "FK_AspNetUserRoles_AspNetRoles_RoleId",
    column: x => x.RoleId,
    principalTable: "AspNetRoles",
    principalColumn: "Id",
    onDelete: ReferentialAction.Cascade);
    table.ForeignKey(
    name: "FK_AspNetUserRoles_AspNetUsers_UserId",
    column: x => x.UserId,
    principalTable: "AspNetUsers",
    principalColumn: "Id",
    onDelete: ReferentialAction.Cascade);
    });

    If anyone can please point me in the right direction it would be appreciated!

    Many Thanks,

    Kevin

    Friday, May 10, 2019 3:08 PM

All replies

  • User1724605321 posted

    Hi kjporbis ,

    Why not directly assign that part of users the "Admin" role ,  you could just adjust to retire roles of current user and write your own logic .

    Best Regards,

    Nan Yu

    Monday, May 13, 2019 2:47 AM
  • User1129881744 posted

    Hi, @kjporbis I have the same problem. Did you manage to find a solution and get past the Entity error?

    Thank you for the help.

    Thursday, May 23, 2019 2:04 PM
  • User-395982222 posted

    Hi,

    Yes, I managed to fix this problem by creating my own User Store and Role Store derived from their base class customising the generic parameters where required. In this case the important one was AppUserRole.

    public class AppUserStore : UserStore<AppUser, AppRole, ApplicationDbContext, Int64, IdentityUserClaim<Int64>, AppUserRole, IdentityUserLogin<Int64>, IdentityUserToken<Int64>, IdentityRoleClaim<long>>
     {
            public ConsoleUserStore(ApplicationDbContext dbContext) : base(dbContext)
            {

            }
    }

    public class AppRoleStore : RoleStore<AppRole, ApplicationDbContext, Int64, AppUserRole, IdentityRoleClaim<Int64>>
    {
            public AppRoleStore(ApplicationDbContext context) : base(context)
            {
            }
    }

    You then inject these in the Startup.cs

    services.AddTransient<IUserStore<ConsoleUser>, ConsoleUserStore>();
    services.AddTransient<IRoleStore<ConsoleRole>, ConsoleRoleStore>();

    Thursday, May 23, 2019 2:34 PM
  • User-395982222 posted

    Sorry, the injecting would be..

    services.AddTransient<IUserStore<ConsoleUser>, AppUserStore>();
    services.AddTransient<IRoleStore<ConsoleRole>, AppRoleStore>();

    Friday, May 24, 2019 7:36 AM