データ アクセス層の作成

作成者 : 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 Forms アプリケーションのデータ モデルを定義するエンティティ クラスを作成します。 次に、エンティティ クラスを管理し、データベースへのデータ アクセスを提供するコンテキスト クラスを作成します。 また、データベースの設定に使用する初期化子クラスも作成します。

Entity Framework と参照

既定では、Web Forms テンプレートを使用して新しい ASP.NET Web アプリケーションを作成するときに Entity Framework 含まれます。 Entity Framework は、NuGet パッケージとしてインストール、アンインストール、および更新できます。

この NuGet パッケージには、プロジェクト内に次の ランタイム アセンブリが含まれています。

  • EntityFramework.dll – Entity Framework で使用されるすべての一般的なランタイム コード
  • EntityFramework.SqlServer.dll – Entity Framework の Microsoft SQL Server プロバイダー

エンティティ クラス

データのスキーマを定義するために作成するクラスは、エンティティ クラスと呼ばれます。 データベースの設計を初めて使用する場合は、エンティティ クラスをデータベースのテーブル定義と考えてください。 クラスの各プロパティは、データベースのテーブル内の列を指定します。 これらのクラスは、オブジェクト指向コードとデータベースのリレーショナル テーブル構造の間に、軽量のオブジェクト リレーショナル インターフェイスを提供します。

このチュートリアルでは、まず、製品とカテゴリのスキーマを表す単純なエンティティ クラスを追加します。 products クラスには、各製品の定義が含まれます。 製品クラスの各メンバーの名前は、、、ProductNameDescriptionImagePathUnitPriceCategoryIDおよび CategoryになりますProductID。 カテゴリ クラスには、製品が属できる各カテゴリ (Car、Boat、Plane など) の定義が含まれます。 カテゴリ クラスの各メンバーの名前は、、CategoryNameDescription、および ProductsになりますCategoryID。 各製品は、カテゴリのいずれかに属します。 これらのエンティティ クラスは、プロジェクトの既存の Models フォルダーに追加されます。

  1. ソリューション エクスプローラーで、[モデル] フォルダーを右クリックし、[追加] ->[新しい項目] の順に選択します。

    [モデル] フォルダーが強調表示され、ドロップダウン メニューの [追加] と [新しい項目] が選択されている [ソリューション エクスプローラー] ウィンドウのスクリーンショット。

    [新しい項目の追加] ダイアログ ボックスが表示されます。

  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"、"Boats"、"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 、Entity Framework によって提供される基底クラスから DbContext 派生します。

初期化子クラス

コンテキストが初めて使用されるときにデータベースを初期化するには、いくつかのカスタム ロジックを実行する必要があります。 これにより、シード データをデータベースに追加して、製品とカテゴリをすぐに表示できるようになります。

この手順では、新しい 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変更が加えられない場合、データベースはシード データで再初期化されません。

Note

アプリケーションを実行するたびにデータベースを再作成する場合は、 クラスではなく クラスをDropCreateDatabaseAlwaysDropCreateDatabaseIfModelChanges使用できます。 ただし、このチュートリアル シリーズでは、 クラスを使用します DropCreateDatabaseIfModelChanges

このチュートリアルのこの時点で、4 つの新しいクラスと 1 つの既定のクラスを含む Models フォルダーがあります。

データ アクセス層の作成 - Models フォルダー

データ モデルを使用するようにアプリケーションを構成する

データを表すクラスを作成したので、クラスを使用するようにアプリケーションを構成する必要があります。 Global.asax ファイルで、モデルを初期化するコードを追加します。 Web.config ファイルに、新しいデータ クラスによって表されるデータを格納するために使用するデータベースをアプリケーションに通知する情報を追加します。 Global.asax ファイルを使用して、アプリケーション イベントまたはメソッドを処理できます。 Web.config ファイルを使用すると、ASP.NET Web アプリケーションの構成を制御できます。

Global.asax ファイルの更新

アプリケーションの起動時にデータ モデルを初期化するには、Global.asax.cs ファイル内のハンドラーを更新Application_Startします。

Note

ソリューション エクスプローラーでは、Global.asax ファイルまたは Global.asax.cs ファイルのいずれかを選択して、Global.asax.cs ファイルを編集できます。

  1. Global.asax.cs ファイルの メソッドにApplication_Start黄色で強調表示された次のコードを追加します。

    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());
            }
        }
    }
    

Note

ブラウザーでこのチュートリアル シリーズを表示するときに、黄色で強調表示されたコードを表示するには、ブラウザーで HTML5 をサポートする必要があります。

上記のコードに示すように、アプリケーションの起動時に、アプリケーションはデータに初めてアクセスするときに実行される初期化子を指定します。 オブジェクトと オブジェクトにアクセスするには、2 つの追加の 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 FirstRelationships Fluent API を使用したコードファースト開発
Code First Data Annotations (Code First のデータ注釈)
Entity Framework の生産性の向上