配置抽象基类而不在 EF Core 中创建表 [重复]

Posted

技术标签:

【中文标题】配置抽象基类而不在 EF Core 中创建表 [重复]【英文标题】:Configuring abstract base class without creating table in EF Core [duplicate] 【发布时间】:2019-01-05 06:11:18 【问题描述】:

我通过创建BaseEntity.cs 类为每个实体添加了DateCreatedUserCreatedDateModifiedUserModified 字段。我的要求是生成单个表,其中包含base 类的所有字段作为列。

public class BaseEntity

     public DateTime? DateCreated  get; set; 
     public string UserCreated  get; set; 
     public DateTime? DateModified  get; set; 
     public string UserModified  get; set; 

这是我的Student 课程

public class Student : BaseEntity

    public int Id  get; set; 
    public string Name  get; set; 

这是我的上下文类

public class SchoolContext: DbContext
       
    public LibraContext(DbContextOptions<SchoolContext> options)
        : base(options)
     

    public DbSet<Student> Students  get; set; 


protected override void OnModelCreating(ModelBuilder modelBuilder)

        //CreatedDate 
        modelBuilder.Entity<BaseEntity>().Property(x => x.DateCreated).HasDefaultValueSql("GETDATE()");
        //Updated Date
        modelBuilder.Entity<BaseEntity>().Property(x => x.DateModified).HasDefaultValueSql("GETDATE()");
 

我已运行以下迁移命令来创建脚本和更新数据库

Add-Migration -Name "InitialMigration" -Context "SchoolContext"
update-database

它在 SQL Server 数据库中创建了单独的表 BaseEntityStudent。我的期望是创建一个 Student 表,其中所有 BaseEntity 字段作为列。

如何实现?

我正在使用 ASP.NET CORE2.1

【问题讨论】:

您想要一个将基类的所有字段作为列的单个表。然而我的期望是创建一个Student 表,其中所有BaseEntity 字段作为列。对我来说这是矛盾的。至于后者,你可能喜欢this的做法。 看link 查看标记为重复的问题的答案。罪魁祸首是您不应该在模型导航属性或流式 API 中直接引用 BaseEntity。帖子中具体代码的问题是modelBuilder.Entity&lt;BaseEntity&gt;(),它将BaseEntity标记为TPH基础实体。 【参考方案1】:

根据Microsoft Documentation,您可以使用[NotMapped] 数据注释或modelBuilder.Ignore&lt;TEntity&gt;(); 忽略BaseEntity 的表创建,如下所示:

但在您的情况下,[NotMapped] 对您没有帮助,因为Fluent API 始终具有比数据注释(属性)更高的优先级。所以你可以在OnModelCreating 中的BaseEntity 配置之后调用modelBuilder.Ignore&lt;BaseEntity&gt;();,但调用modelBuilder.Ignore&lt;BaseEntity&gt;(); 将丢失BaseEntity 配置。

所以就我而言,最好的解决方案如下:

BaseEntity编写配置如下:

public class BaseEntityConfigurations<TEntity> : IEntityTypeConfiguration<TEntity> where TEntity : BaseEntity

    public virtual void Configure(EntityTypeBuilder<TEntity> builder)
    
        //CreatedDate 
        builder.Property(x => x.DateCreated).HasDefaultValueSql("GETDATE()");
        //Updated Date
        builder.Property(x => x.DateModified).HasDefaultValueSql("GETDATE()");
    

然后为Student编写配置如下:

public class StudentConfigurations : BaseEntityConfigurations<Student>

    public override void Configure(EntityTypeBuilder<Student> builder)
    
        base.Configure(builder); // Must call this

       // other configurations here
    

然后在OnModelCreating中如下:

protected override void OnModelCreating(ModelBuilder modelBuilder)

     base.OnModelCreating(modelBuilder);

     modelBuilder.ApplyConfiguration(new StudentConfigurations());

迁移将生成唯一的Student 表,BaseEntity 配置如下:

migrationBuilder.CreateTable(
            name: "Students",
            columns: table => new
            
                Id = table.Column<int>(nullable: false)
                    .Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn),
                DateCreated = table.Column<DateTime>(nullable: true, defaultValueSql: "GETDATE()"),
                UserCreated = table.Column<string>(nullable: true),
                DateModified = table.Column<DateTime>(nullable: true, defaultValueSql: "GETDATE()"),
                UserModified = table.Column<string>(nullable: true),
                Name = table.Column<string>(nullable: true)
            ,
            constraints: table =>
            
                table.PrimaryKey("PK_Students", x => x.Id);
            );

【讨论】:

听起来不错,同时我也找到了不同的方法来解决它。请查看我的答案并进行审核。 Fluent API(在这种情况下为modelBuilder.Entity&lt;BaseEntity&gt;())比数据注释(属性)具有更高的优先级。这绝对是不是解决方案,因为该属性与流畅的配置冲突。 @IvanStoev 不明白!进一步解释我。您是否希望致电modelBuilder.Ignore&lt;BaseEntity&gt;(); 这消除了错误,但现在属性设置(DefaultValueSql 等)丢失(不包含在生成的迁移代码中)。所以不幸的是,黑客不起作用。处理这种情况的正确方法显示在重复链接和其他一些类似的帖子中,它迭代 modelBuilder.Model.GetEntityTypes() 并为所有派生实体类型调用一些通用配置代码。这种情况下的基类永远不应该被指定为实体,也不能为所有派生实体配置一次其属性。 我喜欢你的创造力和热情,真的:)【参考方案2】:

TPC 模式目前在 backlog 上,这意味着它正在考虑作为一项功能包含在内,但尚未确定日期。

read more.

【讨论】:

【参考方案3】:

我已经通过简单的更改解决了这个问题!我们必须在modelbuilder 中使用Student 实体。我被误用了BaseEntity

  protected override void OnModelCreating(ModelBuilder modelBuilder)
  
     modelBuilder.Entity<Student>().Property(x => x.Created).HasDefaultValueSql("GETDATE()");
     modelBuilder.Entity<Student>().Property(x => x.Updated).HasDefaultValueSql("GETDATE()");
  

未解决问题:order number 无法控制生成表列的顺序。 .net core 目前不支持此功能。

【讨论】:

这不是最好的解决方案。如果您有另一个名为Teacher 的模型类继承自BaseEntity,那么它将无法工作。您必须再次为Teacher 模型类编写配置。现在想想如果你有 100 个实体怎么办?你必须写100次。所以请按照我的解决方案。 没错,我同意你的看法。让我试试你的解决方案并及时通知你 好的!检查并确认! 我有小问题。我有复合键要求,它是基类和派生类的键的组合。 modelBuilder.Entity().HasKey(c => new c.Key, c.Id );在您建议更改后,我收到“无法在 'Student' 上配置密钥,因为它是派生类型。必须在根类型 'BaseEntity' 上配置密钥。我做错表设计了吗? 好的!你可以这样做,我已经这样做了。与 BaseEntity 键和学生键组合。请绿色检查我的答案,然后用复合键问题提出另一个问题。我正在回答这个问题。

以上是关于配置抽象基类而不在 EF Core 中创建表 [重复]的主要内容,如果未能解决你的问题,请参考以下文章

EF Core 错误地为 Array 执行多次插入

Spring Boot 不在 Postgres 中创建表

实体框架 6 不在 SQLite 数据库中创建表

如何在 JavaScript 中创建抽象基类?

添加Ef Core 连接 Seq Service

如何在 JavaScript 中创建无法实例化的抽象基类