演练:序列化自跟踪实体(实体框架)

本主题中的演练演示了一个方案,其中 Windows Communication Foundation (WCF) 服务公开可返回实体关系图的一系列操作。 接下来,一个客户端应用程序操作此关系图,并向一个使用实体框架来验证和保存对数据库所做的更新的服务操作提交修改内容。 有关更多信息,请参见使用自跟踪实体

通常,需要将模型项目与包含自跟踪实体类型的项目分开。 然后,客户端只需要包括该实体类型项目。

实现此隔离的一种方式是,将自跟踪实体类型与模型分离,并将用于生成实体类型的模板移动到单独的类库中。 .edmx 文件的位置将需要包含在自跟踪实体模板中以便访问元数据。 如果将模板从原始项目移至其他项目中,则必须在编辑器中打开模板文件并将 inputFile 字符串修改为 .edmx 文件的相对位置。 有关使用模板的更多信息,请参见 ADO.NET Self-Tracking Entity Template

将实体类型与模型相分离的另一个方法是:将模板文件保留在原始项目中,但禁用该模板的代码生成功能。 从其他项目链接到模板,这样就可以在其他项目中生成代码,而不是在原始项目中生成代码。 在将某项作为链接添加到项目时,该项的实际内容将保留在原始项目指示的位置中。 本演练中将演示将实体类型与模型分离的这一方法。

此演练完成以下操作:

  • 创建包含基于 School 的模型的类库项目。

  • 使用 ADO.NET 自跟踪实体生成器模板生成实体类型、类型化 ObjectContext 以及包含重载的 ApplyChanges 方法的扩展类。

  • 创建链接到第一个项目中创建的自跟踪实体类型模板的类库项目。

  • 创建公开可返回实体关系图的一组操作的 WCF 服务,并使用实体框架 将在客户端上所做的更改应用到数据库。

  • 创建客户端应用程序(控制台和 Windows Presentation Foundation [WPF]),这些应用程序使用 WCF 服务上公开的操作来操作关系图并提交修改内容。

Ee789839.note(zh-cn,VS.100).gif注意:
可以在 MSDN 代码库中的实体框架文档示例(可能为英文网页)站点上下载 STESchoolModelExample。

创建包含自跟踪实体和对象上下文类的类库项目

  1. 创建一个新类库项目。 键入 STESchoolModel 作为项目和解决方案的名称。

  2. 移除添加到项目中的默认源代码文件。

  3. 使用实体数据模型向导基于 School 数据库中的 DepartmentCourseOnlineCourseOnsiteCourse 表生成模型。 有关更多信息,请参见 School 模型

  4. ADO.NET Entity Data Model Designer(实体设计器)中打开 .edmx 文件。

  5. 按照Walkthrough: Mapping Inheritance - Table-per-Type主题中的继承映射说明进行操作。

  6. 右击实体设计器图面的空白区域,指向**“添加代码生成项”,然后选择“ADO.NET 自跟踪实体生成器”[ADO.NET Self-Tracking Entity Generator]**。 将默认的模板名称更改为 SchoolModel

    Ee789839.note(zh-cn,VS.100).gif注意:
    在将模板文件添加到项目时,可能会弹出一个安全警告,要求您仅接受来自信任的模板来源的模板。单击“接受”

  7. SchoolModel.Context.ttSchoolModel.tt 文件夹即会添加到项目中。 在 SchoolModel.Context.tt 文件夹下有两个文件,用于定义类型化 ObjectContext 和包含重载的 ApplyChanges 方法的扩展类。 在 SchoolModel.tt 文件夹下有一些文件,用于定义实体类型和包含自跟踪实体使用的更改跟踪逻辑的 Helper 类。

    以下两个步骤演示如何在此项目中禁用代码生成。 稍后将会为 STESchoolModelTypes 类库中的类型和 STESchoolModelService 中的对象上下文启用代码生成。

  8. 选择 SchoolModel.tt。 在**“属性”窗口中,从“CustomTool”**属性中清除 TextTemplatingFileGenerator。 删除 SchoolModel.tt 文件夹下的文件。

  9. 选择 SchoolModel.Context.tt。 在**“属性”窗口中,清除“CustomTool”**属性的值。 删除 SchoolModel.Context.tt 文件夹下的文件。

    如果您正在使用 Visual Basic 项目,则可能需要单击**“解决方案资源管理器”中的“显示所有文件”**才能查看项目中的所有文件。

  10. 编译该项目。

创建链接到自跟踪类型模板的类库项目

  1. 在与前一项目相同的解决方案中,创建名为 STESchoolModelTypes 的新类库项目。

  2. 移除添加到项目中的默认源代码文件。

  3. 添加指向 SchoolModel.tt 文件的链接,以便在此解决方案中生成自跟踪实体类型。 在**“解决方案资源管理器”中右击“STESchoolModelTypes”,单击“添加”,然后单击“现有项”**。

  4. 在**“添加现有项”对话框中,浏览到 STESchoolModel 项目,然后单击 SchoolModel.tt(不要按 Enter)。 在“添加”列表中,选择“添加为链接”**。

  5. 添加对 System.Runtime.Serialization 库的引用。 用于可序列化的实体类型的 WCF 的 DataContractDataMember 特性需要此库。

  6. 编译该项目。

创建并配置 WCF 服务应用程序项目

  1. 在与前一项目相同的解决方案中,创建名为 STESchoolModelService 的 WCF 服务应用程序项目。

  2. 添加对 System.Data.Entity.dll 的引用。

  3. 添加对 STESchoolModelSTESchoolModelTypes 项目的引用。

  4. 添加指向 SchoolModel.Context.tt 文件的链接,以便在此解决方案中生成上下文类型。 在**“解决方案资源管理器”中右击“STESchoolModelService”,单击“添加”,然后单击“现有项”**。

  5. 在**“添加现有项”对话框中,浏览到 STESchoolModel 项目,然后单击 SchoolModel.Context.tt(不要按 Enter)。 在“添加”列表中,选择“添加为链接”**。

  6. SchoolModel.Context.tt 文件的“属性”窗口中,在**“自定义工具命名空间”**属性中键入 STESchoolModelTypes。 这会将对象上下文类型添加到与自跟踪实体类型(该类型是必需的)的命名空间相同的命名空间中。

  7. (仅适用于 Visual Basic)将 Import STESchoolModelTypes 添加到从 SchoolModel.Context.tt 文件生成的源文件中。 打开 SchoolModel.Context.tt 文件并查找 Imports System 字符串。 在其他导入的后面添加 Import STESchoolModelTypes。 生成的源文件将包括此命名空间。

  8. 将连接字符串添加到 Web.config 文件,以便实体框架 运行时可以找到元数据。 打开**“STESchoolModel”**项目的 app.config 文件。 复制 connectionStrings 元素,并将其添加为 Web.config 文件的 configuration 元素的子元素。

  9. 打开服务接口文件。 默认情况下,该文件名为**“IService1”**。

  10. 添加定义自跟踪实体的命名空间:STESchoolModelTypes

  11. 使用以下代码替换服务接口定义:

    <ServiceContract()> _
    Public Interface IService1
        <OperationContract()> _
        Sub UpdateDepartment(ByVal updated As Department)
        <OperationContract()> _
        Function GetDepartments() As List(Of Department)
    End Interface
    
    [ServiceContract]
    public interface IService1
    {
        [OperationContract]
        void UpdateDepartment(Department updated);
        [OperationContract]
        List<Department> GetDepartments();
    }
    
  12. 打开服务源代码。 默认情况下,该代码名为 Service1.srv.cs 或 Service1.srv.vb。

  13. 添加定义自跟踪实体的命名空间:STESchoolModelTypes

  14. (仅适用于 Visual Basic)将 Imports STESchoolModelService.STESchoolModelTypes 添加到 Service1.srv.cs 文件中。

  15. 使用以下代码替换服务类定义:

Ee789839.Important(zh-cn,VS.100).gif 注意:
在应用任何更改之前,始终应对已更新的对象执行验证。

Public Class Service1
    Implements IService1
    ''' <summary>
    ''' Updates department and its related courses. 
    ''' </summary>
    Public Sub UpdateDepartment(ByVal updated As Department) Implements IService1.UpdateDepartment
        Using context As New STESchoolModelTypes.SchoolEntities()
            Try
                ' Perform validation on the updated order before applying the changes.

                ' The ApplyChanges method examines the change tracking information 
                ' contained in the graph of self-tracking entities to infer the set of operations
                ' that need to be performed to reflect the changes in the database. 
                context.Departments.ApplyChanges(updated)

                context.SaveChanges()
            Catch ex As UpdateException
                ' To avoid propagating exception messages that contain sensitive data to the client tier, 
                ' calls to ApplyChanges and SaveChanges should be wrapped in exception handling code.
                Throw New InvalidOperationException("Failed to update the department. Try your request again.")
            End Try
        End Using
    End Sub

    ''' <summary>
    ''' Gets all the departments and related courses. 
    ''' </summary>
    Public Function GetDepartments() As List(Of Department) Implements IService1.GetDepartments
        Using context As New STESchoolModelTypes.SchoolEntities()
            ' Use System.Data.Objects.ObjectQuery(T).Include to eagrly load the related courses.
            Return context.Departments.Include("Courses").OrderBy(Function(d) d.Name).ToList()
        End Using
    End Function

End Class
public class Service1 : IService1
{
    /// <summary>
    /// Updates department and its related courses. 
    /// </summary>
    public void UpdateDepartment(Department updated)
    {
        using (SchoolEntities context =
            new SchoolEntities())
        {
            try
            {
                // Perform validation on the updated order before applying the changes.

                // The ApplyChanges method examines the change tracking information 
                // contained in the graph of self-tracking entities to infer the set of operations
                // that need to be performed to reflect the changes in the database. 
                context.Departments.ApplyChanges(updated);
                context.SaveChanges();

            }
            catch (UpdateException ex)
            {
                // To avoid propagating exception messages that contain sensitive data to the client tier, 
                // calls to ApplyChanges and SaveChanges should be wrapped in exception handling code.
                throw new InvalidOperationException("Failed to update the department. Try your request again.");
            }
        }
    }

    /// <summary>
    /// Gets all the departments and related courses. 
    /// </summary>
    public List<Department> GetDepartments()
    {
        using (SchoolEntities context = new SchoolEntities())
        {
            // Use System.Data.Objects.ObjectQuery(T).Include to eagrly load the related courses.
            return context.Departments.Include("Courses").OrderBy(d => d.Name).ToList();
        }
    }

}

使用控制台客户端应用程序测试服务

  1. 创建一个控制台应用程序。 在与前一项目相同的解决方案中,键入 STESchoolModelTest 作为项目名称。

  2. 添加对 STEASchoolModelService 服务的引用。 若要添加对服务的引用,请在**“解决方案资源管理器”中右击“引用”文件夹并选择“添加服务引用”**。

    默认情况下,WCF 将生成一个返回 IEnumerable 集合的代理。 由于 STESchoolModelServiceGetDepartments 方法返回 List,因此您必须配置该服务以指定适当的返回类型。

  3. 右击该服务名称(“ServiceReference1”),然后选择**“配置服务引用...”。 在“配置服务引用”对话框中,从“集合类型”列表中选择“System.Collections.Generic.List”**类型。

  4. 添加对 STESchoolModelTypes 项目的引用。

  5. 打开 app.config 文件,并将连接字符串添加到该文件中。 打开 STESchoolModel 项目的 app.config 文件,复制 connectionStrings 元素,并将其添加为 Web.config 文件的 configuration 元素的子元素。

  6. 打开包含主函数的文件。 包括以下命名空间:STESchoolModelTest.ServiceReference1 命名空间和 STESchoolModelTypes 命名空间(其中定义了自跟踪类型)。

  7. 将以下代码粘贴到主函数中。 此代码包含对下一步骤中定义的方法的函数调用。

    ' Note, the service's GetDepartments method returns System.Collections.Generic.List.
    ' By default, when WCF generates a proxy the return collection types are converted to IEnumerable.
    ' The WCF service has to be configured to specify the List return type. 
    ' To specify the List collection type, open the Configure Service Reference dialog and 
    ' select the System.Collections.Generic.List type from the Collection type list. 
    
    Console.WriteLine("See the existing departments and courses.")
    DisplayDepartmentsAndCourses()
    Console.WriteLine()
    Console.WriteLine()
    
    ' Use some IDs to create
    ' new Department and Course. 
    ' The newly created objects will
    ' be then deleted.
    
    Dim departmentID As Integer = 100
    
    Dim courseID As Integer = 50
    
    AddNewDepartmentAndCourses(departmentID, courseID)
    Console.WriteLine("See existing and added.")
    DisplayDepartmentsAndCourses()
    Console.WriteLine()
    UpdateDepartmentAndCourses(departmentID, courseID)
    Console.WriteLine("See existing and updated.")
    DisplayDepartmentsAndCourses()
    Console.WriteLine()
    DeleteDepartmentAndCourses(departmentID)
    
    // Note, the service's GetDepartments method returns System.Collections.Generic.List.
    // By default, when WCF generates a proxy the return collection types are converted to IEnumerable.
    // The WCF service has to be configured to specify the List return type. 
    // To specify the List collection type, open the Configure Service Reference dialog and 
    // select the System.Collections.Generic.List type from the Collection type list. 
    
    Console.WriteLine("See the existing departments and courses.");
    DisplayDepartmentsAndCourses();
    Console.WriteLine();
    Console.WriteLine();
    
    // Use some IDs to create
    // new Department and Course. 
    // The newly created objects will
    // be then deleted.
    
    int departmentID = 100;
    
    int courseID = 50;
    
    AddNewDepartmentAndCourses(departmentID, courseID);
    Console.WriteLine("See existing and added.");
    DisplayDepartmentsAndCourses();
    Console.WriteLine();
    UpdateDepartmentAndCourses(departmentID, courseID);
    Console.WriteLine("See existing and updated.");
    DisplayDepartmentsAndCourses();
    Console.WriteLine();
    DeleteDepartmentAndCourses(departmentID);
    
  8. 将下面的方法添加到此类中。 这些方法演示如何执行下列操作:显示由该服务返回的对象、添加新对象、更新对象和删除对象。 有关更多信息,请参见代码注释。

    Private Sub DisplayDepartmentsAndCourses()
        Using service = New Service1Client()
            ' Get all the departments.
            Dim departments As List(Of Department) = service.GetDepartments()
            For Each d In departments
                Console.WriteLine("ID: {0}, Name: {1}", d.DepartmentID, d.Name)
                ' Get all the courses for each department. 
                ' The reason we are able to access
                ' the related courses is because the service eagrly loaded the related objects 
                ' (using the System.Data.Objects.ObjectQuery(T).Include method).
                For Each c In d.Courses.OfType(Of OnlineCourse)()
                    Console.WriteLine(" OnLineCourse ID: {0}, Title: {1}", c.CourseID, c.Title)
                Next
                For Each c In d.Courses.OfType(Of OnsiteCourse)()
                    Console.WriteLine(" OnSiteCourse ID: {0}, Title: {1}", c.CourseID, c.Title)
                Next
            Next
        End Using
    End Sub
    
    
    Private Sub AddNewDepartmentAndCourses(ByVal departmentID As Integer, ByVal courseID As Integer)
        Using service = New Service1Client()
    
            Dim newDepartment As New Department() _
                With {.DepartmentID = departmentID, _
                      .Budget = 13000D, _
                      .Name = "New Department", _
                      .StartDate = DateTime.Now _
                     }
    
            Dim newCourse As New OnlineCourse() _
                With {.CourseID = courseID, _
                     .DepartmentID = departmentID, _
                     .___URL = "http://www.fineartschool.net/Trigonometry", _
                     .Title = "New Onsite Course", _
                     .Credits = 4 _
                     }
    
            ' Add the course to the department.
            newDepartment.Courses.Add(newCourse)
    
            ' The newly create objects are marked as added, the service will insert these into the store. 
            service.UpdateDepartment(newDepartment)
    
            ' Let’s make few more changes to the saved object. 
            ' Since the previous changes have now been persisted, call AcceptChanges to
            ' reset the ChangeTracker on the objects and mark the state as ObjectState.Unchanged.
            ' Note, AcceptChanges sets the tracking on, so you do not need to call StartTracking
            ' explicitly.
            newDepartment.AcceptChanges()
            newCourse.AcceptChanges()
    
            ' Because the change tracking is enabled
            ' the following change will set newCourse.ChangeTracker.State to ObjectState.Modified.
            newCourse.Credits = 6
    
            service.UpdateDepartment(newDepartment)
        End Using
    End Sub
    
    Private Sub UpdateDepartmentAndCourses(ByVal departmentID As Integer, ByVal courseID As Integer)
        Using service = New Service1Client()
            ' Get all the departments.
            Dim departments As List(Of Department) = service.GetDepartments()
            ' Use LINQ to Objects to query the departments collection 
            ' for the specific department object.
            Dim department As Department = departments.Single(Function(d) d.DepartmentID = departmentID)
            department.Budget = department.Budget - 1000D
    
            ' Get the specified course that belongs to the department.
            ' The reason we are able to access the related course
            ' is because the service eagrly loaded the related objects 
            ' (using the System.Data.Objects.ObjectQuery(T).Include method).
            Dim existingCourse As Course = department.Courses.[Single](Function(c) c.CourseID = courseID)
            existingCourse.Credits = 3
    
            service.UpdateDepartment(department)
        End Using
    End Sub
    
    Private Sub DeleteDepartmentAndCourses(ByVal departmentID As Integer)
        Using service = New Service1Client()
            Dim departments As List(Of Department) = service.GetDepartments()
    
            Dim department As Department = departments.Single(Function(d) d.DepartmentID = departmentID)
    
            ' When MarkAsDeleted is called, the entity is removed from the collection,
            ' if we modify the collection over which foreach is looping an exception will be thrown.
            ' That is why we need to make a copy of the courses collection by 
            ' calling department.Courses.ToList();
            Dim courses As List(Of Course) = department.Courses.ToList()
            For Each c In courses
    
                ' Marks each comment for the post as Deleted.
                ' If another entity have a foreign key relationship with this Course object
                ' an exception will be thrown during save operation. 
                c.MarkAsDeleted()
            Next
    
            department.MarkAsDeleted()
            service.UpdateDepartment(department)
        End Using
    End Sub
    
    static void DisplayDepartmentsAndCourses()
    {
        using (var service = new Service1Client())
        {
            // Get all the departments.
            List<Department> departments = service.GetDepartments();
            foreach (var d in departments)
            {
                Console.WriteLine("ID: {0}, Name: {1}", d.DepartmentID, d.Name);
                // Get all the courses for each department. 
                // The reason we are able to access
                // the related courses is because the service eagrly loaded the related objects 
                // (using the System.Data.Objects.ObjectQuery(T).Include method).
                foreach (var c in d.Courses.OfType<OnlineCourse>())
                {
                    Console.WriteLine("  OnLineCourse ID: {0}, Title: {1}", c.CourseID, c.Title);
                }
                foreach (var c in d.Courses.OfType<OnsiteCourse>())
                {
                    Console.WriteLine("  OnSiteCourse ID: {0}, Title: {1}", c.CourseID, c.Title);
                }
            }
        }
    }
    
    
    static void AddNewDepartmentAndCourses(int departmentID, int courseID)
    {
        using (var service = new Service1Client())
        {
            Department newDepartment = new Department()
            {
                DepartmentID = departmentID,
                Budget = 13000.000m,
                Name = "New Department",
                StartDate = DateTime.Now
            };
    
            OnlineCourse newCourse = new OnlineCourse()
            { 
                CourseID = courseID,
                DepartmentID = departmentID,
                URL = "http://www.fineartschool.net/Trigonometry",
                Title = "New Onsite Course",
                Credits = 4
            };
    
            // Add the course to the department.
            newDepartment.Courses.Add(newCourse);
    
            // The newly create objects are marked as added, the service will insert these into the store. 
            service.UpdateDepartment(newDepartment);
    
            // Let’s make few more changes to the saved object. 
            // Since the previous changes have now been persisted, call AcceptChanges to
            // reset the ChangeTracker on the objects and mark the state as ObjectState.Unchanged.
            // Note, AcceptChanges sets the tracking on, so you do not need to call StartTracking
            // explicitly.
            newDepartment.AcceptChanges();
            newCourse.AcceptChanges();
    
            // Because the change tracking is enabled
            // the following change will set newCourse.ChangeTracker.State to ObjectState.Modified.
            newCourse.Credits = 6;
            service.UpdateDepartment(newDepartment);
    
        }
    }
    
    static void UpdateDepartmentAndCourses(int departmentID, int courseID)
    {
        using (var service = new Service1Client())
        {
            // Get all the departments.
            List<Department> departments = service.GetDepartments();
            // Use LINQ to Objects to query the departments collection 
            // for the specific department object.
            Department department = departments.Single(d => d.DepartmentID == departmentID);
            department.Budget = department.Budget - 1000.00m;
    
            // Get the specified course that belongs to the department.
            // The reason we are able to access the related course
            // is because the service eagrly loaded the related objects 
            // (using the System.Data.Objects.ObjectQuery(T).Include method).
            Course existingCourse = department.Courses.Single(c => c.CourseID == courseID);
            existingCourse.Credits = 3;
    
            service.UpdateDepartment(department);
        }
    }
    
    static void DeleteDepartmentAndCourses(int departmentID)
    {
        using (var service = new Service1Client())
        {
            List<Department> departments = service.GetDepartments();
    
            Department department = departments.Single(d => d.DepartmentID == departmentID);
    
            // When MarkAsDeleted is called, the entity is removed from the collection,
            // if we modify the collection over which foreach is looping an exception will be thrown.
            // That is why we need to make a copy of the courses collection by 
            // calling department.Courses.ToList();
            List<Course> courses = department.Courses.ToList();
            foreach (var c in courses)
            {
    
                // Marks each comment for the post as Deleted.
                // If another entity have a foreign key relationship with this Course object
                // an exception will be thrown during save operation. 
                c.MarkAsDeleted();
            }
    
            department.MarkAsDeleted();
            service.UpdateDepartment(department);
        }
    }
    

使用 WPF 客户端应用程序测试服务

  1. 创建一个 WPF 应用程序。 在与前一项目相同的解决方案中,键入 STESchoolModelWPFTest 作为项目名称。

  2. 添加对 STEASchoolModelService 服务的引用。 若要添加对服务的引用,请在**“解决方案资源管理器”中右击“引用”文件夹并选择“添加服务引用”**。

    默认情况下,WCF 将生成一个返回 IEnumerable 集合的代理。 由于 STESchoolModelServiceGetDepartments 方法返回 List,因此您必须配置该服务以指定适当的返回类型。

  3. 右击该服务名称(“ServiceReference1”),然后选择**“配置服务引用...”。 在“配置服务引用”对话框中,从“集合类型”列表中选择“System.Collections.Generic.List”**类型。

  4. 添加对 STESchoolModelTypes 项目的引用。

  5. 打开 app.config 文件,并将连接字符串添加到该文件中。 打开 STESchoolModel 项目的 app.config 文件,复制 connectionStrings 元素,并将其添加为 Web.config 文件的 configuration 元素的子元素。

    现在可以删除**“STESchoolModel”**项目的 app.config 文件,由于从未使用,因此它从未使用过。

    默认情况下,项目模板会将 MainWindow.xaml 文件及相应的代码隐藏文件添加到项目中。

  6. 打开 MainWindow.xaml,然后将默认的 XAML 代码替换为在 WPF 中定义 STESchoolModelWPFTest 窗口的 XAML。 有关更多信息,请参见代码注释。

    <Window x:Class="STESchoolModelWPFTest.MainWindow"
            xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"
            Title="MainWindow" Height="508" Width="919" Loaded="Window_Loaded">
        <!-- The code begind code sets the departmentsItemsGrid to the root of the object graph.-->
        <Grid Name="departmentsItemsGrid">
            <!-- comboBoxDepartment points to the root of the graph, that is why the Path is not specified-->
            <ComboBox DisplayMemberPath="DepartmentID" ItemsSource="{Binding}"
                      IsSynchronizedWithCurrentItem="true" 
                      Height="23" Margin="122,12,198,0" Name="comboBoxDepartment" VerticalAlignment="Top"/>
            <!-- listViewItems Path is set to Courses because it is bound to Department.Courses.-->
            <ListView ItemsSource="{Binding Path=Courses}" Name="listViewItems" Margin="34,46,34,50" >
                <ListView.View>
                    <GridView AllowsColumnReorder="False" ColumnHeaderToolTip="Courses" >
                        <GridViewColumn DisplayMemberBinding="{Binding Path=CourseID}" 
                            Header="CourseID" Width="70"/>
                        <!--The TextBox controls are embedded in the two of the following columns.
                            This is done to enable editing in the ListView control. -->
                        <GridViewColumn Header="Title" Width="100">
                            <GridViewColumn.CellTemplate>
                                <DataTemplate>
                                    <TextBox Height="25" Width="100" Text="{Binding Path=Title}" />
                                </DataTemplate>
                            </GridViewColumn.CellTemplate>
                        </GridViewColumn>
                        <GridViewColumn Header="Credits" Width="100" >
                            <GridViewColumn.CellTemplate>
                                <DataTemplate>
                                    <TextBox Height="25" Width="100" Text="{Binding Path=Credits}" />
                                </DataTemplate>
                            </GridViewColumn.CellTemplate>
                        </GridViewColumn>
                    </GridView>
                </ListView.View>
            </ListView>
            <Label Height="28" Margin="34,12,0,0" Name="departmentLabel" VerticalAlignment="Top" 
                   HorizontalAlignment="Left" Width="93">Department:</Label>
            <!--When the Save and Close button is clicked all the objects will be sent to the service 
                where all the updated objects will be saved to the database. -->
            <Button Height="23" HorizontalAlignment="Right" Margin="0,0,34,12" 
                    Name="buttonClose" VerticalAlignment="Bottom" Width="127" Click="buttonClose_Click">Save and Close</Button>
        </Grid>
    </Window>
    
  7. 打开 MainWindow.xaml.cs(或 .vb)文件,然后将默认的隐藏代码替换为下面的代码(有关更多说明,请查看代码注释)。

    Class MainWindow
        Dim departments As List(Of Department)
    
        Private Sub Window_Loaded(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs) Handles MyBase.Loaded
            Using service = New Service1Client()
                ' Set the parent of of your data bound controls to the root of the graph.
                ' In the xaml page the appropriate paths should be set on each data bound control.
                ' For the comboBoxDepartment it is empty because it is bound to Departments (which is root).
                ' For the listViewItems it is set to Courses because it is bound to Department.Courses.
                ' Note, that the TextBox controls are embedded in the two of the columns in the listViewItems.
                ' This is done to enable editing in the ListView control.
                departments = service.GetDepartments()
                Me.departmentsItemsGrid.DataContext = departments
            End Using
        End Sub
    
        Private Sub buttonSave_Click(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs) Handles buttonSave.Click
            Using service = New Service1Client()
                ' Save all the departments and their courses. 
                For Each department In departments
                    service.UpdateDepartment(department)
    
                    ' Call AcceptChanges on all the objects 
                    ' to resets the change tracker and set the state of the objects to Unchanged.
                    department.AcceptChanges()
                    For Each course In department.Courses
                        course.AcceptChanges()
                    Next
                Next
            End Using
        End Sub
    
        Private Sub buttonClose_Click(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs) Handles buttonClose.Click
            ' Close the form.
            Me.Close()
        End Sub
    End Class
    
    public partial class MainWindow : Window
    {
        private List<Department> departments;
    
        public MainWindow()
        {
            InitializeComponent();
        }
    
        private void Window_Loaded(object sender, RoutedEventArgs e)
        {
            using (var service = new Service1Client())
            {
                // Set the parent of of your data bound controls to the root of the graph.
                // In the xaml page the appropriate paths should be set on each data bound control.
                // For the comboBoxDepartment it is empty because it is bound to Departments (which is root).
                // For the listViewItems it is set to Courses because it is bound to Department.Courses.
                // Note, that the TextBox controls are embedded in the two of the columns in the listViewItems.
                // This is done to enable editing in the ListView control.
                departments = service.GetDepartments();
                this.departmentsItemsGrid.DataContext = departments;
            }
        }
    
        private void buttonSave_Click(object sender, RoutedEventArgs e)
        {
            using (var service = new Service1Client())
            {
                // Save all the departments and their courses. 
                foreach (var department in departments)
                {
                    service.UpdateDepartment(department);
    
                    // Call AcceptChanges on all the objects 
                    // to resets the change tracker and set the state of the objects to Unchanged.
                    department.AcceptChanges();
                    foreach (var course in department.Courses)
                        course.AcceptChanges();
                }
            }
    
        }
    
        private void buttonClose_Click(object sender, RoutedEventArgs e)
        {
            //Close the form.
            this.Close();
        }
    }
    

另请参见

概念

使用自跟踪实体
序列化对象(实体框架)
生成 N 层应用程序(实体框架)

其他资源

ADO.NET Self-Tracking Entity Template