本主题演示如何使用 ADO.NET 实体数据模型设计器(实体设计器)将实体类型的插入、更新和删除操作映射到存储过程。实体类型的插入、更新和删除操作可以使用系统自动生成的 SQL 语句(默认),也可以使用开发人员所指定的存储过程。无论是否使用存储过程来更新数据库,用于创建、更新和删除实体的应用程序代码都是一样的。
在本演练中,将通过修改 CourseManager 应用程序中所用的实体数据模型 (EDM),将两个实体类型映射到存储过程(有关更多信息,请参见本主题后面的“系统必备”一节)。此外,还将编写用于插入、更新和删除实体类型的代码。
系统必备
要完成本演练,必须生成 CourseManager 应用程序。有关更多信息和说明,请参见实体框架快速入门。生成该应用程序后,将通过把两个实体类型映射到存储过程来修改其 EDM。
| .gif) 注意 | 
|---|
| 因为本文档中的许多演练主题都使用该 CourseManager 应用程序作为起点,所以建议在本演练中使用 CourseManager 应用程序的副本,而不要编辑原始 CourseManager 代码。 | 
本演练假定读者具备 Visual Studio、.NET Framework 的基本知识,并能使用 Visual C# 或 Visual Basic 进行编程。
将 Person 实体映射到存储过程
将实体的插入操作映射到存储过程时,如果服务器为插入的行创建主键值,则必须将该值映射回实体的键属性。在本示例中,InsertPerson 存储过程返回新创建的主键,作为存储过程的结果集的一部分。主键是使用实体设计器的**“<添加结果绑定>”**功能映射到实体键 (PersonID) 的。
将 Person 实体映射到存储过程
- 在 Visual Studio 中打开 CourseManager 解决方案。 
- 在解决方案资源管理器中,双击 School.edmx 文件。 - 该 School.edmx 文件将在 ADO.NET 实体数据模型设计器(实体设计器)中打开。 
- 右键单击 Person 实体类型并选择**“存储过程映射”[Stored Procedure Mapping]**。 - 存储过程映射将显示在**“映射详细信息”**窗口中。 
- 单击**“<选择插入函数>”**。 - 该字段即成为一个下拉列表,其中显示 EDM 中包含的存储过程。 
- 从下拉列表中选择 InsertPerson。 - 此时将显示存储过程参数和实体属性之间的默认映射。请注意,箭头指示映射方向:属性值是提供给存储过程参数的。 
- 单击**“<添加结果绑定>”**。 - 该字段即变为可编辑状态。 
- 用 InsertPerson 存储过程返回的参数的名称 NewPersonID 替换**“<添加结果绑定>”。按“Enter”**。 - 默认情况下,NewPersonID 映射到实体键 PersonID。请注意,箭头指示映射方向:结果列的值是提供给属性的。 
- 单击**“<选择更新函数>”**,然后从产生的下拉列表中选择 UpdatePerson。 - 此时将显示存储过程参数和实体属性之间的默认映射。 
- 单击**“<选择删除函数>”**,然后从产生的下拉列表中选择 DeletePerson。 - 此时将显示存储过程参数和实体属性之间的默认映射。 
现在,Person 实体类型的插入、更新和删除操作即映射到存储过程。
将 OfficeAssignment 实体映射到存储过程
如果将一对一关联某一端的实体类型映射到存储过程,则必须将该关联另一端的实体也映射到存储过程。在本示例中,我们将 OfficeAssignment 实体类型映射到存储过程,因为它和 Person 实体类型具有一对一关联。在此映射中,我们在更新操作中使用**“使用原始值”**选项,以便能够方便地检查应用程序代码中的并发。
将 OfficeAssignment 实体映射到存储过程
- 右键单击 OfficeAssignment 实体类型并选择 Stored Procedure Mapping。 - 存储过程映射将显示在**“映射详细信息”**窗口中。 
- 单击**“<选择插入函数>”**,然后从产生的下拉列表中选择 InsertOfficeAssignment。 - 此时将显示存储过程参数和实体属性之间的默认映射。 
- 单击**“<添加结果绑定>”**。 - 该字段即变为可编辑状态。 
- 键入 Timestamp 代替**“<添加结果绑定>”**。 
- 单击 Propery/Value 列中 Timestamp 旁的空字段。 - 该字段即变为一个属性下拉列表,我们可将 InsertOfficeAssignment 存储过程所返回的值映射到其中的属性。 
- 从下拉列表中选择 Timestamp。 
- 单击**“<选择更新函数>”**,然后从产生的下拉列表中选择 UpdateOfficeAssignment。 - 此时将显示存储过程参数和实体属性之间的默认映射。每个映射的属性旁的**“使用原始值”**列中都会显示复选框。 
- 单击**“属性”**列中对应于 OrigTimestamp 参数的空字段,然后从产生的下拉列表中选择 Timestamp。 - 因为参数名与属性名不是完全匹配的,所以实体设计器不将它作为默认映射。 
- 选中**“使用原始值”**列中对应于 Timestamp 属性的框。 - 如果尝试更新,则向数据库写回数据时,将使用最初从数据库读取的 Timestamp 属性的值。如果值与数据库中的值不匹配,将引发 OptimisticConcurrencyException。 
- 单击**“<添加结果绑定>”**。 - 该字段即变为可编辑状态。 
- 将**“<添加结果绑定>”**替换为 Timestamp。 
- 单击 Propery/Value 列中 Timestamp 旁的空字段。 - 该字段即变为一个属性下拉列表,我们可将 UpdateOfficeAssignment 存储过程所返回的结果列映射到其中的属性。 
- 从下拉列表中选择 Timestamp。 
- 单击**“<选择删除函数>”**,然后从产生的下拉列表中选择 DeleteOfficeAssignment。 - 此时将显示存储过程参数和实体属性之间的默认映射。 
现在,OfficeAssignment 实体类型的插入、更新和删除操作即映射到存储过程。
构造用户界面
接下来,将两个窗体添加到 CourseManager 应用程序中。一个窗体提供用于查看和更新教师信息的界面。另一个窗体提供用于查看和更新办公室分配的界面。
构造用户界面
- 在**“解决方案资源管理器”中右键单击 CourseManager 项目,指向“添加”,然后选择“新建项”**。 - 出现**“添加新项”**对话框。 
- 选择**“Windows 窗体”,将窗体名称设置为“InstructorViewer.vb”或“InstructorViewer.cs”,然后单击“添加”**。 - 新窗体即被添加到项目中,并在窗体设计器中打开。窗体的名称设置为 InstructorViewer,文本设置为 InstructorViewer。 
- 将 DataGridView 控件从工具箱拖动到窗体上,并在**“属性”窗口中将其“名称”**设置为 instructorGridView。 
- 将 Button 控件从工具箱拖到窗体上。将其**“名称”设置为 updateInstructor,并将其“文本”**设置为 Update Instructor。 
- 将另一个 Button 控件从工具箱拖动到窗体上。将其**“名称”设置为 viewOffices,并将其“文本”**设置为 View Offices。 
- 在**“解决方案资源管理器”中右键单击 CourseManager 项目,指向“添加”,然后选择“新建项”**。 - 出现**“添加新项”**对话框。 
- 选择**“Windows 窗体”,将窗体名称设置为“OfficeViewer.vb”或“OfficeViewer.cs”,然后单击“添加”**。 - 新窗体即被添加到项目中,并在窗体设计器中打开。窗体的名称设置为 OfficeViewer,文本设置为 OfficeViewer。 
- 将 ComboBox 控件从工具箱拖动到窗体上,并将其**“名称”**设置为 instructorList。 
- 将 TextBox 控件从工具箱拖动到窗体上,并将其**“名称”**设置为 officeLocation。 
- 将 Button 控件从工具箱拖到窗体上。将其**“名称”设置为 updateOffice,并将其“文本”**设置为 Update Office。 
- 在**“解决方案资源管理器”**中,双击 CourseViewer.vb 或 CourseViewer.cs。 - 出现 CourseViewer 窗体的设计视图。 
- 将 Button 控件从工具箱拖到窗体上。 
- 在**“属性”窗口中,将 Button 的“名称”属性设置为 viewInstructors,并将“文本”**属性设置为 View Instructors。 
- 双击 viewInstructorsButton 控件。 - 此时将打开 CourseViewer 窗体的代码隐藏文件。 
- 将下面的代码添加到 viewInstructors_Click 事件处理程序中: - Dim instructorViewer As New InstructorViewer() instructorViewer.Visible = True- InstructorViewer instructorViewer = new InstructorViewer(); instructorViewer.Visible = true;
- 返回到 InstructorViewer 窗体的设计视图。 
- 双击 viewOfficesButton 控件。 - 此时将打开 Form2 的代码隐藏文件。 
- 将下面的代码添加到 viewOffices_Click 事件处理程序中: - Dim officeViewer As New OfficeViewer() officeViewer.Visible = True- OfficeViewer officeViewer = new OfficeViewer(); officeViewer.Visible = true;
现在,就完成了用户界面。
查看和更新教师信息
在本过程中,将向 InstructorViewer 窗体添加一些代码,用于查看和更新教师信息。具体而言,这些代码执行以下操作:
- 将 DataGridView 绑定到用于返回 Person 类型为教师的相关信息的查询。有关将对象绑定到控件的更多信息,请参见将对象绑定到控件(实体框架)。 
- 将 DataGridView 控件中的任何更改(插入、更新或删除)保存到数据库。 
- 在 updateInstructor_Click 事件处理程序中调用 SaveChanges() 时,使用先前映射的存储过程将数据写入数据库。 
查看和更新教师信息
- 在窗体设计器中打开 InstructorViewer 窗体后,双击 InstructorViewer 窗体。 - 此时将打开 InstructorViewer 窗体的代码隐藏文件。 
- 添加下面的 using (C#) 或 Imports (Visual Basic) 语句: - Imports System.Data.Objects Imports System.Data.Objects.DataClasses- using System.Data.Objects; using System.Data.Objects.DataClasses;
- 向表示对象上下文的 InstructorViewer 类添加一个属性: - ' Create an ObjectContext instance based on SchoolEntity. Private schoolContext As SchoolEntities- // Create an ObjectContext instance based on SchoolEntity. private SchoolEntities schoolContext;
- 在 InstructorViewer_Load 事件处理程序中,添加代码以初始化对象上下文,并将 DataGridView 控件的数据源设置为用于返回所有不具有 nullHireDate 的 Person 类型的查询。 - ' Initialize the ObjectContext. schoolContext = New SchoolEntities() Dim instructorQuery As ObjectQuery(Of Person) = _ schoolContext.Person.Include("OfficeAssignment") _ .Where("it.HireDate is not null") _ .OrderBy("it.LastName") instructorGridView.DataSource = instructorQuery _ .Execute(MergeOption.OverwriteChanges) instructorGridView.Columns("EnrollmentDate").Visible = False- // Initialize schoolContext. schoolContext = new SchoolEntities(); // Define the query to retrieve instructors. ObjectQuery<Person> instructorQuery = schoolContext.Person .Include("OfficeAssignment") .Where("it.HireDate is not null") .OrderBy("it.LastName"); // Execute and bind the instructorList control to the query. instructorGridView.DataSource = instructorQuery. Execute(MergeOption.OverwriteChanges); instructorGridView.Columns["EnrollmentDate"].Visible = false;
- 返回 InstructorViewer 窗体的设计视图,然后双击 updateInstructorButton 控件。 - updateInstructor_Click 事件处理程序即添加到代码隐藏文件。 
- 将代码添加到 updateInstructor_Click 事件处理程序,用于保存在 instructorGridViewDataGridView 控件中对教师信息所做的所有更改。 - Dim numChanges As Integer ' Save object changes to the database, display a ' message, and refresh the form. numChanges = schoolContext.SaveChanges() MessageBox.Show(numChanges.ToString() + _ " change(s) saved to the database.") Me.Refresh()- int numChanges; // Save object changes to the database, display a // message, and refresh the form. numChanges = schoolContext.SaveChanges(); MessageBox.Show(numChanges.ToString() + " change(s) saved to the database."); this.Refresh();
按 Ctrl+F5 运行应用程序。现在,通过单击 View Instructors,在显示的表中进行更改,然后单击 Update Instructor,即可查看和更新教师信息。
查看和更新办公室信息
在本过程中,将向 OfficeViewer 窗体添加一些代码,用于查看和更新办公室分配信息。具体而言,这些代码执行以下操作:
- 将 ComboBox 绑定到用于返回教师信息的查询。 
- 在 TextBox 中显示所选教师的办公室位置信息。 
- 在 updateOffice_Click 事件处理程序中调用 SaveChanges() 时,使用先前映射的存储过程将数据写入数据库。 
查看和更新办公室信息
- 在窗体设计器中打开 OfficeViewer 窗体后,双击 OfficeViewer 窗体。 - 此时将打开 OfficeViewer 窗体的代码隐藏文件。 
- 添加下面的 using (C#) 或 Imports (Visual Basic) 语句: - Imports System.Data.Objects Imports System.Data.Objects.DataClasses- using System.Data.Objects; using System.Data.Objects.DataClasses;
- 向表示对象上下文的 OfficeViewer 类添加一个属性: - ' Create an ObjectContext instance based on SchoolEntity. Private schoolContext As SchoolEntities- // Create an ObjectContext instance based on SchoolEntity. private SchoolEntities schoolContext;
- 将下面的方法添加到窗体: - Private Sub ExecuteInstructorQuery() ' Define the query to retrieve instructors. Dim instructorQuery As ObjectQuery(Of Person) = _ schoolContext.Person.Include("OfficeAssignment"). _ Where("it.HireDate is not null").OrderBy("it.LastName") 'Execute and bind the instructorList control to the query. 'Using MergeOption.OverwriteChanges overwrites local data 'with data from the database. instructorList.DataSource = instructorQuery _ .Execute(MergeOption.OverwriteChanges) instructorList.DisplayMember = "LastName" End Sub- private void ExecuteInstructorQuery() { // Define the query to retrieve instructors. ObjectQuery<Person> instructorQuery = schoolContext.Person .Include("OfficeAssignment") .Where("it.HireDate is not null") .OrderBy("it.LastName"); //Execute and bind the instructorList control to the query. //Using MergeOption.OverwriteChanges overwrites local data //with data from the database. instructorList.DataSource = instructorQuery .Execute(MergeOption.OverwriteChanges); instructorList.DisplayMember = "LastName"; }- 此方法执行一个查询,用于返回教师信息并将结果绑定到 instructorListComboBox 控件。 
- 在 OfficeViewer_Load 事件处理程序中,添加代码以初始化对象上下文,然后调用一个方法将 ComboBox 控件绑定到用于返回所有不具有 nullHireDate 的 Person 类型的查询。 - schoolContext = New SchoolEntities() ExecuteInstructorQuery()- schoolContext = new SchoolEntities(); ExecuteInstructorQuery();
- 返回 OfficeViewer 窗体的设计视图,然后双击 instructorListComboBox 控件。 - instructorList_SelectedIndexChanged 事件处理程序即添加到代码隐藏文件。 
- 将代码添加到事件处理程序,用于在 ListBox 控件中显示所选教师的办公室位置,并禁用 updateOfficeButton 控件。对所选办公室位置进行更改后,将启用此控件。 - Dim instructor As Person = CType(Me.instructorList _ .SelectedItem(), Person) If Not instructor.OfficeAssignment Is Nothing Then Me.officeLocation.Text = instructor _ .OfficeAssignment.Location.ToString() Else Me.officeLocation.Text = "" End If ' Disable the updateOffice button until a change ' has been made to the office location. updateOffice.Enabled = False 'forceChanges.Enabled = False- Person instructor = (Person)this.instructorList. SelectedItem; if (instructor.OfficeAssignment != null) { this.officeLocation.Text = instructor. OfficeAssignment.Location.ToString(); } else { this.officeLocation.Text = ""; } // Disable the updateOffice button until a change // has been made to the office location. updateOffice.Enabled = false; //forceChanges.Enabled = false;
- 返回 OfficeViewer 窗体的设计视图,然后双击 updateOfficeButton 控件。 - updateOffice_Click 事件处理程序即添加到代码隐藏文件。 
- 添加一些代码,用于保存在 officeLocationTextBox 控件中对办公室信息所做的所有更改: - Try Dim numChanges As Integer Dim currentInstructor As Person = CType(Me.instructorList _ .SelectedItem(), Person) If Me.officeLocation.Text <> String.Empty Then If Not currentInstructor.OfficeAssignment Is Nothing Then currentInstructor.OfficeAssignment.Location() = _ Me.officeLocation.Text Else Dim temp(8) As Byte currentInstructor.OfficeAssignment = _ OfficeAssignment.CreateOfficeAssignment( _ currentInstructor.PersonID, _ Me.officeLocation.Text, temp) End If Else schoolContext.DeleteObject(currentInstructor. _ OfficeAssignment) End If numChanges = schoolContext.SaveChanges() MessageBox.Show(numChanges.ToString() _ + " change(s) saved to the database.") Catch oce As OptimisticConcurrencyException MessageBox.Show(oce.Message + " The conflict " & _ "occurred on " & oce.StateEntries(0).Entity _ .ToString() & "with key value " & _ oce.StateEntries(0).EntityKey.EntityKeyValues(0) _ .Value) 'forceChanges.Enabled = True Catch ue As UpdateException MessageBox.Show(ue.Message & " Click OK to retrieve " _ & "the latest data from the database.") ExecuteInstructorQuery() Me.Refresh() Finally ' Disable the updateOffice button until another ' change has been made to the location. updateOffice.Enabled = False End Try- try { int numChanges; Person currentInstructor = (Person)this.instructorList. SelectedItem; if (this.officeLocation.Text != string.Empty) { if (currentInstructor.OfficeAssignment != null) { currentInstructor.OfficeAssignment.Location = this.officeLocation.Text; } else { currentInstructor.OfficeAssignment = OfficeAssignment.CreateOfficeAssignment( currentInstructor.PersonID, this.officeLocation.Text, new byte[8]); } } else { schoolContext.DeleteObject(currentInstructor .OfficeAssignment); } numChanges = schoolContext.SaveChanges(); MessageBox.Show(numChanges.ToString() + " change(s) saved to the database."); } catch (OptimisticConcurrencyException oce) { MessageBox.Show(oce.Message + " The conflict " + "occurred on " + oce.StateEntries[0].Entity + " with key value " + oce.StateEntries[0]. EntityKey.EntityKeyValues[0].Value); //forceChanges.Enabled = true; } catch (UpdateException ue) { MessageBox.Show(ue.Message + " Click OK to retrieve " + "the latest data from the database."); ExecuteInstructorQuery(); this.Refresh(); } finally { // Disable the updateOffice button until another // change has been made to the location. updateOffice.Enabled = false; }
- 返回 OfficeViewer 窗体的设计视图,然后双击 officeLocationTextBox 控件。 - officeLocation_TextChanged 事件处理程序即添加到代码隐藏文件。 
- 添加一些代码,以便在对所选办公室位置进行更改后启用 updateOfficeButton 控件: - ' Enable the udateOffice button when there is a change ' to write to the database. updateOffice.Enabled = True- // Enable the udateOffice button when there is a change // to write to the database. updateOffice.Enabled = true;
现在,就完成了应用程序。按 Ctrl+F5 运行应用程序。现在,即可在 OfficeViewer 窗体中查看和更新办公室信息。
处理并发冲突
在本过程中,将向 Office Viewer 窗体添加代码,以便在发生并发冲突后将客户端更改强制保存到数据库。
处理并发冲突
- 在**“解决方案资源管理器”**中双击 InstructorViewer.vb 或 InstructorViewer.cs。 - 窗体即在窗体设计器中打开。 
- 双击 View Offices 按钮。 - 此时将打开 InstructorViewer 窗体的代码隐藏文件。 
- 将下面的代码添加到 viewOffices_Click 事件处理程序,以便在单击 View Offices 按钮时加载两个 OfficeViewer 窗体。 - Dim officeViewer2 As New OfficeViewer() officeViewer2.Text = "Demonstrate Conflict" officeViewer2.Visible = True- OfficeViewer officeViewer2 = new OfficeViewer(); officeViewer2.Text = "Demonstrate Conflict"; officeViewer2.Visible = true;
- 在**“解决方案资源管理器”**中双击 OfficeViewer.vb 或 OfficeViewer.cs。 - 窗体即在窗体设计器中打开。 
- 将 Button 控件从工具箱拖到窗体上。将其**“名称”设置为 forceChanges,并将其“文本”**设置为 Force Changes。 
- 双击 Force Changes 按钮。 - 此时将打开 Office Viewer 窗体的代码隐藏文件。 
- 将下面的代码添加到 forceChanges_Click 事件处理程序,以便将客户端上的更改强制保存到服务器,或从数据库刷新绑定到 instructorListComboBox 控件的数据。 - Dim numChanges As Integer Dim currentInstructor As Person = CType(Me.instructorList _ .SelectedItem(), Person) Try currentInstructor.OfficeAssignment.Location = _ Me.officeLocation.Text ' Using RefreshMode.ClientWins disables the ' optimistic concurrency check. schoolContext.Refresh(RefreshMode.ClientWins, _ currentInstructor.OfficeAssignment) numChanges = schoolContext.SaveChanges() MessageBox.Show(numChanges.ToString() + _ " change(s) saved to the database.") 'forceChanges.Enabled = False Catch ioe As InvalidOperationException MessageBox.Show(ioe.Message + " Click OK to retrieve " _ + "the latest data from the database.") ExecuteInstructorQuery() Me.Refresh() End Try- int numChanges; Person currentInstructor = (Person)this.instructorList .SelectedItem; try { currentInstructor.OfficeAssignment.Location = this.officeLocation.Text; // Using RefreshMode.ClientWins disables the // optimistic concurrency check. schoolContext.Refresh(RefreshMode.ClientWins, currentInstructor.OfficeAssignment); numChanges = schoolContext.SaveChanges(); MessageBox.Show(numChanges.ToString() + " change(s) saved to the database."); //forceChanges.Enabled = false; } catch (InvalidOperationException ioe) { MessageBox.Show(ioe.Message + " Click OK to retrieve " + "the latest data from the database."); ExecuteInstructorQuery(); this.Refresh(); }
- 取消注释 instructorList_SelectedIndexChanged 事件处理程序中的 - forceChanges = False(Visual Basic) 或- forceChanges = false;(C#) 代码行,以便在选择新教师时禁用 Force Changes 按钮。
- 取消注释 updateOffice_Click 事件处理程序中的 - forceChanges = True(Visual Basic) 或- forceChanges = true;(C#) 代码行,以便在发生并发冲突时启用 Force Changes 按钮。
- 取消注释 forceChanges_Click 事件处理程序中的 - forceChanges = False(Visual Basic) 或- forceChanges = false;(C#) 代码行,以便在将更改强制保存到数据库后禁用 Force Changes 按钮。
若要查看应用程序处理并发冲突,请运行应用程序(按 Ctrl+F5),单击 View Instructors,然后单击 View Offices。在 Office Viewer 窗体中更新一个办公室位置,然后尝试在另一个 Demonstrate Conflict 窗体中更新该办公室位置。此时将出现一个消息框,通知发生了并发冲突。若要将更改从 Demonstrate Conflict 窗体强制保存到数据库,请单击 Force Changes。
代码清单
本节包含 InstructorViewer 和 OfficeViewer 窗体的代码隐藏文件的最终版本。
Imports System.Data.Objects
Imports System.Data.Objects.DataClasses
Public Class InstructorViewer
    ' Create an ObjectContext instance based on SchoolEntity.
    Private schoolContext As SchoolEntities
    Private Sub viewOffices_Click(ByVal sender As System.Object, _
            ByVal e As System.EventArgs) Handles viewOffices.Click
        Dim officeViewer As New OfficeViewer()
        officeViewer.Visible = True
        Dim officeViewer2 As New OfficeViewer()
        officeViewer2.Text = "Demonstrate Conflict"
        officeViewer2.Visible = True
    End Sub
    Private Sub InstructorViewer_Load(ByVal sender As System.Object, _
                    ByVal e As System.EventArgs) Handles MyBase.Load
        ' Initialize the ObjectContext.
        schoolContext = New SchoolEntities()
        Dim instructorQuery As ObjectQuery(Of Person) = _
            schoolContext.Person.Include("OfficeAssignment") _
            .Where("it.HireDate is not null") _
            .OrderBy("it.LastName")
        instructorGridView.DataSource = instructorQuery _
            .Execute(MergeOption.OverwriteChanges)
        instructorGridView.Columns("EnrollmentDate").Visible = False
    End Sub
    Private Sub updateInstructor_Click(ByVal sender As System.Object, _
            ByVal e As System.EventArgs) Handles updateInstructor.Click
        Dim numChanges As Integer
        ' Save object changes to the database, display a 
        ' message, and refresh the form.
        numChanges = schoolContext.SaveChanges()
        MessageBox.Show(numChanges.ToString() + _
            " change(s) saved to the database.")
        Me.Refresh()
    End Sub
End Class
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Data.Objects;
using System.Data.Objects.DataClasses;
namespace CourseManager
{
    public partial class InstructorViewer : Form
    {
        // Create an ObjectContext instance based on SchoolEntity.
        private SchoolEntities schoolContext;
        public InstructorViewer()
        {
            InitializeComponent();
        }
        private void viewOffices_Click(object sender, EventArgs e)
        {
            OfficeViewer officeViewer = new OfficeViewer();
            officeViewer.Visible = true;
            OfficeViewer officeViewer2 = new OfficeViewer();
            officeViewer2.Text = "Demonstrate Conflict";
            officeViewer2.Visible = true;
        }
        private void InstructorViewer_Load(object sender, EventArgs e)
        {
            // Initialize schoolContext.
            schoolContext = new SchoolEntities();
            // Define the query to retrieve instructors.
            ObjectQuery<Person> instructorQuery = schoolContext.Person
                .Include("OfficeAssignment")
                .Where("it.HireDate is not null")
                .OrderBy("it.LastName");
            // Execute and bind the instructorList control to the query.
            instructorGridView.DataSource = instructorQuery.
                Execute(MergeOption.OverwriteChanges);
            instructorGridView.Columns["EnrollmentDate"].Visible = false;
        }
        private void updateInstructor_Click(object sender, EventArgs e)
        {
            int numChanges;
            // Save object changes to the database, display a 
            // message, and refresh the form.
            numChanges = schoolContext.SaveChanges();
            MessageBox.Show(numChanges.ToString() +
                " change(s) saved to the database.");
            this.Refresh();
        }
    }
}
Imports System.Data.Objects
Imports System.Data.Objects.DataClasses
Public Class OfficeViewer
    ' Create an ObjectContext instance based on SchoolEntity.
    Private schoolContext As SchoolEntities
    Private Sub OfficeViewer_Load(ByVal sender As System.Object, _
                ByVal e As System.EventArgs) Handles MyBase.Load
        schoolContext = New SchoolEntities()
        ExecuteInstructorQuery()
    End Sub
    Private Sub instructorList_SelectedIndexChanged(ByVal sender As  _
                System.Object, ByVal e As System.EventArgs) Handles _
                instructorList.SelectedIndexChanged
        Dim instructor As Person = CType(Me.instructorList _
            .SelectedItem(), Person)
        If Not instructor.OfficeAssignment Is Nothing Then
            Me.officeLocation.Text = instructor _
                .OfficeAssignment.Location.ToString()
        Else
            Me.officeLocation.Text = ""
        End If
        ' Disable the updateOffice button until a change
        ' has been made to the office location.
        updateOffice.Enabled = False
        'forceChanges.Enabled = False
    End Sub
    Private Sub updateOffice_Click(ByVal sender As System.Object, _
            ByVal e As System.EventArgs) Handles updateOffice.Click
        Try
            Dim numChanges As Integer
            Dim currentInstructor As Person = CType(Me.instructorList _
                .SelectedItem(), Person)
            If Me.officeLocation.Text <> String.Empty Then
                If Not currentInstructor.OfficeAssignment Is Nothing Then
                    currentInstructor.OfficeAssignment.Location() = _
                        Me.officeLocation.Text
                Else
                    Dim temp(8) As Byte
                    currentInstructor.OfficeAssignment = _
                        OfficeAssignment.CreateOfficeAssignment( _
                        currentInstructor.PersonID, _
                        Me.officeLocation.Text, temp)
                End If
            Else
                schoolContext.DeleteObject(currentInstructor. _
                                           OfficeAssignment)
            End If
            numChanges = schoolContext.SaveChanges()
            MessageBox.Show(numChanges.ToString() _
                    + " change(s) saved to the database.")
        Catch oce As OptimisticConcurrencyException
            MessageBox.Show(oce.Message + " The conflict " & _
                    "occurred on " & oce.StateEntries(0).Entity _
                    .ToString() & "with key value " & _
                    oce.StateEntries(0).EntityKey.EntityKeyValues(0) _
                    .Value)
            'forceChanges.Enabled = True
        Catch ue As UpdateException
            MessageBox.Show(ue.Message & " Click OK to retrieve " _
                    & "the latest data from the database.")
            ExecuteInstructorQuery()
            Me.Refresh()
        Finally
            ' Disable the updateOffice button until another
            ' change has been made to the location.
            updateOffice.Enabled = False
        End Try
    End Sub
    Private Sub officeLocation_TextChanged(ByVal sender As  _
                System.Object, ByVal e As System.EventArgs) _
                Handles officeLocation.TextChanged
        ' Enable the udateOffice button when there is a change
        ' to write to the database.
        updateOffice.Enabled = True
    End Sub
    Private Sub forceChanges_Click(ByVal sender As System.Object, _
            ByVal e As System.EventArgs) Handles forceChanges.Click
        Dim numChanges As Integer
        Dim currentInstructor As Person = CType(Me.instructorList _
            .SelectedItem(), Person)
        Try
            currentInstructor.OfficeAssignment.Location = _
                Me.officeLocation.Text
            ' Using RefreshMode.ClientWins disables the
            ' optimistic concurrency check.
            schoolContext.Refresh(RefreshMode.ClientWins, _
                        currentInstructor.OfficeAssignment)
            numChanges = schoolContext.SaveChanges()
            MessageBox.Show(numChanges.ToString() + _
                    " change(s) saved to the database.")
            'forceChanges.Enabled = False
        Catch ioe As InvalidOperationException
            MessageBox.Show(ioe.Message + " Click OK to retrieve " _
                    + "the latest data from the database.")
            ExecuteInstructorQuery()
            Me.Refresh()
        End Try
    End Sub
    Private Sub ExecuteInstructorQuery()
        ' Define the query to retrieve instructors.
        Dim instructorQuery As ObjectQuery(Of Person) = _
            schoolContext.Person.Include("OfficeAssignment"). _
            Where("it.HireDate is not null").OrderBy("it.LastName")
        'Execute and bind the instructorList control to the query.
        'Using MergeOption.OverwriteChanges overwrites local data
        'with data from the database.
        instructorList.DataSource = instructorQuery _
            .Execute(MergeOption.OverwriteChanges)
        instructorList.DisplayMember = "LastName"
    End Sub
End Class
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Data.Objects;
using System.Data.Objects.DataClasses;
namespace CourseManager
{
    public partial class OfficeViewer : Form
    {
        // Create an ObjectContext instance based on SchoolEntity.
        private SchoolEntities schoolContext;
        public OfficeViewer()
        {
            InitializeComponent();
        }
        private void OfficeViewer_Load(object sender, EventArgs e)
        {
            schoolContext = new SchoolEntities();
            ExecuteInstructorQuery();
        }
        private void instructorList_SelectedIndexChanged(object sender, 
        EventArgs e)
        {
            Person instructor = (Person)this.instructorList.
                SelectedItem;
            if (instructor.OfficeAssignment != null)
            {
                this.officeLocation.Text = instructor.
                    OfficeAssignment.Location.ToString();
            }
            else
            {
                this.officeLocation.Text = "";
            }
            // Disable the updateOffice button until a change
            // has been made to the office location.
            updateOffice.Enabled = false;
            //forceChanges.Enabled = false;
        }
        private void updateOffice_Click(object sender, EventArgs e)
        {
            try
            {
                int numChanges;
                Person currentInstructor = (Person)this.instructorList.
                    SelectedItem;
                if (this.officeLocation.Text != string.Empty)
                {
                    if (currentInstructor.OfficeAssignment != null)
                    {
                        currentInstructor.OfficeAssignment.Location
                            = this.officeLocation.Text;
                    }
                    else
                    {
                        currentInstructor.OfficeAssignment
                            = OfficeAssignment.CreateOfficeAssignment(
                            currentInstructor.PersonID, this.officeLocation.Text,
                            new byte[8]);
                    }
                }
                else
                {
                    schoolContext.DeleteObject(currentInstructor
                        .OfficeAssignment);
                }
                numChanges = schoolContext.SaveChanges();
                MessageBox.Show(numChanges.ToString() +
                    " change(s) saved to the database.");
            }
            catch (OptimisticConcurrencyException oce)
            {
                MessageBox.Show(oce.Message + " The conflict "
                    + "occurred on " + oce.StateEntries[0].Entity
                    + " with key value " + oce.StateEntries[0].
                    EntityKey.EntityKeyValues[0].Value);
                //forceChanges.Enabled = true;
            }
            catch (UpdateException ue)
            {
                MessageBox.Show(ue.Message + " Click OK to retrieve "
                    + "the latest data from the database.");
                ExecuteInstructorQuery();
                this.Refresh();
            }
            finally
            {
                // Disable the updateOffice button until another
                // change has been made to the location.
                updateOffice.Enabled = false;
            }
        }
        private void officeLocation_TextChanged(object sender, EventArgs e)
        {
            // Enable the udateOffice button when there is a change
            // to write to the database.
            updateOffice.Enabled = true;
        }
        private void forceChanges_Click(object sender, EventArgs e)
        {
            int numChanges;
            Person currentInstructor = (Person)this.instructorList
                .SelectedItem;
            try
            {
                currentInstructor.OfficeAssignment.Location
                            = this.officeLocation.Text;
                // Using RefreshMode.ClientWins disables the
                // optimistic concurrency check.
                schoolContext.Refresh(RefreshMode.ClientWins,
                        currentInstructor.OfficeAssignment);
                numChanges = schoolContext.SaveChanges();
                MessageBox.Show(numChanges.ToString() +
                    " change(s) saved to the database.");
                //forceChanges.Enabled = false;
            }
            catch (InvalidOperationException ioe)
            {
                MessageBox.Show(ioe.Message + " Click OK to retrieve "
                    + "the latest data from the database.");
                ExecuteInstructorQuery();
                this.Refresh();
            }
        }
        private void ExecuteInstructorQuery()
        {
            // Define the query to retrieve instructors.
            ObjectQuery<Person> instructorQuery = schoolContext.Person
                .Include("OfficeAssignment")
                .Where("it.HireDate is not null")
                .OrderBy("it.LastName");
            //Execute and bind the instructorList control to the query.
            //Using MergeOption.OverwriteChanges overwrites local data
            //with data from the database.
            instructorList.DataSource = instructorQuery
                .Execute(MergeOption.OverwriteChanges);
            instructorList.DisplayMember = "LastName";
        }
    }
}
后续步骤
您已成功地将实体的插入、更新和删除操作映射到存储过程。有关实体框架中的存储过程支持的更多信息,请参见存储过程支持(实体框架)。有关如何生成使用实体框架的应用程序的更多信息,请参见编程指南(实体框架)。