Entity Framework 4.0 的新增功能

作者 :Tom Dykstra

本教程系列基于由入门使用 Entity Framework 教程系列创建的 Contoso University Web 应用程序。 如果未完成前面的教程,作为本教程的起点,可以下载已创建 的应用程序 。 还可以下载完整教程系列创建 的应用程序 。 如果对教程有疑问,可将其发布到 ASP.NET 实体框架论坛

在上一教程中,你了解了一些方法,用于最大程度地提高使用实体框架的 Web 应用程序的性能。 本教程回顾了实体框架版本 4 中的一些最重要的新功能,并链接到了提供对所有新功能的更完整介绍的资源。 本教程中突出显示的功能包括:

  • 外键关联。
  • 执行用户定义的 SQL 命令。
  • 模型优先开发。
  • POCO 支持。

此外,本教程将简要介绍 代码优先开发,这是实体框架下一版本中推出的一项功能。

若要开始本教程,请启动 Visual Studio 并打开在上一教程中正在使用的 Contoso University Web 应用程序。

Foreign-Key关联

实体框架版本 3.5 包含导航属性,但不包括数据模型中的外键属性。 例如,CourseID将从 实体中省略StudentGrade表的 StudentGradeStudentID 列。

Image01

此方法的原因是,严格地说,外键是物理实现细节,不属于概念数据模型。 但是,实际上,当你能够直接访问外键时,在代码中处理实体通常更容易。

有关数据模型中的外键如何简化代码的示例,请考虑如何在没有外键的情况下对 DepartmentsAdd.aspx 页面进行编码。 在 实体中DepartmentAdministrator, 属性是实体中对应于 PersonIDPerson外键。 为了在新部门与其管理员之间建立关联,只需在数据绑定控件的事件处理程序中ItemInserting设置 属性的值Administrator

protected void DepartmentsDetailsView_ItemInserting(object sender, DetailsViewInsertEventArgs e)
{
    e.Values["Administrator"] = administratorsDropDownList.SelectedValue;
}

如果没有数据模型中的外键,你将处理 Inserting 数据源控件的 事件,而不是 ItemInserting 数据绑定控件的事件,以便在将实体添加到实体集之前获取对实体本身的引用。 当有该引用时,可以使用如下示例中所示的代码建立关联:

departmentEntityToBeInserted.PersonReference.EntityKey = new System.Data.EntityKey("SchoolEntities.Departments", "PersonID", Convert.ToInt32(administratorsDropDownList.SelectedValue));
departmentEntityToBeInserted.Person = context.People.Single(p => p.PersonID == Convert.ToInt32(administratorsDropDownList.SelectedValue));

正如实体框架团队 关于外键关联的博客文章所见,在其他情况下,代码复杂性的差异要大得多。 为了满足那些为了简化代码而倾向于在概念数据模型中使用实现详细信息的用户的需求,实体框架现在提供了在数据模型中包括外键的选项。

在实体框架术语中,如果在数据模型中包括外键,则使用 外键关联,如果排除外键,则使用 独立关联

执行User-Defined SQL 命令

在早期版本的实体框架中,无法轻松创建自己的 SQL 命令并运行它们。 实体框架动态生成 SQL 命令,或者必须创建存储过程并将其作为函数导入。 版本 4 添加 ExecuteStoreQueryExecuteStoreCommand 方法类 ObjectContext ,以便更轻松地将任何查询直接传递到数据库。

假设 Contoso University 管理员希望能够在数据库中执行批量更改,而无需完成创建存储过程并将其导入数据模型的过程。 他们的第一个请求是针对一个页面,该页面允许他们更改数据库中所有课程的学分数。 在网页上,他们希望能够输入一个数字,以将每 Course 行的 Credits 列的值相乘。

创建使用 Site.Master 母版页的新页面,并将其命名为 UpdateCredits.aspx。 然后将以下标记添加到名为 的ContentContent2控件:

<h2>Update Credits</h2>
    Enter the number to multiply the current number of credits by: 
    <asp:TextBox ID="CreditsMultiplierTextBox" runat="server"></asp:TextBox>
    <br /><br />
    <asp:Button ID="ExecuteButton" runat="server" Text="Execute" OnClick="ExecuteButton_Click" /><br /><br />
    Rows affected:
    <asp:Label ID="RowsAffectedLabel" runat="server" Text="0" ViewStateMode="Disabled"></asp:Label><br /><br />

此标记创建一个 TextBox 控件,其中用户可以输入乘数值,一个 Button 要单击以执行命令的控件,以及一个 Label 用于指示受影响的行数的控件。

打开 UpdateCredits.aspx.cs,为按钮Click的 事件添加以下 using 语句和处理程序:

using ContosoUniversity.DAL;
protected void ExecuteButton_Click(object sender, EventArgs e)
{
    using (SchoolEntities context = new SchoolEntities())
    {
        RowsAffectedLabel.Text = context.ExecuteStoreCommand("UPDATE Course SET Credits = Credits * {0}", CreditsMultiplierTextBox.Text).ToString();
    }
}

此代码使用文本框中的值执行 SQL Update 命令,并使用 标签显示受影响的行数。 在运行页面之前,请运行 Courses.aspx 页面以获取某些数据的“之前”图片。

Image02

运行 UpdateCredits.aspx,输入“10”作为乘数,然后单击“ 执行”。

Image03

再次运行 Courses.aspx 页面以查看更改的数据。

Image04

(如果要将额度数设置回其原始值,请在 UpdateCredits.aspx.cs 中将 更改为 Credits * {0}Credits / {0} 并重新运行页面,输入 10 作为 divisor.)

有关执行代码中定义的查询的详细信息,请参阅 如何:针对数据源直接执行命令

Model-First开发

在这些演练中,首先创建了数据库,然后基于数据库结构生成了数据模型。 在 Entity Framework 4 中,可以从数据模型开始,并根据数据模型结构生成数据库。 如果要创建数据库尚不存在的应用程序,则模型优先方法使你能够创建在概念上对应用程序有意义的实体和关系,同时无需担心物理实现细节。 (但是,这仅在开发的初始阶段才有效。最终,将创建数据库,并在其中具有生产数据,从模型重新创建该数据库将不再可行:此时,你将回到数据库优先方法。)

在本教程的这一部分中,你将创建一个简单的数据模型并从中生成数据库。

解决方案资源管理器中,右键单击 DAL 文件夹,然后选择“添加新项”。 在 “添加新项 ”对话框中的“ 已安装的模板 ”下,选择“ 数据 ”,然后选择 “ADO.NET 实体数据模型 ”模板。 将新文件命名为 “AlumniAssociationModel.edmx ”,然后单击“ 添加”。

Image06

这会启动实体数据模型向导。 在 “选择模型内容 ”步骤中,选择“ 空模型 ”,然后单击“ 完成”。

Image07

“实体数据模型”Designer以空白设计图面打开。 将 “实体 ”项从 “工具箱” 拖到设计图面上。

Image08

将实体名称从 Entity1 更改为 ,将Id属性名称更改为 AlumnusId,并添加名为 Name的新Alumnus标量属性。 若要添加新属性,可以在更改列名称 Id 后按 Enter,或右键单击实体并选择“ 添加标量属性”。 新属性的默认类型是 String,对于此简单演示来说,这很好,但当然可以在 “属性” 窗口中更改数据类型等内容。

以相同的方式创建另一个实体,并将其命名为 Donation。 将 Id 属性更改为 DonationId ,并添加名为 的 DateAndAmount标量属性。

Image09

若要在这两个实体之间添加关联,请右键单击该 Alumnus 实体,选择“ 添加”,然后选择“ 关联”。

Image10

添加关联 ”对话框中的默认值是所需的值 (一对多,包括导航属性,包括外键) ,因此只需单击“ 确定”。

Image11

设计器添加关联行和外键属性。

Image12

现在,你已准备好创建数据库。 右键单击设计图面,然后选择“ 从模型生成数据库”。

Image13

这会启动生成数据库向导。 (如果看到指示实体未映射的警告,则可以暂时忽略这些警告。)

“选择数据连接” 步骤中,单击“ 新建连接”。

Image14

在“连接属性”对话框中,选择本地SQL Server Express实例,并将数据库AlumniAssociation命名为 。

Image15

当系统询问是否要创建数据库时,请单击“ ”。 再次显示 “选择数据连接” 步骤时,单击“ 下一步”。

“摘要和设置”步骤中 ,单击“ 完成”。

Image18

已创建具有数据定义语言 (DDL) 命令的 .sql 文件,但尚未运行这些命令。

Image20

使用 SQL Server Management Studio 等工具来运行脚本和创建表,就像为 入门 系列教程的第一个教程创建School数据库时所做的那样。 (除非已下载 database.)

现在可以按照 AlumniAssociation 使用模型的方式 School 在网页中使用数据模型。 若要尝试此操作,请将一些数据添加到表,并创建显示数据的网页。

使用 服务器资源管理器,将以下行添加到 AlumnusDonation 表中。

Image21

创建名为 Alumni.aspx 的新网页,该网页使用 Site.Master 母版页。 将以下标记添加到名为 的ContentContent2控件:

<h2>Alumni</h2>
    <asp:EntityDataSource ID="AlumniEntityDataSource" runat="server" 
        ContextTypeName="ContosoUniversity.DAL.AlumniAssociationModelContainer" EnableFlattening="False" 
        EntitySetName="Alumni">
    </asp:EntityDataSource>
    <asp:GridView ID="AlumniGridView" runat="server" 
        DataSourceID="AlumniEntityDataSource" AutoGenerateColumns="False"
        OnRowDataBound="AlumniGridView_RowDataBound"
        DataKeyNames="AlumnusId">
        <Columns>
            <asp:BoundField DataField="Name" HeaderText="Name" SortExpression="Name" />
            <asp:TemplateField HeaderText="Donations">
                <ItemTemplate>
                    <asp:GridView ID="DonationsGridView" runat="server" AutoGenerateColumns="False">
                        <Columns>
                            <asp:BoundField DataField="DateAndAmount" HeaderText="Date and Amount" />
                        </Columns>
                    </asp:GridView>
                </ItemTemplate>
            </asp:TemplateField>
        </Columns>
    </asp:GridView>

此标记创建嵌套 GridView 控件,外部控件用于显示校友姓名,内部控件用于显示捐赠日期和金额。

打开 Alumni.aspx.csusing为数据访问层添加 语句,并为外部GridView控件的 RowDataBound 事件添加处理程序:

using ContosoUniversity.DAL; 

// ...

protected void AlumniGridView_RowDataBound(object sender, GridViewRowEventArgs e)
{
    if (e.Row.RowType == DataControlRowType.DataRow)
    {
        var alumnus = e.Row.DataItem as Alumnus;
        var donationsGridView = (GridView)e.Row.FindControl("DonationsGridView");
        donationsGridView.DataSource = alumnus.Donations.ToList();
        donationsGridView.DataBind();
    }
}

此代码使用Donations当前行Alumnus实体的导航属性对内部GridView控件进行数据绑定。

运行页面。

Image22

(注意:此页面包含在可下载的项目中,但要使其正常工作,必须在本地SQL Server Express实例中创建数据库;数据库不会作为 .mdf 文件包含在 App_Data 文件夹中。)

有关使用实体框架的模型优先功能的详细信息,请参阅 Entity Framework 4 中的 Model-First

POCO 支持

使用域驱动设计方法时,需要设计表示与业务领域相关的数据和行为的数据类。 这些类应独立于用于存储 (持久保存) 数据的任何特定技术;换句话说,它们应该是 持久性无知的。 持久性无知也会使类更易于进行单元测试,因为单元测试项目可以使用最方便测试的任何持久性技术。 早期版本的实体框架提供对持久性无知的有限支持,因为实体类必须继承自 EntityObject 类,因此包含大量特定于实体框架的功能。

Entity Framework 4 引入了使用实体类的功能,这些实体类不是从 EntityObject 类继承的,因此是持久性无知的。 在实体框架的上下文中,此类通常称为 (POCO 或 POCO) 的 普通旧 CLR 对象 。 可以手动编写 POCO 类,也可以使用实体框架提供的文本模板转换工具包 (T4) 模板,根据现有数据模型自动生成它们。

有关在实体框架中使用 POCO 的详细信息,请参阅以下资源:

  • 使用 POCO 实体。 这是一个 MSDN 文档,概述了 POCO,其中包含指向具有更详细信息的其他文档的链接。
  • 演练:实体框架的 POCO 模板 这是来自实体框架开发团队的博客文章,其中包含指向有关 POCO 的其他博客文章的链接。

Code-First开发

实体框架 4 中的 POCO 支持仍要求创建数据模型并将实体类链接到数据模型。 实体框架的下一个版本将包括一项称为 代码优先开发的功能。 此功能使你能够将实体框架与你自己的 POCO 类一起使用,而无需使用数据模型设计器或数据模型 XML 文件。 (因此,此选项也称为 仅代码; 代码优先仅代码 引用相同的实体框架功能。)

有关使用代码优先开发方法的详细信息,请参阅以下资源:

此外,构建类似于 Contoso University 应用程序的新 MVC Code-First教程预计将于 2011 年春季在 上发布。 https://asp.net/entity-framework/tutorials

更多信息

这将完成实体框架中的新增功能概述,以及此继续学习实体框架教程系列。 有关实体框架 4 中未在此处介绍的新功能的详细信息,请参阅以下资源: