使用 WinForms 的資料繫結Databinding with WinForms

本逐步解說將示範如何將 POCO 型別系結至視窗表單 (在「主從階層」表單中 WinForms) 控制項。This step-by-step walkthrough shows how to bind POCO types to Window Forms (WinForms) controls in a “master-detail" form. 應用程式會使用 Entity Framework,以資料庫中的資料填入物件、追蹤變更,以及將資料保存到資料庫。The application uses Entity Framework to populate objects with data from the database, track changes, and persist data to the database.

此模型會定義兩種參與一對多關聯性的類型:類別 (主體 \ 主機) 和產品 (相依 \ 詳細資料) 。The model defines two types that participate in one-to-many relationship: Category (principal\master) and Product (dependent\detail). 然後,Visual Studio 工具會用來將模型中定義的類型系結至 WinForms 控制項。Then, the Visual Studio tools are used to bind the types defined in the model to the WinForms controls. WinForms 資料系結架構會啟用相關物件之間的導覽:選取主視圖中的資料列會導致詳細資料檢視以對應的子資料更新。The WinForms data-binding framework enables navigation between related objects: selecting rows in the master view causes the detail view to update with the corresponding child data.

本逐步解說中的螢幕擷取畫面和程式代碼清單取自 Visual Studio 2013,但您可以使用 Visual Studio 2012 或 Visual Studio 2010 來完成此逐步解說。The screen shots and code listings in this walkthrough are taken from Visual Studio 2013 but you can complete this walkthrough with Visual Studio 2012 or Visual Studio 2010.

必要條件Pre-Requisites

您必須安裝 Visual Studio 2013、Visual Studio 2012 或 Visual Studio 2010,才能完成此逐步解說。You need to have Visual Studio 2013, Visual Studio 2012 or Visual Studio 2010 installed to complete this walkthrough.

如果您使用 Visual Studio 2010,您也必須安裝 NuGet。If you are using Visual Studio 2010, you also have to install NuGet. 如需詳細資訊,請參閱 安裝 NuGetFor more information, see Installing NuGet.

建立應用程式Create the Application

  • 開啟 Visual StudioOpen Visual Studio
  • 檔案- > 新增- > 專案 .。。File -> New -> Project….
  • 在左窗格中選取 [ windows ],然後在右窗格中選取 [ windows FormsApplication ]Select Windows in the left pane and Windows FormsApplication in the right pane
  • 輸入 WinFormswithEFSample 作為名稱Enter WinFormswithEFSample as the name
  • 選取 [確定]Select OK

安裝 Entity Framework NuGet 套件Install the Entity Framework NuGet package

  • 在方案總管中,以滑鼠右鍵按一下 WinFormswithEFSample 專案In Solution Explorer, right-click on the WinFormswithEFSample project
  • 選取 [管理 NuGet 套件 ... ]Select Manage NuGet Packages…
  • 在 [管理 NuGet 封裝] 對話方塊中,選取 [ 線上 ] 索引標籤,然後選擇 EntityFramework 套件In the Manage NuGet Packages dialog, Select the Online tab and choose the EntityFramework package
  • 按一下 [安裝]Click Install

    注意

    除了 EntityFramework 元件之外,也會加入 ComponentModel. DataAnnotations 的參考。In addition to the EntityFramework assembly a reference to System.ComponentModel.DataAnnotations is also added. 如果專案具有 system.string 的參考,則會在安裝 EntityFramework 套件時將其移除。If the project has a reference to System.Data.Entity, then it will be removed when the EntityFramework package is installed. Entity Framework 6 應用程式不再使用 System.object 元件。The System.Data.Entity assembly is no longer used for Entity Framework 6 applications.

針對集合執行 IListSourceImplementing IListSource for Collections

集合屬性必須執行 IListSource 介面,以在使用 Windows Forms 時啟用雙向資料系結與排序。Collection properties must implement the IListSource interface to enable two-way data binding with sorting when using Windows Forms. 為了達到此目的,我們將擴充 ObservableCollection 以新增 IListSource 功能。To do this we are going to extend ObservableCollection to add IListSource functionality.

  • ObservableListSource 類別新增至專案:Add an ObservableListSource class to the project:
    • 以滑鼠右鍵按一下專案名稱Right-click on the project name
    • 選取 [加入 > 新專案]Select Add -> New Item
    • 選取 [ 類別 ],然後輸入 ObservableListSource 做為類別名稱Select Class and enter ObservableListSource for the class name
  • 以下列程式碼取代預設產生的程式碼:Replace the code generated by default with the following code:

這個類別會啟用雙向資料系結以及排序。類別衍生自 ObservableCollection < T > ,並新增 IListSource 的明確實作為。IListSource 的 GetList ( # A1 方法會實作為傳回與 ObservableCollection 保持同步的 IBindingList 執行。ToBindingList 所產生的 IBindingList 實支援排序。ToBindingList 擴充方法定義于 EntityFramework 元件中。This class enables two-way data binding as well as sorting. The class derives from ObservableCollection<T> and adds an explicit implementation of IListSource. The GetList() method of IListSource is implemented to return an IBindingList implementation that stays in sync with the ObservableCollection. The IBindingList implementation generated by ToBindingList supports sorting. The ToBindingList extension method is defined in the EntityFramework assembly.

    using System.Collections;
    using System.Collections.Generic;
    using System.Collections.ObjectModel;
    using System.ComponentModel;
    using System.Diagnostics.CodeAnalysis;
    using System.Data.Entity;

    namespace WinFormswithEFSample
    {
        public class ObservableListSource<T> : ObservableCollection<T>, IListSource
            where T : class
        {
            private IBindingList _bindingList;

            bool IListSource.ContainsListCollection { get { return false; } }

            IList IListSource.GetList()
            {
                return _bindingList ?? (_bindingList = this.ToBindingList());
            }
        }
    }

定義模型Define a Model

在這個逐步解說中,您可以選擇使用 Code First 或 EF 設計工具來執行模型。In this walkthrough you can chose to implement a model using Code First or the EF Designer. 完成下列兩節的其中一節。Complete one of the two following sections.

選項1:使用 Code First 定義模型Option 1: Define a Model using Code First

本節說明如何使用 Code First 建立模型及其相關聯的資料庫。This section shows how to create a model and its associated database using Code First. 跳到下一節, (**選項2:使用 Database First 來定義模型) ** 如果您想要 DATABASE FIRST 使用 EF 設計工具,從資料庫將模型反向工程Skip to the next section (Option 2: Define a model using Database First) if you would rather use Database First to reverse engineer your model from a database using the EF designer

使用 Code First 開發時,通常會先撰寫 .NET Framework 類別,以定義您的概念 (網域) 模型。When using Code First development you usually begin by writing .NET Framework classes that define your conceptual (domain) model.

  • 將新的 產品 類別加入至專案Add a new Product class to project
  • 以下列程式碼取代預設產生的程式碼:Replace the code generated by default with the following code:
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;

    namespace WinFormswithEFSample
    {
        public class Product
        {
            public int ProductId { get; set; }
            public string Name { get; set; }

            public int CategoryId { get; set; }
            public virtual Category Category { get; set; }
        }
    }
  • 類別目錄 類別加入至專案。Add a Category class to the project.
  • 以下列程式碼取代預設產生的程式碼:Replace the code generated by default with the following code:
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;

    namespace WinFormswithEFSample
    {
        public class Category
        {
            private readonly ObservableListSource<Product> _products =
                    new ObservableListSource<Product>();

            public int CategoryId { get; set; }
            public string Name { get; set; }
            public virtual ObservableListSource<Product> Products { get { return _products; } }
        }
    }

除了定義實體之外,您還必須定義衍生自DbCoNtext的類別,並公開**DbSet < TEntity > **屬性。In addition to defining entities, you need to define a class that derives from DbContext and exposes DbSet<TEntity> properties. DbSet屬性可讓內容知道您想要包含在模型中的類型。The DbSet properties let the context know which types you want to include in the model. DbCoNtextDbSet類型是在 EntityFramework 元件中定義。The DbContext and DbSet types are defined in the EntityFramework assembly.

DbCoNtext 衍生類型的實例會在運行時間管理實體物件,包括將資料庫的資料填入物件、變更追蹤,以及將資料保存到資料庫。An instance of the DbContext derived type manages the entity objects during run time, which includes populating objects with data from a database, change tracking, and persisting data to the database.

  • 將新的 ProductCoNtext 類別新增至專案。Add a new ProductContext class to the project.
  • 以下列程式碼取代預設產生的程式碼:Replace the code generated by default with the following code:
    using System;
    using System.Collections.Generic;
    using System.Data.Entity;
    using System.Linq;
    using System.Text;

    namespace WinFormswithEFSample
    {
        public class ProductContext : DbContext
        {
            public DbSet<Category> Categories { get; set; }
            public DbSet<Product> Products { get; set; }
        }
    }

編譯專案。Compile the project.

選項2:使用 Database First 定義模型Option 2: Define a model using Database First

本節說明如何使用 EF designer,從資料庫使用 Database First 對模型進行反向工程。This section shows how to use Database First to reverse engineer your model from a database using the EF designer. 如果您已完成上一節 (**選項1:使用 Code First) 來定義模型 **,請略過本節,並直接移至消極式 載入 區段。If you completed the previous section (Option 1: Define a model using Code First), then skip this section and go straight to the Lazy Loading section.

建立現有的資料庫Create an Existing Database

通常當您將目標設為現有的資料庫時,就會建立該資料庫,但在此逐步解說中,我們需要建立要存取的資料庫。Typically when you are targeting an existing database it will already be created, but for this walkthrough we need to create a database to access.

隨 Visual Studio 安裝的資料庫伺服器會根據您所安裝的 Visual Studio 版本而有所不同:The database server that is installed with Visual Studio is different depending on the version of Visual Studio you have installed:

  • 如果您使用 Visual Studio 2010,將會建立 SQL Express 資料庫。If you are using Visual Studio 2010 you'll be creating a SQL Express database.
  • 如果您使用 Visual Studio 2012,則會建立 LocalDB 資料庫。If you are using Visual Studio 2012 then you'll be creating a LocalDB database.

讓我們繼續產生資料庫。Let's go ahead and generate the database.

  • View- > 伺服器總管View -> Server Explorer

  • 以滑鼠右鍵按一下 [資料連線- > 新增連接 ... ]Right click on Data Connections -> Add Connection…

  • 如果您還沒有從伺服器總管連接到資料庫,則必須選取 Microsoft SQL Server 作為資料來源If you haven’t connected to a database from Server Explorer before you’ll need to select Microsoft SQL Server as the data source

    變更資料來源

  • 連接至 LocalDB 或 SQL Express (視您安裝的版本而定),並輸入 產品 做為資料庫名稱Connect to either LocalDB or SQL Express, depending on which one you have installed, and enter Products as the database name

    加入連接 LocalDB

    新增連接快速

  • 選取 [確定] ,系統會詢問您是否要建立新的資料庫,請選取 [是]Select OK and you will be asked if you want to create a new database, select Yes

    建立資料庫

  • 新資料庫現在會出現在伺服器總管中,以滑鼠右鍵按一下該資料庫,然後選取 [追加查詢]。The new database will now appear in Server Explorer, right-click on it and select New Query

  • 將下列 SQL 複製到新的查詢,然後以滑鼠右鍵按一下查詢並選取 [執行]Copy the following SQL into the new query, then right-click on the query and select Execute

    CREATE TABLE [dbo].[Categories] (
        [CategoryId] [int] NOT NULL IDENTITY,
        [Name] [nvarchar](max),
        CONSTRAINT [PK_dbo.Categories] PRIMARY KEY ([CategoryId])
    )

    CREATE TABLE [dbo].[Products] (
        [ProductId] [int] NOT NULL IDENTITY,
        [Name] [nvarchar](max),
        [CategoryId] [int] NOT NULL,
        CONSTRAINT [PK_dbo.Products] PRIMARY KEY ([ProductId])
    )

    CREATE INDEX [IX_CategoryId] ON [dbo].[Products]([CategoryId])

    ALTER TABLE [dbo].[Products] ADD CONSTRAINT [FK_dbo.Products_dbo.Categories_CategoryId] FOREIGN KEY ([CategoryId]) REFERENCES [dbo].[Categories] ([CategoryId]) ON DELETE CASCADE

反向工程模型Reverse Engineer Model

我們將利用 Entity Framework Designer,其中包含做為 Visual Studio 的一部分,以建立我們的模型。We’re going to make use of Entity Framework Designer, which is included as part of Visual Studio, to create our model.

  • 專案- > 加入新專案 .。。Project -> Add New Item…

  • 從左側功能表中選取 [ 資料 ],然後 ADO.NET 實體資料模型Select Data from the left menu and then ADO.NET Entity Data Model

  • 輸入 ProductModel 做為名稱,然後按一下 [確定]Enter ProductModel as the name and click OK

  • 這會啟動 實體資料模型 WizardThis launches the Entity Data Model Wizard

  • 選取 [ 從資料庫產生 ],然後按 [下一步]Select Generate from Database and click Next

    選擇模型內容

  • 選取您在第一個區段中建立之資料庫的連接,輸入ProductCoNtext做為連接字串的名稱,然後按 [下一步]Select the connection to the database you created in the first section, enter ProductContext as the name of the connection string and click Next

    選擇您的連接

  • 按一下 [資料表] 旁的核取方塊以匯入所有資料表,然後按一下 [完成]Click the checkbox next to ‘Tables’ to import all tables and click ‘Finish’

    選擇您的物件

一旦反向工程程式完成,新的模型就會加入至您的專案並開啟,以供您在 Entity Framework Designer 中查看。Once the reverse engineer process completes the new model is added to your project and opened up for you to view in the Entity Framework Designer. 您的專案中也已加入 App.config 檔案,其中包含資料庫的連接詳細資料。An App.config file has also been added to your project with the connection details for the database.

Visual Studio 2010 中的其他步驟Additional Steps in Visual Studio 2010

如果您在 Visual Studio 2010 中工作,則必須更新 EF 設計工具,才能使用 EF6 程式碼產生。If you are working in Visual Studio 2010 then you will need to update the EF designer to use EF6 code generation.

  • 在 EF 設計工具中,以滑鼠右鍵按一下模型的空白位置,然後選取 [新增程式碼產生專案 ... ]。Right-click on an empty spot of your model in the EF Designer and select Add Code Generation Item…
  • 從左側功能表中選取 [ 線上範本 ],然後搜尋 DbCoNtextSelect Online Templates from the left menu and search for DbContext
  • 選取 適用于 C 的 EF 6.X DbCoNtext 產生器 # , 輸入 ProductsModel 做為名稱,然後按一下 [新增]。Select the EF 6.x DbContext Generator for C#, enter ProductsModel as the name and click Add

更新資料系結的程式碼產生Updating code generation for data binding

EF 會使用 T4 範本從您的模型產生程式碼。EF generates code from your model using T4 templates. 隨附于 Visual Studio 或從 Visual Studio 資源庫下載的範本,適用于一般用途。The templates shipped with Visual Studio or downloaded from the Visual Studio gallery are intended for general purpose use. 這表示,從這些範本產生的實體會有簡單的 ICollection < T > 屬性。This means that the entities generated from these templates have simple ICollection<T> properties. 不過,在進行資料系結時,最好具有可執行 IListSource 的集合屬性。However, when doing data binding it is desirable to have collection properties that implement IListSource. 這就是我們建立上述 ObservableListSource 類別的原因,現在我們要修改範本以利用這個類別。This is why we created the ObservableListSource class above and we are now going to modify the templates to make use of this class.

  • 開啟 方案總管 並尋找 ProductModel .edmx 檔案Open the Solution Explorer and find ProductModel.edmx file

  • 尋找 ProductModel.tt 檔案,該檔案將會嵌套在 ProductModel .edmx 檔下Find the ProductModel.tt file which will be nested under the ProductModel.edmx file

    產品型號範本

  • 按兩下 ProductModel.tt 檔案,在 Visual Studio 編輯器中開啟該檔案Double-click on the ProductModel.tt file to open it in the Visual Studio editor

  • 尋找並以 "ObservableListSource" 取代兩個出現的 "ICollection"。Find and replace the two occurrences of “ICollection” with “ObservableListSource”. 這些都位於大約行296和484。These are located at approximately lines 296 and 484.

  • 尋找並取代第一個出現的 "HashSet" 和 "ObservableListSource"。Find and replace the first occurrence of “HashSet” with “ObservableListSource”. 這次出現的位置大約是50行。This occurrence is located at approximately line 50. 請勿 取代稍後在程式碼中找到的第二次 HashSet。Do not replace the second occurrence of HashSet found later in the code.

  • 儲存 ProductModel.tt 檔案。Save the ProductModel.tt file. 這應該會導致重新產生實體的程式碼。This should cause the code for entities to be regenerated. 如果程式碼不會自動重新產生,請在 ProductModel.tt 上按一下滑鼠右鍵,然後選擇 [執行自訂工具]。If the code does not regenerate automatically, then right click on ProductModel.tt and choose “Run Custom Tool”.

如果您現在開啟 Category.cs 檔案 (該檔案在 ProductModel.tt) ,您應該會看到 Products 集合的類型為**ObservableListSource < Product > **。If you now open the Category.cs file (which is nested under ProductModel.tt) then you should see that the Products collection has the type ObservableListSource<Product>.

編譯專案。Compile the project.

消極式載入Lazy Loading

Product類別的Category類別和category屬性上的Products屬性為導覽屬性。The Products property on the Category class and Category property on the Product class are navigation properties. 在 Entity Framework 中,導覽屬性會提供一種方式來導覽兩個實體類型之間的關聯性。In Entity Framework, navigation properties provide a way to navigate a relationship between two entity types.

EF 讓您可以選擇在第一次存取導覽屬性時,自動從資料庫載入相關實體。EF gives you an option of loading related entities from the database automatically the first time you access the navigation property. 使用這種類型的載入 (稱為消極式載入) ,請注意,當您第一次存取每個導覽屬性時,如果內容中還沒有內容,則會對資料庫執行個別的查詢。With this type of loading (called lazy loading), be aware that the first time you access each navigation property a separate query will be executed against the database if the contents are not already in the context.

使用 POCO 實體類型時,EF 會在執行時間建立衍生 proxy 型別的實例,然後覆寫類別中的虛擬屬性以新增載入攔截,藉以達到消極式載入。When using POCO entity types, EF achieves lazy loading by creating instances of derived proxy types during runtime and then overriding virtual properties in your classes to add the loading hook. 若要取得相關物件的消極式載入,您必須在 Visual Basic) 中將導覽屬性 getter 宣告為 publicvirtual (可覆 ,而且您的類別不得在 Visual Basic) 中 密封 (NotOverridableTo get lazy loading of related objects, you must declare navigation property getters as public and virtual (Overridable in Visual Basic), and you class must not be sealed (NotOverridable in Visual Basic). 使用 Database First 導覽屬性會自動設為虛擬,以啟用消極式載入。When using Database First navigation properties are automatically made virtual to enable lazy loading. 在 [Code First] 區段中,我們選擇讓導覽屬性基於相同的原因進行虛擬In the Code First section we chose to make the navigation properties virtual for the same reason

將物件系結至控制項Bind Object to Controls

將模型中定義的類別新增為此 WinForms 應用程式的資料來源。Add the classes that are defined in the model as data sources for this WinForms application.

  • 從主功能表選取 [專案- > 加入新的資料來源...]From the main menu, select Project -> Add New Data Source … (在 Visual Studio 2010 中,您必須選取 [ 資料- > 新增資料來源 ]) (in Visual Studio 2010, you need to select Data -> Add New Data Source…)

  • 在 [選擇資料來源類型] 視窗中選取 [物件],然後按 [下一步]In the Choose a Data Source Type window, select Object and click Next

  • 在 [選取資料物件] 對話方塊中,將 WinFormswithEFSample 展開兩次,然後選取 [ 類別 ],就不需要選取 [產品] 資料來源,因為我們會透過類別資料來源上的產品屬性來取得它。In the Select the Data Objects dialog, unfold the WinFormswithEFSample two times and select Category There is no need to select the Product data source, because we will get to it through the Product’s property on the Category data source.

    資料來源

  • 按一下 [完成] 。Click Finish. 如果 [資料來源] 視窗未顯示,請選取 [ View- > 其他 Windows- > 資料來源]。If the Data Sources window is not showing up, select View -> Other Windows-> Data Sources

  • 按下釘選圖示,讓 [資料來源] 視窗不會自動隱藏。Press the pin icon, so the Data Sources window does not auto hide. 如果視窗已顯示,您可能需要按 [重新整理] 按鈕。You may need to hit the refresh button if the window was already visible.

    資料來源2

  • 在方案總管中,按兩下 Form1.cs 檔案,以在 [設計師] 中開啟主表單。In Solution Explorer, double-click the Form1.cs file to open the main form in designer.

  • 選取 [ 類別 ] 資料來源,然後將它拖曳到表單上。Select the Category data source and drag it on the form. 預設會將新的 DataGridView (categoryDataGridView) 和導覽工具列控制項新增至設計工具。By default, a new DataGridView (categoryDataGridView) and Navigation toolbar controls are added to the designer. 這些控制項系結至 BindingSource (categoryBindingSource) 和系結導覽器 (categoryBindingNavigator) 元件也會建立。These controls are bound to the BindingSource (categoryBindingSource) and Binding Navigator (categoryBindingNavigator) components that are created as well.

  • 編輯 categoryDataGridView上的資料行。Edit the columns on the categoryDataGridView. 我們想要將 [ 類別類別 ] 資料行設定為唯讀。We want to set the CategoryId column to read-only. 在儲存資料之後,資料庫會產生 [ 類別類別 ] 屬性的值。The value for the CategoryId property is generated by the database after we save the data.

    • 以滑鼠右鍵按一下 DataGridView 控制項,然後選取 [編輯資料行]。Right-click the DataGridView control and select Edit Columns…
    • 選取 [類別類別] 資料行,並將 ReadOnly 設定為 TrueSelect the CategoryId column and set ReadOnly to True
    • 按下 [確定]Press OK
  • 從 [類別目錄] 資料來源底下選取 [產品],然後將它拖曳到表單上。Select Products from under the Category data source and drag it on the form. ProductDataGridView 和 productBindingSource 會新增到表單中。The productDataGridView and productBindingSource are added to the form.

  • 編輯 productDataGridView 上的資料行。Edit the columns on the productDataGridView. 我們想要隱藏 [類別目錄] 和 [類別] 資料行,並將 [ProductId] 設定為唯讀。We want to hide the CategoryId and Category columns and set ProductId to read-only. 在儲存資料之後,資料庫會產生 ProductId 屬性的值。The value for the ProductId property is generated by the database after we save the data.

    • 以滑鼠右鍵按一下 DataGridView 控制項,然後選取 [ 編輯資料行]。Right-click the DataGridView control and select Edit Columns....
    • 選取 [ ProductId ] 資料行,並將 ReadOnly 設定為 TrueSelect the ProductId column and set ReadOnly to True.
    • 選取 [ 類別類別 ] 資料行,然後按 [ 移除 ] 按鈕。Select the CategoryId column and press the Remove button. 請使用 [ 類別 ] 資料行來進行相同的動作。Do the same with the Category column.
    • 按下 [確定]Press OK.

    到目前為止,我們在設計工具中將 DataGridView 控制項與 BindingSource 元件相關聯。So far, we associated our DataGridView controls with BindingSource components in the designer. 在下一節中,我們會將程式碼加入至程式碼後後方,以將 categoryBindingSource 設定為目前由 DbCoNtext 追蹤的實體集合。In the next section we will add code to the code behind to set categoryBindingSource.DataSource to the collection of entities that are currently tracked by DbContext. 當我們從類別下拖放產品時,WinForms 會負責設定 productsBindingSource 的資料,將 categoryBindingSource 和 productsBindingSource 屬性設定為 Products。When we dragged-and-dropped Products from under the Category, the WinForms took care of setting up the productsBindingSource.DataSource property to categoryBindingSource and productsBindingSource.DataMember property to Products. 由於這個系結,因此只有屬於目前所選類別的產品會顯示在 productDataGridView 中。Because of this binding, only the products that belong to the currently selected Category will be displayed in the productDataGridView.

  • 按一下滑鼠右鍵並選取 [已啟用],以啟用導覽工具列上的 [儲存] 按鈕。Enable the Save button on the Navigation toolbar by clicking the right mouse button and selecting Enabled.

    表單1設計師

  • 按兩下按鈕,以新增 [儲存] 按鈕的事件處理常式。Add the event handler for the save button by double-clicking on the button. 這會加入事件處理常式,並帶您前往表單的程式碼後移。This will add the event handler and bring you to the code behind for the form. CategoryBindingNavigatorSaveItem _ Click事件處理常式的程式碼將會在下一節中新增。The code for the categoryBindingNavigatorSaveItem_Click event handler will be added in the next section.

新增處理資料互動的程式碼Add the Code that Handles Data Interaction

我們現在會新增程式碼,以使用 ProductCoNtext 來執行資料存取。We'll now add the code to use the ProductContext to perform data access. 更新主表單視窗的程式碼,如下所示。Update the code for the main form window as shown below.

此程式碼會宣告長時間執行的 ProductCoNtext 實例。The code declares a long-running instance of ProductContext. ProductCoNtext 物件是用來查詢資料庫,並將資料儲存至資料庫。The ProductContext object is used to query and save data to the database. 然後,會從覆寫的依序 onclosing 方法呼叫 ProductCoNtext 實例上的 Dispose ( # A1 方法。The Dispose() method on the ProductContext instance is then called from the overridden OnClosing method. 程式碼批註會提供程式碼用途的詳細資料。The code comments provide details about what the code does.

    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Data;
    using System.Drawing;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    using System.Windows.Forms;
    using System.Data.Entity;

    namespace WinFormswithEFSample
    {
        public partial class Form1 : Form
        {
            ProductContext _context;
            public Form1()
            {
                InitializeComponent();
            }

            protected override void OnLoad(EventArgs e)
            {
                base.OnLoad(e);
                _context = new ProductContext();

                // Call the Load method to get the data for the given DbSet
                // from the database.
                // The data is materialized as entities. The entities are managed by
                // the DbContext instance.
                _context.Categories.Load();

                // Bind the categoryBindingSource.DataSource to
                // all the Unchanged, Modified and Added Category objects that
                // are currently tracked by the DbContext.
                // Note that we need to call ToBindingList() on the
                // ObservableCollection<TEntity> returned by
                // the DbSet.Local property to get the BindingList<T>
                // in order to facilitate two-way binding in WinForms.
                this.categoryBindingSource.DataSource =
                    _context.Categories.Local.ToBindingList();
            }

            private void categoryBindingNavigatorSaveItem_Click(object sender, EventArgs e)
            {
                this.Validate();

                // Currently, the Entity Framework doesn’t mark the entities
                // that are removed from a navigation property (in our example the Products)
                // as deleted in the context.
                // The following code uses LINQ to Objects against the Local collection
                // to find all products and marks any that do not have
                // a Category reference as deleted.
                // The ToList call is required because otherwise
                // the collection will be modified
                // by the Remove call while it is being enumerated.
                // In most other situations you can do LINQ to Objects directly
                // against the Local property without using ToList first.
                foreach (var product in _context.Products.Local.ToList())
                {
                    if (product.Category == null)
                    {
                        _context.Products.Remove(product);
                    }
                }

                // Save the changes to the database.
                this._context.SaveChanges();

                // Refresh the controls to show the values         
                // that were generated by the database.
                this.categoryDataGridView.Refresh();
                this.productsDataGridView.Refresh();
            }

            protected override void OnClosing(CancelEventArgs e)
            {
                base.OnClosing(e);
                this._context.Dispose();
            }
        }
    }

測試 Windows Forms 應用程式Test the Windows Forms Application

  • 編譯並執行應用程式,您可以測試此功能。Compile and run the application and you can test out the functionality.

    儲存前的表單1

  • 儲存產生的金鑰儲存後,會顯示在畫面上。After saving the store generated keys are shown on the screen.

    儲存後的表單1

  • 如果您使用 Code First,您也會看到為您建立了 WinFormswithEFSample ProductCoNtext 資料庫。If you used Code First, then you will also see that a WinFormswithEFSample.ProductContext database is created for you.

    伺服器物件總管