簡體   English   中英

如何在實體框架中處理抽象基礎 class(代碼優先)?

[英]How to to handle abstract base class in entity framework(code first)?

我正在實施審計功能,以跟蹤對任何類型的 object 所做的任何更改(創建、更新、刪除)。 為此,我需要一種方法來聲明一個通用的 object,它可以指向任何其他 class 的 object,這些 class 派生自抽象基 ZA2F2ED4F8EBC2CABDD4ZC2。

基本 class 我不想在數據庫中有表:

  public abstract class EntityBase {
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    [DataType(DataType.Date)]
    public DateTime CreationDateTime { get; set; }

    [DatabaseGenerated(DatabaseGeneratedOption.Computed)]
    [DataType(DataType.Date)]
    public DateTime ModificationDateTime { get; set; }
  }

  public class EntityBaseConfigurations<TEntity> : IEntityTypeConfiguration<TEntity> where TEntity : EntityBase {
    public virtual void Configure(EntityTypeBuilder<TEntity> builder) {
      builder.Property(e => e.CreationDateTime).IsRequired().HasDefaultValueSql("GETUTCDATE()");
      builder.Property(e => e.ModificationDateTime).IsRequired().HasDefaultValueSql("GETUTCDATE()");
    }
  }

以及從 base 派生的幾個模型,例如:

  [Table("Initiative")]
  public class Initiative : EntityBase {
    [Key]
    public int InitiativeId { get; set; }

    // ...

    [Required]
    public Contributor Assignee { get; set; }
  }

  public class InitiativeConfiguration : EntityBaseConfigurations<Initiative> {
    public override void Configure(EntityTypeBuilder<Initiative> builder) => base.Configure(builder);

    // ...
  }
  [Table("Contributor")]
  public class Contributor : EntityBase {
    [Key]
    public int ContributorId { get; set; }

    // ...
  }

  public class ContributorConfiguration : EntityBaseConfigurations<Contributor> {
    public override void Configure(EntityTypeBuilder<Contributor> builder) => base.Configure(builder);

    // ...
  }

現在我嘗試創建審計 model 失敗

  [Table("Audit")]
  public class Audit : EntityBase {
    [Key]
    public int AuditId { get; set; }

    public User Actor {get; set;}

    // ...

    public EntityBase Entity { get; set; } // I want to be able to point to objct of any class derived from EntityBase (ex. Initiative, Contributor)
  }

  public class AuditConfiguration : EntityBaseConfigurations<Audit> {
    public override void Configure(EntityTypeBuilder<Audit> builder) => base.Configure(builder);
    // ...
  }

當我嘗試為審計 class 創建遷移時,出現以下錯誤

派生類型“Audit”不能在屬性“AuditId”上具有 KeyAttribute,因為只能在根類型上聲明主鍵。

如果需要,這是我的 Db 上下文

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

    public DbSet<Initiative> Initiatives { get; set; }
    public DbSet<Contributor> Contributors { get; set; }

    protected override void OnModelCreating(ModelBuilder modelBuilder) {
      base.OnModelCreating(modelBuilder);
      modelBuilder.ApplyConfiguration(new InitiativeConfiguration());
      modelBuilder.ApplyConfiguration(new ContributorConfiguration());
    }
  }

嘗試從 EntityBase 的公共 DateTime CreationDateTime 屬性中刪除 [DatabaseGenerated(DatabaseGeneratedOption.Identity)] 屬性。

我也會刪除

public EntityBase Entity { get; set; }  

從審核 class,您將無法使用它。 或者您可以嘗試使其未映射

[NotMapped]
public EntityBase Entity { get; set; }  

通過Audit中的這個Entity屬性,EF 在實現 model 時采用了完全不同的路徑。

  • 沒有它,它將每個實體映射到自己的獨立表,每個表都有所有列,包括EntityBase中的列。 事實上,它完全忽略了EntityBase 至於EF,沒有inheritance。

  • 如果存在,則 EF 會識別Audit需要從EntityBase派生的任何類型的外鍵。 為了實現這種多態關聯,需要一個“收集” EntityBase實體的所有主鍵值的基表。 Audit引用此基表的主鍵。

    此外,表EntityBase包含所有共享屬性( CreationDateTime等),並且單獨的實體表只有它們自己的屬性一個主鍵,它也是EntityBase的外鍵。 這稱為按類型表(TPT) inheritance。

此 TPT inheritance 還要求EntityBase具有主鍵屬性,這意味着繼承實體不應該。

總而言之,如果您從繼承實體中刪除關鍵屬性並添加

public int ID { get; set; }

EntityBase ,EF 將能夠 map 你的 class model。

但是,請注意TPT inheritance 的來龍去脈,尤其是。 警告

在許多情況下,與 TPH 相比,TPT 的性能較差。

當然,與沒有 inheritance 相比,更是如此。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM