如何使用 EF Core 实现 Table-Per-Type 以实现层次结构?

How to implement a Table-Per-Type for hiearchy with EF Core?

提问人:Özlem Karabulut 提问时间:11/6/2023 更新时间:11/6/2023 访问量:19

问:

  • 我尝试使用 ef core 在数据库中实现层次结构。我有继承自 User 类的 RetailerAdmin 类。 我尝试创建迁移并使用 TPT 方法。似乎为所有三个类创建了表,但我无法使用 id 属性将表相互链接。我试图用MSSQL实现insert sql,它说

“表'dbo。Admin' 没有 identity 属性。无法执行 SET 操作。 以下是我的表视图: 管理员和零售商用户表

  • 我的课程如下:
namespace ECommerce.Data.Domain;

[Table("User", Schema = "dbo")]
public class User : BaseModel
{
    public string Name { get; set; }
    public string LastName { get; set; }
    public string Email { get; set; }
    public string Password { get; set; }
    public string UserName { get; set; }
    
    public int RoleId { get; set; }
    public  virtual Role Role { get; set; }

    public virtual List<Address> Addresses { get; set; }

    public DateTime LastActivityDate { get; set; }

    public int PasswordRetryCount { get; set; } 

}


public class UserConfiguration : IEntityTypeConfiguration<User>
{
    public void Configure(EntityTypeBuilder<User> builder)
    {
        builder.Property(x => x.InsertUserId).IsRequired();
        builder.Property(x => x.UpdateUserId).IsRequired().HasDefaultValue(0);
        builder.Property(x => x.InsertDate).IsRequired();
        builder.Property(x => x.UpdateDate).IsRequired(false);
        builder.Property(x => x.IsActive).IsRequired().HasDefaultValue(true);

        builder.Property(x => x.Id).IsRequired(true);
        builder.Property(x => x.Email).IsRequired(true).HasMaxLength(50);
        builder.Property(x => x.Password).IsRequired(true).HasMaxLength(50);
        builder.Property(x => x.Name).IsRequired().HasMaxLength(50);
        builder.Property(x => x.LastName).IsRequired().HasMaxLength(50);
        builder.Property(x => x.UserName).IsRequired(true).HasMaxLength(10);
        builder.Property(x => x.RoleId).IsRequired(true);
        builder.Property(x => x.LastActivityDate).IsRequired();
        builder.Property(x => x.PasswordRetryCount).IsRequired().HasDefaultValue(0);

        builder.HasIndex(x => x.Id).IsUnique(true);
        builder.HasIndex(x => x.Email).IsUnique(true);
        builder.HasIndex(x => x.UserName).IsUnique(true);


        builder.HasMany(x => x.Addresses)
            .WithOne(x => x.User)
            .HasForeignKey(x => x.UserId)
            .IsRequired(true);


    }
}
[Table("Admin", Schema = "dbo")]
public class Admin : User
{

}

public class AdminConfiguration : IEntityTypeConfiguration<Admin>
{
    public void Configure(EntityTypeBuilder<Admin> builder)
    {
        builder.Property(x => x.InsertUserId).IsRequired();
        builder.Property(x => x.UpdateUserId).IsRequired().HasDefaultValue(0);
        builder.Property(x => x.InsertDate).IsRequired();
        builder.Property(x => x.UpdateDate).IsRequired(false);
        builder.Property(x => x.IsActive).IsRequired().HasDefaultValue(true);

        builder.Property(x => x.Id).IsRequired(true);
        builder.Property(x => x.Name).IsRequired().HasMaxLength(20);

    }
}
namespace ECommerce.Data.Domain;

[Table("Retailer", Schema = "dbo")]
public class Retailer : User
{
    //retailer may have multiple receipt infos and choose between them in order process.
    public virtual List<ReceiptInfo> ReceiptInfos { get; set; } = new List<ReceiptInfo>();

    public virtual List<Order> Orders { get; set; } = new List<Order>();

}

public class RetailerConfiguration : IEntityTypeConfiguration<Retailer>
{
    public void Configure(EntityTypeBuilder<Retailer> builder)
    {
        builder.Property(x => x.InsertUserId).IsRequired();
        builder.Property(x => x.UpdateUserId).IsRequired().HasDefaultValue(0);
        builder.Property(x => x.InsertDate).IsRequired();
        builder.Property(x => x.UpdateDate).IsRequired(false);
        builder.Property(x => x.IsActive).IsRequired().HasDefaultValue(true);

        builder.Property(x => x.Id).IsRequired();

        builder.HasMany(x => x.Orders)
       .WithOne(x => x.Retailer)
       .HasForeignKey(x => x.RetailerId)
       .IsRequired(true);


        builder.HasMany(x => x.ReceiptInfos)
            .WithOne(x => x.Retailer)
            .HasForeignKey(x => x.RetailerId)
            .IsRequired(true)
            .OnDelete(DeleteBehavior.Restrict);

    }
}

我的dbcontext配置:

public class ECommerceDbContext : DbContext
{
    public ECommerceDbContext(DbContextOptions<ECommerceDbContext> options) : base(options)
    {
    }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<User>().UseTptMappingStrategy();
        modelBuilder.ApplyConfiguration(new AddressConfiguration ());
        modelBuilder.ApplyConfiguration(new AdminConfiguration());

        modelBuilder.ApplyConfiguration(new RetailerConfiguration());
        modelBuilder.ApplyConfiguration(new UserConfiguration());

        base.OnModelCreating(modelBuilder);
    }



  • 我在互联网上研究了各种示例,没有人在他们的子类中添加额外的 Id 字段。我也没有,因为子类会自动从 User 类继承 Id 属性。我在初始迁移中看到表具有 Id 属性。我之前可以将 id 添加到 Admin 和 Retailer 表中。而不是我必须从头开始创建迁移。

  • 我对这个话题很陌生。告诉我我是否错了,但迁移不应该使用 Id 列自动将表相互链接吗?我在我的孩子班上什么也没看到。

  • 我还添加了我的迁移文件:

           migrationBuilder.CreateTable(
               name: "User",
               schema: "dbo",
               columns: table => new
               {
                   Id = table.Column<int>(type: "int", nullable: false)
                       .Annotation("SqlServer:Identity", "1, 1"),
                   Name = table.Column<string>(type: "nvarchar(50)", maxLength: 50, nullable: false),
                   LastName = table.Column<string>(type: "nvarchar(50)", maxLength: 50, nullable: false),
                   Email = table.Column<string>(type: "nvarchar(50)", maxLength: 50, nullable: false),
                   Password = table.Column<string>(type: "nvarchar(50)", maxLength: 50, nullable: false),
                   UserName = table.Column<string>(type: "nvarchar(10)", maxLength: 10, nullable: false),
                   RoleId = table.Column<int>(type: "int", nullable: false),
                   LastActivityDate = table.Column<DateTime>(type: "datetime2", nullable: false),
                   PasswordRetryCount = table.Column<int>(type: "int", nullable: false, defaultValue: 0),
                   InsertUserId = table.Column<int>(type: "int", nullable: false),
                   InsertDate = table.Column<DateTime>(type: "datetime2", nullable: false),
                   UpdateUserId = table.Column<int>(type: "int", nullable: false, defaultValue: 0),
                   UpdateDate = table.Column<DateTime>(type: "datetime2", nullable: true),
                   IsActive = table.Column<bool>(type: "bit", nullable: false, defaultValue: true)
               },
               constraints: table =>
               {
                   table.PrimaryKey("PK_User", x => x.Id);
                   table.ForeignKey(
                       name: "FK_User_Role_RoleId",
                       column: x => x.RoleId,
                       principalSchema: "dbo",
                       principalTable: "Role",
                       principalColumn: "Id",
                       onDelete: ReferentialAction.Cascade);
               });

 migrationBuilder.CreateTable(
     name: "Admin",
     schema: "dbo",
     columns: table => new
     {
         Id = table.Column<int>(type: "int", nullable: false)
     },
     constraints: table =>
     {
         table.PrimaryKey("PK_Admin", x => x.Id);
         table.ForeignKey(
             name: "FK_Admin_User_Id",
             column: x => x.Id,
             principalSchema: "dbo",
             principalTable: "User",
             principalColumn: "Id",
             onDelete: ReferentialAction.Cascade);
     });

 migrationBuilder.CreateTable(
     name: "Retailer",
     schema: "dbo",
     columns: table => new
     {
         Id = table.Column<int>(type: "int", nullable: false)
     },
     constraints: table =>
     {
         table.PrimaryKey("PK_Retailer", x => x.Id);
         table.ForeignKey(
             name: "FK_Retailer_User_Id",
             column: x => x.Id,
             principalSchema: "dbo",
             principalTable: "User",
             principalColumn: "Id",
             onDelete: ReferentialAction.Cascade);
     });

我还可以看到我的 Id 属性已创建。

有人可以帮我适当地实施 TPT 吗?谢谢你的帮助。

entity-framework-core database-migration entity-framework-migrations 层次结构 table-per-type

评论


答: 暂无答案