ASP.NET MVC アプリケーション用の Entity Framework データ モデルの作成 (1/10)

著者: Tom Dykstra

Note

Visual Studio 2013、Entity Framework 6、MVC 5 用には、このチュートリアル シリーズのより新しいバージョンが利用できます。

Contoso University のサンプル Web アプリケーションでは、Entity Framework 5 と Visual Studio 2012 を使用して ASP.NET MVC 4 アプリケーションを作成する方法を示します。 サンプル アプリケーションは架空の Contoso University の Web サイトです。 学生の受け付け、講座の作成、講師の割り当てなどの機能が含まれています。 このチュートリアル シリーズでは、Contoso University サンプル アプリケーションをビルドする方法について説明します。

Code First

Entity Framework では、Database FirstModel FirstCode Firstの 3 つの方法でデータを操作できます。 このチュートリアルは Code First 用です。 これらのワークフローの違いと、シナリオに最適なものを選択する方法に関するガイダンスについては、「Entity Framework 開発ワークフロー」を参照してください。

MVC

サンプル アプリケーションは、ASP.NET MVC 上に構築されています。 ASP.NET Web Forms モデルを使用する場合は、「モデル バインドと Web Forms」チュートリアル シリーズと「ASP.NET データ アクセス コンテンツ マップ」を参照してください。

ソフトウェア バージョン

チュートリアルで説明 以下でも動作可
Windows 8 Windows 7
Visual Studio 2012 Visual Studio 2012 Express for Web。 VS 2012 または VS 2012 Express for Web がない場合は、Windows Azure SDK によって自動的にインストールされます。 Visual Studio 2013 は動作するはずですが、このチュートリアルではテストされていません。また、一部のメニュー選択とダイアログ ボックスが異なります。 Windows Azure のデプロイには、VS 2013 バージョンの Windows Azure SDK が必要です。
.NET 4.5 表示される機能のほとんどは .NET 4 で動作しますが、機能しないものもあります。 たとえば、EF での列挙型のサポートには .NET 4.5 が必要です。
Entity Framework 5
Windows Azure SDK 2.1 Windows Azure のデプロイ手順をスキップする場合は、SDK は必要ありません。 新しいバージョンの SDK がリリースされると、リンクによって新しいバージョンがインストールされます。 その場合は、一部の手順を新しい UI と機能に合わせることが必要な場合があります。

質問

チュートリアルに直接関連しない質問がある場合は、ASP.NET Entity Framework フォーラムEntity Framework および LINQ to Entities フォーラム、または StackOverflow.com に投稿できます。

謝辞

謝辞と VB に関するメモについては、シリーズの最後のチュートリアルを参照してください。

Contoso University Web アプリケーション

一連のチュートリアルで作成するアプリケーションは、簡単な大学向け Web サイトです。

ユーザーは学生、講座、講師の情報を見たり、更新したりできます。 次のような画面をこれから作成します。

Students_Index_page

サンプルの Contoso University Web アプリケーションの [学生] 検索ページと [新しい学生の作成] ページを示すスクリーンショット。

このサイトの UI スタイルは、組み込みテンプレートで生成されるスタイルに近いものになっています。それにより、このチュートリアルでは主に、Entity Framework の使い方を取り上げることができます。

前提条件

このチュートリアルの指示とスクリーン ショットでは、2013 年 7 月の時点で最新の更新プログラムと Azure SDK for .NET がインストールされた Visual Studio 2012 または Visual Studio 2012 Express for Web を使用していることを前提としています。 これらすべては、次のリンクから取得できます。

Azure SDK for .NET (Visual Studio 2012)

Visual Studio がインストールされている場合は、上記のリンクでは不足しているコンポーネントがインストールされます。 Visual Studio をお持ちでない場合は、リンクから Visual Studio 2012 Express for Web がインストールされます。 Visual Studio 2013 は使用できますが、必要な手順と画面の一部が異なります。

MVC Web アプリケーションを作成する

Visual Studio を開き、ASP.NET MVC 4 Web アプリケーション テンプレートを使用して、"ContosoUniversity" という名前の新しい C# プロジェクトを作成します。 .NET Framework 4.5 をターゲットにしていることを確認します (enum プロパティを使用します。それには .NET 4.5 が必要です)。

New_project_dialog_box

[新しい ASP.NET MVC 4 プロジェクト] ダイアログ ボックスで、インターネット アプリケーション テンプレートを 選択します。

Razor ビュー エンジンは選択したままにし、[単体テスト プロジェクト作成] チェック ボックスはオフのままにします。

OK をクリックします。

Project_template_options

サイトのスタイルを設定する

簡単な変更をいくつか行い、サイトのメニュー、レイアウト、ホーム ページを決めます。

Views\Shared\_Layout.cshtml を開き、ファイルの内容を次のコードに置き換えます。 変更が強調表示されます。

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="utf-8" />
        <title>@ViewBag.Title - Contoso University</title>
        <link href="~/favicon.ico" rel="shortcut icon" type="image/x-icon" />
        <meta name="viewport" content="width=device-width" />
        @Styles.Render("~/Content/css")
        @Scripts.Render("~/bundles/modernizr")
    </head>
    <body>
        <header>
            <div class="content-wrapper">
                <div class="float-left">
                    <p class="site-title">@Html.ActionLink("Contoso University", "Index", "Home")</p>
                </div>
                <div class="float-right">
                    <section id="login">
                        @Html.Partial("_LoginPartial")
                    </section>
                    <nav>
                        <ul id="menu">
                            <li>@Html.ActionLink("Home", "Index", "Home")</li>
                            <li>@Html.ActionLink("About", "About", "Home")</li>
                            <li>@Html.ActionLink("Students", "Index", "Student")</li>
                            <li>@Html.ActionLink("Courses", "Index", "Course")</li>
                            <li>@Html.ActionLink("Instructors", "Index", "Instructor")</li>
                            <li>@Html.ActionLink("Departments", "Index", "Department")</li>
                        </ul>
                    </nav>
                </div>
            </div>
        </header>
        <div id="body">
            @RenderSection("featured", required: false)
            <section class="content-wrapper main-content clear-fix">
                @RenderBody()
            </section>
        </div>
        <footer>
            <div class="content-wrapper">
                <div class="float-left">
                    <p>&copy; @DateTime.Now.Year - Contoso University</p>
                </div>
            </div>
        </footer>

        @Scripts.Render("~/bundles/jquery")
        @RenderSection("scripts", required: false)
    </body>
</html>

このコードにより、次の変更が行われます。

  • "My ASP.NET MVC Application" と "your logo here" のテンプレート インスタンスを "Contoso University" に置き換えます。
  • チュートリアルの後半で使用するアクション リンクをいくつか追加します。

Views\Home\Index.cshtml で、ファイルの内容を次のコードに置き換えて、ASP.NET と MVC に関するテンプレート段落を削除します。

@{
    ViewBag.Title = "Home Page";
}
@section featured {
    <section class="featured">
        <div class="content-wrapper">
            <hgroup class="title">
                <h1>@ViewBag.Title.</h1>
                <h2>@ViewBag.Message</h2>
            </hgroup>
        </div>
    </section>
}

Controllers\HomeController.csで、次の例に示すように、Index Action メソッドの ViewBag.Message の値を "Welcome to Contoso University!" に変更します。

public ActionResult Index()
{
    ViewBag.Message = "Welcome to Contoso University";

    return View();
}

Ctrl キーを押しながら F5 キーを押してサイトを実行します。 メイン メニューが表示されたホーム ページが表示されます。

Contoso_University_home_page

データ モデルを作成する

次に、Contoso University アプリケーションのエンティティ クラスを作成します。 次の 3 つのエンティティから始めます。

Class_diagram

Student エンティティと Enrollment エンティティの間に一対多の関係があり、Course エンティティと Enrollment エンティティの間に一対多の関係があります。 言い換えると、1 人の学生をさまざまな講座に登録し、1 つの講座にたくさんの学生を登録できます。

次のセクションでは、エンティティごとにクラスを作成します。

Note

これらのエンティティ クラスの作成を完了する前にプロジェクトをコンパイルしようとすると、コンパイラ エラーが発生します。

Student エンティティ

Student_entity

[Models] フォルダーで、Course.cs を作成し、既存のコードを次のコードに変更します。

using System;
using System.Collections.Generic;

namespace ContosoUniversity.Models
{
    public class Student
    {
        public int StudentID { get; set; }
        public string LastName { get; set; }
        public string FirstMidName { get; set; }
        public DateTime EnrollmentDate { get; set; }
        
        public virtual ICollection<Enrollment> Enrollments { get; set; }
    }
}

StudentID プロパティは、このクラスに相当するデータベース テーブルの主キー列になります。 既定では、Entity Framework は、 ID または classname ID という名前のプロパティを主キーとして解釈します。

Enrollments プロパティはナビゲーション プロパティです。 ナビゲーション プロパティには、このエンティティに関連する他のエンティティが含まれます。 この例では、Student エンティティの Enrollments プロパティに、その Student エンティティに関連するすべての Enrollment エンティティが保持されます。 つまり、データベース内の特定の Student 行に 関連する Enrollment 行 (StudentID 外部キー列にその学生の主キー値を含む行) が2 つある場合、その Student エンティティの Enrollments ナビゲーション プロパティには、その 2 つの Enrollment エンティティが含まれます。

ナビゲーション プロパティは、通常、"遅延読み込み" などの特定の Entity Framework 機能を利用できるように、virtual として定義されます。 (遅延読み込みについては、このシリーズの後半の「関連データの読み取り」チュートリアルで説明します。

ナビゲーション プロパティに複数のエンティティが含まれる場合 (多対多または一対多の関係で)、その型はリストにする必要があります。ICollection のように、エンティティを追加、削除、更新できるリストです。

Enrollment エンティティ

Enrollment_entity

[Models] フォルダーで、Enrollment.cs を作成し、既存のコードを次のコードに変更します。

namespace ContosoUniversity.Models
{
    public enum Grade
    {
        A, B, C, D, F
    }

    public class Enrollment
    {
        public int EnrollmentID { get; set; }
        public int CourseID { get; set; }
        public int StudentID { get; set; }
        public Grade? Grade { get; set; }
        
        public virtual Course Course { get; set; }
        public virtual Student Student { get; set; }
    }
}

Grade プロパティは列挙型です。 Grade の型宣言の後の疑問符は、Grade プロパティが nullable であることを示します。 成績が null 値であることは、成績 0 とは異なります。null 値は成績がわからないか、まだ評価されていないことを意味します。

StudentID プロパティは外部キーです。それに対応するナビゲーション プロパティは Student です。 Enrollment エンティティは 1 つの Student エンティティに関連付けられており、1 つの Student エンティティだけを保持できます (先に見た、複数の Enrollment エンティティを保持できる Student.Enrollments ナビゲーション プロパティとは異なります)。

CourseID プロパティは外部キーです。それに対応するナビゲーション プロパティは Course です。 Enrollment エンティティは 1 つの Course エンティティに関連付けられます。

Course エンティティ

Course_entity

[Models] フォルダーで、Course.cs を作成し、既存のコードを次のコードに変更します。

using System.Collections.Generic;
using System.ComponentModel.DataAnnotations.Schema;

namespace ContosoUniversity.Models
{
    public class Course
    {
        [DatabaseGenerated(DatabaseGeneratedOption.None)]
        public int CourseID { get; set; }
        public string Title { get; set; }
        public int Credits { get; set; }
        
        public virtual ICollection<Enrollment> Enrollments { get; set; }
    }
}

Enrollments プロパティはナビゲーション プロパティです。 1 つの Course エンティティにたくさんの Enrollment エンティティを関連付けることができます。

[DatabaseGenerated(DatabaseGeneratedOption.None)] 属性については、次のチュートリアルでさらに説明します。 基本的に、この属性によって、講座の主キーをデータベースに生成させず、自分で入力できるようになります。

データベース コンテキストの作成

所与のデータ モデルの Entity Framework 機能を調整するメイン クラスは、"データベース コンテキスト" クラスです。 このクラスは、System.Data.Entity.DbContext クラスから派生して作成します。 自分のコードでは、データ モデルに含めるエンティティを自分で指定します。 Entity Framework の特定の動作をカスタマイズすることもできます。 このプロジェクトでは、クラスに SchoolContext という名前が付けられています。

DAL (データ アクセス層用) という名前のフォルダーを作成します。 そのフォルダーに、SchoolContext.cs という名前の新しいクラス ファイルを作成し、テンプレート コードを次のコードに変更します。

using ContosoUniversity.Models;
using System.Data.Entity;
using System.Data.Entity.ModelConfiguration.Conventions;

namespace ContosoUniversity.DAL
{
    public class SchoolContext : DbContext
    {
        public DbSet<Student> Students { get; set; }
        public DbSet<Enrollment> Enrollments { get; set; }
        public DbSet<Course> Courses { get; set; }

        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
        }
    }
}

このコードでは、エンティティ セットごとに DbSet プロパティを作成します。 Entity Framework の用語では、"エンティティ セット" は通常はデータベース テーブルに対応し、"エンティティ" はテーブルの行に対応します。

OnModelCreating メソッドの modelBuilder.Conventions.Remove ステートメントを使用すると、テーブル名が複数形化されなくなります。 これを行わない場合、生成されたテーブルには StudentsCourses、および Enrollments という名前が付けられます。 その代わりに、テーブル名は StudentCourse、および Enrollment になります。 テーブル名を複数形にするかどうかについては、開発者の間で意見が分かれるでしょう。 このチュートリアルでは単数形を使用しますが、重要な点は、このコード行を含めたり省略したりして、任意のフォームを選択できることです。

SQL Server Express LocalDB

LocalDB は、オンデマンドで起動し、ユーザー モードで実行される SQL Server Express データベース エンジンの軽量バージョンです。 LocalDB は SQL Server Express の特別な実行モードで実行され、データベースを .mdf ファイルとして操作できます。 通常、LocalDB データベース ファイルは Web プロジェクトの App_Data フォルダーに保持されます。 SQL Server Express のユーザー インスタンス機能を使用して .mdf ファイルを操作することもできますが、ユーザー インスタンス機能は非推奨です。そのため、.mdf ファイルを操作する場合は LocalDB をお勧めします。

通常、SQL Server Express は運用環境の Web アプリケーションには使用されません。 特に LocalDB は、IIS で動作するように設計されていないため、Web アプリケーションでの運用環境での使用はお勧めしません。

Visual Studio 2012 以降のバージョンでは、LocalDB は Visual Studio と共に既定でインストールされます。 Visual Studio 2010 以前のバージョンでは、SQL Server Express (LocalDB なし) が Visual Studio と共に既定でインストールされます。Visual Studio 2010 を使用している場合は、手動でインストールする必要があります。

このチュートリアルでは、LocalDB を使用して、データベースを .mdf ファイルとして App_Data フォルダーに格納できるようにします。 次の例に示すように、ルート Web.config ファイルを開き、connectionStrings コレクションに新しい接続文字列を追加します。 (ルート プロジェクト フォルダー内の Web.config ファイルを必ず更新してください。Views サブフォルダーにも Web.config ファイルがありますが、これは更新する必要はありません。)

<add name="SchoolContext" connectionString="Data Source=(LocalDb)\v11.0;Initial Catalog=ContosoUniversity;Integrated Security=SSPI;AttachDBFilename=|DataDirectory|\ContosoUniversity.mdf" providerName="System.Data.SqlClient" />

既定では、Entity Framework は、DbContext クラスと同じ名前 (このプロジェクトでは SchoolContext) の接続文字列を検索します。 追加した接続文字列は、App_Data フォルダーにある ContosoUniversity.mdf という名前の LocalDB データベースを指定します。 詳細については、「ASP.NET Web アプリケーションの SQL Server 接続文字列」を参照してください。

実際には接続文字列を指定しなくても大丈夫です。 接続文字列を指定しない場合は、Entity Framework によって作成されます。しかし、データベースがアプリの App_data フォルダーにない可能性があります。 データベースが作成される場所については、「Code First による新しいデータベース」を参照してください。

connectionStrings コレクションには、メンバーシップ データベースに使用される DefaultConnection という名前の接続文字列もあります。 このチュートリアルでは、メンバーシップ データベースを使用しません。 2 つの接続文字列の唯一の違いは、データベース名と名前の属性値です。

Code First での移行を設定して実行する

新しいアプリケーションを開発する際には、データ モデルが頻繁に変更され、モデルが変更されるたびに、モデルはデータベースと同期されなくなります。 データ モデルを変更するたびにデータベースを自動的に削除して再作成するように Entity Framework を構成できます。 テスト データは簡単に再作成されるため、開発の初期段階では問題になりませんが、運用環境にデプロイした後は、通常、データベースを削除せずにデータベース スキーマを更新する必要があります。 Migrations 機能を使用すると、データベースを削除して再作成することなしに、Code First によりデータベースを更新できます。 新しいプロジェクトの開発サイクルの早い段階では、DropCreateDatabaseIfModelChanges を使用して、モデルが変更されるたびにデータベースを削除、再作成、再シードすることができます。 アプリケーションをデプロイする準備ができたら、移行アプローチに変換できます。 このチュートリアルでは、移行のみを使用します。 詳細については、「Code First Migrations」および「Migrations スクリーンキャスト シリーズ」を参照してください。

Code First Migrations の有効化

  1. [ツール] メニューで [NuGet パッケージ マネージャー][パッケージ マネージャー コンソール] の順にクリックします。

    Selecting_Package_Manager_Console

  2. PM> プロンプトで、次のコマンドを入力します。

    enable-migrations -contexttypename SchoolContext
    

    enable-migrations コマンド

    このコマンドは、ContosoUniversity プロジェクトに Migrations フォルダーを作成し、そのフォルダーに、編集して Migrations を構成するための Configuration.cs ファイルを格納します。

    Configuration クラスには、データベースの作成時およびデータ モデル変更後に更新されるたびに呼び出される Seed メソッドが含まれます。

    internal sealed class Configuration : DbMigrationsConfiguration<ContosoUniversity.Models.SchoolContext>
    {
        public Configuration()
        {
            AutomaticMigrationsEnabled = false;
        }
    
        protected override void Seed(ContosoUniversity.Models.SchoolContext context)
        {
            //  This method will be called after migrating to the latest version.
    
            //  You can use the DbSet<T>.AddOrUpdate() helper extension method 
            //  to avoid creating duplicate seed data. E.g.
            //
            //    context.People.AddOrUpdate(
            //      p => p.FullName,
            //      new Person { FullName = "Andrew Peters" },
            //      new Person { FullName = "Brice Lambson" },
            //      new Person { FullName = "Rowan Miller" }
            //    );
            //
        }
    }
    

    この Seed メソッドの目的は、Code First でテスト データを作成または更新した後に、データベースにテスト データを挿入できるようにすることです。

Seed メソッドを設定する

Seed メソッドは、Code First Migrations によってデータベースが作成さる時と、データベースが最新の移行に更新されるたびに実行されます。 Seed メソッドの目的は、アプリケーションが初めてデータベースにアクセスする前に、テーブルにデータを挿入できるようにすることです。

Migrations がリリースされる前の以前のバージョンの Code First では、Seed メソッドがテスト データを挿入するのが一般的でした。開発時にモデルが変更されるたびに、データベースを完全に削除して最初から再作成する必要があるためです。 Code First Migrations では、データベースの変更後もテスト データが保持されるため、Seed メソッドにテスト データを含める必要は通常ありません。 実際、Migrations を使用してデータベースを運用環境にデプロイする場合は、Seed メソッドでテスト データを挿入する必要はありません。運用環境で Seed メソッドが実行されるためです。 この場合は、Seed メソッドを運用環境に挿入するデータのみをデータベースに挿入します。 たとえば、アプリケーションが運用環境で使用可能になったときに、データベースに実際の部署名を Department テーブルに含める場合があります。

このチュートリアルでは、デプロイに Migrations を使用しますが、大量のデータを手動で挿入しなくてもアプリケーションの機能のしくみを簡単に確認できるように、Seed メソッドでテスト データを挿入します。

  1. Configuration.cs ファイルの内容を次のコードに置き換えます。このコードにより、テスト データが新しいデータベースに読み込まれます。

    namespace ContosoUniversity.Migrations
    {
       using System;
       using System.Collections.Generic;
       using System.Data.Entity.Migrations;
       using System.Linq;
       using ContosoUniversity.Models;
    
       internal sealed class Configuration : DbMigrationsConfiguration<ContosoUniversity.DAL.SchoolContext>
       {
          public Configuration()
          {
             AutomaticMigrationsEnabled = false;
          }
    
          protected override void Seed(ContosoUniversity.DAL.SchoolContext context)
          {
             var students = new List<Student>
                {
                    new Student { FirstMidName = "Carson",   LastName = "Alexander", 
                        EnrollmentDate = DateTime.Parse("2010-09-01") },
                    new Student { FirstMidName = "Meredith", LastName = "Alonso",    
                        EnrollmentDate = DateTime.Parse("2012-09-01") },
                    new Student { FirstMidName = "Arturo",   LastName = "Anand",     
                        EnrollmentDate = DateTime.Parse("2013-09-01") },
                    new Student { FirstMidName = "Gytis",    LastName = "Barzdukas", 
                        EnrollmentDate = DateTime.Parse("2012-09-01") },
                    new Student { FirstMidName = "Yan",      LastName = "Li",        
                        EnrollmentDate = DateTime.Parse("2012-09-01") },
                    new Student { FirstMidName = "Peggy",    LastName = "Justice",   
                        EnrollmentDate = DateTime.Parse("2011-09-01") },
                    new Student { FirstMidName = "Laura",    LastName = "Norman",    
                        EnrollmentDate = DateTime.Parse("2013-09-01") },
                    new Student { FirstMidName = "Nino",     LastName = "Olivetto",  
                        EnrollmentDate = DateTime.Parse("2005-08-11") }
                };
             students.ForEach(s => context.Students.AddOrUpdate(p => p.LastName, s));
             context.SaveChanges();
    
             var courses = new List<Course>
                {
                    new Course {CourseID = 1050, Title = "Chemistry",      Credits = 3, },
                    new Course {CourseID = 4022, Title = "Microeconomics", Credits = 3, },
                    new Course {CourseID = 4041, Title = "Macroeconomics", Credits = 3, },
                    new Course {CourseID = 1045, Title = "Calculus",       Credits = 4, },
                    new Course {CourseID = 3141, Title = "Trigonometry",   Credits = 4, },
                    new Course {CourseID = 2021, Title = "Composition",    Credits = 3, },
                    new Course {CourseID = 2042, Title = "Literature",     Credits = 4, }
                };
             courses.ForEach(s => context.Courses.AddOrUpdate(p => p.Title, s));
             context.SaveChanges();
    
             var enrollments = new List<Enrollment>
                {
                    new Enrollment { 
                        StudentID = students.Single(s => s.LastName == "Alexander").StudentID, 
                        CourseID = courses.Single(c => c.Title == "Chemistry" ).CourseID, 
                        Grade = Grade.A 
                    },
                     new Enrollment { 
                        StudentID = students.Single(s => s.LastName == "Alexander").StudentID,
                        CourseID = courses.Single(c => c.Title == "Microeconomics" ).CourseID, 
                        Grade = Grade.C 
                     },                            
                     new Enrollment { 
                        StudentID = students.Single(s => s.LastName == "Alexander").StudentID,
                        CourseID = courses.Single(c => c.Title == "Macroeconomics" ).CourseID, 
                        Grade = Grade.B
                     },
                     new Enrollment { 
                         StudentID = students.Single(s => s.LastName == "Alonso").StudentID,
                        CourseID = courses.Single(c => c.Title == "Calculus" ).CourseID, 
                        Grade = Grade.B 
                     },
                     new Enrollment { 
                         StudentID = students.Single(s => s.LastName == "Alonso").StudentID,
                        CourseID = courses.Single(c => c.Title == "Trigonometry" ).CourseID, 
                        Grade = Grade.B 
                     },
                     new Enrollment {
                        StudentID = students.Single(s => s.LastName == "Alonso").StudentID,
                        CourseID = courses.Single(c => c.Title == "Composition" ).CourseID, 
                        Grade = Grade.B 
                     },
                     new Enrollment { 
                        StudentID = students.Single(s => s.LastName == "Anand").StudentID,
                        CourseID = courses.Single(c => c.Title == "Chemistry" ).CourseID
                     },
                     new Enrollment { 
                        StudentID = students.Single(s => s.LastName == "Anand").StudentID,
                        CourseID = courses.Single(c => c.Title == "Microeconomics").CourseID,
                        Grade = Grade.B         
                     },
                    new Enrollment { 
                        StudentID = students.Single(s => s.LastName == "Barzdukas").StudentID,
                        CourseID = courses.Single(c => c.Title == "Chemistry").CourseID,
                        Grade = Grade.B         
                     },
                     new Enrollment { 
                        StudentID = students.Single(s => s.LastName == "Li").StudentID,
                        CourseID = courses.Single(c => c.Title == "Composition").CourseID,
                        Grade = Grade.B         
                     },
                     new Enrollment { 
                        StudentID = students.Single(s => s.LastName == "Justice").StudentID,
                        CourseID = courses.Single(c => c.Title == "Literature").CourseID,
                        Grade = Grade.B         
                     }
                };
    
             foreach (Enrollment e in enrollments)
             {
                var enrollmentInDataBase = context.Enrollments.Where(
                    s =>
                         s.Student.StudentID == e.StudentID &&
                         s.Course.CourseID == e.CourseID).SingleOrDefault();
                if (enrollmentInDataBase == null)
                {
                   context.Enrollments.Add(e);
                }
             }
             context.SaveChanges();
          }
       }
    }
    

    Seed メソッドはデータベース コンテキスト オブジェクトを入力パラメーターとして受け取り、メソッド内のコードはそのオブジェクトを使用して新しいエンティティをデータベースに追加します。 エンティティの種類ごとに、コードによって新しいエンティティのコレクションが作成され、適切な DbSet プロパティに追加された後、変更がデータベースに保存されます。 エンティティの各グループの後に、ここで行なったように SaveChanges メソッドを呼び出す必要はないのですが、これを行うことで、コードがデータベースに書き込んでいる間に例外が発生した場合に問題の原因を特定するのに役立ちます。

    データを挿入するステートメントの一部では、AddOrUpdate メソッドを使用して "upsert" 操作を実行します。 Seed メソッドは移行ごとに実行されるため、単にデータを挿入することはできません。なぜなら、追加しようとしている行はデータベースを作成する最初の移行の後に既に存在するためです。 "upsert" 操作は、既に存在する行を挿入しようとした場合に発生するエラーを防ぎますが、アプリケーションのテスト中に行った可能性のあるデータに対する変更を "オーバーライド" します。 一部のテーブルのテスト データではデータを変更したくない場合があります。テスト中にデータを変更した場合、データベースの更新後も変更を維持したいことがあります。 その場合は、条件付き挿入操作を実行します。行がまだ存在しない場合にのみ行を挿入します。 Seed メソッドは両方の方法を使用します。

    AddOrUpdate メソッドに渡される最初のパラメーターは、行が既に存在するかどうかを確認するために使用するプロパティを指定します。 指定するテスト 学生データの場合、リスト内の姓はそれぞれ一意であるため、LastName プロパティをこの目的に使用できます。

    context.Students.AddOrUpdate(p => p.LastName, s)
    

    このコードでは、姓が一意であることを前提としています。 姓が重複する学生を手動で追加すると、次に移行を実行するときに次の例外が発生します。

    シーケンスに複数の要素が含まれています

    AddOrUpdate メソッドの詳細については、Julie Lerman のブログの「EF 4.3 AddOrUpdate メソッドに注意する」を参照してください。

    Enrollment エンティティを追加するコードでは、 AddOrUpdate メソッドは使用されません。 エンティティが既に存在するかどうかを確認し、存在しない場合はエンティティを挿入します。 この方法では、登録成績に加えた変更が移行の実行時に保持されます。 このコードは、Enrollment リスト の各メンバーをループ処理し、登録がデータベースで見つからない場合は、登録をデータベースに追加します。 データベースを初めて更新すると、データベースは空になるため、各登録が追加されます。

    foreach (Enrollment e in enrollments)
    {
        var enrollmentInDataBase = context.Enrollments.Where(
            s => s.Student.StudentID == e.Student.StudentID &&
                 s.Course.CourseID == e.Course.CourseID).SingleOrDefault();
        if (enrollmentInDataBase == null)
        {
            context.Enrollments.Add(e);
        }
    }
    

    Seed メソッドをデバッグする方法と、"Alexander Carson" という名前の 2 人の学生などの冗長データを処理する方法については、Rick Anderson のブログの「Entity Framework (EF) DB のシード処理とデバッグ」を参照してください。

  2. プロジェクトをビルドします。

最初の移行を作成して実行する

  1. [パッケージ マネージャー コンソール] ウィンドウで、次のコマンドを入力します。

    add-migration InitialCreate
    update-database
    

    パッケージ マネージャー コンソール ウィンドウを示すスクリーンショット。ハイフン移行アンダースコアを追加するコマンドの初期作成と更新ハイフン データベースが強調表示されています。

    add-migration コマンドは、データベースを作成するコードを含む [DateStamp]_InitialCreate.cs ファイルを Migrations フォルダーに追加します。 最初のパラメーター (InitialCreate) はファイル名に使用され、任意の名前を指定できます。通常は、移行で行われていることを要約した単語または語句を選択します。 たとえば、後で移行に "AddDepartmentTable" という名前を付ける可能性があります。

    初期移行を含む Migrations フォルダー

    InitialCreate クラスの Up メソッドは、データ モデル エンティティ セットに対応するデータベース テーブルを作成し、Down メソッドはそれらを削除します。 移行は、Up メソッドを呼び出して、移行のためのデータ モデルの変更を実装します。 更新をロールバックするためのコマンドを入力すると、移行が Down メソッドを呼び出します。 次のコードは、InitialCreate ファイルの内容を表示します。

    namespace ContosoUniversity.Migrations
    {
        using System;
        using System.Data.Entity.Migrations;
        
        public partial class InitialCreate : DbMigration
        {
            public override void Up()
            {
                CreateTable(
                    "dbo.Student",
                    c => new
                        {
                            StudentID = c.Int(nullable: false, identity: true),
                            LastName = c.String(),
                            FirstMidName = c.String(),
                            EnrollmentDate = c.DateTime(nullable: false),
                        })
                    .PrimaryKey(t => t.StudentID);
                
                CreateTable(
                    "dbo.Enrollment",
                    c => new
                        {
                            EnrollmentID = c.Int(nullable: false, identity: true),
                            CourseID = c.Int(nullable: false),
                            StudentID = c.Int(nullable: false),
                            Grade = c.Int(),
                        })
                    .PrimaryKey(t => t.EnrollmentID)
                    .ForeignKey("dbo.Course", t => t.CourseID, cascadeDelete: true)
                    .ForeignKey("dbo.Student", t => t.StudentID, cascadeDelete: true)
                    .Index(t => t.CourseID)
                    .Index(t => t.StudentID);
                
                CreateTable(
                    "dbo.Course",
                    c => new
                        {
                            CourseID = c.Int(nullable: false),
                            Title = c.String(),
                            Credits = c.Int(nullable: false),
                        })
                    .PrimaryKey(t => t.CourseID);
                
            }
            
            public override void Down()
            {
                DropIndex("dbo.Enrollment", new[] { "StudentID" });
                DropIndex("dbo.Enrollment", new[] { "CourseID" });
                DropForeignKey("dbo.Enrollment", "StudentID", "dbo.Student");
                DropForeignKey("dbo.Enrollment", "CourseID", "dbo.Course");
                DropTable("dbo.Course");
                DropTable("dbo.Enrollment");
                DropTable("dbo.Student");
            }
        }
    }
    

    update-database コマンドは、Up メソッドを実行してデータベースを作成し、Seed メソッドを実行してデータベースを設定します。

これで、データ モデル用に SQL Server データベースが作成されました。 データベースの名前は ContosoUniversity であり、.mdf ファイルはプロジェクトの App_Data フォルダーにあります。これは接続文字列で指定した内容に従っているためです。

サーバー エクスプローラーまたは SQL Server オブジェクト エクスプローラー (SSOX) を 使用して、Visual Studio でデータベースを表示できます。 このチュートリアルでは、サーバー エクスプローラーを使用します。 Visual Studio Express 2012 for Web では、 サーバー エクスプローラーデータベース エクスプローラーと呼ばれます。

  1. [表示] メニューの [サーバー エクスプローラー] をクリックします。

  2. [接続の追加] アイコンを クリックします。

    [データベース エクスプローラー] ウィンドウを示すスクリーンショット。[接続の追加] アイコンが強調表示されています。

  3. [データ ソース の選択] ダイアログボックスが表示されたら、[Microsoft SQL Server ] をクリックし、[続行] をクリックします。

    [データ ソースの選択] ダイアログ ボックスを示すスクリーンショット。Microsoft S Q L Server データ ソースが選択されています。

  4. [接続の追加] ダイアログ ボックスで、[サーバー名] として「(localdb)\v11.0」と入力します。 [データベース名を選択または入力] で、[ContosoUniversity] を選択します。

    [接続の追加] ダイアログ ボックスを示すスクリーンショット。サンプルサーバー名と Contoso University データベースが強調表示されています。

  5. OK をクリックします。

  6. [SchoolContext] を展開し、[テーブル] を展開します。

    [サーバー エクスプローラー] ページを示すスクリーンショット。[学校コンテキスト] タブと [テーブル] タブが展開されます。

  7. [Student] テーブルを右クリックし、[テーブル データの表示] をクリックすると、作成された列とテーブルに挿入された行が表示されます。

    Student テーブル

学生コントローラーとビューの作成

次の手順では、これらのテーブルのいずれかを操作できる ASP.NET MVC コントローラーとビューをアプリケーションに作成します。

  1. Student コントローラーを作成するには、ソリューション エクスプローラーControllers フォルダーを右クリックし、[追加] を選択してから、[コントローラー] をクリックします。 [コントローラーの追加] ダイアログ ボックスで、次の選択を行い、[追加] をクリックします。

    • コントローラー名: StudentController

    • テンプレート: Entity Framework を使用した読み取り/書き込みアクションとビューがある MVC コントローラー

    • モデル クラス: Student (ContosoUniversity.Models)。 (ドロップダウン リストにこのオプションが表示されない場合は、プロジェクトをビルドしてやり直してください)。

    • データ コンテキスト クラス: SchoolContext (ContosoUniversity.Models)

    • ビュー: Razor (CSHTML)。 (既定値)

      Add_Controller_dialog_box_for_Student_controller

  2. Visual Studio で Controllers\StudentController.cs ファイルが 開きます。 データベース コンテキスト オブジェクトをインスタンス化するクラス変数が作成されていることがわかります。

    private SchoolContext db = new SchoolContext();
    

    Index アクション メソッドは、データベース コンテキスト インスタンスの Students プロパティを読み取ることによって、Students エンティティ セットから学生の一覧を取得します。

    public ViewResult Index()
    {
        return View(db.Students.ToList());
    }
    

    Students/Index.cshtml ビューでは、この一覧が表形式で表示されます。

    <table>
        <tr>
            <th>
                @Html.DisplayNameFor(model => model.LastName)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.FirstMidName)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.EnrollmentDate)
            </th>
            <th></th>
        </tr>
    
    @foreach (var item in Model) {
        <tr>
            <td>
                @Html.DisplayFor(modelItem => item.LastName)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.FirstMidName)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.EnrollmentDate)
            </td>
            <td>
                @Html.ActionLink("Edit", "Edit", new { id=item.StudentID }) |
                @Html.ActionLink("Details", "Details", new { id=item.StudentID }) |
                @Html.ActionLink("Delete", "Delete", new { id=item.StudentID })
            </td>
        </tr>
    }
    
  3. Ctrl キーを押しながら F5 キーを押してプロジェクトを実行します。

    [Students] タブをクリックすると、Seed メソッドによって挿入されたテスト データが表示されます。

    [Student Index]\(学生インデックス\) ページ

規約

Entity Framework が完全なデータベースを作成できるようにするために記述する必要があるコードの量は、"規則"、つまり Entity Framework が想定する前提を使用するため、最小限で済みます。 その一部には、既に確認した次の点が含まれます。

  • テーブル名にはエンティティ クラス名の複数形が使用されます。
  • 列名には、エンティティ プロパティ名が使用されます。
  • ID または classname ID という名前のエンティティ プロパティは、主キー プロパティとして認識されます。

規則をオーバーライドできることを確認しました (たとえば、テーブル名を複数形化しないように指定した場合など)。規則とそのオーバーライド方法の詳細については、このシリーズの後半の「より複雑なデータ モデルの作成」チュートリアルを参照してください。 詳細については、「Code First 規則」を参照してください。

まとめ

これで Entity Framework と SQL Server Express を利用してデータを保存し、表示する単純なアプリケーションが作成されました。 次のチュートリアルでは、基本的な CRUD (作成、読み取り、更新、削除) 操作を実行する方法について学習します。 フィードバックは、このページの下部に残すことができます。 チュートリアルのこの部分を気に入った方法と、それを改善する方法をお知らせください。

他の Entity Framework リソースへのリンクは、「ASP.NET データ アクセス コンテンツ マップ」にあります。