Nic*_*las 9 c# sql-server asp.net inheritance entity-framework
为了避免使用Table Per Hierarchy(TPH),我一直在研究如何在我的数据库模型中最好地实现Table-Per-Concrete Class(TPC)继承.我遇到了官方文档和本文.
下面是一些带有一些简单继承的模型类.
public class BaseEntity
{
public BaseEntity()
{
ModifiedDateTime = DateTime.Now;
}
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public DateTime ModifiedDateTime { get; set; }
}
public class Person : BaseEntity
{
public string FirstName { get; set; }
public string LastName { get; set; }
}
public class Business : BaseEntity
{
public string Name { get; set; }
public string Location { get; set; }
}
Run Code Online (Sandbox Code Playgroud)
根据两篇文章中的示例使用的DbModelBuilder配置.
modelBuilder.Entity<BaseEntity>()
.Property(c => c.Id)
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);
modelBuilder.Entity<Person>().Map(m =>
{
m.MapInheritedProperties();
m.ToTable("Person");
});
modelBuilder.Entity<Business>().Map(m =>
{
m.MapInheritedProperties();
m.ToTable("Business");
});
Run Code Online (Sandbox Code Playgroud)
应用程序运行成功,但当我返回数据库时,我找到三(3)个表而不是我期望找到的两个(2)表.经过一些测试后,似乎会创建"BaseEntity"表,但从未使用过.除了这个空的孤立表外,一切似乎都运行得很好.
我搞乱了DbModelBuilder配置,最终删除了提供预期结果的"BaseEntity"配置; 两(2)个表,每个表都具有正确的属性并且功能正常.
我做最后一次测试,删除所有DbModelBuilder配置,只包括"Person"和"Business"的两(2)个DbSet属性并再次测试.
public DbSet<Person> People { get; set; }
public DbSet<Business> Businesses { get; set; }
Run Code Online (Sandbox Code Playgroud)
令我惊讶的是,项目构建,转到数据库,只创建具有所有类属性的两个表,包括来自"BaseEntity"类的继承属性.我可以毫无问题地进行CRUD操作.
在运行了很多测试后,我发现最终测试没有任何问题,而且我无法重现两篇文章所警告的重复键错误.
已成功提交对数据库的更改,但更新对象上下文时发生错误.ObjectContext可能处于不一致状态.内部异常消息:AcceptChanges无法继续,因为对象的键值与ObjectStateManager中的另一个对象冲突.在调用AcceptChanges之前,请确保键值是唯一的.
为了让这一切变得更简单,我已经移动了强制 TablePerConcrete 开源所需的代码。其目的是允许通常仅在 Fluent Interface 中可用的功能(您必须将大量代码分散到 Db 类的 OnModelCreating 方法中)迁移到基于属性的功能。
它允许你做这样的事情:
[TablePerConcrete]
public class MySubclassTable : MyParentClassEntity
Run Code Online (Sandbox Code Playgroud)
强制 TPC,无论 EF 可能决定从父类/子类关系中推断出什么。
这里的一个有趣的挑战是,有时 EF 会搞砸继承的 Id 属性,将其设置为用显式值填充,而不是由数据库生成。您可以通过让父类实现接口(它只是说:它有一个 Id 属性)来确保它不会这样做IId,然后用[ForcePKId].
public class MyParentClassEntity : IId
{
public int Id { get; set; }
. . .
[TablePerConcrete]
[ForcePKId]
public class MySubclassTable : MyParentClassEntity
{
// No need for PK/Id property here, it was inherited and will work as
// you intended.
Run Code Online (Sandbox Code Playgroud)
启动为您处理所有这些的代码非常简单 - 只需向您的 Db 类添加几行即可:
public class Db : DbContext
{
. . .
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
var modelsProject = Assembly.GetExecutingAssembly();
B9DbExtender.New().Extend(modelBuilder, modelsProject);
Run Code Online (Sandbox Code Playgroud)
您可以通过以下两种方式之一访问它:
通过将所有相关类复制粘贴到单个文件中的单个要点,此处:https ://gist.github.com/b9chris/8efd30687d554d1ceeb3fee359c179f9
通过我们的 Brass9.Data 库,我们将其开源。它包含许多其他 EF6 工具,例如数据迁移。它也更有组织性,如您通常期望的那样,类分解为单独的文件: https: //github.com/b9chris/Brass9.Data
| 归档时间: |
|
| 查看次数: |
8882 次 |
| 最近记录: |