共用方式為


逐步解說:序列化自我追蹤實體

本主題中的逐步解說將示範 Windows Communication Foundation (WCF) 服務用以公開一系列傳回實體圖形之作業的案例。 接著,用戶端應用程式會操作該圖形並提交修改至服務作業,以便使用 Entity Framework 來驗證更新並將更新儲存至資料庫。 如需詳細資訊,請參閱使用自我追蹤實體

一般而言,您會想要將模型專案與包含自我追蹤實體類型的專案分隔開。 然後,您的用戶端就只要包括實體類型專案即可。

達成這項隔離的其中一種方式為,將自我追蹤實體類型與模型分隔開,再將會產生實體類型的範本移至不同的類別庫中。 您必須將 .edmx 檔案的位置加入至自我追蹤實體範本,才能存取中繼資料。 如果您將範本從原始專案移至其他專案中,就必須在編輯器中開啟範本檔,再將 inputFile 字串修改為 .edmx 檔案的相對位置。 如需使用範本的詳細資訊,請參閱 ADO.NET Self-Tracking Entity Template

將這些實體類型與模型分隔開的另一種方式為,讓範本檔留在原始專案中,但要停用範本的程式碼產生功能。 從不同的專案連結至範本,就會在不同的專案中產生程式碼,而不是產生在原始專案中。 當您將項目加入至專案做為連結,則項目的實際內容會保持在原始專案所指定的位置。 本逐步解說將示範這種將實體類型與模型分隔開的方法。

這個逐步解說會完成下列動作:

  • 建立包含 School 架構模型的類別庫專案。

  • 使用 [ADO.NET 自我追蹤實體產生器] 範本來產生實體類型、具型別 ObjectContext,以及內含多載之 ApplyChanges 方法的擴充類別。

  • 建立連結至自我追蹤實體類型範本的類別庫專案,該範本是在第一個專案中建立的。

  • 建立會公開一組傳回實體圖形之作業的 WCF 服務,並使用 Entity Framework ,將用戶端上的變更套用至資料庫。

  • 建立會操作圖形的用戶端應用程式 (主控台和 Windows Presentation Foundation [WPF]),並使用 WCF 服務上所公開的作業來提交修改。

Ee789839.note(zh-tw,VS.100).gif注意:
您可以在 MSDN Code Gallery 的 Entity Framework 文件範例 (英文) 網站上下載 STESchoolModelExample。

若要建立包含自我追蹤實體和物件內容類別的類別庫專案

  1. 建立新的類別庫專案。 輸入 STESchoolModel 做為專案和方案名稱。

  2. 移除加入至專案的預設原始程式碼檔。

  3. 使用 [實體資料模型精靈] 來產生以 School 資料庫中之 DepartmentCourseOnlineCourseOnsiteCourse 資料表為基礎的模型。 如需詳細資訊,請參閱 School 模型

  4. ADO.NET Entity Data Model Designer (Entity Designer) 中開啟 .edmx 檔案。

  5. 遵循Walkthrough: Mapping Inheritance - Table-per-Type主題中的繼承對應指示進行。

  6. 以滑鼠右鍵按一下 Entity Designer 介面的空白區域、指向 [加入產生程式碼項目],然後選取 [ADO.NET 自我追蹤實體產生器]。 將預設的範本名稱變更為 SchoolModel

    Ee789839.note(zh-tw,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 檔案,如此 Entity Framework 執行階段才能尋找中繼資料。 開啟 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-tw,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. 編譯專案。

若要使用主控台用戶端應用程式來測試服務

  1. 建立主控台應用程式。 在與前一個專案相同的方案中,輸入 STESchoolModelTest 做為專案名稱。

  2. 加入 STEASchoolModelService 服務的參考。 若要加入服務的參考,請在 [方案總管] 中,以滑鼠右鍵按一下參考資料夾,然後選取 [加入服務參考]。

    根據預設,WCF 會產生傳回 IEnumerable 集合的 Proxy。 因為 STESchoolModelServiceGetDepartments 方法會傳回 List,所以您必須設定服務來指定適當的傳回型別。

  3. 以滑鼠右鍵按一下服務名稱 (ServiceReference1),然後選取 [設定服務參考...]。 在 [設定服務參考] 對話方塊的 [集合型別] 清單中,選取 [System.Collections.Generic.List] 型別。

  4. 加入 STESchoolModelTypes 專案的參考。

  5. 開啟 app.config 檔案,然後將連接字串加入至檔案。 開啟 STESchoolModel 專案的 app.config 檔案,然後複製 connectionStrings 項目,以便將它加入成為 Web.config 檔案之 configuration 項目的子項目。

  6. 開啟包含 main 函式的檔案。 加入下列命名空間:STESchoolModelTest.ServiceReference1STESchoolModelTypes 命名空間 (其中定義自我追蹤類型)。

  7. 將下列程式碼貼入 main 函式。 此程式碼包含在下一個步驟中定義之方法的函式呼叫。

    ' 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 集合的 Proxy。 因為 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();
        }
    }
    

另請參閱

概念

使用自我追蹤實體
序列化物件 (Entity Framework)
建置 N-Tier 應用程式

其他資源

ADO.NET Self-Tracking Entity Template