65.9K
CodeProject 正在变化。 阅读更多。
Home

理解 Entity Framework 中的表级类型(TPT)继承的初学者教程

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.94/5 (14投票s)

2013 年 2 月 13 日

CPOL

5分钟阅读

viewsIcon

64041

downloadIcon

956

本文讨论了使用 Entity Framework 实现表类型继承层次结构。

引言

本文将讨论使用 Entity Framework 实现表类型继承层次结构。我们将通过一个小型示例,逐步了解如何实现这一点。

背景

有时,我们的数据库设计中的表可能在逻辑上与应用程序所需实体不匹配。数据库中的表数量可能过多或过少,与应用程序逻辑所需的实体数量不符。还有些时候,虽然表是为每个逻辑实体创建的,但它们之间的关系从应用程序和实体的角度来看并不符合逻辑。

Entity Framework 中的继承提供了一种创建所需逻辑实体的方法,以作用于一组数据库表,并通过继承创建实体之间更有意义的关系。

Entity Framework 中有三种继承关系:表类型(TPT)表层次(TPH)表具体类型(TPC)

在本文中,我们将重点介绍表类型继承关系,因为从效率的角度来看,这种关系通常更有效(相对而言),并且它提供了一种对具有一对一关系表进行建模的绝佳方式。

使用代码

当我们在多个表之间使用外键约束实现一对一关系时,TPT 关系尤其有用。如果我们为这些表创建实体,Entity Framework 将生成默认的一对一关系实体,而这可以通过使用继承关系来更好地建模。

让我们通过一个小型示例来理解这个概念。假设我们有一个表用于跟踪自行车数据。此表将记录商店中所有自行车的记录。现在,商店还有另外两个表用于二手自行车打折自行车。为此,他们创建了与原始自行车表具有外键关系的单独表。要可视化此设计:


现在,从我们的应用程序来看,如果我们尝试为此设计生成实体模型,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");
}

在这里,我们从用户那里获取输入,然后创建一个二手自行车对象。然后,我们将此对象添加到自行车实体集合中(由于我们存在继承关系,所有二手自行车都是自行车,因此此调用将负责将所有数据插入到相应的表中)。

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

string name = txtNameD.Text;
string manf = txtManfD.Text;
int years = Convert.ToInt32(txtRateD.Text);

using (BikeDbEntities entities = new BikeDbEntities())
{
    DiscountedBike bike = new DiscountedBike { BikeName = name, Manufacturer = manf, DiscountRate = years };

    entities.AddToBikes(bike);
    entities.SaveChanges();
    Response.Redirect("Default.aspx");
}

Select

要从表中选择数据,我们可以选择表中的所有数据,也可以指定选择标准来从表中选择。要选择相应表中的所有数据,我们只需使用Context类的集合属性。

// Get the bikes 
GridView1.DataSource = entities.Bikes;
GridView1.DataBind();

// Get the bikes of type preowned bikes
GridView2.DataSource = entities.Bikes.OfType<PreOwnedBike>();
GridView2.DataBind();

// Get the bikes of type discounted bikes
GridView3.DataSource = entities.Bikes.OfType<DiscountedBike>();
GridView3.DataBind();

在需要使用某些搜索条件选择自行车类型的另一种情况下,我们可以从自行车集合中获取结果,并使用typeof运算符检查实体的实际类型。

int id = Convert.ToInt32(TextBox1.Text);

using (BikeDbEntities entities = new BikeDbEntities())
{
    Bike bike = entities.Bikes.SingleOrDefault<Bike>(b => b.BikeID == id);
    if (bike != null)
    {
        txtNameP.Text = bike.BikeName;
        txtManfP.Text = bike.Manufacturer;

        if (bike.GetType() == typeof(PreOwnedBike))
        {
            lblField.Text = "Years Old";
            txtFieldData.Text = ((PreOwnedBike)bike).YearsOld.ToString();
        }
        else if (bike.GetType() == typeof(DiscountedBike))
        {
            lblField.Text = "Discount Rate";
            txtFieldData.Text = ((DiscountedBike)bike).DiscountRate.ToString();
        }
    }
}

在上面的代码中,我们要求用户输入自行车的 ID,然后相应地选择自行车。上面的代码中需要注意的重要一点是,我们是通过基类自行车获取详细信息的,然后使用typeof检查其类型,以获取派生实体的详细信息。

更新和删除

要更新记录,我们首先需要确定自行车的实际类型,这可以通过使用typeof运算符来完成,然后我们可以像处理普通实体一样更新自行车的属性。

int id = Convert.ToInt32(TextBox1.Text);

using (BikeDbEntities entities = new BikeDbEntities())
{
    Bike bike = entities.Bikes.SingleOrDefault<Bike>(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<Bike>(b => b.BikeID == id);
    if (bike != null)
    {
        entities.DeleteObject(bike);
        entities.SaveChanges();        
    }
}

执行上述所有操作都将更改相应表中的数据,并将处理表之间的参照完整性。

注意:请参阅附件中的示例代码以全面了解代码。本文中的代码片段仅显示与讨论主题相关的代码。

值得关注的点:

在这篇简短的文章中,我们了解了如何创建逻辑实体关系以更好地使用它们。我们在这篇文章中专门讨论了表类型继承关系。要讨论其他关系类型,我可能会写单独的文章(以避免混淆)。本文是从初学者的角度撰写的。希望这有点启发性。

历史

  • 2013 年 2 月 13 日:第一个版本
© . All rights reserved.