共用方式為


建立資料存取層

作者 :Erik Reitan

本教學課程系列將教導您使用 ASP.NET 4.5 和 Microsoft Visual Studio Express 2013 for Web 建置 ASP.NET Web Forms應用程式的基本概念。 具有C# 原始程式碼的Visual Studio 2013專案隨附于本教學課程系列。

本教學課程說明如何使用 ASP.NET Web Forms 和 Entity Framework Code First,從資料庫建立、存取和檢閱資料。 本教學課程是以上一個教學課程「建立專案」為基礎,屬於 Wingtip Toy Store 教學課程系列。 完成本教學課程之後,您將會建置一組資料存取類別,這些類別位於專案的 Models 資料夾中。

您將學到什麼:

  • 如何建立資料模型。
  • 如何初始化和植入資料庫。
  • 如何更新和設定應用程式以支援資料庫。

這些是教學課程中引進的功能:

  • Entity Framework Code First
  • LocalDB
  • 資料註釋

建立資料模型

Entity Framework 是 ORM) 架構 (物件關聯式對應。 它可讓您使用關聯式資料做為物件,消除您通常需要撰寫的大部分資料存取程式碼。 使用 Entity Framework,您可以使用 LINQ發出查詢,然後將資料擷取並操作為強型別物件。 LINQ 提供查詢和更新資料的模式。 使用 Entity Framework 可讓您專注于建立應用程式的其餘部分,而不是專注于資料存取基本概念。 在本教學課程系列稍後,我們將示範如何使用資料來填入導覽和產品查詢。

Entity Framework 支援稱為 Code First的開發範例。 Code First 可讓您使用類別來定義資料模型。 「類別」是一種建構,可讓您將其他類型、方法和事件的變數群組在一起,以建立您自己的自訂類型。 您可以將類別對應至現有的資料庫,或使用它們來產生資料庫。 在本教學課程中,您將藉由撰寫資料模型類別來建立資料模型。 然後,您會讓 Entity Framework 從這些新類別即時建立資料庫。

首先,您會建立實體類別來定義Web Form應用程式的資料模型。 然後,您將建立可管理實體類別的內容類別別,並提供資料庫的資料存取權。 您也會建立初始化運算式類別,以用來填入資料庫。

Entity Framework 和參考

根據預設,當您使用 Web Form範本建立新的ASP.NET Web 應用程式時,會包含 Entity Framework。 Entity Framework 可以安裝、卸載及更新為 NuGet 套件。

此 NuGet 套件包含專案中的下列 執行時間 元件:

  • EntityFramework.dll – Entity Framework 所使用的所有通用執行時間程式碼
  • EntityFramework.SqlServer.dll – 適用于 Entity Framework 的 Microsoft SQL Server 提供者

實體類別

您建立以定義資料架構的類別稱為實體類別。 如果您不熟悉資料庫設計,請將實體類別視為資料庫的資料表定義。 類別中的每個屬性都會在資料庫的資料表中指定一個資料行。 這些類別提供物件導向程式碼與資料庫關聯式資料表結構之間的輕量型物件關聯式介面。

在本教學課程中,您將從新增代表產品和類別架構的簡單實體類別開始。 products 類別將包含每個產品的定義。 產品類別的每個成員名稱將會是 ProductIDProductNameDescriptionImagePathUnitPriceCategoryIDCategory 。 類別類別會包含產品可屬於的每個類別定義,例如汽車、船或平面。 類別類別的每個成員名稱會是 CategoryIDCategoryNameDescriptionProducts 。 每個產品都會屬於其中一個類別。 這些實體類別會新增至專案現有的 Models 資料夾。

  1. 方案總管中,以滑鼠右鍵按一下Models資料夾,然後選取 [新增- >新增專案]。

    [方案總管] 視窗的螢幕擷取畫面,其中已醒目提示 Models 資料夾,並已選取下拉式功能表 [新增] 和 [新增專案]。

    [ 加入新項目 ] 對話方塊隨即出現。

  2. 從左側 [已安裝] 窗格的[Visual C#] 底下,選取 [程式碼]。

    [新增專案] 視窗的螢幕擷取畫面,其中顯示左側已開啟 Visual C# 且已選取 [程式碼] 的 [已安裝] 窗格。

  3. 從中間窗格中選取 [類別 ],並將這個新類別命名為 Product.cs

  4. 按一下 [新增] 。
    新的類別檔案會顯示在編輯器中。

  5. 使用下列程式碼來取代預設程式碼:

    using System.ComponentModel.DataAnnotations;
    
    namespace WingtipToys.Models
    {
        public class Product
        {
            [ScaffoldColumn(false)]
            public int ProductID { get; set; }
    
            [Required, StringLength(100), Display(Name = "Name")]
            public string ProductName { get; set; }
    
            [Required, StringLength(10000), Display(Name = "Product Description"), DataType(DataType.MultilineText)]
            public string Description { get; set; }
    
            public string ImagePath { get; set; }
    
            [Display(Name = "Price")]
            public double? UnitPrice { get; set; }
    
            public int? CategoryID { get; set; }
    
            public virtual Category Category { get; set; }
        }
    }
    
  6. 不過,重複步驟 1 到 4 來建立另一個類別,將新的類別命名為 Category.cs ,並以下列程式碼取代預設程式碼:

    using System.Collections.Generic;
    using System.ComponentModel.DataAnnotations;
    
    namespace WingtipToys.Models
    {
        public class Category
        {
            [ScaffoldColumn(false)]
            public int CategoryID { get; set; }
    
            [Required, StringLength(100), Display(Name = "Name")]
            public string CategoryName { get; set; }
    
            [Display(Name = "Product Description")]
            public string Description { get; set; }
    
            public virtual ICollection<Product> Products { get; set; }
        }
    }
    

如先前所述,類別 Category 代表應用程式設計來銷售 (的產品類型,例如 「Cars」、「Toyss」、「Rockets」 等等) ,而 Product 類別則代表資料庫中 (玩具) 的個別產品。 物件的每個實例 Product 都會對應至關係資料庫資料表內的資料列,而 Product 類別的每個屬性都會對應至關係資料庫資料表中的資料行。 稍後在本教學課程中,您將檢閱資料庫中所包含的產品資料。

資料註釋

您可能已注意到,類別的某些成員具有指定成員詳細資料的屬性,例如 [ScaffoldColumn(false)] 。 這些是 資料批註。 資料批註屬性可以描述如何驗證該成員的使用者輸入、指定其格式,以及指定建立資料庫時模型化的方式。

Context 類別

若要開始使用類別進行資料存取,您必須定義內容類別別。 如先前所述,內容類別別會管理實體類別 (,例如 Product 類別和 Category 類別) 並提供資料庫的資料存取。

此程式會將新的 C# 內容類別別新增至 Models 資料夾。

  1. 以滑鼠右鍵按一下Models資料夾,然後選取 [新增- >新增專案]。
    [ 加入新項目 ] 對話方塊隨即出現。

  2. 從中間窗格中選取 [ 類別 ],將它命名為 ProductCoNtext.cs ,然後按一下 [ 新增]。

  3. 以下列程式碼取代 類別中包含的預設程式碼:

    using System.Data.Entity;
    namespace WingtipToys.Models
    {
        public class ProductContext : DbContext
        {
            public ProductContext() : base("WingtipToys")
            {
            }
            public DbSet<Category> Categories { get; set; }
            public DbSet<Product> Products { get; set; }
        }
    }
    

此程式碼會新增 System.Data.Entity 命名空間,讓您可以存取 Entity Framework 的所有核心功能,包括使用強型別物件來查詢、插入、更新和刪除資料的功能。

類別 ProductContext 代表 Entity Framework 產品資料庫內容,可處理資料庫中的擷取、儲存和更新 Product 類別實例。 類別 ProductContext 衍生自 DbContext Entity Framework 所提供的基類。

Initializer 類別

您必須先執行一些自訂邏輯,才能在第一次使用內容時初始化資料庫。 這可讓種子資料新增至資料庫,以便您立即顯示產品和類別。

此程式會將新的 C# 初始化運算式類別新增至 Models 資料夾。

  1. 在 Models 資料夾中建立另一個 Class 並將它命名為 ProductDatabaseInitializer.cs

  2. 以下列程式碼取代 類別中包含的預設程式碼:

    using System.Collections.Generic;
    using System.Data.Entity;
    
    namespace WingtipToys.Models
    {
      public class ProductDatabaseInitializer : DropCreateDatabaseIfModelChanges<ProductContext>
      {
        protected override void Seed(ProductContext context)
        {
          GetCategories().ForEach(c => context.Categories.Add(c));
          GetProducts().ForEach(p => context.Products.Add(p));
        }
    
        private static List<Category> GetCategories()
        {
          var categories = new List<Category> {
                    new Category
                    {
                        CategoryID = 1,
                        CategoryName = "Cars"
                    },
                    new Category
                    {
                        CategoryID = 2,
                        CategoryName = "Planes"
                    },
                    new Category
                    {
                        CategoryID = 3,
                        CategoryName = "Trucks"
                    },
                    new Category
                    {
                        CategoryID = 4,
                        CategoryName = "Boats"
                    },
                    new Category
                    {
                        CategoryID = 5,
                        CategoryName = "Rockets"
                    },
                };
    
          return categories;
        }
    
        private static List<Product> GetProducts()
        {
          var products = new List<Product> {
                    new Product
                    {
                        ProductID = 1,
                        ProductName = "Convertible Car",
                        Description = "This convertible car is fast! The engine is powered by a neutrino based battery (not included)." + 
                                      "Power it up and let it go!", 
                        ImagePath="carconvert.png",
                        UnitPrice = 22.50,
                        CategoryID = 1
                   },
                    new Product 
                    {
                        ProductID = 2,
                        ProductName = "Old-time Car",
                        Description = "There's nothing old about this toy car, except it's looks. Compatible with other old toy cars.",
                        ImagePath="carearly.png",
                        UnitPrice = 15.95,
                         CategoryID = 1
                   },
                    new Product
                    {
                        ProductID = 3,
                        ProductName = "Fast Car",
                        Description = "Yes this car is fast, but it also floats in water.",
                        ImagePath="carfast.png",
                        UnitPrice = 32.99,
                        CategoryID = 1
                    },
                    new Product
                    {
                        ProductID = 4,
                        ProductName = "Super Fast Car",
                        Description = "Use this super fast car to entertain guests. Lights and doors work!",
                        ImagePath="carfaster.png",
                        UnitPrice = 8.95,
                        CategoryID = 1
                    },
                    new Product
                    {
                        ProductID = 5,
                        ProductName = "Old Style Racer",
                        Description = "This old style racer can fly (with user assistance). Gravity controls flight duration." + 
                                      "No batteries required.",
                        ImagePath="carracer.png",
                        UnitPrice = 34.95,
                        CategoryID = 1
                    },
                    new Product
                    {
                        ProductID = 6,
                        ProductName = "Ace Plane",
                        Description = "Authentic airplane toy. Features realistic color and details.",
                        ImagePath="planeace.png",
                        UnitPrice = 95.00,
                        CategoryID = 2
                    },
                    new Product
                    {
                        ProductID = 7,
                        ProductName = "Glider",
                        Description = "This fun glider is made from real balsa wood. Some assembly required.",
                        ImagePath="planeglider.png",
                        UnitPrice = 4.95,
                        CategoryID = 2
                    },
                    new Product
                    {
                        ProductID = 8,
                        ProductName = "Paper Plane",
                        Description = "This paper plane is like no other paper plane. Some folding required.",
                        ImagePath="planepaper.png",
                        UnitPrice = 2.95,
                        CategoryID = 2
                    },
                    new Product
                    {
                        ProductID = 9,
                        ProductName = "Propeller Plane",
                        Description = "Rubber band powered plane features two wheels.",
                        ImagePath="planeprop.png",
                        UnitPrice = 32.95,
                        CategoryID = 2
                    },
                    new Product
                    {
                        ProductID = 10,
                        ProductName = "Early Truck",
                        Description = "This toy truck has a real gas powered engine. Requires regular tune ups.",
                        ImagePath="truckearly.png",
                        UnitPrice = 15.00,
                        CategoryID = 3
                    },
                    new Product
                    {
                        ProductID = 11,
                        ProductName = "Fire Truck",
                        Description = "You will have endless fun with this one quarter sized fire truck.",
                        ImagePath="truckfire.png",
                        UnitPrice = 26.00,
                        CategoryID = 3
                    },
                    new Product
                    {
                        ProductID = 12,
                        ProductName = "Big Truck",
                        Description = "This fun toy truck can be used to tow other trucks that are not as big.",
                        ImagePath="truckbig.png",
                        UnitPrice = 29.00,
                        CategoryID = 3
                    },
                    new Product
                    {
                        ProductID = 13,
                        ProductName = "Big Ship",
                        Description = "Is it a boat or a ship. Let this floating vehicle decide by using its " + 
                                      "artifically intelligent computer brain!",
                        ImagePath="boatbig.png",
                        UnitPrice = 95.00,
                        CategoryID = 4
                    },
                    new Product
                    {
                        ProductID = 14,
                        ProductName = "Paper Boat",
                        Description = "Floating fun for all! This toy boat can be assembled in seconds. Floats for minutes!" + 
                                      "Some folding required.",
                        ImagePath="boatpaper.png",
                        UnitPrice = 4.95,
                        CategoryID = 4
                    },
                    new Product
                    {
                        ProductID = 15,
                        ProductName = "Sail Boat",
                        Description = "Put this fun toy sail boat in the water and let it go!",
                        ImagePath="boatsail.png",
                        UnitPrice = 42.95,
                        CategoryID = 4
                    },
                    new Product
                    {
                        ProductID = 16,
                        ProductName = "Rocket",
                        Description = "This fun rocket will travel up to a height of 200 feet.",
                        ImagePath="rocket.png",
                        UnitPrice = 122.95,
                        CategoryID = 5
                    }
                };
    
          return products;
        }
      }
    }
    

如您在上述程式碼中所見,建立和初始化資料庫時, Seed 會覆寫並設定 屬性。 Seed設定 屬性時,會使用類別和產品中的值來填入資料庫。 如果您在建立資料庫之後修改上述程式碼來嘗試更新種子資料,當您執行 Web 應用程式時,將不會看到任何更新。 原因是上述程式碼會使用 類別的實作 DropCreateDatabaseIfModelChanges 來辨識模型 (架構) 在重設種子資料之前是否已變更。 如果未對 和 Product 實體類別進行任何 Category 變更,則不會使用種子資料重新初始化資料庫。

注意

如果您想要每次執行應用程式時重新建立資料庫,您可以使用 DropCreateDatabaseAlways 類別,而不是 DropCreateDatabaseIfModelChanges 類別。 不過,在本教學課程系列中,請使用 類別 DropCreateDatabaseIfModelChanges

在本教學課程中,您會有一個 Models 資料夾,其中包含四個新類別和一個預設類別:

建立資料存取層 - Models 資料夾

將應用程式設定為使用資料模型

既然您已建立代表資料的類別,您必須將應用程式設定為使用類別。 在 Global.asax 檔案中,您會新增可初始化模型的程式碼。 在 Web.config 檔案中,您會新增資訊,告知應用程式您將用來儲存新資料類別所代表的資料。 Global.asax檔案可用來處理應用程式事件或方法。 Web.config檔案可讓您控制 ASP.NET Web 應用程式的組態。

更新 Global.asax 檔案

若要在應用程式啟動時初始化資料模型,您將更新 Application_StartGlobal.asax.cs 檔案中的處理常式。

注意

在 方案總管中,您可以選取Global.asax 檔案或 Global.asax.cs檔案,以編輯Global.asax.cs檔案。

  1. 將下列以黃色醒目提示的程式碼新增至 Application_StartGlobal.asax.cs 檔案中的 方法。

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Web;
    using System.Web.Optimization;
    using System.Web.Routing;
    using System.Web.Security;
    using System.Web.SessionState;
    using System.Data.Entity;
    using WingtipToys.Models;
    
    namespace WingtipToys
    {
        public class Global : HttpApplication
        {
            void Application_Start(object sender, EventArgs e)
            {
                // Code that runs on application startup
                RouteConfig.RegisterRoutes(RouteTable.Routes);
                BundleConfig.RegisterBundles(BundleTable.Bundles);
    
                // Initialize the product database.
                Database.SetInitializer(new ProductDatabaseInitializer());
            }
        }
    }
    

注意

瀏覽器必須支援 HTML5,才能在瀏覽器中檢視本教學課程系列時,以黃色醒目提示的程式碼。

如上述程式碼所示,當應用程式啟動時,應用程式會指定在第一次存取資料時將執行的初始化運算式。 需要兩個額外的命名空間才能存取 Database 物件和 ProductDatabaseInitializer 物件。

修改Web.Config檔案

雖然 Entity Framework Code First 會在資料庫填入種子資料時,在預設位置為您產生資料庫,但將您自己的連接資訊新增至應用程式可讓您控制資料庫位置。 您可以在專案的根目錄中,使用應用程式 Web.config 檔案中的連接字串來指定此資料庫連線。 藉由新增連接字串,您可以將資料庫的位置導向 (wingtiptoys.mdf) ,以在應用程式的資料目錄中建置 (App_Data) ,而不是預設位置。 進行這項變更可讓您在本教學課程稍後尋找及檢查資料庫檔案。

  1. 方案總管中,尋找並開啟Web.config檔案。

  2. 將醒目提示的連接字串新增至 <connectionStrings>Web.config 檔案的 區段,如下所示:

    <connectionStrings>
    <add name="DefaultConnection" connectionString="Data Source=(LocalDb)\MSSQLLocalDB;AttachDbFilename=|DataDirectory|\aspnet-WingtipToys-20131119102907.mdf;Initial Catalog=aspnet-WingtipToys-20131119102907;Integrated Security=True"
    providerName="System.Data.SqlClient" />
    <add name="WingtipToys"
    connectionString="Data Source=(LocalDB)\MSSQLLocalDB;AttachDbFilename=|DataDirectory|\wingtiptoys.mdf;Integrated Security=True"
    providerName="System.Data.SqlClient" />
    </connectionStrings>
    

第一次執行應用程式時,它會在連接字串所指定的位置建置資料庫。 但在執行應用程式之前,讓我們先建置它。

建置應用程式

若要確定 Web 應用程式的所有類別和變更都能運作,您應該建置應用程式。

  1. 從 [ 偵錯] 功能表中,選取 [建置 WingtipToys]。
    [ 輸出] 視窗隨即顯示,如果一切順利,您會看到 成功的 訊息。

    建立資料存取層 - 輸出視窗

如果您遇到錯誤,請重新檢查上述步驟。 [ 輸出] 視窗中的資訊會指出哪些檔案有問題,以及檔案中需要變更的位置。 這項資訊可讓您判斷在專案中檢閱和修正上述步驟的哪些部分。

總結

在本系列教學課程中,您已建立資料模型,以及新增將用來初始化和植入資料庫的程式碼。 您也已將應用程式設定為在執行應用程式時使用資料模型。

在下一個教學課程中,您將更新 UI、新增流覽,以及從資料庫擷取資料。 這會導致資料庫根據您在本教學課程中建立的實體類別自動建立。

其他資源

Entity Framework 概觀
ADO.NET Entity Framework 的初學者指南
使用 Entity Framework Code First 關聯性 Fluent API進行程式碼優先開發
Code First 資料註解
Entity Framework 的生產力改善