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

理解和实现 Entity Framework 中关系模型的初学者教程

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.71/5 (18投票s)

2013 年 2 月 4 日

CPOL

8分钟阅读

viewsIcon

55482

downloadIcon

1557

在本文中,我们将尝试了解如何使用 Entity Framework 来建模具有一对多和多对多关系的数据表。

引言

在本文中,我们将尝试了解如何使用 Entity Framework 来建模具有一对多和多对多关系的数据表。我们将尝试了解 Entity Framework 在后台为我们做了什么,以及如何使用 Entity Framework 高效地处理具有关系的数据表。

背景

每当我们尝试根据应用程序需求来建模数据库时,我们都会发现表之间会存在某种关系。可能存在一种情况,即一个表中的数据与另一个表中的数据相关。在本文中,我们将尝试了解表之间的这种关系,并了解如何在存在此类关系的情况下使用 Entity Framework 来处理数据库。

什么是“一对多”关系

假设表 A 中的每一行都与表 B 中的多行相关。(或者可以这样理解:表 B 中的每一行在表 A 中都只有一个父行)。这是通过在表 B 中设置一个外键来实现的,该外键引用表 A 的主键。这种关系称为“一对多”关系。

为了说明这一点,让我们在示例数据库中创建两个简单的表。第一个表是 Rooms,它包含任何办公室各种房间的信息。第二个表是 Assets,它包含任何组织的各种资产的信息。现在,每项资产都将放置在一个房间中,因此对于每项资产,我们需要一种方法将其与房间关联起来。这是通过在 Assets 表和 Rooms 表之间创建“一对多”关系来实现的,其中 Assets 表包含一个引用 Rooms 表中房间的外键。


什么是“多对多”关系

现在也可能出现这样的情况:一个表中的实体引用另一个表中的多个实体,同时另一个表中的实体也可以引用第一个表中的多个实体。这种情况就需要表之间存在“多对多”关系。这通常是通过创建另一个表来实现的,该表将与两个表都建立外键关系,并仅跟踪原始表之间的关系。这个表本身将只包含建模关系所需的列,而这些列将构成该表的主键。

现在,为了说明上述概念,假设在同一个示例数据库中,我们将多个项目数据存储在 Projects 表中。现在,每个项目都可以从一组允许的房间中选择一个房间用于日常会议。同时,每个房间也可以被多个项目请求。为了建模这种关系,我们需要创建一个表来跟踪 ProjectsRooms。让我们通过向数据库添加 ProjectsProjectRooms(用于实现此关系的表)来了解如何做到这一点。


在上面的表中,Projects 表包含项目的详细信息,而 ProjectRooms 表则跟踪项目可以预订的房间,反之亦然。

使用代码

现在我们有了准备好关系的数据库,接下来我们需要了解如何使用 Entity Framework 在我们的应用程序中使用这些关系。让我们从向网站添加一个 ADO.NET entity data model 开始。


我们将从示例数据库生成此模型并为其创建实体。


目前,我们就使用这些实体,并在接下来的章节中尝试理解这些实体是如何生成的。

一对多关系 - 生成实体

从上面的图表中,我们可以看到 Entity Framework 足够智能,能够理解 AssetRoom 之间存在“一对多”关系。我们可以看到,在导航属性中,它创建了相关实体的属性,即 Room 实体包含一个属性来获取 Assets(复数,因为可能有多个关联的 Assets),而 Asset 实体包含一个属性来获取关联的 Room(单数,因为一个 Asset 可能只有一个 Room)。

现在,有了这些生成的实体,需要注意的重要一点是,我们可以通过原始实体关联的属性来执行(CRUD 操作)所有操作。下一节将展示如何做到这一点。

对相关表执行 CRUD 操作

现在,在这一节中,让我们尝试通过使用 Room 实体来对 Asset 实体执行所有 CRUD 操作。我们将使用 Room 实体,并利用其关联的 Assets 的导航属性来执行所有操作。

SELECT 

让我们开始看看如何选择与任何房间关联的所有资产。在此示例应用程序中,房间数据会显示在下拉列表中,以便用户可以选择他们想要查看资产数据的房间。一旦用户选择任何房间,与之关联的房间就会被获取并显示给用户。

using (SampleDbEntities entities = new SampleDbEntities())
{
    // Let us get the selected rooms ID
    string selectedValue = drpRooms.SelectedValue;
    int roomIndex = Convert.ToInt32(selectedValue);

    // Now let us fetch the selected room entity
    Room selectedRoom = entities.Rooms.SingleOrDefault<Room>(room => room.RoomID == roomIndex);

    // Now let us utilize the relationship to extract the assets associated with this room
    GridAssets.DataSource = selectedRoom.Assets;
    GridAssets.DataBind();
}

然后将显示与所选房间关联的 Assets


注意:上面的代码片段(以及后续的代码片段)仅显示了使用当前选定的房间获取 Assets 数据的部分。应用程序中用于向用户显示房间的其他部分并未在此处显示。因此,要完全理解应用程序,请查看示例项目。

INSERT

现在让我们看看如何执行插入操作,使用选定的 Room 添加 Asset

using (SampleDbEntities entities = new SampleDbEntities())
{
    // Let us get the selected rooms ID
    string selectedValue = drpRooms.SelectedValue;
    int roomIndex = Convert.ToInt32(selectedValue);

    // Now let us fetch the selected room entity
    Room selectedRoom = entities.Rooms.SingleOrDefault<Room>(room => room.RoomID == roomIndex);

    // Lets create the new asset to add to db
    Asset asset = new Asset();
    asset.AssetName = txtAddAsset.Text;
    
    // Now let us utilize the relationship to Add the asset into db with proper relation with selected room
    selectedRoom.Assets.Add(asset);

    // Persist the changes in the database
    entities.SaveChanges();
}

用户现在可以简单地选择一个 Room,然后向该房间添加一个 Asset


更新

类似地,我们可以通过先选择一个 Room,获取关联的 Assets,然后更新任何 Asset 来 更新 Asset 的详细信息。

using (SampleDbEntities entities = new SampleDbEntities())
{
    // Let us get the selected asset ID
    GridViewRow row = GridAssets.SelectedRow;
    int assetIndex = Convert.ToInt32(row.Cells[1].Text);

    // Let us get the selected rooms ID
    string selectedValue = drpRooms.SelectedValue;
    int roomIndex = Convert.ToInt32(selectedValue);

    // Now let us fetch the selected room entity
    Room selectedRoom = entities.Rooms.SingleOrDefault<Room>(room => room.RoomID == roomIndex);

    // get the asset that needs to be updated
    Asset asset = selectedRoom.Assets.SingleOrDefault<Asset>(ast => ast.AssetID == assetIndex);

    asset.AssetName = TextBox1.Text;
    asset.RoomID = Convert.ToInt32(drpNewRoom.SelectedValue);

    entities.SaveChanges();
}

这里需要注意的重要一点是,可以更改任何资产的关联 Room,Entity Framework 将负责更新所有相关表。

删除

删除 Asset 也可以通过简单地获取给定 Room 的所选 Asset 并将其从 Assets 集合中删除来完成。Entity Framework 将负责将其从相应的表中删除并更新关系。

using (SampleDbEntities entities = new SampleDbEntities())
{
    // Let us get the selected asset ID
    GridViewRow row = GridAssets.SelectedRow;
    int assetIndex = Convert.ToInt32(row.Cells[1].Text);

    // Let us get the selected rooms ID
    string selectedValue = drpRooms.SelectedValue;
    int roomIndex = Convert.ToInt32(selectedValue);

    // Now let us fetch the selected room entity
    Room selectedRoom = entities.Rooms.SingleOrDefault<Room>(room => room.RoomID == roomIndex);

    // get the asset that needs to be updated
    Asset asset = selectedRoom.Assets.SingleOrDefault<Asset>(ast => ast.AssetID == assetIndex);

    entities.DeleteObject(asset);

    entities.SaveChanges();
}

对于上述所有操作,我们都使用了选定的 Room 实体,然后对关联的 Assets 执行了所有操作。Entity Framework 负责更新相应的表。

注意:上述所有代码片段仅显示了实际在代码中使用关系的部分。应用程序中用于向用户显示房间的其他部分并未在此处显示。因此,要完全理解应用程序,请查看示例项目。

多对多关系 - 生成实体

从数据库生成实体时,缺少了一些东西。ProjectRoom 表没有生成相应的实体。这是为什么?如果我们仔细查看实体,我们会发现 Entity Framework 做了一件非常聪明的事情。它理解 ProjectRooms 表是为了建模多对多关系而创建的,因此它通过在相应实体中创建导航属性来创建多对多关系,从而使应用程序代码无需处理仅用于建模关系的表。

简单来说,Entity Framework 读取了 ProjectRooms 表,并在 Rooms(复数,因为会有许多关联的 Project)中创建了一个 Project 属性,并在 Project 实体中创建了一个 Rooms 属性(再次使用复数来反映一个房间关联了多个项目)。

现在,在本节中,让我们尝试通过利用 Entity Framework 创建的导航属性来对这些相关实体执行一些操作。

对相关表执行操作

让我们创建一个页面,管理员可以在其中简单地更改分配给项目的房间。


根据选定的 Project 选择关联的 Rooms

using (SampleDbEntities entities = new SampleDbEntities())
{
    // Let us get the selected rooms ID
    string selectedValue = drpProjects.SelectedValue;
    int projectIndex = Convert.ToInt32(selectedValue);

    // Now let us fetch the selected room entity
    Project selectedProject = entities.Projects.SingleOrDefault<Project>(prj => prj.ProjectID == projectIndex);

    // Now let us utilize the relationship to extract the assets associated with this room
    GridRooms.DataSource = selectedProject.Rooms;
    GridRooms.DataBind();
}

将特定 Room 添加到选定的 Project

currentPrj.Rooms.Add(entities.Rooms.Where(room => room.RoomName == item.Text).First());

从选定的 Project 中移除特定 Room

currentPrj.Rooms.Remove(entities.Rooms.Where(room => room.RoomName == item.Text).First());

对于上述所有操作,我们都使用了选定的 Project 实体,然后对关联的 Rooms 执行了所有操作。Entity Framework 负责更新相应的表。

注意:上述所有代码片段仅显示了实际在代码中使用关系的部分。应用程序中用于向用户显示房间的其他部分并未在此处显示。因此,要完全理解应用程序,请查看示例项目。

因此,我们了解了如何使用 Entity Framework 来建模和使用“一对多”和“多对多”关系。在结束之前,有一件重要的事情需要理解,那就是 延迟加载。Entity Framework 默认不会加载所有关联的实体。例如,当我们获取 Room 数据时,会创建一个 Assets 的导航属性,但这些 Assets 数据不会被加载,除非它们被请求,即延迟加载。多对多关系也是如此。

值得关注的点:

在这篇短文中,我尝试解释如何使用 Entity Framework 来建模和使用“一对多”和“多对多”关系。项目本身包含大量代码,因此我尽量使本文中的代码片段简短并与讨论主题相关。为了更好地理解这些内容,我建议查看相关的示例代码文件。本文是从初学者的角度编写的。希望它能带来启发。

历史

  • 2013年2月4日: 初稿。
© . All rights reserved.