Удостоверение AspNet - Пользовательский IdentityRoleClaim & IdentityUserRole & IdentityUserClaim, который дублирует внешний ключ - PullRequest
1 голос
/ 23 мая 2019

В настоящее время я использую AspNetCore и конфигурацию dbcontext, как показано ниже:

public class AppDbContext : IdentityDbContext<ApplicationUser, ApplicationRole, string, ApplicationUserClaim, ApplicationUserRole, IdentityUserLogin<string>, ApplicationRoleClaim, IdentityUserToken<string>>
{
    public AppDbContext(DbContextOptions<AppDbContext> options)
        : base(options)
    { }

Итак, я хотел бы настроить AspNetIdentity, но для простоты я только что настроил внешний ключ между таблицами:

  • ApplicationUser является производным от IdentityUser
  • ApplicationRole является производным от IdentityRole
  • ApplicationUserClaim является производным от IdentityUserClaim <string>
  • ApplicationUserRole является производным от IdentityUserRole <string>
  • ApplicationRoleClaim является производным от IdentityRoleClaim <string>

public class ApplicationUser : IdentityUser
{
    public virtual ICollection<ApplicationUserRole> UserRoles { get; set; }
    public virtual ICollection<ApplicationUserClaim> UserClaims { get; set; }
}

public class ApplicationRole : IdentityRole
{
     public virtual ICollection<ApplicationUserRole> UserRoles { get; set; }
     public virtual ICollection<ApplicationRoleClaim> RoleClaims { get; set; }
}

public class ApplicationRoleClaim : IdentityRoleClaim<string>
{
     public virtual ApplicationRole Role { get; set; }
}

public class ApplicationUserRole : IdentityUserRole<string>
{
     public int Id { get; set; }

     public virtual ApplicationRole Role { get; set; }
     public virtual ApplicationUser User { get; set; }
}

public class ApplicationUserClaim : IdentityUserClaim<string>
{
    public virtual ApplicationUser User { get; set; }
}

И следует, как производный класс реализует IEntityTypeConfiguration для конкретной пользовательской модели идентификации.

public class ApplicationUserEntityBuilder : IEntityTypeConfiguration<ApplicationUser>
{
    public void Configure(EntityTypeBuilder<ApplicationUser> builder)
    {
        // still use default table name
        builder.ToTable("AspNetUsers");

        builder.Property(p => p.Id)
               .HasColumnType("CHAR(36)");
    }
}

public class ApplicationRoleEntityBuilder : IEntityTypeConfiguration<ApplicationRole>
 {
     public void Configure(EntityTypeBuilder<ApplicationRole> builder)
     {
         // still use default table name
         builder.ToTable("AspNetRoles");
     }

 }

 public class ApplicationUserClaimEntityBuilder : IEntityTypeConfiguration<ApplicationUserClaim>
  {
        public void Configure(EntityTypeBuilder<ApplicationUserClaim> builder)
        {
            builder.Property(p => p.UserId)
                   .HasColumnName(nameof(ApplicationUserClaim.UserId))
                   .HasColumnType("CHAR(36)");

            builder.HasOne(p => p.User)
                   .WithMany(u => u.UserClaims)
                   .HasForeignKey(p => p.UserId)
                   .IsRequired();
        }

    }

public class ApplicationUserRoleEntityBuilder : IEntityTypeConfiguration<ApplicationUserRole>
{
    public void Configure(EntityTypeBuilder<ApplicationUserRole> builder)
    {

        // still use default table name
        builder.ToTable("AspNetUserRoles");

        builder.HasKey(p => p.Id);
        builder.Property(p => p.Id)
               .ValueGeneratedOnAdd();

        builder.HasOne(p => p.Role)
               .WithMany(r => r.UserRoles)
               .HasForeignKey(p => p.RoleId)
               .IsRequired();

        builder.HasOne(p => p.User)
               .WithMany(r => r.UserRoles)
               .HasForeignKey(p => p.UserId)
               .IsRequired();
    }
}

public class ApplicationRoleClaimEntityBuilder : IEntityTypeConfiguration<ApplicationRoleClaim>
{
    public void Configure(EntityTypeBuilder<ApplicationRoleClaim> builder)
    {
        // still use default table name
        builder.ToTable("AspNetRoleClaims");

        builder.HasKey(p => p.Id);

        builder.Property(p => p.Id)
               .ValueGeneratedOnAdd();

        builder.Property(p => p.RoleId)
               .HasColumnName(nameof(ApplicationRoleClaim.RoleId))
               .HasColumnType("CHAR(36)");

        builder.HasOne(p => p.Role)
               .WithMany(r => r.RoleClaims)
               .HasForeignKey(p => p.RoleId)
               .IsRequired();
    }
}

После всех этих плавных настроек API EF Core сгенерировал дублированный внешний ключ в таблице AspNetRoleClaims, AspNetUserClaims, AspNetUserRoles

migrationBuilder.CreateTable(
                name: "AspNetRoleClaims",
                columns: table => new
                {
                    Id = table.Column<int>(nullable: false)
                        .Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn),
                    RoleId = table.Column<string>(type: "CHAR(36)", nullable: false),
                    ClaimType = table.Column<string>(nullable: true),
                    ClaimValue = table.Column<string>(nullable: true),
                    RoleId1 = table.Column<string>(nullable: true) // DUPLICATE
                },
                constraints: table =>
                {
                    table.PrimaryKey("PK_AspNetRoleClaims", x => x.Id);
                    table.ForeignKey(
                        name: "FK_AspNetRoleClaims_AspNetRoles_RoleId",
                        column: x => x.RoleId,
                        principalTable: "AspNetRoles",
                        principalColumn: "Id",
                        onDelete: ReferentialAction.Cascade);
                    table.ForeignKey(
                        name: "FK_AspNetRoleClaims_AspNetRoles_RoleId1",
                        column: x => x.RoleId1,
                        principalTable: "AspNetRoles",
                        principalColumn: "Id",
                        onDelete: ReferentialAction.Restrict);
                });


migrationBuilder.CreateTable(
                name: "AspNetUserClaims",
                columns: table => new
                {
                    Id = table.Column<int>(nullable: false)
                        .Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn),
                    UserId = table.Column<string>(type: "CHAR(36)", nullable: false),
                    ClaimType = table.Column<string>(nullable: true),
                    ClaimValue = table.Column<string>(nullable: true),
                    UserId1 = table.Column<string>(nullable: true) //DUPLICATE
                },
                constraints: table =>
                {
                    table.PrimaryKey("PK_AspNetUserClaims", x => x.Id);
                    table.ForeignKey(
                        name: "FK_AspNetUserClaims_AspNetUsers_UserId",
                        column: x => x.UserId,
                        principalTable: "AspNetUsers",
                        principalColumn: "Id",
                        onDelete: ReferentialAction.Cascade);
                    table.ForeignKey(
                        name: "FK_AspNetUserClaims_AspNetUsers_UserId1",
                        column: x => x.UserId1,
                        principalTable: "AspNetUsers",
                        principalColumn: "Id",
                        onDelete: ReferentialAction.Restrict);
                });


migrationBuilder.CreateTable(
                name: "AspNetUserRoles",
                columns: table => new
                {
                    UserId = table.Column<string>(nullable: false),
                    RoleId = table.Column<string>(nullable: false),
                    Id = table.Column<int>(nullable: false)
                        .Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn),
                    RoleId1 = table.Column<string>(nullable: true), //DUPLICATE
                    UserId1 = table.Column<string>(nullable: true)  //DUPLICATE
                },
                constraints: table =>
                {
                    table.PrimaryKey("PK_AspNetUserRoles", x => new { x.UserId, x.RoleId });
                    table.UniqueConstraint("AK_AspNetUserRoles_Id", x => x.Id);
                    table.ForeignKey(
                        name: "FK_AspNetUserRoles_AspNetRoles_RoleId",
                        column: x => x.RoleId,
                        principalTable: "AspNetRoles",
                        principalColumn: "Id",
                        onDelete: ReferentialAction.Cascade);
                    table.ForeignKey(
                        name: "FK_AspNetUserRoles_AspNetRoles_RoleId1",
                        column: x => x.RoleId1,
                        principalTable: "AspNetRoles",
                        principalColumn: "Id",
                        onDelete: ReferentialAction.Restrict);
                    table.ForeignKey(
                        name: "FK_AspNetUserRoles_AspNetUsers_UserId",
                        column: x => x.UserId,
                        principalTable: "AspNetUsers",
                        principalColumn: "Id",
                        onDelete: ReferentialAction.Cascade);
                    table.ForeignKey(
                        name: "FK_AspNetUserRoles_AspNetUsers_UserId1",
                        column: x => x.UserId1,
                        principalTable: "AspNetUsers",
                        principalColumn: "Id",
                        onDelete: ReferentialAction.Restrict);
                });

Пожалуйста, просмотрите это для меня, если что-то я сделал не так. Заранее спасибо, ребята.


ОБНОВЛЕНИЕ 1

Из-за свойства навигации, которое приводит к дублированию внешнего ключа. Если я удалю эти свойства в пользовательской модели идентификации, то EFCore не будет генерировать «DuplicateProperty1». Так что мне теперь делать?


ФИНАЛЬНОЕ ОБНОВЛЕНИЕ

Основная причина, которую я поставил base.OnModelCreating(builder); в конце метода protected override void OnModelCreating(ModelBuilder builder). Поэтому все, что я настроил во внутренних производных классах, реализовало IEntityTypeConfiguration, которое будет переопределено базовым классом. И окончательная версия, как показано ниже:

public class AppDbContext : IdentityDbContext<ApplicationUser, ApplicationRole, string, ApplicationUserClaim, ApplicationUserRole, IdentityUserLogin<string>, ApplicationRoleClaim, IdentityUserToken<string>>
{
    public AppDbContext(DbContextOptions<AppDbContext> options)
            : base(options)
    { }

    protected override void OnModelCreating(ModelBuilder builder)
    {
        base.OnModelCreating(builder);

        var entitiesBuilder = Assembly.GetExecutingAssembly()
                                      .GetTypes()
                                      .Where(type => type.ContainsGenericParameters == false && type.GetInterface(nameof(IEntityBuilder)) != null)
                                      .ToList();

        foreach (var entityBuilder in entitiesBuilder)
        {
            var instance = Activator.CreateInstance(entityBuilder) as IEntityBuilder;
            instance.RunConfiguration(builder);
        }

        // base.OnModelCreating(builder); move this line to the beginning of this method
    }
}

Ответы [ 3 ]

1 голос
/ 23 мая 2019

Отношения между таблицами уже установлены внутри Идентификационные данные IdentityDbContext конфигурация по умолчанию .Это означает, что вам не нужно указывать их явно (используя навигационные настройки props / fluent api).

Однако похоже, что вы хотите использовать Guid / Uuid вместо string в качестве типа столбца для вашего Primary /Внешние ключи.Если это так, то правильным способом для этого является использование общих вариантов классов, которые вы уже унаследовали.

Например, вы можете определить только те классы, которые вам нужно расширить, и добавить к ним дополнительные свойства, чтобудет включен в миграцию БД:

public class ApplicationUser : IdentityUser<Guid>
{
}

public class ApplicationRole : IdentityRole<Guid>
{
}

Кроме того, ваш DbContext должен наследовать правильный вариант IdentityDbContext:

public class ApplicationUserDbContext : IdentityDbContext<ApplicationUser, ApplicationRole, Guid>
{
}

После этого ваша миграция сгенерируетскрипт для изменения PK / FK в правильный тип столбца.Если вам необходимо расширить другие типы с Identity , вы можете использовать наиболее общий вариант IdentityDbContext, который принимает набор параметров типа .

0 голосов
/ 24 мая 2019

Убедитесь, что вы настроили IEntityTypeConfiguration, как показано ниже в AppDbContext.

public class ApplicationDbContext : IdentityDbContext<ApplicationUser, ApplicationRole, string, ApplicationUserClaim, ApplicationUserRole, IdentityUserLogin<string>, ApplicationRoleClaim, IdentityUserToken<string>>
{
    public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
        : base(options)
    { }

    protected override void OnModelCreating(ModelBuilder builder)
    {
        base.OnModelCreating(builder);
        builder.ApplyConfiguration(new ApplicationUserEntityBuilder());
        builder.ApplyConfiguration(new ApplicationRoleEntityBuilder());
        builder.ApplyConfiguration(new ApplicationUserClaimEntityBuilder());
        builder.ApplyConfiguration(new ApplicationUserRoleEntityBuilder());
        builder.ApplyConfiguration(new ApplicationRoleClaimEntityBuilder());
    }
}
0 голосов
/ 23 мая 2019

Это происходит потому, что когда вы наследуете от моделей идентичности, у него уже есть эти отношения и другие свойства. Ваш код только что добавил дополнительные отношения между сущностями. Вы можете прочитать больше о настройке личности в документации: https://docs.microsoft.com/en-us/aspnet/core/security/authentication/customize-identity-model?view=aspnetcore-2.1

...