[英]How to to handle abstract base class in entity framework(code first)?
I am implementing an audit functionality to keep track of any changes (create, update, delete) made to any type of object.我正在实施审计功能,以跟踪对任何类型的 object 所做的任何更改(创建、更新、删除)。 For this I need a way to declare a generic object which can point to object of any other class derived from an abstract base class.
为此,我需要一种方法来声明一个通用的 object,它可以指向任何其他 class 的 object,这些 class 派生自抽象基 ZA2F2ED4F8EBC2CABDD4ZC2。
Base class I do not want to have table for in database:基本 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()");
}
}
and a couple of models derived from base for example:以及从 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);
// ...
}
Now my failed attempt to create the Audit model现在我尝试创建审计 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);
// ...
}
When I try to create a migration for audit class, I get below error当我尝试为审计 class 创建迁移时,出现以下错误
The derived type 'Audit' cannot have KeyAttribute on property 'AuditId' since primary key can only be declared on the root type.
派生类型“Audit”不能在属性“AuditId”上具有 KeyAttribute,因为只能在根类型上声明主键。
Here is my Db Context if needed如果需要,这是我的 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());
}
}
try to remove [DatabaseGenerated(DatabaseGeneratedOption.Identity)] attribute from public DateTime CreationDateTime propert of EntityBase.尝试从 EntityBase 的公共 DateTime CreationDateTime 属性中删除 [DatabaseGenerated(DatabaseGeneratedOption.Identity)] 属性。
Also I would remove我也会删除
public EntityBase Entity { get; set; }
from Audit class, you would not be able to use it.从审核 class,您将无法使用它。 Or you can try to make it not mapped
或者您可以尝试使其未映射
[NotMapped]
public EntityBase Entity { get; set; }
By this Entity
property in Audit
, EF takes a completely different path in implementing the model.通过
Audit
中的这个Entity
属性,EF 在实现 model 时采用了完全不同的路径。
Without it, it maps each entity to its own independent table, each table having all columns including the ones in EntityBase
.没有它,它将每个实体映射到自己的独立表,每个表都有所有列,包括
EntityBase
中的列。 In fact, it completely ignores EntityBase
.事实上,它完全忽略了
EntityBase
。 As for EF, there is no inheritance.至于EF,没有inheritance。
If it's there, EF recognizes that Audit
needs a foreign key to any type derived from EntityBase
.如果存在,则 EF 会识别
Audit
需要从EntityBase
派生的任何类型的外键。 In order to implement this polymorphic association , a base table is required that "collects" all primary key values of EntityBase
entities.为了实现这种多态关联,需要一个“收集”
EntityBase
实体的所有主键值的基表。 Audit
refers to this base table's primary key. Audit
引用此基表的主键。
Also, table EntityBase
contains all shared properties ( CreationDateTime
etc), and the separate entity table only have their own properties and a primary key that's also a foreign key to EntityBase
.此外,表
EntityBase
包含所有共享属性( CreationDateTime
等),并且单独的实体表只有它们自己的属性和一个主键,它也是EntityBase
的外键。 This is referred to as Table per Type (TPT) inheritance.这称为按类型表(TPT) inheritance。
This TPT inheritance also requires EntityBase
to have the primary key property, implying that the inheriting entities shouldn't.此 TPT inheritance 还要求
EntityBase
具有主键属性,这意味着继承实体不应该。
All in all, if you remove the key properties from the inheriting entities and add总而言之,如果您从继承实体中删除关键属性并添加
public int ID { get; set; }
to EntityBase
, EF will be able to map your class model.到
EntityBase
,EF 将能够 map 你的 class model。
However, be aware of the ins and outs of TPT inheritance , esp.但是,请注意TPT inheritance 的来龙去脉,尤其是。 the warning
警告
In many cases, TPT shows inferior performance when compared to TPH.
在许多情况下,与 TPH 相比,TPT 的性能较差。
And, of course, more so compared to no inheritance.当然,与没有 inheritance 相比,更是如此。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.