Entity Framework Inheritance: Table Per Type (TPT)

在数据库设计中,有时会发现,数据库中的表格与应用程序所需的实体并不完全匹配。可能的情况是,数据库中的表格数量要么过多,要么过少,不符合应用程序逻辑上对实体的需求。另一种情况是,虽然每个逻辑实体都对应一个表格,但它们之间的关系从应用程序和实体的角度来看并不合逻辑。Entity Framework中的继承机制提供了一种方法,可以创建所需的逻辑实体来操作一组数据库表格,并且还可以利用继承关系创建更有意义的实体之间的关系。

Entity Framework中存在三种类型的继承关系:每类型表格(Table per Type, TPT)、每层次表格(Table per Hierarchy, TPH)和每具体类型表格(Table per Concrete Type, TPC)。本文将尝试探讨每类型表格继承关系,因为从效率角度来看,这种方式相对更为高效,并且可以很好地模拟具有一对一关系的表格。

当有多个表格通过外键约束具有一对一关系时,TPT关系特别有用。如果为这些表格创建实体,Entity Framework将生成默认的实体,这些实体具有一对一的关系,而使用继承关系可以更好地建模这些关系。

让通过一个简单的例子来理解这个概念。假设有一个表格用来跟踪“自行车”数据。这个表格将记录商店中所有的自行车。现在商店还有两个额外的表格用于“二手自行车”和“打折自行车”。它们与原始自行车表格之间建立了外键关系。从应用程序的角度来看,所有的“二手自行车”和“打折自行车”也都是自行车。这意味着它们之间存在逻辑上的继承关系。

为了创建继承关系,需要删除实体之间的现有关系,并在它们之间添加继承关系(这可以通过在实体设计器上右键点击来完成)。在进行这些更改后,实体之间的继承关系将如下所示:

如果希望“自行车”表中的所有实体都是“二手自行车”或“打折自行车”,需要将“自行车”实体标记为抽象的。目前,让保持可以创建“自行车”实体的可能性,即它不是抽象的。

现在有了具有一些逻辑关系的实体,并且准备好使用这些实体了。让现在看看如何对这些相关实体执行各种CRUD操作,这将反过来更新相应的表格。

插入操作:让从插入操作开始。让看看如何将实体添加到“二手自行车”实体: string name = txtNameP.Text; string manf = txtManfP.Text; int years = Convert.ToInt32(txtYearsP.Text); using (BikeDbEntities entities = new BikeDbEntities()) { PreOwnedBike bike = new PreOwnedBike { BikeName = name, Manufacturer = manf, YearsOld = years }; entities.AddToBikes(bike); entities.SaveChanges(); Response.Redirect("Default.aspx"); } 在这里,从用户那里获取输入,然后创建一个“二手自行车”对象。然后将其添加到实体的“自行车”集合中(由于有继承关系,所有的“二手自行车”都是自行车,因此这个调用将负责在相应的表格中插入所有数据)。

同样,也可以插入到“打折自行车”表中。

选择操作:要从表中选择数据,可以从表中选择所有数据,或者可以指定选择标准来选择表中的数据。要从相应的表格中选择所有数据,只需要使用Context类集合属性。 // 获取自行车 GridView1.DataSource = entities.Bikes; GridView1.DataBind(); // 获取二手自行车 GridView2.DataSource = entities.Bikes.OfType(); GridView2.DataBind(); // 获取打折自行车 GridView3.DataSource = entities.Bikes.OfType(); GridView3.DataBind(); 如果需要使用一些搜索标准来选择“自行车”的类型,可以从“自行车”集合中获取结果,并使用typeof操作符检查实体的实际类型。

更新和删除:要更新记录,首先需要确定自行车的实际类型,可以通过使用typeof操作符来做到这一点,然后可以像处理普通实体一样更新自行车的属性。 int id = Convert.ToInt32(TextBox1.Text); using (BikeDbEntities entities = new BikeDbEntities()) { Bike bike = entities.Bikes.SingleOrDefault(b => b.BikeID == id); if (bike != null) { bike.BikeName = txtNameP.Text; bike.Manufacturer = txtManfP.Text; if (bike.GetType() == typeof(PreOwnedBike)) { ((PreOwnedBike)bike).YearsOld = Convert.ToInt32(txtFieldData.Text); } else if (bike.GetType() == typeof(DiscountedBike)) { lblField.Text = "Discount Rate"; ((DiscountedBike)bike).DiscountRate = Convert.ToInt32(txtFieldData.Text); } entities.SaveChanges(); } } 在上面的代码中,直接更新了与基实体相关的详细信息,并且为了更新与派生实体相关的详细信息,首先通过检查typeof来转换类型,然后更改实体的属性。

删除操作也将遵循相同的理念,它不会更新记录,而是简单地从表中删除记录。 int id = Convert.ToInt32(TextBox1.Text); using (BikeDbEntities entities = new BikeDbEntities()) { Bike bike = entities.Bikes.SingleOrDefault(b => b.BikeID == id); if (bike != null) { entities.DeleteObject(bike); entities.SaveChanges(); } } 执行上述所有操作将更改相应表中的数据,并且还会处理表格之间的参照完整性。

沪ICP备2024098111号-1
上海秋旦网络科技中心:上海市奉贤区金大公路8218号1幢 联系电话:17898875485