Entity-framework-quick-guide

提供:Dev Guides
移動先:案内検索

Entity Framework-概要

Entity Frameworkとは何ですか?

Entity Frameworkは2008年に最初にリリースされました。これは、Microsoftが.NETアプリケーションとリレーショナルデータベースの間でやり取りするための主要な手段です。 Entity Frameworkはオブジェクトリレーショナルマッパー(ORM)です。これは、ソフトウェア内のオブジェクトからリレーショナルデータベースのテーブルおよび列へのマッピングを簡素化するツールの一種です。

  • Entity Framework(EF)は、.NET Frameworkの一部であるADO.NET用のオープンソースORMフレームワークです。
  • ORMは、データベース接続の作成とコマンドの実行、クエリ結果の取得、およびそれらの結果をアプリケーションオブジェクトとして自動的に具体化する処理を行います。
  • ORMは、これらのオブジェクトへの変更を追跡するのにも役立ちます。また、指示されると、それらの変更をデータベースに永続的に保持します。

Entity Frameworkを使用する理由

Entity FrameworkはORMであり、ORMは、アプリケーションで使用されるデータを保持する冗長なタスクを削減することにより、開発者の生産性を向上させることを目的としています。

  • Entity Frameworkは、データベース内のデータの読み取りまたは書き込みに必要なデータベースコマンドを生成し、それらを実行できます。
  • クエリを実行している場合、LINQ to entitiesを使用してドメインオブジェクトに対してクエリを表現できます。
  • Entity Frameworkは、データベースで関連するクエリを実行し、結果をドメインオブジェクトのインスタンスに具体化して、アプリ内で作業できるようにします。

NHibernateやLLBLGen Proなど、市場には他のORMがあります。 ほとんどのORMは通常、ドメインタイプをデータベーススキーマに直接マップします。

標準ORM

Entity Frameworkには、よりきめ細かなマッピングレイヤーがあるため、たとえば、単一のエンティティを複数のデータベーステーブルにマッピングしたり、複数のエンティティを単一のテーブルにマッピングしたりして、マッピングをカスタマイズできます。

EFランタイムメタデータ

  • Entity Frameworkは、新しいアプリケーション向けにマイクロソフトが推奨するデータアクセステクノロジーです。
  • ADO.NETは、データセットとデータテーブルのテクノロジを直接参照しているようです。
  • エンティティフレームワークは、すべての将来の投資が行われている場所であり、これは既に何年もの間そうでした。
  • マイクロソフトは、すべての新規開発のために、ADO.NETまたはLINQ to SQL上のEntity Frameworkを使用することをお勧めします。

概念モデル

データベース中心の開発に慣れている開発者にとって、Entity Frameworkの最大の変化は、ビジネスドメインに集中できることです。 データベースが実行できることによって制限されることなく、アプリケーションに実行させたいことは何ですか?

  • Entity Frameworkでは、フォーカルポイントは概念モデルと呼ばれます。 これは、アプリケーションデータの永続化に使用するデータベースのモデルではなく、アプリケーション内のオブジェクトのモデルです。
  • 概念モデルは、データベーススキーマと一致する場合もあれば、まったく異なる場合もあります。
  • ビジュアルデザイナーを使用して概念モデルを定義し、最終的にアプリケーションで使用するクラスを生成できます。
  • クラスを定義するだけで、Code FirstというEntity Frameworkの機能を使用できます。 そして、Entity Frameworkは概念モデルを理解します。

概念モデル

いずれにしても、Entity Frameworkは、概念モデルからデータベースに移行する方法を決定します。 したがって、概念モデルオブジェクトに対してクエリを実行し、それらを直接操作できます。

特徴

以下は、Entity Frameworkの基本機能です。 このリストは、最も注目に値する機能に基づいて作成され、Entity Frameworkに関するよくある質問からも作成されます。

  • Entity FrameworkはMicrosoftのツールです。
  • Entity Frameworkは、オープンソース製品として開発されています。
  • Entity Frameworkは、.NETリリースサイクルに関連付けられなくなり、依存しなくなりました。
  • 有効なEntity Frameworkプロバイダーを備えたリレーショナルデータベースで動作します。
  • LINQからエンティティへのSQLコマンド生成。
  • Entity Frameworkは、パラメーター化されたクエリを作成します。
  • インメモリオブジェクトへの変更を追跡します。
  • コマンド生成の挿入、更新、削除を許可します。
  • ビジュアルモデルまたは独自のクラスで動作します。
  • Entity Frameworkには、プロシージャサポートが格納されています。

Entity Framework-アーキテクチャ

Entity Frameworkのアーキテクチャは、ボトムアップで、次のもので構成されています-

データプロバイダー

これらはソース固有のプロバイダーで、ADO.NETインターフェイスを抽象化して、概念スキーマに対してプログラミングするときにデータベースに接続します。

LINQなどの一般的なSQL言語をコマンドツリー経由でネイティブSQL式に変換し、特定のDBMSシステムに対して実行します。

エンティティクライアント

この層は、エンティティ層を上位層に公開します。 エンティティクライアントは、開発者が概念SQLを表すクラスを生成する必要なく、エンティティSQLクエリを使用して行と列の形式でエンティティを操作する機能を提供します。 エンティティクライアントには、コアフレームワークであるエンティティフレームワークレイヤーが表示されます。 これらの層は、エンティティデータモデルと呼ばれます。

エンティティデータモデル

  • Storage Layer には、XML形式のデータベーススキーマ全体が含まれています。
  • XMLファイルでもある Entity Layer は、エンティティと関係を定義します。
  • *マッピング層*は、概念層で定義されたエンティティと関係を、論理層で定義された実際の関係とテーブルにマップするXMLファイルです。
  • エンティティクライアントでも表される*メタデータサービス*は、エンティティ、マッピング、およびストレージレイヤーに格納されたメタデータにアクセスするための集中型APIを提供します。

オブジェクトサービス

オブジェクトサービスレイヤーはオブジェクトコンテキストであり、アプリケーションとデータソース間の対話のセッションを表します。

  • オブジェクトコンテキストの主な用途は、エンティティのインスタンスの追加、削除などのさまざまな操作を実行し、クエリを使用して変更された状態をデータベースに保存することです。
  • Entity FrameworkのORMレイヤーであり、エンティティのオブジェクトインスタンスに対するデータ結果を表します。
  • このサービスにより、開発者は主キーマッピング、変更追跡などの豊富なORM機能を使用できます。 LINQとEntity SQLを使用してクエリを作成します。

Entity Framework-環境設定

Entity Framework 6の新機能は?

フレームワークには複雑なAPIがあり、モデリングから実行時の動作に至るすべてをきめ細かく制御できます。 Entity Framework 5の一部は.NET内にあります。 また、別の部分は、NuGetを使用して配布される追加のアセンブリ内にあります。

  • Entity Frameworkのコア機能は、.NET Frameworkに組み込まれています。
  • Code Firstのサポート。これにより、Entity Frameworkは視覚モデルの代わりにクラスを使用できます。また、EFと対話するためのより軽い方法のAPIはNuGetパッケージに含まれています。
  • コアは、クエリ、変更の追跡、およびクエリからSQLクエリへの変換、およびオブジェクトへのデータリターンのすべてを提供するものです。
  • EF 5 NuGetパッケージは、.NET 4と.NET 4.5の両方で使用できます。
  • 混乱の1つの大きなポイント-.NET 4.5は、Entity Framework APIのコアに列挙と空間データのサポートを追加しました。つまり、.NET 4でEF 5を使用している場合、これらの新機能は得られません。 EF5と.NET 4.5を組み合わせた場合にのみ取得できます。

フレームワーク6

Entity Framework 6を​​見てみましょう。 Entity Framework 6の.NET内にあったコアAPIは、NuGetパッケージの一部になりました。

Entity Framework 6

という意味です-

  • Entity Frameworkはすべて、NuGetによって配布されるこのアセンブリ内に存在します。
  • Entity Framework列挙型サポートや特別なデータサポートなどの特定の機能を提供するために.NETに依存することはありません。
  • EF6の機能の1つは、.NET 4の列挙と空間データをサポートすることです。

Entity Frameworkの作業を開始するには、次の開発ツールをインストールする必要があります-

  • Visual Studio 2013以降
  • SQL Server 2012以降
  • NuGetパッケージからのEntity Frameworkの更新

Microsoftは、SQL Serverを含むVisual Studioの無料バージョンを提供しており、https://www.visualstudio.com/downloads/download-visual-studio-vs/[www.visualstudio.com]からダウンロードできます。

インストール

  • ステップ1 *-ダウンロードが完了したら、インストーラーを実行します。 次のダイアログが表示されます。

Visual Studio Installer

  • ステップ2 *-[インストール]ボタンをクリックすると、インストールプロセスが開始されます。

インストールプロセス

  • ステップ3 *-インストールプロセスが正常に完了すると、次のダイアログが表示されます。 このダイアログを閉じて、必要に応じてコンピューターを再起動します。

セットアップ完了

  • ステップ4 *-スタートメニューからVisual Studioを開き、次のダイアログを開きます。 準備は初めてです。

Visual Studio

  • ステップ5 *-すべてが完了すると、Visual Studioのメインウィンドウが表示されます。

メインウィンドウ

ファイル→新規→プロジェクトから新しいプロジェクトを作成しましょう

新しいプロジェクト

  • ステップ1 *-コンソールアプリケーションを選択し、[OK]ボタンをクリックします。
  • ステップ2 *-ソリューションエクスプローラーで、プロジェクトを右クリックします。

コンソールアプリケーション

  • ステップ3 *-上記の画像に示すように、NuGetパッケージの管理を選択すると、Visual Studioで次のウィンドウが開きます。

Visual Studio 1

  • ステップ4 *-Entity Frameworkを検索し、インストールボタンを押して最新バージョンをインストールします。

プレビュー

  • ステップ5 *-[OK]をクリックします。 インストールが完了すると、出力ウィンドウに次のメッセージが表示されます。

出力ウィンドウ

これで、アプリケーションを開始する準備ができました。

Entity Framework-データベースのセットアップ

このチュートリアルでは、単純な大学データベースを使用します。 大学のデータベースは全体としてはるかに複雑になる可能性がありますが、デモと学習の目的で、このデータベースの最も単純な形式を使用しています。 次の図には、3つのテーブルが含まれています。

  • 学生
  • コース
  • 入会

データベース

用語データベースが使用されるときはいつでも、1つのことが頭に浮かびます。それは、ある種の関係を持つ異なる種類のテーブルです。 テーブル間の関係には3つのタイプがあり、異なるテーブル間の関係は、関連する列の定義方法によって異なります。

  • 1対多の関係
  • 多対多の関係
  • 一対一の関係

1対多の関係

1対多の関係は、最も一般的なタイプの関係です。 このタイプの関係では、テーブルAの行はテーブルBの多くの一致する行を持つことができますが、テーブルBの行はテーブルAの一致する行を1つだけ持つことができます。 たとえば、上の図では、StudentとEnrollmentテーブルには1対多の関係があり、各学生には多くの登録がありますが、各登録は1人の学生のみに属します。

多対多の関係

多対多の関係では、テーブルAの行はテーブルBの多くの一致する行を持つことができ、逆もまた同様です。 そのような関係を作成するには、ジャンクションテーブルと呼ばれる3番目のテーブルを定義します。このテーブルの主キーは、テーブルAとテーブルBの両方からの外部キーで構成されます。 たとえば、StudentテーブルとCourseテーブルには、これらの各テーブルからEnrollmentテーブルへの1対多の関係によって定義される多対多の関係があります。

一対一の関係

1対1の関係では、テーブルAの行はテーブルBの一致する行を1つしか持つことができません。逆も同様です。 関連する列の両方が主キーであるか、一意の制約がある場合、1対1の関係が作成されます。

この方法で関連するほとんどの情報はオールインワンテーブルになるため、このタイプの関係は一般的ではありません。 あなたは1対1の関係を使用するかもしれません-

  • 多くの列を持つテーブルを分割します。
  • セキュリティ上の理由から、テーブルの一部を分離します。
  • 寿命が短く、テーブルを削除するだけで簡単に削除できるデータを保存します。
  • メインテーブルのサブセットにのみ適用される情報を保存します。

エンティティフレームワーク-データモデル

エンティティデータモデル(EDM)は、エンティティリレーションシップモデルの拡張バージョンであり、さまざまなモデリング手法を使用してデータの概念モデルを指定します。 また、格納されている形式に関係なく、データ構造を説明する一連の概念も指します。

EDMは、概念モデルのプロパティを定義するプリミティブデータ型のセットをサポートしています。 Entity Frameworkの基礎を形成する3つのコアパーツを検討する必要があり、これらはまとめてEntity Data Modelとして知られています。 EDMの3つの主要部分は次のとおりです。

  • ストレージスキーマモデル
  • 概念モデル
  • マッピングモデル

ストレージスキーマモデル

ストレージスキーマ定義レイヤー(SSDL)とも呼ばれるストレージモデルは、バックエンドデータストアの概略図を表します。

EDM

概念モデル

概念スキーマ定義レイヤー(CSDL)とも呼ばれる概念モデルは、実際のエンティティモデルであり、これに対してクエリを記述します。

マッピングモデル

Mapping Layerは、概念モデルとストレージモデル間の単なるマッピングです。

論理スキーマと物理スキーマとのマッピングは、EDMとして表されます。

  • Visual Studioは、EDMおよびマッピング仕様の視覚的な作成のために、Entity Designerも提供します。
  • ツールの出力は、スキーマとマッピングを指定するXMLファイル(* .edmx)です。
  • Edmxファイルには、Entity Frameworkのメタデータアーティファクトが含まれています。

スキーマ定義言語

ADO.NET Entity Frameworkは、スキーマ定義言語(SDL)と呼ばれるXMLベースのデータ定義言語を使用して、EDMスキーマを定義します。

  • SDLは、特にString、Int32、Double、Decimal、DateTimeなど、他のプリミティブ型と同様の単純型を定義します。
  • プリミティブ値と名前のマップを定義する列挙も単純型と見なされます。
  • 列挙は、フレームワークバージョン5.0以降でのみサポートされます。
  • 複合型は、他の型の集合から作成されます。 これらのタイプのプロパティのコレクションは、エンティティタイプを定義します。

データモデルには、主にデータ構造を記述するための3つの重要な概念があります-

  • エンティティタイプ
  • 関連付けタイプ
  • 物件

エンティティタイプ

エンティティタイプは、EDMでデータの構造を記述するための基本的な構成要素です。

  • 概念モデルでは、エンティティタイプはプロパティから構築され、ビジネスアプリケーションの受講者や登録などのトップレベルの概念の構造を記述します。
  • エンティティは、特定の学生や登録などの特定のオブジェクトを表します。
  • 各エンティティには、エンティティセット内に一意のエンティティキーが必要です。 エンティティセットは、特定のエンティティタイプのインスタンスのコレクションです。 エンティティセット(および関連付けセット)は、エンティティコンテナに論理的にグループ化されます。
  • 継承はエンティティタイプでサポートされています。つまり、あるエンティティタイプを別のエンティティタイプから派生させることができます。

エンティティタイプ

関連付けタイプ

これは、EDMで関係を記述するためのもう1つの基本的な構成要素です。 概念モデルでは、関連付けは、StudentやEnrollmentなどの2つのエンティティタイプ間の関係を表します。

  • すべてのアソシエーションには、アソシエーションに関係するエンティティタイプを指定する2つのアソシエーションエンドがあります。
  • 各関連付けの終わりは、関連付けの終わりに存在できるエンティティの数を示す関連付けの終わりの多重度も指定します。
  • アソシエーションの終了の多重度は、1(1)、0または1(0..1)、または多数(*)の値を持つことができます。
  • 関連付けの一方のエンティティは、ナビゲーションプロパティを介して、またはエンティティタイプで公開されている場合は外部キーを介してアクセスできます。

物件

エンティティタイプには、構造と特性を定義するプロパティが含まれています。 たとえば、Studentエンティティタイプには、Student Id、Nameなどのプロパティがあります。

プロパティには、プリミティブデータ(文字列、整数、ブール値など)または構造化データ(複合型など)を含めることができます。

エンティティフレームワーク-DbContext

Entity Frameworkを使用すると、エンティティと呼ばれる共通言語ランタイム(CLR)オブジェクトを使用して、データのクエリ、挿入、更新、および削除を行うことができます。 Entity Frameworkは、モデルで定義されているエンティティと関係をデータベースにマップします。 また、施設を提供します-

  • データベースから返されたデータをエンティティオブジェクトとして具体化する
  • オブジェクトに加えられた変更を追跡する
  • 並行性を処理する
  • オブジェクトの変更をデータベースに伝播します
  • オブジェクトをコントロールにバインド

オブジェクトとしてデータとやり取りする主なクラスはSystem.Data.Entity.DbContextです。 DbContext APIは、.NET Frameworkの一部としてリリースされていません。 Code FirstおよびDbContext APIへの新機能のリリースをより柔軟かつ頻繁に行うために、Entity FrameworkチームはMicrosoftのNuGet配布機能を通じてEntityFramework.dllを配布します。

  • NuGetを使用すると、関連するDLLをWebからプロジェクトに直接取り込むことにより、.NETプロジェクトへの参照を追加できます。
  • ライブラリパッケージマネージャーと呼ばれるVisual Studioの拡張機能により、Webからプロジェクトに適切なアセンブリを簡単にプルできます。

DbContext

  • DbContext APIの主な目的は、Entity Frameworkとの対話を簡素化することです。
  • また、一般的に使用されるタスクにアクセスするために必要なメソッドとプロパティの数を減らします。
  • Entity Frameworkの以前のバージョンでは、これらのタスクを発見してコーディングするのは複雑でした。
  • コンテキストクラスは、実行中にエンティティオブジェクトを管理します。これには、データベースからのデータの入力、変更の追跡、データベースへのデータの永続化が含まれます。

DbContext派生クラスの定義

コンテキストを操作するための推奨される方法は、DbContextから派生し、コンテキスト内の指定されたエンティティのコレクションを表すDbSetプロパティを公開するクラスを定義することです。 EF Designerで作業している場合、コンテキストが自動的に生成されます。 Code Firstを使用している場合、通常は自分でコンテキストを記述します。

次のコードは、UniContextがDbContextから派生したことを示す簡単な例です。

  • DbSetでは、getterやsetterなどの自動プロパティを使用できます。
  • また、コードはずっときれいになりますが、適用する他のロジックがない場合は、DbSetを作成する目的で使用する必要はありません。
public class UniContext : DbContext {
   public UniContext() : base("UniContext") { }
   public DbSet<Student> Students { get; set; }
   public DbSet<Enrollment> Enrollments { get; set; }
   public DbSet<Course> Courses { get; set; }
}
  • 以前は、EDMはObjectContextクラスから派生したコンテキストクラスを生成するために使用されていました。
  • ObjectContextの操作は少し複雑でした。
  • DbContextはObjectContextのラッパーであり、実際にはObjectContextに似ており、Code First、Model First、Database Firstなどのすべての開発モデルで便利で簡単です。

問い合わせ

あなたが使用できるクエリの3種類があります-

  • 新しいエンティティを追加します。
  • 既存のエンティティのプロパティ値を変更または更新します。
  • 既存のエンティティを削除します。

新しいエンティティを追加する

Entity Frameworkで新しいオブジェクトを追加するには、オブジェクトの新しいインスタンスを作成し、DbSetのAddメソッドを使用して登録するだけです。 次のコードは、新しい学生をデータベースに追加する場合に使用します。

private static void AddStudent() {

   using (var context = new UniContext()) {

      var student = new Student {
         LastName = "Khan",
         FirstMidName = "Ali",
         EnrollmentDate = DateTime.Parse("2005-09-01")
      };

      context.Students.Add(student);
      context.SaveChanges();

   }
}

既存のエンティティの変更

既存のオブジェクトの変更は、変更するプロパティに割り当てられた値を更新し、SaveChangesを呼び出すのと同じくらい簡単です。 次のコードでは、アリの姓がカーンからアスラムに変更されています。

private static void AddStudent() {

   private static void ChangeStudent() {

      using (var context = new UniContext()) {

         var student = (from d in context.Students
            where d.FirstMidName == "Ali" select d).Single();
         student.LastName = "Aslam";
         context.SaveChanges();

      }
   }
}

既存のエンティティを削除する

Entity Frameworkを使用してエンティティを削除するには、DbSetのRemoveメソッドを使用します。 削除は、既存のエンティティと新しく追加されたエンティティの両方で機能します。 追加されたがまだデータベースに保存されていないエンティティに対してRemoveを呼び出すと、エンティティの追加がキャンセルされます。 エンティティは変更トラッカーから削除され、DbContextによって追跡されなくなります。 変更追跡されている既存のエンティティでRemoveを呼び出すと、次回SaveChangesが呼び出されたときに削除するエンティティが登録されます。 次の例は、学生が名がアリのデータベースから削除されるインスタンスを示しています。

private static void DeleteStudent() {

   using (var context = new UniContext()) {
      var bay = (from d in context.Students where d.FirstMidName == "Ali" select d).Single();
      context.Students.Remove(bay);
      context.SaveChanges();
   }
}

Entity Framework-タイプ

Entity Frameworkには、開発者がデータクラス自体に変更を加えることなく、データモデルとともに独自のカスタムデータクラスを使用できるようにする2種類のエンティティがあります。

  • POCOエンティティ
  • 動的プロキシ

POCOエンティティ

  • POCOは、データモデルで既存のドメインオブジェクトとして使用できる「従来の」CLRオブジェクトの略です。
  • エンティティにマップされるPOCOデータクラスは、データモデルで定義されます。
  • また、エンティティデータモデルツールによって生成されるエンティティタイプと同じクエリ、挿入、更新、および削除の動作のほとんどをサポートします。
  • POCOテンプレートを使用して、概念モデルから永続性を無視するエンティティタイプを生成できます。

概念エンティティデータモデルの次の例を見てみましょう。

概念エンティティモデル

上記のエンティティモデルのPOCOエンティティを生成するには-

  • ステップ1 *-デザイナーウィンドウを右クリックします。 次のダイアログが表示されます。

デザイナーウィンドウ

  • ステップ2 *-[コード生成アイテムの追加…​]を選択します。

コード生成

  • ステップ3 *-EF 6.x DbContext Generatorを選択し、名前を入力して、[追加]ボタンをクリックします。

ソリューションエクスプローラーで、POCODemo.Context.ttおよびPOCODemo.ttテンプレートが生成されていることがわかります。

ソリューションエクスプローラー

POCODemo.ContextはDbContextとオブジェクトセットを生成します。これらのオブジェクトセットを返し、クエリ、たとえばコンテキスト、学生、コースなどに使用できます。

生成

もう1つのテンプレートは、学生、コースなどのすべてのタイプを扱います。 以下は、エンティティモデルから自動的に生成されるStudentクラスのコードです。

namespace ConsoleApplication1 {

   using System;
   using System.Collections.Generic;

   public partial class Student {

      [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage",
         "CA2214:DoNotCallOverridableMethodsInConstructors")]

      public Student() {
         this.Enrollments = new HashSet<Enrollment>();
      }

      public int ID { get; set; }
      public string LastName { get; set; }
      public string FirstMidName { get; set; }
      public System.DateTime EnrollmentDate { get; set; }

      [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage",
         CA2227:CollectionPropertiesShouldBeReadOnly")]

      public virtual ICollection<Enrollment> Enrollments { get; set; }

   }
}

エンティティモデルからコースおよび登録テーブルに対して同様のクラスが生成されます。

動的プロキシ

POCOエンティティタイプのインスタンスを作成する場合、Entity Frameworkは多くの場合、エンティティのプロキシとして機能する動的に生成された派生タイプのインスタンスを作成します。 ITは、POCOエンティティのラッパークラスのようなランタイムプロキシクラスであるとも言えます。

  • エンティティの一部のプロパティをオーバーライドして、プロパティにアクセスしたときにアクションを自動的に実行できます。
  • このメカニズムは、関係の遅延読み込みと自動変更追跡をサポートするために使用されます。
  • この手法は、Code FirstおよびEF Designerで作成されたモデルにも適用されます。

Entity Frameworkで関連オブジェクトの遅延読み込みをサポートし、POCOクラスの変更を追跡する場合、POCOクラスは次の要件を満たす必要があります-

  • カスタムデータクラスは、パブリックアクセスで宣言する必要があります。
  • カスタムデータクラスを封印しないでください。
  • カスタムデータクラスは抽象的であってはなりません。
  • カスタムデータクラスには、パラメーターを持たないパブリックまたは保護されたコンストラクターが必要です。
  • CreateObjectメソッドを使用してPOCOエンティティのプロキシを作成する場合は、パラメーターなしで保護されたコンストラクターを使用します。
  • CreateObjectメソッドを呼び出しても、プロキシの作成は保証されません。POCOクラスは、このトピックで説明されている他の要件に従う必要があります。
  • プロキシクラスはこれらのインターフェイスを実装するため、クラスはIEntityWithChangeTrackerまたはIEntityWithRelationshipsインターフェイスを実装できません。
  • ProxyCreationEnabledオプションをtrueに設定する必要があります。

次の例は、動的プロキシエンティティクラスのものです。

public partial class Course {

   public Course() {
      this.Enrollments = new HashSet<Enrollment>();
   }

   public int CourseID { get; set; }
   public string Title { get; set; }
   public int Credits { get; set; }

   public virtual ICollection<Enrollment> Enrollments { get; set; }
}

プロキシオブジェクトの作成を無効にするには、ProxyCreationEnabledプロパティの値をfalseに設定します。

Entity Framework-関係

リレーショナルデータベースでは、関係は外部キーを介してリレーショナルデータベーステーブル間に存在する状況です。 外部キー(FK)は、2つのテーブルのデータ間のリンクを確立および実施するために使用される列または列の組み合わせです。 次の図には、3つのテーブルが含まれています。

  • 学生
  • コース
  • 入会

リレーショナルデータベース

上の図では、テーブル間のある種の関連付け/関係を見ることができます。 テーブル間の関係には3つのタイプがあり、異なるテーブル間の関係は、関連する列の定義方法によって異なります。

  • 1対多の関係
  • 多対多の関係
  • 一対一の関係

1対多の関係

  • 1対多の関係は、最も一般的なタイプの関係です。
  • このタイプの関係では、テーブルAの行はテーブルBの多くの一致する行を持つことができますが、テーブルBの行はテーブルAの一致する行を1つだけ持つことができます。
  • 外部キーは、関係の多端を表すテーブルで定義されます。
  • たとえば、上の図では、StudentテーブルとEnrollmentテーブルには1対1の関係があり、各学生には多くの登録がありますが、各登録は1人の学生にのみ属します。

エンティティフレームワークでは、これらの関係もコードで作成できます。 以下は、1対多の関係に関連付けられているStudentクラスとEnrollmentクラスの例です。

public class Student {
   public int ID { get; set; }
   public string LastName { get; set; }
   public string FirstMidName { get; set; }
   public DateTime EnrollmentDate { get; set; }

   public virtual ICollection<Enrollment> Enrollments { get; set; }
}

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

上記のコードでは、StudentクラスにはEnrollmentのコレクションが含まれていますが、Enrollmentクラスには単一のStudentオブジェクトがあります。

多対多の関係

多対多の関係では、テーブルAの行はテーブルBの多くの一致する行を持つことができ、逆もまた同様です。

  • そのような関係を作成するには、ジャンクションテーブルと呼ばれる3番目のテーブルを定義します。このテーブルの主キーは、テーブルAとテーブルBの両方からの外部キーで構成されます。
  • たとえば、StudentテーブルとCourseテーブルには、これらの各テーブルからEnrollmentテーブルへの1対多の関係によって定義される多対多の関係があります。

次のコードには、Courseクラスと上記の2つのクラス、つまり StudentEnrollment が含まれています。

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

コースクラスと学生クラスの両方に、ジャンクションクラス登録を介して多対多の関係を作成する登録オブジェクトのコレクションがあることがわかります。

一対一の関係

  • 1対1の関係では、テーブルAの行はテーブルBの一致する行を1つしか持つことができません。逆も同様です。
  • 関連する列の両方が主キーであるか、一意の制約がある場合、1対1の関係が作成されます。
  • 1対1の関係では、主キーはさらに外部キーとして機能し、どちらのテーブルにも個別の外部キー列はありません。

この方法で関連するほとんどの情報はすべて1つのテーブルにあるため、このタイプの関係は一般的ではありません。 あなたは1対1の関係を使用するかもしれません-

  • 多くの列を持つテーブルを分割します。
  • セキュリティ上の理由から、テーブルの一部を分離します。
  • 寿命が短く、テーブルを削除するだけで簡単に削除できるデータを保存します。
  • メインテーブルのサブセットにのみ適用される情報を保存します。

以下のコードは、学生のメールIDとパスワードを含む別のクラス名StudentProfileを追加します。

public class Student {
   public int ID { get; set; }
   public string LastName { get; set; }
   public string FirstMidName { get; set; }
   public DateTime EnrollmentDate { get; set; }

   public virtual ICollection<Enrollment> Enrollments { get; set; }
   public virtual StudentProfile StudentProfile { get; set; }
}

public class StudentProfile {

   public StudentProfile() {}
   public int ID { get; set; }
   public string Email { get; set; }
   public string Password { get; set; }

   public virtual Student Student { get; set; }
}

StudentエンティティクラスにはStudentProfileナビゲーションプロパティが含まれ、StudentProfileにはStudentナビゲーションプロパティが含まれていることがわかります。

各学生は、大学のドメインにログインするための1つのメールとパスワードのみを持っています。 これらの情報はStudentテーブルに追加できますが、セキュリティ上の理由から別のテーブルに分離されます。

Entity Framework-ライフサイクル

一生

コンテキストのライフタイムは、インスタンスが作成されたときに始まり、インスタンスが破棄されるかガベージコレクションされると終了します。

  • コンテキストの有効期間は、ORMを使用するときに行う非常に重要な決定です。
  • コンテキストはエンティティキャッシュのように機能するため、メモリ消費が非常に速くなる可能性のあるロードされたすべてのエンティティへの参照を保持し、メモリリークを引き起こす可能性もあります。
  • 次の図では、コンテキストを介したアプリケーションからデータベースへ、またはその逆のデータワークフローの上位レベルを見ることができます。

データワークフロー

エンティティのライフサイクル

エンティティライフサイクルは、エンティティが作成、追加、変更、削除などされるプロセスを記述します。 エンティティには、その存続期間中に多くの状態があります。 エンティティ状態を取得する方法を見る前に、エンティティ状態とは何かを見てみましょう。 状態は、次の値を宣言する System.Data.EntityState 型の列挙型です-

  • *追加:*エンティティは追加済みとしてマークされます。
  • *削除済み:*エンティティは削除済みとしてマークされます。
  • *変更済み:*エンティティが変更されました。
  • *変更なし:*エンティティは変更されていません。
  • *分離:*エンティティは追跡されません。

エンティティライフサイクルの状態変更

エンティティの状態はコンテキストによって自動的に設定されることもありますが、開発者が手動で変更することもできます。 ある状態から別の状態へのスイッチのすべての組み合わせは可能ですが、それらのいくつかは無意味です。 たとえば、 Added エンティティを Deleted 状態に、またはその逆に。

さまざまな州について議論しましょう。

変更されていない状態

  • エンティティが変更されていない場合、そのエンティティはコンテキストにバインドされていますが、変更されていません。
  • デフォルトでは、データベースから取得されたエンティティはこの状態です。
  • エンティティが(Attachメソッドを使用して)コンテキストにアタッチされると、同様にUnchanged状態になります。
  • コンテキストは、参照していないオブジェクトへの変更を追跡できないため、添付された場合、変更されていないものと見なされます。

分離状態

  • コンテキストはコード内のオブジェクトの作成を追跡できないため、新しく作成されたエンティティのデフォルトの状態はDetachedです。
  • これは、コンテキストのusingブロック内でエンティティをインスタンス化する場合でも当てはまります。
  • 分離は、追跡が無効になっているときにデータベースから取得されたエンティティの状態です。
  • エンティティが切り離されると、そのエンティティはコンテキストにバインドされないため、その状態は追跡されません。
  • 破棄、変更、他のクラスと組み合わせて使用​​、または必要に応じて他の方法で使用できます。
  • それを追跡するコンテキストがないため、Entity Frameworkには意味がありません。

追加された状態

  • エンティティが追加状態の場合、選択肢はほとんどありません。 実際、コンテキストからのみデタッチできます。
  • 当然、一部のプロパティを変更しても、Modified、Unchanged、またはDeletedに移動しても意味がないため、状態は追加のままです。
  • これは新しいエンティティであり、データベース内の行とは対応していません。
  • これは、これらの州のいずれかにいるための基本的な前提条件です(ただし、このルールはコンテキストによって強制されません)。

追加された状態

変更された状態

  • エンティティが変更されると、そのエンティティは変更されていない状態になり、一部のプロパティが変更されたことを意味します。
  • エンティティが変更状態になると、エンティティは切り離された状態または削除された状態に移行できますが、元の値を手動で復元しても、変更されていない状態にロールバックすることはできません。
  • エンティティをデタッチしてコンテキストに追加しない限り、このIDの行はデータベースに既に存在し、永続化すると実行時例外が発生するため、追加に変更することさえできません。

削除された状態

  • エンティティは、UnchangedまたはModifiedであるためDeleted状態になり、DeleteObjectメソッドが使用されました。
  • これは、この状態からDetached以外の値に無意味に変化するため、最も制限的な状態です。

コンテキストが制御するすべてのリソースをブロックの最後に配置する場合は、 using ステートメント。 using ステートメントを使用すると、コンパイラーは自動的にtry/finallyブロックを作成し、finallyブロックでdisposeを呼び出します。

using (var context = new UniContext()) {

   var student = new Student {
      LastName = "Khan",
      FirstMidName = "Ali",
      EnrollmentDate = DateTime.Parse("2005-09-01")
   };

   context.Students.Add(student);
   context.SaveChanges();
}

長期実行コンテキストで作業する場合は、次のことを考慮してください-

  • より多くのオブジェクトとその参照をメモリにロードすると、コンテキストのメモリ消費が急速に増加する場合があります。 これにより、パフォーマンスの問題が発生する可能性があります。
  • 不要になったコンテキストは必ず破棄してください。
  • 例外によってコンテキストが回復不能な状態になると、アプリケーション全体が終了する場合があります。
  • データのクエリと更新の時間のギャップが大きくなると、同時実行に関連する問題が発生する可能性が高くなります。
  • Webアプリケーションを使用する場合、リクエストごとにコンテキストインスタンスを使用します。
  • Windows Presentation Foundation(WPF)またはWindows Formsを使用する場合、フォームごとにコンテキストインスタンスを使用します。 これにより、コンテキストが提供する変更追跡機能を使用できます。

経験則

ウェブアプリケーション

  • 現在、Webアプリケーションでは、リクエストごとにコンテキストが使用されるのが一般的でベストプラクティスです。
  • Webアプリケーションでは、非常に短いリクエストを処理しますが、すべてのサーバートランザクションを保持するため、コンテキストが存在する適切な期間です。

デスクトップアプリケーション

  • Win Forms/WPFなどのデスクトップアプリケーションの場合 コンテキストは、フォーム/ダイアログ/ページごとに使用されます。
  • アプリケーションのシングルトンとしてコンテキストを持ちたくないので、あるフォームから別のフォームに移動するときにそれを破棄します。
  • このようにして、コンテキストの多くの能力を獲得し、長時間実行されるコンテキストの影響を受けなくなります。

エンティティフレームワーク-コードファーストアプローチ

Entity Frameworkには、エンティティモデルを作成するための3つのアプローチがあり、それぞれに長所と短所があります。

  • コードファースト
  • 最初のデータベース
  • モデルファースト

この章では、コードファーストアプローチについて簡単に説明します。 コードでDesignerを使用することを好む開発者もいれば、コードで作業するだけの開発者もいます。 これらの開発者向けに、Entity Frameworkには、コードファーストと呼ばれるモデリングワークフローがあります。

  • Code Firstモデリングワークフローは、存在しないデータベースを対象とし、Code Firstが作成します。
  • 空のデータベースがあり、Code Firstが新しいテーブルを追加する場合にも使用できます。
  • Code Firstでは、C#またはVB.Netクラスを使用してモデルを定義できます。
  • オプションで、クラスおよびプロパティの属性を使用して、または流れるようなAPIを使用して、追加の構成を実行できます。

コードファーストアプローチ

なぜ最初のコードですか?

  • Code Firstは、本当にパズルのピースのセットで構成されています。 まず、ドメインクラスです。
  • ドメインクラスは、Entity Frameworkとは関係ありません。 それらはあなたのビジネスドメインの単なるアイテムです。
  • Entity Frameworkには、これらのクラスとデータベース間の相互作用を管理するコンテキストがあります。
  • コンテキストはCode Firstに固有のものではありません。 これは、Entity Frameworkの機能です。
  • Code Firstは、コンテキストが管理しているクラスを検査するモデルビルダーを追加してから、一連のルールまたは規則を使用して、それらのクラスと関係がモデルを記述する方法、およびそのモデルをデータベースにマップする方法を決定します。
  • これらはすべて実行時に行われます。 あなたはこのモデルを見ることは決してないだろう、それはただの記憶の中にある。
  • Code Firstには、必要に応じてそのモデルを使用してデータベースを作成する機能があります。
  • また、Code First Migrationsと呼ばれる機能を使用して、モデルが変更された場合にデータベースを更新できます。

エンティティフレームワーク-モデルファーストアプローチ

この章では、Model Firstと呼ばれるワークフローを使用して、デザイナーでエンティティデータモデルを作成する方法を学習します。

  • Model Firstは、データベースがまだ存在しない新しいプロジェクトを開始するときに最適です。
  • モデルはEDMXファイルに保存され、Entity Framework Designerで表示および編集できます。
  • Model Firstでは、Entity Frameworkデザイナーでモデルを定義してから、モデルに一致するデータベーススキーマを作成するSQLを生成し、SQLを実行してデータベースにスキーマを作成します。
  • アプリケーションで対話するクラスは、EDMXファイルから自動的に生成されます。

以下は、モデルファーストアプローチを使用して新しいコンソールプロジェクトを作成する簡単な例です。

  • ステップ1 *-Visual Studioを開き、ファイル→新規→プロジェクトを選択します

コンソールプロジェクト

  • ステップ2 *-左ペインから[インストール済み]→[テンプレート]→[Visual C#]→[Windows]を選択し、中央ペインで[コンソールアプリケーション]を選択します。
  • ステップ3 *-[名前]フィールドに「EFModelFirstDemo」と入力します。
  • ステップ4 *-モデルを作成するには、まずソリューションエクスプローラーでコンソールプロジェクトを右クリックし、[追加]→[新しいアイテム…​]を選択します。

モデル

次のダイアログが開きます。

新規追加

  • ステップ5 *-中央のペインからADO.NET Entity Data Modelを選択し、NameフィールドにModelFirstDemoDBという名前を入力します。
  • ステップ6 *-[追加]ボタンをクリックして、[エンティティデータモデルウィザード]ダイアログを起動します。

モデルウィザードダイアログ

  • ステップ7 *-空のEF Designerモデルを選択し、[次へ]ボタンをクリックします。 Entity Framework Designerが開き、空のモデルが表示されます。 これで、エンティティ、プロパティ、および関連付けをモデルに追加できます。
  • ステップ8 *-デザイン画面を右クリックして、[プロパティ]を選択します。 [プロパティ]ウィンドウで、[エンティティコンテナ名]をModelFirstDemoDBContextに変更します。

デザインサーフェス

  • ステップ9 *-デザイン画面を右クリックして、[新規追加]→[エンティティ…]を選択します

新しいエンティティの追加

次の図に示すように、[エンティティの追加]ダイアログが開きます。

エンティティダイアログ

  • ステップ10 *-エンティティ名としてStudent、プロパティ名としてStudent IDを入力して、[OK]をクリックします。

生徒

  • ステップ11 *-デザイン画面で新しいエンティティを右クリックし、[新規追加]→[スカラープロパティ]を選択して、プロパティの名前として[名前]を入力します。

新しいエンティティ

  • ステップ12 *-FirstNameを入力し、LastNameやEnrollmentDateなどの別の2つのスカラープロパティを追加します。

スカラープロパティ

  • ステップ13 *-上記のすべてのステップに従って、さらに2つのエンティティコースと登録を追加し、次のステップに示すようにいくつかのスカラープロパティも追加します。

ビジュアルデザイナー

  • ステップ14 *-Visual Designerに3つのエンティティがあります。それらの間に何らかの関連または関係を追加しましょう。
  • ステップ15 *-デザイン画面を右クリックして、[新規追加]→[関連付け…​]を選択します。

関連付けの追加

  • ステップ16 *-関係の一方の端を生徒の多重度で1つ、もう一方の端点を多数の登録で登録する。

1つの多重度

  • ステップ17 *-これは、学生が多くの登録を持ち、登録が1人の学生に属することを意味します。
  • ステップ18 *-[外部エンティティのプロパティを[投稿]エンティティに追加する]ボックスがオンになっていることを確認し、[OK]をクリックします。
  • ステップ19 *-同様に、コースと登録の間にもう1つの関連付けを追加します。

コースと登録

  • ステップ20 *-エンティティ間に関連付けを追加すると、データモデルは次の画面のようになります。

データモデル

これで、データベースを生成し、データの読み取りと書き込みに使用できる単純なモデルができました。 先に進み、データベースを生成しましょう。

  • ステップ1 *-デザイン画面を右クリックして、[モデルからデータベースを生成…​]を選択します。

モデルからのデータベース

  • ステップ2 *-[新しい接続]をクリックして、既存のデータベースを選択するか、新しい接続を作成できます…

データベースの生成

  • ステップ3 *-新しいデータベースを作成するには、[新しい接続]をクリックします…

接続プロパティ

  • ステップ4 *-サーバー名とデータベース名を入力します。

サーバーとデータベース

  • ステップ5 *-[次へ]をクリックします。

サーバーとデータベース1

ステップ6 *-[完了]をクリックします。 これにより、プロジェクトに .edmx.sqlファイルが追加されます。 .sqlファイルを開いてVisual StudioでDDLスクリプトを実行し、右クリックして[実行]を選択します。

DDLスクリプト

  • ステップ7 *-データベースに接続するための次のダイアログが表示されます。

データベースに接続

  • ステップ8 *-正常に実行されると、次のメッセージが表示されます。

成功した実行

  • ステップ9 *-サーバーエクスプローラーに移動すると、指定された3つのテーブルでデータベースが作成されていることがわかります。

作成されたデータベース

次に、モデルを交換して、DbContext APIを使用するコードを生成する必要があります。

  • ステップ1 *-EF Designerでモデルの空の場所を右クリックし、[コード生成アイテムを追加…]を選択します

EF Designer

次の[新しい項目の追加]ダイアログが開きます。

ダイアログを開く

  • ステップ2 *-中央のペインでEF 6.x DbContext Generatorを選択し、[名前]フィールドにModelFirstDemoModelと入力します。
  • ステップ3 *-ソリューションエクスプローラーで、ModelFirstDemoModel.Context.ttおよびModelFirstDemoModel.ttテンプレートが生成されることがわかります。

テンプレート生成

ModelFirstDemoModel.Contextは、DbCcontextとオブジェクトセットを生成します。これらのオブジェクトセットを返すと、クエリ、コンテキスト、学生、コースなどに使用できます。

他のテンプレートは、学生、コースなどのすべてのタイプを扱います。 以下は、エンティティモデルから自動的に生成されるStudentクラスです。

CSharpコード

以下は、データベースにデータを入力および取得するC#コードです。

using System;
using System.Linq;

namespace EFModelFirstDemo {

   class Program {

      static void Main(string[] args) {

         using (var db = new ModelFirstDemoDBContext()) {

           //Create and save a new Student

            Console.Write("Enter a name for a new Student: ");
            var firstName = Console.ReadLine();

            var student = new Student {
               StudentID = 1,
               FirstName = firstName
            };

            db.Students.Add(student);
            db.SaveChanges();

            var query = from b in db.Students
               orderby b.FirstName select b;

            Console.WriteLine("All student in the database:");

            foreach (var item in query) {
               Console.WriteLine(item.FirstName);
            }

            Console.WriteLine("Press any key to exit...");
            Console.ReadKey();
         }
      }
   }
}

上記のコードが実行されると、次の出力が表示されます-

Enter a name for a new Student:
Ali Khan
All student in the database:
Ali Khan
Press any key to exit...

理解を深めるために、上記の例を段階的に実行することをお勧めします。

Entity Framework-データベースファーストアプローチ

この章では、Database Firstアプローチを使用したエンティティデータモデルの作成について学習します。

  • Database Firstアプローチは、Entity Data Modelに対するCode FirstおよびModel Firstアプローチの代替手段を提供します。 プロジェクト内のデータベースからモデルコード(クラス、プロパティ、DbContextなど)を作成し、それらのクラスがデータベースとコントローラー間のリンクになります。
  • Database Firstアプローチは、既存のデータベースからエンティティフレームワークを作成します。 モデル/データベース同期やコード生成など、他のすべての機能は、モデルファーストアプローチで使用したのと同じ方法で使用します。

簡単な例を見てみましょう。 次の図に示すように、3つのテーブルを含むデータベースが既にあります。

新しいコンソールプロジェクト

  • ステップ1 *-DatabaseFirstDemo名で新しいコンソールプロジェクトを作成しましょう。
  • ステップ2 *-モデルを作成するには、まずソリューションエクスプローラーでコンソールプロジェクトを右クリックし、[追加]→[新しいアイテム]を選択します…

モデルの作成

  • ステップ3 *-中央のペインからADO.NET Entity Data Modelを選択し、[名前]フィールドにDatabaseFirstModelという名前を入力します。
  • ステップ4 *-[追加]ボタンをクリックして、[エンティティデータモデルウィザード]ダイアログを起動します。

モデルの内容

  • ステップ5 *-データベースからEF Designerを選択し、[次へ]ボタンをクリックします。

エンティティモデルウィザード

  • ステップ6 *-既存のデータベースを選択し、[次へ]をクリックします。

既存のデータベース

  • ステップ7 *-Entity Framework 6.xを選択し、[次へ]をクリックします。

Entity Framework

  • ステップ8 *-含めるすべてのテーブルビューとストアドプロシージャを選択し、[完了]をクリックします。

エンティティモデルとPOCOクラスがデータベースから生成されていることがわかります。

POCOクラス

program.csファイルに次のコードを記述して、データベースからすべての生徒を取得します。

using System;
using System.Linq;

namespace DatabaseFirstDemo {

   class Program {

      static void Main(string[] args) {

         using (var db = new UniContextEntities()) {

            var query = from b in db.Students
               orderby b.FirstMidName select b;

            Console.WriteLine("All All student in the database:");

            foreach (var item in query) {
               Console.WriteLine(item.FirstMidName +" "+ item.LastName);
            }

            Console.WriteLine("Press any key to exit...");
            Console.ReadKey();
         }
      }
   }
}

上記のプログラムが実行されると、次の出力が表示されます-

All student in the database:
Ali Khan
Arturo   finand
Bill Gates
Carson Alexander
Gytis Barzdukas
Laura Norman
Meredith Alonso
Nino Olivetto
Peggy Justice
Yan Li
Press any key to exit...

上記のプログラムを実行すると、以前にデータベースに入力されたすべての生徒の名前が表示されます。

理解を深めるために、上記の例を段階的に実行することをお勧めします。

Entity Framework-DEVアプローチ

この章では、DesignerまたはDatabase Firstを使用して、またはCode Firstを使用してモデルを構築することに焦点を当てましょう。 以下は、選択するモデリングワークフローを決定するのに役立つガイドラインです。

  • Code Firstモデリング、Database Firstモデリング、Model Firstモデリングワークフローの例をすでに見てきました。
  • Database FirstおよびModel FirstワークフローではDesignerを使用しましたが、1つはデータベースから開始してモデルを作成し、もう1つはモデルから開始してデータベースを作成します。

デザイナーモデル

  • Visual Designerとコード生成を使用したくない開発者向けに、Entity FrameworkにはCode Firstと呼ばれるまったく異なるワークフローがあります。
  • Code Firstの典型的なワークフローは、データベースさえ持っていない真新しいアプリケーションに最適です。 クラスとコードを定義してから、Code Firstにデータベースの外観を理解させます。
  • データベースを使用してCode Firstを起動することもできますが、これによりCode Firstが少し矛盾します。 ただし、データベースをクラスにリバースエンジニアリングできるツールがあり、これはコーディングを有利に開始するのに最適な方法です。

これらのオプションが与えられたら、デシジョンツリーを見てみましょう。

  • 生成されたコードでビジュアルデザイナーを使用する場合は、EFデザイナーに関連するワークフローのいずれかを選択します。 データベースがすでに存在する場合は、Database Firstがパスです。
  • データベースのないまったく新しいプロジェクトでビジュアルデザイナーを使用する場合は、モデルファーストを使用します。
  • デザイナーではなくコードだけを使用する場合は、おそらくコードファーストが、データベースをクラスにリバースエンジニアリングするツールを使用するオプションと一緒に使用できます。
  • 既存のクラスがある場合、最善の策はそれらをCode Firstで使用することです。

Entity Framework-データベース操作

前の章では、エンティティデータモデルを定義する3つの異なる方法を学びました。

  • そのうちの2つ、Database FirstとModel Firstは、コード生成と組み合わせたEntity Frameworkデザイナーに依存していました。
  • 3番目のコードファーストでは、ビジュアルデザイナーをスキップして、独自のコードを記述することができます。
  • 選択するパスに関係なく、ドメインクラスになります。1つ以上のEntity Framework DbContextクラスを使用すると、それらのクラスに関連するデータを取得して永続化できます。

アプリケーションのDbContext APIは、クラスとデータベースの間のブリッジとして使用されます。 DbContextは、Entity Frameworkで最も重要なクラスの1つです。

  • クエリを表現および実行できます。
  • データベースからクエリ結果を取得し、それらをモデルクラスのインスタンスに変換します。
  • 追加や削除など、エンティティへの変更を追跡し、必要に応じてデータベースに送信される挿入、更新、削除ステートメントの作成をトリガーします。

以下は、この章でさまざまな操作を実行するドメイン広告コンテキストクラスです。 これは、チャプタであるDatabase First Approachで作成したのと同じ例です。

コンテキストクラスの実装

using System;
using System.Data.Entity;
using System.Data.Entity.Infrastructure;
using System.Data.Entity.Core.Objects;
using System.Linq;

namespace DatabaseFirstDemo {

   public partial class UniContextEntities : DbContext {

      public UniContextEntities(): base("name = UniContextEntities") {}

      protected override void OnModelCreating(DbModelBuilder modelBuilder) {
         throw new UnintentionalCodeFirstException();
      }

      public virtual DbSet<Course> Courses { get; set; }
      public virtual DbSet<Enrollment> Enrollments { get; set; }
      public virtual DbSet<Student> Students { get; set; }
   }
}

ドメインクラスの実装

コースクラス

namespace DatabaseFirstDemo {

   using System;
   using System.Collections.Generic;

   public partial class Course {

      [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage",
         "CA2214:DoNotCallOverridableMethodsInConstructors")]

      public Course() {
         this.Enrollments = new HashSet<Enrollment>();
      }

      public int CourseID { get; set; }
      public string Title { get; set; }
      public int Credits { get; set; }

      [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage",
         "CA2227:CollectionPropertiesShouldBeReadOnly")]

      public virtual ICollection<Enrollment> Enrollments { get; set; }
   }
}

学生クラス

namespace DatabaseFirstDemo {

   using System;
   using System.Collections.Generic;

   public partial class Student {

      [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage",
         "CA2214:DoNotCallOverridableMethodsInConstructors")]

      public Student() {
         this.Enrollments = new HashSet<Enrollment>();
      }

      public int ID { get; set; }
      public string LastName { get; set; }
      public string FirstMidName { get; set; }
      public System.DateTime EnrollmentDate { get; set; }

      [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage",
         "CA2227:CollectionPropertiesShouldBeReadOnly")]

      public virtual ICollection<Enrollment> Enrollments { get; set; }
   }
}

入学クラス

namespace DatabaseFirstDemo {

   using System;
   using System.Collections.Generic;

   public partial class Enrollment {

      public int EnrollmentID { get; set; }
      public int CourseID { get; set; }
      public int StudentID { get; set; }
      public Nullable<int> Grade { get; set; }

      public virtual Course Course { get; set; }
      public virtual Student Student { get; set; }
   }
}

操作を作成

Entity Frameworkで新しいオブジェクトを追加するには、オブジェクトの新しいインスタンスを作成し、DbSetのAddメソッドを使用して登録するだけです。 次のコードを使用すると、新しい学生をデータベースに追加できます。

class Program {

   static void Main(string[] args) {

      var newStudent = new Student();

     //set student name

      newStudent.FirstMidName = "Bill";
      newStudent.LastName = "Gates";
      newStudent.EnrollmentDate = DateTime.Parse("2015-10-21");
      newStudent.ID = 100;

     //create DBContext object

      using (var dbCtx = new UniContextEntities()) {

        //Add Student object into Students DBset
         dbCtx.Students.Add(newStudent);

        //call SaveChanges method to save student into database
         dbCtx.SaveChanges();
      }
   }
}

更新操作

既存のオブジェクトの変更は、変更するプロパティに割り当てられた値を更新し、SaveChangesを呼び出すのと同じくらい簡単です。 たとえば、次のコードは、アリの姓をカーンからアスラムに変更するために使用されます。

using (var context = new UniContextEntities()) {

   var student = (from d in context.Students where d.FirstMidName == "Ali" select d).Single();
   student.LastName = "Aslam";
   context.SaveChanges();
}

削除操作

Entity Frameworkを使用してエンティティを削除するには、DbSetのRemoveメソッドを使用します。 削除は、既存のエンティティと新しく追加されたエンティティの両方で機能します。 追加されたがまだデータベースに保存されていないエンティティに対してRemoveを呼び出すと、エンティティの追加がキャンセルされます。 エンティティは変更トラッカーから削除され、DbContextによって追跡されなくなります。 変更追跡されている既存のエンティティでRemoveを呼び出すと、次回SaveChangesが呼び出されたときに削除するエンティティが登録されます。 次の例は、学生が名がアリであるデータベースから削除されるコードです。

using (var context = new UniContextEntities()) {
   var bay = (from d in context.Students where d.FirstMidName == "Ali" select d).Single();
   context.Students.Remove(bay);
   context.SaveChanges();
}

読み取り操作

データベースから既存のデータを読み取るのは非常に簡単です。 以下は、Studentテーブルからすべてのデータが取得され、アルファベット順に生徒の姓と名が表示されたプログラムが表示されるコードです。

using (var db = new UniContextEntities()) {

   var query = from b in db.Students orderby b.FirstMidName select b;
   Console.WriteLine("All All student in the database:");

   foreach (var item in query) {
      Console.WriteLine(item.FirstMidName +" "+ item.LastName);
   }

   Console.WriteLine("Press any key to exit...");
   Console.ReadKey();
}

Entity Framework-並行性

データアクセスの開発者は、「複数の人が同じデータを同時に編集しているとどうなりますか?」というデータの同時実行性に関する質問に答えるのに苦労しています。

  • 幸運なことに、「問題なく、最後に勝った」というビジネスルールに対処できます。
  • この場合、並行性は問題になりません。 おそらく、それはそれほど単純ではなく、すべてのシナリオを一度に解決するための特効薬はありません。
  • 既定では、Entity Frameworkは「勝者の最後の1つ」のパスを使用します。つまり、データが取得されてからデータが保存されるまでに誰かがデータを更新した場合でも、最新の更新が適用されます。

よりよく理解するために例を挙げましょう。 次の例は、Courseテーブルに新しい列VersionNoを追加します。

コース表

デザイナーに移動し、デザイナーウィンドウを右クリックして、データベースから更新モデルを選択します…

デザイナー

別の列がコースエンティティに追加されていることがわかります。

コースエンティティ

次の図に示すように、新しく作成された列VersionNoを右クリックして[プロパティ]を選択し、ConcurrencyModeをFixedに変更します。

新規作成列

Course.VersionNoのConcurrencyModeをFixedに設定すると、コースが更新されるたびに、UpdateコマンドはEntityKeyとVersionNoプロパティを使用してコースを検索します。

簡単なシナリオを見てみましょう。 2人のユーザーが同じコースを同時に取得し、ユーザー1がそのコースのタイトルをMathsに変更し、ユーザー2の前に変更を保存します。 その後、ユーザー2がユーザー1が変更を保存する前に取得したコースのタイトルを変更すると、ユーザー2は同時実行例外 "User2:Optimistic Concurrency exception occurred" を取得します。

using System;
using System.Data.Entity;
using System.Data.Entity.Infrastructure;
using System.Linq;

namespace DatabaseFirstDemo {

   class Program {

      static void Main(string[] args) {

         Course c1 = null;
         Course c2 = null;

        //User 1 gets Course

         using (var context = new UniContextEntities()) {
            context.Configuration.ProxyCreationEnabled = false;
            c1 = context.Courses.Where(s ⇒ s.CourseID == 1).Single();
         }

        //User 2 also get the same Course

         using (var context = new UniContextEntities()) {
            context.Configuration.ProxyCreationEnabled = false;
            c2 = context.Courses.Where(s ⇒ s.CourseID == 1).Single();
         }

        //User 1 updates Course Title
         c1.Title = "Edited from user1";

        //User 2 updates Course Title
         c2.Title = "Edited from user2";

        //User 1 saves changes first

         using (var context = new UniContextEntities()) {

            try {
               context.Entry(c1).State = EntityState.Modified;
               context.SaveChanges();
            } catch (DbUpdateConcurrencyException ex) {
               Console.WriteLine("User1: Optimistic Concurrency exception occurred");
            }
         }

        //User 2 saves changes after User 1.
        //User 2 will get concurrency exection
        //because CreateOrModifiedDate is different in the database

         using (var context = new UniContextEntities()) {

            try {
               context.Entry(c2).State = EntityState.Modified;
               context.SaveChanges();
            } catch (DbUpdateConcurrencyException ex) {
               Console.WriteLine("User2: Optimistic Concurrency exception occurred");
            }
         }
      }
   }
}

Entity Framework-トランザクション

Entity Frameworkのすべてのバージョンでは、* SaveChanges()*を実行してデータベースを挿入、更新、または削除すると、フレームワークはその操作をトランザクションでラップします。 SaveChangesを呼び出すと、永続性が成功したかどうかに応じて、コンテキストがトランザクションを自動的に開始し、コミットまたはロールバックします。

  • これはすべて透過的であり、対処する必要はありません。
  • このトランザクションは操作を実行するのに十分な長さだけ持続し、その後完了します。
  • このような別の操作を実行すると、新しいトランザクションが開始されます。

Entity Framework 6は以下を提供します-

Database.BeginTransaction()

  • これは、ユーザーのトランザクションを開始および完了するための既存のDbContext内のシンプルで簡単な方法です。
  • 同じトランザクション内で複数の操作を組み合わせることができるため、すべてがコミットされるか、すべてが1つのロールバックされます。
  • また、ユーザーはトランザクションの分離レベルをより簡単に指定できます。

Database.UseTransaction()

  • これにより、DbContextは、Entity Frameworkの外部で開始されたトランザクションを使用できます。

単一のトランザクションで複数の操作が実行される次の例を見てみましょう。 コードは次のとおりです-

class Program {

   static void Main(string[] args) {

      using (var context = new UniContextEntities()) {

         using (var dbContextTransaction = context.Database.BeginTransaction()) {

            try {

               Student student = new Student() {
                  ID = 200,
                  FirstMidName = "Ali",
                  LastName = "Khan",
                  EnrollmentDate = DateTime.Parse("2015-12-1")
               };

               context.Students.Add(student);

               context.Database.ExecuteSqlCommand(@"UPDATE Course SET Title =
                  'Calculus'" + "WHERE CourseID = 1045");

               var query = context.Courses.Where(c ⇒ c.CourseID == 1045);

               foreach (var item in query) {
                  Console.WriteLine(item.CourseID.ToString()
                     + " " + item.Title + " " + item.Credits);
               }

               context.SaveChanges();
               var query1 = context.Students.Where(s ⇒ s.ID == 200);

               foreach (var item in query1) {
                  Console.WriteLine(item.ID.ToString()
                     + " " + item.FirstMidName + " " + item.LastName);
               }

               dbContextTransaction.Commit();
            } catch (Exception) {
               dbContextTransaction.Rollback();
            }

         }
      }
   }
}
  • トランザクションを開始するには、基礎となるストア接続が開いている必要があります。
  • したがって、Database.BeginTransaction()を呼び出すと、接続が開かれます(まだ開かれていない場合)。
  • DbContextTransactionが接続を開いた場合、Dispose()が呼び出されたときに接続を閉じます。

Entity Framework-ビュー

ビューは、事前定義されたクエリによって取得されたデータを含むオブジェクトです。 ビューは、結果セットがクエリから派生した仮想オブジェクトまたはテーブルです。 データの列と行が含まれているため、実際のテーブルに非常に似ています。 以下は、ビューのいくつかの典型的な使用法です-

  • 基になるテーブルのデータをフィルター処理する
  • セキュリティのためにデータをフィルタリングする
  • 複数のサーバーに分散されたデータを集中化
  • 再利用可能なデータセットを作成する

ビューは、テーブルを使用できるのと同様の方法で使用できます。 ビューをエンティティとして使用するには、まずデータベースビューをEDMに追加する必要があります。 モデルにビューを追加した後、作成、更新、削除操作を除いて、通常のエンティティと同じ方法でモデルを操作できます。

データベースからモデルにビューを追加する方法を見てみましょう。

  • ステップ1 *-新しいコンソールアプリケーションプロジェクトを作成します。

アプリケーションプロジェクト

  • ステップ2 *-ソリューションエクスプローラーでプロジェクトを右クリックし、[追加]→[新しいアイテム]を選択します。

プロジェクトソリューションエクスプローラー

  • ステップ3 *-中央のペインからADO.NET Entity Data Modelを選択し、[名前]フィールドにViewViewという名前を入力します。
  • ステップ4 *-[追加]ボタンをクリックして、[エンティティデータモデルウィザード]ダイアログを起動します。

ボタンを追加

  • ステップ5 *-データベースからEF Designerを選択し、[次へ]ボタンをクリックします。

エンティティモデルウィザード

  • ステップ6 *-既存のデータベースを選択し、[次へ]をクリックします。

既存のデータベース

  • ステップ7 *-Entity Framework 6.xを選択し、[次へ]をクリックします。

Entity Framework Next

  • ステップ8 *-データベースからテーブルとビューを選択し、[完了]をクリックします。

テーブルビュー

ビューが作成され、エンティティとしてプログラムで使用できることをデザイナーウィンドウで確認できます。

ソリューションエクスプローラーで、MyViewクラスもデータベースから生成されていることがわかります。

すべてのデータがビューから取得される例を見てみましょう。 以下はコードです-

class Program {

   static void Main(string[] args) {

      using (var db = new UniContextEntities()) {

         var query = from b in db.MyViews
            orderby b.FirstMidName select b;

         Console.WriteLine("All student in the database:");

         foreach (var item in query) {
            Console.WriteLine(item.FirstMidName + " " + item.LastName);
         }

         Console.WriteLine("Press any key to exit...");
         Console.ReadKey();
      }
   }
}

上記のコードが実行されると、次の出力が表示されます-

All student in the database:
Ali Khan
Arturo   finand
Bill Gates
Carson Alexander
Gytis Barzdukas
Laura Norman
Meredith Alonso
Nino Olivetto
Peggy Justice
Yan Li
Press any key to exit...

理解を深めるために、上記の例を段階的に実行することをお勧めします。

Entity Framework-ストアドプロシージャ

Entity Frameworkを使用すると、自動コマンド生成の代わりに、または自動コマンド生成と組み合わせて、エンティティデータモデルのストアドプロシージャを使用できます。

  • ストアドプロシージャを使用してデータベーステーブルで事前定義されたロジックを実行できます。多くの組織では、これらのストアドプロシージャの使用を必要とするポリシーが用意されています。
  • また、EFがエンティティの挿入、更新、削除にストアドプロシージャを使用するように指定することもできます。
  • 動的に構築されたコマンドは、安全で効率的であり、一般に自分で作成するコマンドと同等かそれ以上ですが、ストアドプロシージャが既に存在し、会社の慣行によりテーブルの直接使用が制限される場合が多くあります。
  • または、ストアで実行されるものを明示的に制御し、ストアドプロシージャを作成することを希望する場合があります。

次の例では、ファイル→新規→プロジェクトから新しいプロジェクトを作成します。

新しいプロジェクトの手順

  • ステップ1 *-中央のペインからコンソールアプリケーションを選択し、名前フィールドにStoredProceduresDemoと入力します。
  • ステップ2 *-サーバーエクスプローラーで、データベースを右クリックします。
  • ステップ3 *-[新しいクエリ]を選択し、T-SQLエディターに次のコードを入力して、データベースに新しいテーブルを追加します。
IF NOT EXISTS (SELECT * FROM sys.objects WHERE object_id =
   OBJECT_ID(N'[dbo].[StudentGrade]') AND type in (N'U'))

BEGIN

   CREATE TABLE [dbo].[StudentGrade](

      [EnrollmentID] [int] IDENTITY(1,1) NOT NULL,
      [CourseID] [int] NOT NULL,
      [StudentID] [int] NOT NULL,
      [Grade] [decimal](3, 2) NULL,

      CONSTRAINT [PK_StudentGrade] PRIMARY KEY CLUSTERED (
         [EnrollmentID] ASC
      )

      WITH (IGNORE_DUP_KEY = OFF) ON [PRIMARY]

   ) ON [PRIMARY]

END
GO
  • ステップ4 *-エディターを右クリックして、[実行]を選択します。

編集者

  • ステップ5 *-データベースを右クリックして、更新をクリックします。 データベースに新しく追加されたテーブルが表示されます。
  • ステップ6 *-サーバーエクスプローラーで、データベースをもう一度右クリックします。

サーバーデータベース

  • ステップ7 *-[新しいクエリ]を選択し、T-SQLエディターで次のコードを入力して、データベースにストアドプロシージャを追加します。これにより、学生の成績が返されます。
IF NOT EXISTS (SELECT * FROM sys.objects WHERE object_id =
   OBJECT_ID(N'[dbo].[GetStudentGrades]') AND type in (N'P', N'PC'))

BEGIN

   EXEC dbo.sp_executesql @statement = N'
   CREATE PROCEDURE [dbo].[GetStudentGrades]
   @StudentID int
   AS
   SELECT EnrollmentID, Grade, CourseID, StudentID FROM dbo.StudentGrade
   WHERE StudentID = @StudentID

END
GO
  • ステップ8 *-エディターを右クリックして、[実行]を選択します。

実行

  • ステップ9 *-データベースを右クリックして、更新をクリックします。 データベースにストアドプロシージャが作成されていることがわかります。

作成されたストアプロシージャ

  • ステップ10 *-ソリューションエクスプローラーでプロジェクト名を右クリックし、[追加]→[新しいアイテム]を選択します。
  • ステップ11 *-次に、[テンプレート]ペインで[ADO.NETエンティティデータモデル]を選択します。

テンプレートペイン

  • ステップ12 *-名前としてSPModelを入力し、[追加]をクリックします。
  • ステップ13 *-[モデルコンテンツの選択]ダイアログボックスで、データベースからEFデザイナーを選択し、[次へ]をクリックします。

モデルの内容

  • ステップ14 *-データベースを選択して、[次へ]をクリックします。

データベース1

  • ステップ15 *-[データベースオブジェクトの選択]ダイアログボックスで、テーブル、ビューをクリックします。

データベースオブジェクト

  • ステップ16 *-[ストアドプロシージャと関数]ノードの下にあるGetStudentGradesForCourse関数を選択し、[完了]をクリックします。
  • ステップ17 *-[表示]→[他のウィンドウ]→[エンティティデータモデルブラウザー]を選択し、[関数のインポート]で[GetStudentGrades]を右クリックして[編集]を選択します。

エンティティブラウザ

次のダイアログが生成されます。

エンティティブラウザダイアログ

  • ステップ18 *-[エンティティ]ラジオボタンをクリックし、このストアドプロシージャの戻り値の型としてコンボボックスからStudentGradeを選択して、[OK]をクリックします。

GetStudentGradesストアドプロシージャのパラメーターとして学生IDを渡すことにより、すべての成績が取得される次のC#コードを見てみましょう。

class Program {

   static void Main(string[] args) {

      using (var context = new UniContextEntities()) {

         int studentID = 22;
         var studentGrades = context.GetStudentGrades(studentID);

         foreach (var student in studentGrades) {
            Console.WriteLine("Course ID: {0}, Title: {1}, Grade: {2} ",
               student.CourseID, student.Course.Title, student.Grade);
         }

         Console.ReadKey();

      }
   }
}

上記のコードをコンパイルして実行すると、次の出力が表示されます-

Course ID: 4022, Title: Microeconomics, Grade: 3.00
Course ID: 4041, Title: Macroeconomics, Grade: 3.50

理解を深めるために、上記の例を段階的に実行することをお勧めします。

Entity Framework-切断されたエンティティ

この章では、コンテキストによって追跡されていないエンティティに変更を加える方法を見てみましょう。 コンテキストによって追跡されていないエンティティは、「切断された」エンティティと呼ばれます。

  • ユーザーインターフェイスとデータベースアクセスレイヤーが同じアプリケーションプロセスで実行されるほとんどの単層アプリケーションでは、おそらくコンテキストによって追跡されているエンティティに対して操作を実行するだけです。
  • 切断されたエンティティに対する操作は、N層アプリケーションでより一般的です。
  • N層アプリケーションでは、サーバー上の一部のデータを取得し、それをネットワーク経由でクライアントマシンに返します。
  • その後、クライアントアプリケーションはこのデータを操作してから、サーバーに戻して永続化します。

以下は、切断されたエンティティグラフまたは単一の切断されたエンティティで実行する必要がある2つのステップです。

  • エンティティを新しいコンテキストインスタンスに接続し、これらのエンティティに関するコンテキストを認識させます。
  • 適切なEntityStatesをこれらのエンティティに手動で設定します。

エンティティに変更

Studentエンティティが2つのEnrollmentエンティティに追加されている次のコードを見てみましょう。

class Program {

   static void Main(string[] args) {

      var student = new Student {

         ID = 1001,
         FirstMidName = "Wasim",
         LastName = "Akram",

         EnrollmentDate = DateTime.Parse("2015-10-10"),
            Enrollments = new List<Enrollment> {

               new Enrollment{EnrollmentID = 2001,CourseID = 4022, StudentID = 1001 },
               new Enrollment{EnrollmentID = 2002,CourseID = 4025, StudentID = 1001 },
         }
      };

      using (var context = new UniContextEntities()) {

         context.Students.Add(student);
         Console.WriteLine("New Student ({0} {1}): {2}",
            student.FirstMidName, student.LastName, context.Entry(student).State);

         foreach (var enrollment in student.Enrollments) {
            Console.WriteLine("Enrollment ID: {0} State: {1}",
               enrollment.EnrollmentID, context.Entry(enrollment).State);
         }

         Console.WriteLine("Press any key to exit...");
         Console.ReadKey();
      }
   }
}
  • このコードは、新しいStudentインスタンスを作成します。このインスタンスは、Enrollmentsプロパティで2つの新しいEnrollmentインスタンスも参照します。
  • 次に、Addメソッドを使用して、新しい生徒がコンテキストに追加されます。
  • 生徒が追加されると、コードはDbContext.Entryメソッドを使用して、Entity Frameworkが新しい生徒について持っている変更追跡情報にアクセスします。
  • この変更追跡情報から、Stateプロパティを使用してエンティティの現在の状態を書き出します。
  • このプロセスは、新しい生徒から参照される新しく作成された登録ごとに繰り返されます。 あなたがアプリケーションを実行すると、次の出力が表示されます-
New Student   (Wasim  Akram): Added
Enrollment ID: 2001 State: Added
Enrollment ID: 2002 State: Added
Press any key to exit...

DbSet.AddはEntity Frameworkに新しいエンティティを通知するために使用されますが、DbSet.AttachはEntity Frameworkに既存のエンティティを通知するために使用されます。 Attachメソッドは、エンティティをUnchanged状態にマークします。

切断されたエンティティがDbContextでアタッチされている次のC#コードを見てみましょう。

class Program {

   static void Main(string[] args) {

      var student = new Student {

         ID = 1001,
         FirstMidName = "Wasim",
         LastName = "Akram",
         EnrollmentDate = DateTime.Parse("2015-10-10"),

         Enrollments = new List<Enrollment> {
            new Enrollment { EnrollmentID = 2001, CourseID = 4022, StudentID = 1001 },
            new Enrollment { EnrollmentID = 2002, CourseID = 4025, StudentID = 1001 },
         }

      };

      using (var context = new UniContextEntities()) {

         context.Students.Attach(student);
         Console.WriteLine("New Student ({0} {1}): {2}",
            student.FirstMidName, student.LastName, context.Entry(student).State);

         foreach (var enrollment in student.Enrollments) {
            Console.WriteLine("Enrollment ID: {0} State: {1}", enrollment.EnrollmentID,
               context.Entry(enrollment).State);
         }

         Console.WriteLine("Press any key to exit...");
         Console.ReadKey();
      }
   }
}

上記のコードがAttach()メソッドで実行されると、次の出力が表示されます。

New Student   (Wasim  Akram): Unchanged
Enrollment ID: 2001 State: Unchanged
Enrollment ID: 2002 State: Unchanged
Press any key to exit...

Entity Framework-テーブル値関数

この章では、Entity Framework Designerを使用してテーブル値関数(TVF)をマップする方法と、LINQクエリからTVFを呼び出す方法を学習します。

  • TVFは現在、Database Firstワークフローでのみサポートされています。
  • Entity Frameworkバージョン5で初めて導入されました。
  • TVFを使用するには、.NET Framework 4.5以上をターゲットにする必要があります。
  • ストアドプロシージャに非常に似ていますが、1つの重要な違いがあります。つまり、TVFの結果は構成可能です。 つまり、TVFの結果はLINQクエリで使用できますが、ストアドプロシージャの結果は使用できません。

ファイル→新規→プロジェクトから新しいプロジェクトを作成する次の例を見てみましょう。

プロジェクトの作成

  • ステップ1 *-中央のペインからコンソールアプリケーションを選択し、名前フィールドにTableValuedFunctionDemoと入力します。
  • ステップ2 *-サーバーエクスプローラーで、データベースを右クリックします。

エクスプローラーデータベース

  • ステップ3 *-[新しいクエリ]を選択し、T-SQLエディターに次のコードを入力して、データベースに新しいテーブルを追加します。
IF NOT EXISTS (SELECT * FROM sys.objects WHERE object_id =
   OBJECT_ID(N'[dbo].[StudentGrade]') AND type in (N'U'))

BEGIN

   CREATE TABLE [dbo].[StudentGrade](

      [EnrollmentID] [int] IDENTITY(1,1) NOT NULL,
      [CourseID] [int] NOT NULL,
      [StudentID] [int] NOT NULL,
      [Grade] [decimal](3, 2) NULL,

      CONSTRAINT [PK_StudentGrade] PRIMARY KEY CLUSTERED ([EnrollmentID] ASC)

      WITH (IGNORE_DUP_KEY = OFF) ON [PRIMARY]

   ) ON [PRIMARY]

END
GO
  • ステップ4 *-エディターを右クリックして、[実行]を選択します。

実行の選択

  • ステップ5 *-データベースを右クリックして、更新をクリックします。 データベースに新しく追加されたテーブルが表示されます。

テーブルを追加

  • ステップ6 *-コースの学生の成績を返す関数を作成します。 T-SQLエディターで次のコードを入力します。
CREATE FUNCTION [dbo].[GetStudentGradesForCourse]

(@CourseID INT)

RETURNS TABLE

RETURN
   SELECT [EnrollmentID],
      [CourseID],
      [StudentID],
      [Grade]
   FROM   [dbo].[StudentGrade]
   WHERE  CourseID = @CourseID
  • ステップ7 *-エディターを右クリックして、[実行]を選択します。

エディター選択

これで、関数が作成されたことがわかります。

作成された関数

  • ステップ8 *-ソリューションエクスプローラーでプロジェクト名を右クリックし、[追加]→[新しいアイテム]を選択します。
  • ステップ9 *-次に、[テンプレート]ペインで[ADO.NETエンティティデータモデル]を選択します。

エンティティテンプレートペイン

  • ステップ10 *-名前としてTVFModelを入力し、[追加]をクリックします。
  • ステップ11 *-[モデルコンテンツの選択]ダイアログボックスで、データベースからEFデザイナーを選択し、[次へ]をクリックします。

コンテンツダイアログボックス

  • ステップ12 *-データベースを選択して、[次へ]をクリックします。

データベースの選択

  • ステップ13 *-[データベースオブジェクトの選択]ダイアログボックスで、テーブル、ビューを選択します。

オブジェクトダイアログボックス

  • ステップ14 *-[ストアドプロシージャと関数]ノードの下にあるGetStudentGradesForCourse関数を選択し、[完了]をクリックします。
  • ステップ15 *-[表示]→[他のウィンドウ]→[エンティティデータモデルブラウザー]を選択し、[関数のインポート]で[GetStudentGradesForCourse]を右クリックして[編集]を選択します。

ビューの選択

次のダイアログが表示されます。

ダイアログ

  • ステップ16 *-[エンティティ]ラジオボタンをクリックし、この関数の戻り値のタイプとしてコンボボックスから[登録]を選択し、[OK]をクリックします。

データベースのコースID = 4022に登録されているすべての学生の成績が取得される次のC#コードを見てみましょう。

class Program {

   static void Main(string[] args) {

      using (var context = new UniContextEntities()) {

         var CourseID = 4022;

        //Return all the best students in the Microeconomics class.
         var students = context.GetStudentGradesForCourse(CourseID);

         foreach (var result in students) {
            Console.WriteLine("Student ID:  {0}, Grade: {1}",
               result.StudentID, result.Grade);
         }

         Console.ReadKey();
      }
   }
}

上記のコードをコンパイルして実行すると、次の出力が表示されます-

Student ID: 1, Grade: 2
Student ID: 4, Grade: 4
Student ID: 9, Grade: 3.5

理解を深めるために、上記の例を段階的に実行することをお勧めします。

Entity Framework-ネイティブSQL

Entity Frameworkでは、LINQを使用してエンティティクラスでクエリを実行できます。 DbCOntextを使用して、データベースに対して直接生のSQLを使用したクエリを実行することもできます。 この手法は、Code FirstとEF Designerで作成されたモデルに等しく適用できます。

既存のエンティティでのSQLクエリ

DbSetのSqlQueryメソッドを使用すると、エンティティインスタンスを返す生のSQLクエリを作成できます。 返されたオブジェクトは、LINQクエリによって返された場合と同様に、コンテキストによって追跡されます。 たとえば-

class Program {

   static void Main(string[] args) {

      using (var context = new UniContextEntities()) {

         var students = context.Students.SqlQuery("SELECT *FROM dbo.Student").ToList();

         foreach (var student in students) {
            string name = student.FirstMidName + " " + student.LastName;
            Console.WriteLine("ID: {0}, Name: {1}, \tEnrollment Date {2} ",
               student.ID, name, student.EnrollmentDate.ToString());
         }

         Console.ReadKey();
      }
   }
}

上記のコードは、データベースからすべての生徒を取得します。

非エンティティ型のSQLクエリ

DatabaseクラスのSqlQueryメソッドを使用して、プリミティブ型を含む任意の型のインスタンスを返すSQLクエリを作成できます。 たとえば-

class Program {

   static void Main(string[] args) {

      using (var context = new UniContextEntities()) {

         var studentNames = context.Database.SqlQuery
            <string>("SELECT FirstMidName FROM dbo.Student").ToList();

         foreach (var student in studentNames) {
            Console.WriteLine("Name: {0}", student);
         }

         Console.ReadKey();
      }
   }
}

データベースへのSQLコマンド

ExecuteSqlCommnadメソッドは、Insert、Update、Deleteコマンドなどの非クエリコマンドをデータベースに送信する際に使用されます。 生徒の名がID = 1として更新される次のコードを見てみましょう

class Program {

   static void Main(string[] args) {

      using (var context = new UniContextEntities()) {

        //Update command

         int noOfRowUpdated = context.Database.ExecuteSqlCommand("Update
            student set FirstMidName = 'Ali' where ID = 1");

         context.SaveChanges();

         var student = context.Students.SqlQuery("SELECT* FROM
            dbo.Student where ID = 1").Single();

         string name = student.FirstMidName + " " + student.LastName;

         Console.WriteLine("ID: {0}, Name: {1}, \tEnrollment Date {2} ",
            student.ID, name, student.EnrollmentDate.ToString());

         Console.ReadKey();
      }
   }
}

上記のコードは、データベースからすべての生徒の名を取得します。

Entity Framework-列挙型のサポート

Entity Frameworkでは、この機能により、列挙型であるドメインクラスのプロパティを定義し、それを整数型のデータベース列にマップできます。 Entity Frameworkは、データを照会して保存するときに、データベース値と関連する列挙型を相互に変換します。

  • 列挙型には、応答の数が固定されているプロパティを操作するときに、あらゆる種類の利点があります。
  • 列挙を使用すると、アプリケーションのセキュリティと信頼性の両方が向上します。
  • 列挙により、ユーザーは間違いを犯しにくくなり、インジェクション攻撃などの問題は存在しなくなります。
  • Entity Frameworkでは、列挙は次の基本型を持つことができます-
  • Byte
  • Int16
  • Int32
  • Int64
  • SByte
  • 列挙型要素のデフォルトの基本型はintです。
  • デフォルトでは、最初の列挙子の値は0であり、連続する各列挙子の値は1ずつ増加します。

デザイナーでエンティティを作成し、いくつかのプロパティを追加する次の例を見てみましょう。

  • ステップ1 *-[ファイル]→[新規]→[プロジェクト]メニューオプションから新しいプロジェクトを作成します。
  • ステップ2 *-左ペインで、コンソールアプリケーションを選択します。

エンティティの作成

  • ステップ3 *-プロジェクトの名前としてEFEnumDemoを入力し、[OK]をクリックします。
  • ステップ4 *-ソリューションエクスプローラーでプロジェクト名を右クリックし、[追加]→[新しいアイテム]メニューオプションを選択します。
  • ステップ5 *-[テンプレート]ペインで[ADO.NETエンティティデータモデル]を選択します。

データモデルテンプレート

  • ステップ6 *-ファイル名にEFEnumModel.edmxと入力し、[追加]をクリックします。
  • ステップ7 *-エンティティデータモデルウィザードページで、空のEFデザイナーモデルを選択します。

モデルウィザードページ

  • ステップ8 *-[完了]をクリックします
  • ステップ9 *-次にデザイナーウィンドウを右クリックし、[追加]→[エンティティ]を選択します。

デザイナーウィンドウエンティティ

次の図に示すように、[新しいエンティティ]ダイアログボックスが表示されます。

新しいエンティティダイアログ

  • ステップ10 *-エンティティ名としてDepartmentを、プロパティ名としてDeptIDを入力し、プロパティタイプをInt32のままにして、[OK]をクリックします。
  • ステップ11 *-エンティティを右クリックして、[新規追加]→[スカラープロパティ]を選択します。

スカラープロパティ

  • ステップ12 *-新しいプロパティの名前をDeptNameに変更します。
  • ステップ13 *-新しいプロパティのタイプをInt32に変更します(デフォルトでは、新しいプロパティは文字列タイプです)。
  • ステップ14 *-タイプを変更するには、[プロパティ]ウィンドウを開き、[タイプ]プロパティをInt32に変更します。

タイプ

  • ステップ15 *-Entity Framework Designerで、Nameプロパティを右クリックし、Convert to enumを選択します。

Entity Framework Designer

  • ステップ16 *-列挙型の追加ダイアログボックスで、列挙型名にDepartmentNamesを入力し、基になる型をInt32に変更し、次のメンバーを型に追加します:物理、化学、コンピューター、および経済。

列挙型の追加

  • ステップ17 *-[OK]をクリックします。

[モデルブラウザー]ウィンドウに切り替えると、その型が[列挙型]ノードにも追加されたことがわかります。

モデルブラウザウィンドウ

モデルファーストアプローチの章に記載されているすべての手順に従って、モデルからデータベースを生成しましょう。

  • ステップ1 *-エンティティデザイナサーフェスを右クリックし、[モデルからデータベースを生成]を選択します。

データベースの生成ウィザードの[データ接続の選択]ダイアログボックスが表示されます。

  • ステップ2 *-[新しい接続]ボタンをクリックします。

接続ボタン

  • ステップ3 *-データベースのサーバー名とEnumDemoを入力し、[OK]をクリックします。
  • ステップ4 *-新しいデータベースを作成するかどうかを尋ねるダイアログが表示されたら、[はい]をクリックします。
  • ステップ5 *-[次へ]をクリックすると、データベースの作成ウィザードがデータベースを作成するためのデータ定義言語(DDL)を生成します。 [完了]をクリックします。
  • ステップ6 *-T-SQLエディターを右クリックして、[実行]を選択します。

TSql Editor

  • ステップ7 *-生成されたスキーマを表示するには、SQL Serverオブジェクトエクスプローラーでデータベース名を右クリックし、[更新]を選択します。

データベースにDepartments表が表示されます。

部門テーブル

コンテキストに新しいDepartmentオブジェクトがいくつか追加されて保存される次の例を見てみましょう。 そして、コンピュータ部門を取得します。

class Program {

   static void Main(string[] args) {

      using (var context = new EFEnumModelContainer()) {

         context.Departments.Add(new Department { DeptName = DepartmentNames.Physics});
         context.Departments.Add(new Department { DeptName = DepartmentNames.Computer});
         context.Departments.Add(new Department { DeptName = DepartmentNames.Chemistry});
         context.Departments.Add(new Department { DeptName = DepartmentNames.Economics});

         context.SaveChanges();

         var department = (
            from d in context.Departments
            where d.DeptName == DepartmentNames.Computer
            select d
         ).FirstOrDefault();

         Console.WriteLine(
            "Department ID: {0}, Department Name: {1}",
               department.DeptID, department.DeptName
         );

         Console.ReadKey();
      }
   }
}

上記のコードが実行されると、次の出力が表示されます-

Department ID: 2, Department Name: Computer

理解を深めるために、上記の例を段階的に実行することをお勧めします。

Entity Framework-非同期クエリ

  • 非同期プログラミング*では、メインスレッドが独自の操作を継続できるように、バックグラウンドで操作を実行します。 このようにして、バックグラウンドスレッドがタスクを処理している間、メインスレッドはユーザーインターフェイスの応答性を維持できます。
  • Entity Framework 6.0は、データのクエリと保存のための非同期操作をサポートしています。
  • 非同期操作は、次の方法でアプリケーションを支援することができます-
  • ユーザーの操作に対するアプリケーションの応答性を高める
  • アプリケーションの全体的なパフォーマンスを改善する
  • 非同期操作はさまざまな方法で実行できます。 ただし、async/awaitキーワードは.NET Framework 4.5で導入されたため、作業が簡単になります。
  • 従う必要があるのは、次のコードフラグメントに示すように、async/awaitパターンのみです。

DatabaseOperationsメソッドが新しい学生をデータベースに保存し、データベースからすべての学生を取得し、最後に追加のメッセージがコンソールに出力される次の例を見てみましょう(async/awaitを使用しない)。

class Program {

   static void Main(string[] args) {
      Console.WriteLine("Database Operations Started");
      DatabaseOperations();

      Console.WriteLine();
      Console.WriteLine("Database Operations Completed");
      Console.WriteLine();
      Console.WriteLine("Entity Framework Tutorials");

      Console.ReadKey();
   }

   public static void DatabaseOperations() {

      using (var context = new UniContextEntities()) {

        //Create a new student and save it

         context.Students.Add(new Student {
            FirstMidName = "Akram",
            LastName = "Khan",
            EnrollmentDate = DateTime.Parse(DateTime.Today.ToString())});

         Console.WriteLine("Calling SaveChanges.");
         context.SaveChanges();
         Console.WriteLine("SaveChanges completed.");

        //Query for all Students ordered by first name

         var students = (from s in context.Students
            orderby s.FirstMidName select s).ToList();

        //Write all students out to Console

         Console.WriteLine();
         Console.WriteLine("All Student:");

         foreach (var student in students) {
            string name = student.FirstMidName + " " + student.LastName;
            Console.WriteLine(" " + name);
         }
      }
   }
}

上記のコードが実行されると、次の出力が表示されます-

Calling SaveChanges.
SaveChanges completed.
All Student:
Akram Khan
Ali Khan
Ali Alexander
Arturo Anand
Bill Gates
Gytis Barzdukas
Laura  Nornan
Meredith fllonso
Nino Olioetto
Peggy Justice
Yan Li

Entity Framework Tutorials

新しいasyncキーワードとawaitキーワードを使用して、Program.csに次の変更を加えましょう。

  • EF非同期拡張メソッドを提供するSystem.Data.Entity名前空間を追加します。
  • System.Threading.Tasks名前空間を追加して、Taskタイプを使用できるようにします。
  • async としてマークされるように DatabaseOperations を更新し、 Task を返します。
  • SaveChangesの非同期バージョンを呼び出し、その完了を待ちます。
  • ToListの非同期バージョンを呼び出し、結果を待ちます。
class Program {

   static void Main(string[] args) {
      var task = DatabaseOperations();
      Console.WriteLine();
      Console.WriteLine("Entity Framework Tutorials");
      task.Wait();
      Console.ReadKey();
   }

   public static async Task DatabaseOperations() {

      using (var context = new UniContextEntities()) {

        //Create a new blog and save it

         context.Students.Add(new Student {
            FirstMidName = "Salman",
            LastName = "Khan",
            EnrollmentDate = DateTime.Parse(DateTime.Today.ToString())});

         Console.WriteLine("Calling SaveChanges.");
         await context.SaveChangesAsync();
         Console.WriteLine("SaveChanges completed.");

        //Query for all Students ordered by first name

         var students = await (from s in context.Students
            orderby s.FirstMidName select s).ToListAsync();

        //Write all students out to Console

         Console.WriteLine();
         Console.WriteLine("All Student:");

         foreach (var student in students) {
            string name = student.FirstMidName + " " + student.LastName;
            Console.WriteLine(" " + name);
         }
      }
   }
}

実行すると、次の出力が生成されます。

Calling SaveChanges.
Entity Framework Tutorials
SaveChanges completed.
All Student:
Akram Khan
Ali Khan
Ali Alexander
Arturo Anand
Bill Gates
Gytis Barzdukas
Laura  Nornan
Meredith fllonso
Nino Olioetto
Peggy Justice
Salman Khan
Yan Li

コードが非同期になったため、プログラムの異なる実行フローを観察できます。

  • SaveChangesは新しいStudentをデータベースにプッシュし始め、その後DatabaseOperationsメソッドが(実行が完了していなくても)戻り、Mainメソッドのプログラムフローが継続します。
  • 次に、メッセージがコンソールに書き込まれます。
  • マネージスレッドは、データベース操作が完了するまで待機呼び出しでブロックされます。 完了すると、残りのDatabaseOperationsが実行されます。
  • SaveChangesが完了します。
  • データベースからすべての生徒を取得し、コンソールに書き込まれます。

理解を深めるために、上記の例を段階的に実行することをお勧めします。

Entity Framework-永続性

Entity Frameworkにより、アプリケーションのすべての部分にEntity Frameworkを意識させることなく、Entity Frameworkを活用して、エンティティをインフラストラクチャから分離できるようになりました。 永続化の方法(データの保存場所とオブジェクト間のデータのやり取り)に関係なく、ビジネスルールに焦点を当てることができるクラスを作成できます。

永続的無知エンティティの作成

前の段落では、消費するデータのソースに関する詳細な知識がない方法について説明しました。 これは、永続性の無知の本質を浮き彫りにします。これは、クラスとその周りのアプリケーションレイヤーの多くがデータの保存方法を気にしない場合です。

  • .NET 3.5バージョンのEntity Frameworkでは、既存のクラスを使用する場合、それらを強制的にEntityObjectから派生させて変更する必要がありました。
  • .NET 4では、これはもう必要ありません。 Entity Frameworkの操作に参加するためにエンティティを変更する必要はありません。
  • これにより、懸念の疎結合と分離を取り入れたアプリケーションを構築できます。
  • これらのコーディングパターンを使用すると、クラスは独自のジョブにのみ関係し、UIを含むアプリケーションの多くのレイヤーは、Entity Framework APIなどの外部ロジックに依存しませんが、それらの外部APIは、エンティティ。

Entity Frameworkでエンティティを永続化する場合、2つの方法(接続と切断)があります。 どちらの方法にも独自の重要性があります。 接続されたシナリオの場合、変更はコンテキストによって追跡されますが、切断されたシナリオの場合、エンティティの状態についてコンテキストに通知する必要があります。

接続されたシナリオ

接続シナリオは、エンティティがデータベースから取得され、同じコンテキストで変更される場合です。 接続されたシナリオでは、Windowsサービスがあり、そのエンティティでいくつかのビジネスオペレーションを行っていると仮定します。そのため、コンテキストを開き、すべてのエンティティをループし、ビジネスオペレーションを行い、同じコンテキストで変更を保存します。最初に開いた。

学生がデータベースから取得され、学生の名を更新してからデータベースへの変更を保存する次の例を見てみましょう。

class Program {

   static void Main(string[] args) {

      using (var context = new MyContext()) {

         var studentList = context.Students.ToList();

         foreach (var stdnt in studentList) {
            stdnt.FirstMidName = "Edited " + stdnt.FirstMidName;
         }

         context.SaveChanges();

        ////Display all Students from the database

         var students = (from s in context.Students
            orderby s.FirstMidName select s).ToList<Student>();

         Console.WriteLine("Retrieve all Students from the database:");

         foreach (var stdnt in students) {
            string name = stdnt.FirstMidName + " " + stdnt.LastName;
            Console.WriteLine("ID: {0}, Name: {1}", stdnt.ID, name);
         }

         Console.ReadKey();
      }
   }
}

上記のコードをコンパイルして実行すると、次の出力が表示され、次の出力に示すように、名前の前に編集済みの単語が添付されていることがわかります。

Retrieve all Students from the database:
ID: 1, Name: Edited Edited Alain Bomer
ID: 2, Name: Edited Edited Mark Upston

切断されたシナリオ

切断シナリオは、エンティティがデータベースから取得され、異なるコンテキストで変更される場合です。 プレゼンテーション層にいくつかのデータを表示し、n層アプリケーションを使用しているとします。そのため、コンテキストを開いてデータを取得し、最後にコンテキストを閉じた方が良いでしょう。 ここでデータを取得してコンテキストを閉じたため、取得したエンティティは追跡されなくなり、これは切断されたシナリオです。

Addメソッドを使用して、新しい切断されたStudentエンティティがコンテキストに追加される次のコードを見てみましょう。

class Program {

   static void Main(string[] args) {

      var student = new Student {
         ID = 1001,
         FirstMidName = "Wasim",
         LastName = "Akram",
         EnrollmentDate = DateTime.Parse( DateTime.Today.ToString())
      };

      using (var context = new MyContext()) {

         context.Students.Add(student);
         context.SaveChanges();

        ////Display all Students from the database

         var students = (from s in context.Students
            orderby s.FirstMidName select s).ToList<Student>();

         Console.WriteLine("Retrieve all Students from the database:");

         foreach (var stdnt in students) {
            string name = stdnt.FirstMidName + " " + stdnt.LastName;
            Console.WriteLine("ID: {0}, Name: {1}", stdnt.ID, name);
         }

         Console.ReadKey();
      }
   }
}

上記のコードをコンパイルして実行すると、次の出力が表示されます。

Retrieve all Students from the database:
ID: 1, Name: Edited Edited Edited Alain Bomer
ID: 2, Name: Edited Edited Edited Mark Upston
ID: 3, Name: Wasim Akram

Entity Framework-投影クエリ

エンティティへのLINQ

LINQ to Entitiesを理解するための最も重要な概念の1つは、宣言型言語であるということです。 焦点は、情報を取得する方法ではなく、必要な情報を定義することです。

  • つまり、データベースへのアクセスなどのタスクを実行するために必要な基礎となるコードを把握しようとする時間を短縮し、データの操作により多くの時間を費やすことができます。 *宣言型言語は実際には開発者からコントロールを削除しないことを理解することが重要ですが、開発者が重要なことに注意を集中するのに役立ちます。

LINQ to Entities Essential Keywords

LINQクエリの作成に使用される基本的なキーワードを知ることが重要です。 覚えておくべきキーワードはわずかですが、さまざまな方法で組み合わせて特定の結果を得ることができます。 次のリストには、これらの基本的なキーワードが含まれており、それぞれの簡単な説明が記載されています。

Sr. No. Keyword & Description
1
  • Ascending*

ソート操作が範囲の最小(または最小)要素から範囲の最高要素まで行われることを指定します。 これは通常、デフォルト設定です。 たとえば、アルファベット順のソートを実行する場合、ソートはAからZの範囲になります。

2

By

グループ化の実装に使用されるフィールドまたは式を指定します。 フィールドまたは式は、グループ化タスクの実行に使用されるキーを定義します。

3

Descending

範囲の最大(または最高)要素から範囲の最小要素までソート操作が行われることを指定します。 たとえば、アルファベット順のソートを実行する場合、ソートはZからAの範囲になります。

4

Equals

プライマリコンテキストデータソースをセカンダリコンテキストデータソースに結合するために、結合ステートメントの左と右の句の間で使用されます。 equalsキーワードの左側のフィールドまたは式はプライマリデータソースを指定し、equalsキーワードの右側のフィールドまたは式はセカンダリデータソースを指定します。

5

From

必要な情報を取得するために使用されるデータソースを指定し、範囲変数を定義します。 この変数の目的は、ループの反復に使用される変数と同じです。

6

Group

指定したキー値を使用して、出力をグループに編成します。 複数のグループ句を使用して、出力組織の複数のレベルを作成します。 グループ句の順序により、グループ化順序で特定のキー値が表示される深さが決まります。 このキーワードをbyと組み合わせて、特定のコンテキストを作成します。

7

In

さまざまな方法で使用されます。 この場合、キーワードはクエリに使用されるコンテキストデータベースソースを決定します。 結合を使用する場合、結合に使用されるコンテキストデータベースソースごとにinキーワードが使用されます。

8

Into

結合、グループ、選択などのLINQクエリ句の参照として使用できる識別子を指定します。

9

Join

マスター/詳細設定など、2つの関連データソースから単一のデータソースを作成します。 結合では、内部結合、グループ結合、または左外部結合を指定できます。内部結合がデフォルトです。 参加の詳細については、http://msdn.microsoft.com/library/bb311040.aspx。[msdn.microsoft.com]をご覧ください。

10

Let

部分式の結果をクエリ式に格納するために使用できる範囲変数を定義します。 通常、範囲変数は、追加の列挙出力を提供するため、またはクエリの効率を高めるために使用されます(したがって、文字列の小文字の値を見つけるなどの特定のタスクを複数回行う必要はありません)。

11

On

結合の実装に使用されるフィールドまたは式を指定します。 フィールドまたは式は、両方のコンテキストデータソースに共通の要素を定義します。

12

Orderby

クエリのソート順を作成します。 昇順または降順のキーワードを追加して、ソートの順序を制御できます。 複数のorderby句を使用して、複数レベルのソートを作成します。 orderby句の順序によって、ソート式が処理される順序が決まります。したがって、異なる順序を使用すると、出力が異なります。

13

Where

LINQがデータソースから取得するものを定義します。 1つ以上のブール式を使用して、取得する内容の詳細を定義します。 ブール式は、&&(AND)および

を使用して互いに分離されます。 (OR)演算子。
14

Select

返す情報を指定して、LINQクエリからの出力を決定します。 このステートメントは、反復処理中にLINQが返す要素のデータ型を定義します。

投影

投影クエリは、データベースから特定のフィールドを取得するだけで、アプリケーションの効率を向上させます。

  • データを取得したら、必要に応じてデータを投影またはフィルタリングして、出力前にデータを整形できます。
  • LINQ to Entities式の主なタスクは、データを取得して出力として提供することです。

この章の「LINQ to Entitiesクエリの開発」セクションでは、この基本的なタスクを実行するためのテクニックを示しています。

学生のリストが取得される次のコードを見てみましょう。

using (var context = new UniContextEntities()) {

   var studentList = from s in context.Students select s;

   foreach (var student in studentList) {
      string name = student.FirstMidName + " " + student.LastName;
      Console.WriteLine("ID : {0}, Name: {1}", student.ID, name);
   }
}

単一オブジェクト

単一の学生オブジェクトを取得するには、シーケンスの最初の要素を返すFirst()またはFirstOrDefault列挙可能メソッドを使用できます。 FirstとFirstOrDefaultの違いは、指定された条件に結果データがない場合、First()は例外をスローするのに対して、FirstOrDefault()は結果データがない場合にデフォルト値nullを返すことです。 以下のコードスニペットでは、リストから最初の生徒が取得され、名がAliになります。

using (var context = new UniContextEntities()) {

   var student = (from s in context.Students where s.FirstMidName
      == "Ali" select s).FirstOrDefault<Student>();

   string name = student.FirstMidName + " " + student.LastName;
   Console.WriteLine("ID : {0}, Name: {1}", student.ID, name);
}

Single()またはSingleOrDefaultを使用して、シーケンスの単一の特定の要素を返す単一の学生オブジェクトを取得することもできます。 次の例では、IDが2の単一の学生が取得されます。

using (var context = new UniContextEntities()) {

   var student = (from s in context.Students where s.ID
      == 2 select s).SingleOrDefault<Student>();
   string name = student.FirstMidName + " " + student.LastName;

   Console.WriteLine("ID : {0}, Name: {1}", student.ID, name);
   Console.ReadKey();
}

オブジェクトのリスト

名前がAliである生徒のリストを取得する場合は、ToList()列挙可能メソッドを使用できます。

using (var context = new UniContextEntities()) {

   var studentList = (from s in context.Students where s.FirstMidName
      == "Ali" select s).ToList();

   foreach (var student in studentList) {
      string name = student.FirstMidName + " " + student.LastName;
      Console.WriteLine("ID : {0}, Name: {1}", student.ID, name);
   }

   Console.ReadKey();
}

注文

特定の順序でデータ/リストを取得するには、orderbyキーワードを使用できます。 次のコードでは、学生のスニペットリストが昇順で取得されます。

using (var context = new UniContextEntities()) {

   var studentList = (from s in context.Students orderby
      s.FirstMidName ascending select s).ToList();

   foreach (var student in studentList) {
      string name = student.FirstMidName + " " + student.LastName;
      Console.WriteLine("ID : {0}, Name: {1}", student.ID, name);
   }

   Console.ReadKey();
}

標準対投影エンティティフレームワーククエリ

ID、FirstMidName、LastName、およびEnrollmentDateを含むStudentモデルがあるとします。 生徒のリストを返したい場合、標準クエリはすべてのフィールドを返します。 ただし、ID、FirstMidName、およびLastNameフィールドを含む学生のリストのみを取得する場合。 これは、投影クエリを使用する場所です。 次に、投影クエリの簡単な例を示します。

using (var context = new UniContextEntities()) {

   var studentList = from s in context.Students
      orderby s.FirstMidName ascending
      where s.FirstMidName == "Ali"

   select new {s.ID, s.FirstMidName, s.LastName};

   foreach (var student in studentList) {
      string name = student.FirstMidName + " " + student.LastName;
      Console.WriteLine("ID : {0}, Name: {1}", student.ID, name);
   }

   Console.ReadKey();
}

上記の投影クエリでは、EnrollmentDateフィールドが除外されています。 これにより、アプリケーションがはるかに高速になります。

Entity Framework-コマンドロギング

Entity Framework 6.0では、 Logging SQL と呼ばれる新しい機能が導入されています。 Entity Frameworkでの作業中に、コマンドまたは同等のSQLクエリをデータベースに送信して、CRUD(作成、読み取り、更新、および削除)操作を実行します。

  • Entity Frameworkのこの機能は、Entity Frameworkによって内部的に生成された同等のSQLクエリをキャプチャし、出力として提供することです。
  • Entity Framework 6より前は、データベースクエリとコマンドをトレースする必要があるときはいつでも、開発者にはサードパーティのトレースユーティリティまたはデータベーストレースツールを使用する以外の選択肢がありませんでした。
  • Entity Framework 6では、この新しい機能により、Entity Frameworkによって実行されたすべての操作をログに記録する簡単な方法が提供されます。
  • Entity Frameworkによって実行されるすべてのアクティビティは、DbContext.Database.Logを使用して記録されます。

新しい生徒がデータベースに追加される次のコードを見てみましょう。

class Program {

   static void Main(string[] args) {

      using (var context = new UniContextEntities()) {

         context.Database.Log = Console.Write;

        //Create a new student and save it

         context.Students.Add(new Student {
            FirstMidName = "Salman",
            LastName = "Khan",
            EnrollmentDate = DateTime.Parse(DateTime.Today.ToString())
         });

         context.SaveChanges();
         Console.ReadKey();
      }
   }
}

上記のコードが実行されると、次の出力を受け取ります。これは、実際には上記のコードでEFによって実行されたすべてのアクティビティのログです。

Opened connection at 10/28/2015 6:27:35 PM +05:00
Started transaction at 10/28/2015 6:27:35 PM +05:00
INSERT [dbo].[Student]([LastName], [FirstMidName], [EnrollmentDate])
VALUES (@0, @1, @2)
SELECT [ID]
FROM [dbo].[Student]
WHERE @@ROWCOUNT > 0 AND [ID] = scope_identity()
-- @0: 'Khan' (Type = String, Size = -1)
-- @1: 'Salman' (Type = String, Size = -1)
-- @2: '10/28/2015 12:00:00 AM' (Type = DateTime)
-- Executing at 10/28/2015 6:27:35 PM +05:00
-- Completed in 5 ms with result: SqlDataReader
Committed transaction at 10/28/2015 6:27:35 PM +05:00
Closed connection at 10/28/2015 6:27:35 PM +05:00

ログプロパティが設定されている場合、次のアクティビティがログに記録されます-

  • すべての異なる種類のコマンド用のSQL SaveChangesの一部として生成された挿入、更新、削除を含むクエリ
  • パラメーター
  • コマンドが非同期に実行されているかどうか
  • コマンドがいつ実行を開始したかを示すタイムスタンプ
  • コマンドは正常に完了または失敗しました
  • 結果値のいくつかの表示
  • コマンドの実行にかかったおおよその時間

他の場所へのロギング

既にロギングフレームワークがあり、ロギングメソッドが定義されている場合は、他の場所にロギングすることもできます。

別のクラスMyLoggerがある次の例を見てみましょう。

class Program {

   static void Main(string[] args) {

      using (var context = new UniContextEntities()) {

         context.Database.Log = s ⇒ MyLogger.Log("EFLoggingDemo", s);

        //Create a new student and save it

         context.Students.Add(new Student {
            FirstMidName = "Salman",
            LastName = "Khan",
            EnrollmentDate = DateTime.Parse(DateTime.Today.ToString())
         });

         context.SaveChanges();
         Console.ReadKey();
      }
   }
}

public class MyLogger {

   public static void Log(string application, string message) {
      Console.WriteLine("Application: {0}, EF Message: {1} ",application, message);
   }
}

理解を深めるために、上記の例を段階的に実行することをお勧めします。

Entity Framework-コマンド代行受信

Entity Framework 6.0には、 Interceptor またはInterceptionと呼ばれる別の新機能があります。 インターセプトコードは、*インターセプションインターフェイス*の概念に基づいて構築されています。 たとえば、IDbCommandInterceptorインターフェイスは、EFがExecuteNonQuery、ExecuteScalar、ExecuteReader、および関連するメソッドを呼び出す前に呼び出されるメソッドを定義します。

  • Entity Frameworkはインターセプトを使用することで真に輝きます。 このアプローチを使用すると、コードを乱雑にすることなく、より多くの情報を一時的にキャプチャできます。
  • これを実装するには、独自のカスタムインターセプターを作成し、それに応じて登録する必要があります。
  • IDbCommandInterceptorインターフェイスを実装するクラスが作成されると、DbInterceptionクラスを使用してEntity Frameworkに登録できます。
  • IDbCommandInterceptorインターフェイスには6つのメソッドがあり、これらすべてのメソッドを実装する必要があります。 これらのメソッドの基本的な実装は次のとおりです。

IDbCommandInterceptorインターフェースが実装されている次のコードを見てみましょう。

public class MyCommandInterceptor : IDbCommandInterceptor {

   public static void Log(string comm, string message) {
      Console.WriteLine("Intercepted: {0}, Command Text: {1} ", comm, message);
   }

   public void NonQueryExecuted(DbCommand command,
      DbCommandInterceptionContext<int> interceptionContext) {
         Log("NonQueryExecuted: ", command.CommandText);
   }

   public void NonQueryExecuting(DbCommand command,
      DbCommandInterceptionContext<int> interceptionContext) {
         Log("NonQueryExecuting: ", command.CommandText);
   }

   public void ReaderExecuted(DbCommand command,
      DbCommandInterceptionContext<DbDataReader> interceptionContext) {
         Log("ReaderExecuted: ", command.CommandText);
   }

   public void ReaderExecuting(DbCommand command,
      DbCommandInterceptionContext<DbDataReader> interceptionContext) {
         Log("ReaderExecuting: ", command.CommandText);
   }

   public void ScalarExecuted(DbCommand command,
      DbCommandInterceptionContext<object> interceptionContext) {
         Log("ScalarExecuted: ", command.CommandText);
   }

   public void ScalarExecuting(DbCommand command,
      DbCommandInterceptionContext<object> interceptionContext) {
         Log("ScalarExecuting: ", command.CommandText);
   }

}

インターセプターの登録

1つ以上のインターセプションインターフェイスを実装するクラスが作成されると、次のコードに示すように、DbInterceptionクラスを使用してEFに登録できます。

DbInterception.Add(new MyCommandInterceptor());

インターセプターは、次のコードに示すように、DbConfigurationコードベースの構成を使用してアプリドメインレベルで登録することもできます。

public class MyDBConfiguration : DbConfiguration {

   public MyDBConfiguration() {
      DbInterception.Add(new MyCommandInterceptor());
   }
}

また、コードを使用してインターセプター構成ファイルを構成することができます-

<entityFramework>
   <interceptors>
      <interceptor type = "EFInterceptDemo.MyCommandInterceptor, EFInterceptDemo"/>
   </interceptors>
</entityFramework>

Entity Framework-空間データ型

Entity Framework 5で空間タイプのサポートが導入されました。 クエリで空間データを分析できるようにするための一連の演算子も含まれています。 たとえば、クエリは、2つの地理的位置間の距離に基づいてフィルタリングできます。

  • Entity Frameworkを使用すると、新しい空間データ型をクラスのプロパティとして公開し、データベースの空間列にマップできます。
  • また、空間演算子を使用してデータベースで実行される空間計算に基づいてフィルター処理、並べ替え、グループ化を行うLINQクエリを作成することもできます。

2つの主な空間データタイプがあります-

  • 地理データタイプには、GPSの緯度と経度の座標などの楕円体データが格納されます。
  • ジオメトリデータタイプは、ユークリッド(フラット)座標系を表します。

次のクリケット場の例を見てみましょう。

  • ステップ1 *-[ファイル]→[新規]→[プロジェクト]メニューオプションから新しいプロジェクトを作成します。
  • ステップ2 *-左ペインで、コンソールアプリケーションを選択します。

クリケットプロジェクト

  • ステップ3 *-プロジェクト名を右クリックして、[NuGetパッケージの管理…]を選択します

NuGetプロジェクト

  • ステップ4 *-Entity Frameworkをインストールします。
  • ステップ5 *-System.Data.Entityアセンブリへの参照を追加し、空間データ型のステートメントを使用してSystem.Data.Spatialも追加します。

参照の追加

  • ステップ6 *-Program.csファイルに次のクラスを追加します。
public class CricketGround {
   public int ID { get; set; }
   public string Name { get; set; }
   public DbGeography Location { get; set; }
}
  • ステップ7 *-エンティティの定義に加えて、DbContextから派生し、DbSet <TEntity>プロパティを公開するクラスを定義する必要があります。

Program.csでコンテキスト定義を追加します。

public partial class CricketGroundContext : DbContext {
   public DbSet<CricketGround> CricketGrounds { get; set; }
}
  • ステップ8 *-次のコードをMain関数に追加します。これにより、2つの新しいCricketGroundオブジェクトがコンテキストに追加されます。
class Program {

   static void Main(string[] args) {

      using (var context = new CricketGroundContext()) {

         context.CricketGrounds.Add(new CricketGround() {
            Name = "Shalimar Cricket Ground",
            Location = DbGeography.FromText("POINT(-122.336106 47.605049)"),
         });

         context.CricketGrounds.Add(new CricketGround() {
            Name = "Marghazar Stadium", Location = DbGeography
               .FromText("POINT(-122.335197 47.646711)"),
         });

         context.SaveChanges();

         var myLocation = DbGeography.FromText("POINT(-122.296623 47.640405)");

         var cricketGround = (from cg in context.CricketGrounds
            orderby cg.Location.Distance(myLocation) select cg).FirstOrDefault();

         Console.WriteLine("The closest Cricket Ground to you is: {0}.", cricketGround.Name);
      }
   }
}

空間プロパティは、DbGeography.FromTextメソッドを使用して初期化されます。 WellKnownTextとして表される地理ポイントがメソッドに渡され、データが保存されます。 その後、その場所が指定された場所に最も近いCricketGroundオブジェクトが取得されます。

上記のコードが実行されると、次の出力が表示されます-

The closest Cricket Ground to you is: Marghazar Stadium

理解を深めるために、上記の例を段階的に実行することをお勧めします。

エンティティフレームワーク - 継承

継承により、開発者の考え方をより適切に反映する複雑なモデルを作成でき、これらのモデルとのやり取りに必要な作業も削減できます。 エンティティで使用される継承は、クラスで使用される継承と同じ目的を果たすため、開発者はこの機能の動作の基本をすでに知っています。

次の例を見て、新しいコンソールアプリケーションプロジェクトを作成してみましょう。

  • ステップ1 *-プロジェクト名を右クリックしてADO.NETエンティティデータモデルを追加し、[追加]→[新しいアイテム]を選択します…
  • ステップ2 *-1つのエンティティを追加し、Model Firstアプローチの章に記載されているすべてのステップに従ってPersonという名前を付けます。
  • ステップ3 *-次の画像に示すように、いくつかのスカラープロパティを追加します。

スカラープロパティの追加

ステップ4 *-さらに2つのエンティティ *Student および Teacher を追加し、Person Tableからプロパティを継承します。

  • ステップ5 *-次の図に示すように、Studentエンティティを追加し、BaseタイプコンボボックスからPersonを選択します。

ベースタイプコンボボックス

  • ステップ6 *-同様に、教師エンティティを追加します。
  • ステップ7 *-EnrollmentDateスカラープロパティを学生エンティティに、HireDateプロパティを教師エンティティに追加します。

EnrollmentDate

  • ステップ8 *-先に進んでデータベースを生成しましょう。
  • ステップ9 *-デザイン画面を右クリックし、[モデルからデータベースを生成…​]を選択します

データベースの生成

  • ステップ10 *-新しいデータベースを作成するには、[新しい接続]をクリックします。次のダイアログが開きます。 OKをクリックしてください。

新しいデータベース

ステップ11 *-[完了]をクリックします。 これにより、プロジェクトに .edmx.sqlファイルが追加されます。 .sqlファイルを開くことにより、Visual StudioでDDLスクリプトを実行できます。 次に、右クリックして「実行」を選択します。

  • ステップ12 *-サーバーエクスプローラーに移動すると、指定された3つのテーブルでデータベースが作成されていることがわかります。

データベース作成テーブル

  • ステップ13 *-次のドメインクラスも自動的に生成されることもわかります。
public partial class Person {
   public int ID { get; set; }
   public string FirstMidName { get; set; }
   public string LastName { get; set; }
}

public partial class Student : Person {
   public System.DateTime EnrollmentDate { get; set; }
}

public partial class Teacher : Person {
   public System.DateTime HireDate { get; set; }
}

以下は、Contextクラスです。

public partial class InheritanceModelContainer : DbContext {

   public InheritanceModelContainer() :
      base("name = InheritanceModelContainer") {}

   protected override void OnModelCreating(DbModelBuilder modelBuilder) {
      throw new UnintentionalCodeFirstException();
   }

   public virtual DbSet<Person> People { get; set; }
}

データベースに生徒と教師を追加して、データベースから取得します。

class Program {

   static void Main(string[] args) {

      using (var context = new InheritanceModelContainer()) {

         var student = new Student {
            FirstMidName = "Meredith",
            LastName = "Alonso",
            EnrollmentDate = DateTime.Parse(DateTime.Today.ToString())
         };

         context.People.Add(student);

         var student1 = new Student {
            FirstMidName = "Arturo",
            LastName = "Anand",
            EnrollmentDate = DateTime.Parse(DateTime.Today.ToString())
         };

         context.People.Add(student1);

         var techaer = new Teacher {
            FirstMidName = "Peggy",
            LastName = "Justice",
            HireDate = DateTime.Parse(DateTime.Today.ToString())
         };

         context.People.Add(techaer);

         var techaer1 = new Teacher {
            FirstMidName = "Yan",
            LastName = "Li",
            HireDate = DateTime.Parse(DateTime.Today.ToString())
         };

         context.People.Add(techaer1);
         context.SaveChanges();
      }
   }
}

生徒と教師がデータベースに追加されます。 NToは生徒と教師を取得します。 OfType メソッドを使用する必要があります。これは、指定された部門に関連する生徒と教師を返します。

Console.WriteLine("All students in database");
Console.WriteLine("");

foreach (var student in context.People.OfType<Student>()) {
   string name = student.FirstMidName + " " + student.LastName;
   Console.WriteLine("ID: {0}, Name: {1}, \tEnrollment Date {2} ",
      student.ID, name, student.EnrollmentDate.ToString());
}

Console.WriteLine("");
Console.WriteLine("************************************************************ *****");
Console.WriteLine("");
Console.WriteLine("All teachers in database");
Console.WriteLine("");

foreach (var teacher in context.People.OfType<Teacher>()) {
   string name = teacher.FirstMidName + " " + teacher.LastName;
   Console.WriteLine("ID: {0}, Name: {1}, \tHireDate {2} ",
      teacher.ID, name, teacher.HireDate.ToString());
}

Console.WriteLine("");
Console.WriteLine("************************************************************ *****");
Console.ReadKey();

最初のクエリで、OfType <Student>()を使用すると、HireDateプロパティはTeacher Entityの一部であり、同様にOfType <Teacher>()を使用するとEnrollmentDateプロパティにアクセスできないため、HireDateにアクセスできません

上記のコードが実行されると、次の出力が表示されます-

All students in database
ID: 1, Name: Meredith Alonso,   Enrollment Date 10/30/2015 12:00:00 AM
ID: 2, Name: Arturo Anand,      Enrollment Date 10/30/2015 12:00:00 AM
*****************************************************************
All teachers in database
ID: 3, Name: Peggy Justice,     HireDate 10/30/2015 12:00:00 AM
ID: 4, Name: Yan Li,    HireDate 10/30/2015 12:00:00 AM
*****************************************************************

理解を深めるために、上記の例を段階的に実行することをお勧めします。

Entity Framework-移行

Entity Framework 5および以前のバージョンのEntity Frameworkでは、コードは.NET Frameworkの一部として出荷されるコアライブラリ(主にSystem.Data.Entity.dll)に分割され、追加のライブラリ(主にEntityFramework.dll)が配布され、次の図に示すように、NuGetを使用して出荷されます。

DotNet Framework

Entity Framework 6では、以前は.NETフレームワークの一部であったコアAPIもNuGetパッケージの一部として出荷および配布されます。

コアAPI

これは、Entity Frameworkをオープンソースにするために必要でした。 ただし、結果として、アプリケーションを古いバージョンのEntity FrameworkからEF 6に移行またはアップグレードする必要がある場合は常に、アプリケーションを再構築する必要があります。

アプリケーションがEF 4.1以降で出荷されたDbContextを使用している場合、移行プロセスは簡単です。 ただし、アプリケーションがObjectContextである場合、それ以上の作業は必要ありません。

既存のアプリケーションをEF6にアップグレードするために必要な次の手順を見てみましょう。

  • ステップ1 *-最初のステップは、.NET Framework 4.5.2以降を対象とし、プロジェクトを右クリックしてプロパティを選択することです。

EF6のアップグレード

  • ステップ2 *-プロジェクトをもう一度右クリックして、[NuGetパッケージの管理…​]を選択します。

NuGetパッケージの管理

  • ステップ3 *-[オンライン]タブで[EntityFramework]を選択し、[インストール]をクリックします。 System.Data.Entity.dllへのアセンブリ参照が削除されていることを確認してください。

EF6 NuGetパッケージをインストールすると、プロジェクトからSystem.Data.Entityへの参照が自動的に削除されます。

  • ステップ4 *-EF Designerで作成されたモデルがある場合は、コード生成テンプレートを更新してEF6互換コードを生成する必要もあります。
  • ステップ5 *-ソリューションエクスプローラーのedmxファイルで、通常<edmx_file_name> .ttおよび<edmx_file_name> .Context.ttという名前の既存のコード生成テンプレートを削除します。

Edmx

  • ステップ6 *-EF Designerでモデルを開き、デザイン画面を右クリックして、[コード生成アイテムの追加…​]を選択します。
  • ステップ7 *-適切なEF 6.xコード生成テンプレートを追加します。

コード生成テンプレート

また、EF6互換コードを自動的に生成します。

アプリケーションがEF 4.1以降を使用している場合、DbContextおよびCode Firstタイプのネームスペースは変更されていないため、コードを変更する必要はありません。

ただし、アプリケーションが以前のバージョンのEntity Frameworkを使用している場合、以前System.Data.Entity.dllにあったObjectContextのような型は新しい名前空間に移動されました。

  • ステップ8 *-EF6に対してビルドするには、usingまたはImportディレクティブを更新する必要があります。

名前空間の変更に関する一般的な規則は、System.Data。*のすべての型がSystem.Data.Entity.Core。*に移動されることです。 以下はそれらのいくつかです-

*System.Data.EntityException⇒System.Data* *。Entity.Core。** EntityException
*System.Data.Objects.ObjectContext⇒System.Data* *。Entity.Core。** Objects.ObjectContext;
*System.Data.Objects.DataClasses.RelationshipManager⇒System.Data* *。Entity.Core。** Objects.DataClasses.RelationshipManager;

一部のタイプは、ほとんどのDbContextベースのアプリケーションで直接使用されないため、_Core_名前空間にあります。

  • System.Data.EntityState⇒System.Data.Entity.EntityState
  • System.Data.Objects.DataClasses.EdmFunctionAttribute⇒System.Data.Entity.DbFunctionAttribute

既存のEntity Frameworkプロジェクトは、大きな変更なしでEntity Framework 6.0で機能します。

Entity Framework-Eager Loading

積極的な読み込みとは、あるタイプのエンティティに対するクエリが、クエリの一部として関連するエンティティも読み込むプロセスです。 積極的な読み込みは、* Includeメソッド*を使用して実現されます。

これは、関連データのリクエストがデータベースからのクエリ結果とともに返されることを意味します。 データソースへの接続は1つだけで、最初のクエリで大量のデータが返されます。

たとえば、学生にクエリを実行する場合、登録を熱心にロードします。 学生とその登録は、単一のクエリで取得されます。

次の例を見てみましょう。ここでは、それぞれの在籍者を持つすべての学生が、イーガーロードを使用してデータベースから取得されます。

class Program {

   static void Main(string[] args) {

      using (var context = new UniContextEntities()) {
        //Load all students and related enrollments
         var students = context.Students
            .Include(s ⇒ s.Enrollments).ToList();

         foreach (var student in students) {
            string name = student.FirstMidName + " " + student.LastName;
            Console.WriteLine("ID: {0}, Name: {1}", student.ID, name);

            foreach (var enrollment in student.Enrollments) {
               Console.WriteLine("Enrollment ID: {0}, Course ID: {1}",
                  enrollment.EnrollmentID, enrollment.CourseID);
            }
         }

         Console.ReadKey();
      }
   }
}

上記のコードをコンパイルして実行すると、次の出力が表示されます。

ID: 1, Name: Ali Alexander
       Enrollment ID: 1, Course ID: 1050
       Enrollment ID: 2, Course ID: 4022
       Enrollment ID: 3, Course ID: 4041
ID: 2, Name: Meredith Alonso
       Enrollment ID: 4, Course ID: 1045
       Enrollment ID: 5, Course ID: 3141
       Enrollment ID: 6, Course ID: 2021
ID: 3, Name: Arturo Anand
       Enrollment ID: 7, Course ID: 1050
ID: 4, Name: Gytis Barzdukas
       Enrollment ID: 8, Course ID: 1050
       Enrollment ID: 9, Course ID: 4022

以下は、使用できる他の形式の熱心な読み込みクエリです。

//Load one Student and its related enrollments

var student1 = context.Students
   .Where(s ⇒ s.FirstMidName == "Ali")
   .Include(s ⇒ s.Enrollments).FirstOrDefault();

//Load all Students and related enrollments
//using a string to specify the relationship

var studentList = context.Students
   .Include("Enrollments").ToList();

//Load one Student and its related enrollments
//using a string to specify the relationship

var student = context.Students
   .Where(s ⇒ s.FirstMidName == "Salman")
   .Include("Enrollments").FirstOrDefault();

複数のレベル

関連するエンティティの複数のレベルを積極的にロードすることもできます。 次のクエリは、Student、Enrollments、およびCourseの例を示しています。

//Load all Students, all related enrollments, and all related courses

var studentList = context.Students
   .Include(s ⇒ s.Enrollments.Select(c ⇒ c.Course)).ToList();

//Load all Students, all related enrollments, and all related courses
//using a string to specify the relationships

var students = context.Students
   .Include("Enrollments.Course").ToList();

理解を深めるために、上記の例を段階的に実行することをお勧めします。

Entity Framework-遅延読み込み

遅延読み込みとは、エンティティを参照するプロパティに最初にアクセスしたときに、エンティティまたはエンティティのコレクションがデータベースから自動的に読み込まれるプロセスです。 遅延読み込みとは、特に要求するまで、関連データの読み込みを遅らせることです。

  • POCOエンティティタイプを使用する場合、派生プロキシタイプのインスタンスを作成し、仮想プロパティをオーバーライドしてロードフックを追加することにより、遅延ロードが実現されます。
  • 遅延読み込みはほとんどデフォルトです。
  • 既定の構成をそのままにして、遅延ロード以外の何かが必要であることをクエリでEntity Frameworkに明示的に指定しない場合、遅延ロードが得られます。
  • たとえば、Studentエンティティクラスを使用する場合、Enrollmentsナビゲーションプロパティに最初にアクセスすると、関連するEnrollmentsがロードされます。
  • ナビゲーションプロパティは、パブリック、仮想として定義する必要があります。 プロパティが仮想として定義されていない場合、コンテキストは遅延読み込みを実行しません。

以下は、Enrollmentsのナビゲーションプロパティを含むStudentクラスです。

public partial class Student {

   public Student() {
      this.Enrollments = new HashSet<Enrollment>();
   }

   public int ID { get; set; }
   public string LastName { get; set; }
   public string FirstMidName { get; set; }
   public System.DateTime EnrollmentDate { get; set; }

   public virtual ICollection<Enrollment> Enrollments { get; set; }
}

最初に学生リストがデータベースからロードされ、次に必要なときに特定の学生の登録がロードされる単純な例を見てみましょう。

class Program {

   static void Main(string[] args) {

      using (var context = new UniContextEntities()) {

        //Loading students only
         IList<Student> students = context.Students.ToList<Student>();

         foreach (var student in students) {

            string name = student.FirstMidName + " " + student.LastName;
            Console.WriteLine("ID: {0}, Name: {1}", student.ID, name);

            foreach (var enrollment in student.Enrollments) {
               Console.WriteLine("Enrollment ID: {0}, Course ID: {1}",
                  enrollment.EnrollmentID, enrollment.CourseID);
            }
         }

         Console.ReadKey();
      }
   }
}

上記のコードをコンパイルして実行すると、次の出力が表示されます。

ID: 1, Name: Ali Alexander
       Enrollment ID: 1, Course ID: 1050
       Enrollment ID: 2, Course ID: 4022
       Enrollment ID: 3, Course ID: 4041
ID: 2, Name: Meredith Alonso
       Enrollment ID: 4, Course ID: 1045
       Enrollment ID: 5, Course ID: 3141
       Enrollment ID: 6, Course ID: 2021
ID: 3, Name: Arturo Anand
       Enrollment ID: 7, Course ID: 1050
ID: 4, Name: Gytis Barzdukas
       Enrollment ID: 8, Course ID: 1050
       Enrollment ID: 9, Course ID: 4022
ID: 5, Name: Yan Li
       Enrollment ID: 10, Course ID: 4041
ID: 6, Name: Peggy Justice
       Enrollment ID: 11, Course ID: 1045
ID: 7, Name: Laura Norman
       Enrollment ID: 12, Course ID: 3141

遅延読み込みをオフにする

遅延読み込みとシリアル化はうまく混ざりません。注意しないと、遅延読み込みが有効になっているからといって、データベース全体をクエリすることになります。 エンティティをシリアル化する前に、遅延読み込みをオフにすることをお勧めします。

特定のナビゲーションプロパティをオフにする

次の例に示すように、Enrollmentsプロパティを非仮想にすることで、Enrollmentsコレクションの遅延読み込みをオフにできます。

public partial class Student {

   public Student() {
      this.Enrollments = new HashSet<Enrollment>();
   }

   public int ID { get; set; }
   public string LastName { get; set; }
   public string FirstMidName { get; set; }
   public System.DateTime EnrollmentDate { get; set; }

   public ICollection<Enrollment> Enrollments { get; set; }
}

すべてのエンティティに対してオフにする

次の例に示すように、Configurationプロパティのフラグをfalseに設定することにより、コンテキスト内のすべてのエンティティの遅延読み込みをオフにできます。

public partial class UniContextEntities : DbContext {

   public UniContextEntities(): base("name = UniContextEntities") {
      this.Configuration.LazyLoadingEnabled = false;
   }

   protected override void OnModelCreating(DbModelBuilder modelBuilder) {
      throw new UnintentionalCodeFirstException();
   }
}

遅延読み込みをオフにした後、上記の例を再度実行すると、登録が読み込まれず、学生データのみが取得されることがわかります。

ID: 1, Name: Ali Alexander
ID: 2, Name: Meredith Alons
ID: 3, Name: Arturo Anand
ID: 4, Name: Gytis Barzduka
ID: 5, Name: Yan Li
ID: 6, Name: Peggy Justice
ID: 7, Name: Laura Norman
ID: 8, Name: Nino Olivetto

理解を深めるために、上記の例を段階的に実行することをお勧めします。

Entity Framework-明示的なロード

遅延読み込みを無効にしても、関連するエンティティを遅延読み込みすることは可能ですが、明示的な呼び出しで行う必要があります。

  • 遅延読み込みとは異なり、クエリが実行されるタイミングに関してあいまいさや混乱の可能性はありません。
  • これを行うには、関連するエンティティのエントリでLoadメソッドを使用します。
  • 1対多の関係の場合は、コレクションでLoadメソッドを呼び出します。
  • また、1対1の関係の場合は、参照でLoadメソッドを呼び出します。

遅延読み込みが無効になっている次の例を見てみましょう。名前がAliの学生が取得されます。

その後、学生情報がコンソールに書き込まれます。 コードを見ると、登録情報も書き込まれますが、登録エンティティはまだロードされていないため、foreachループは実行されません。

Enrollmentsエンティティが明示的に読み込まれた後、学生情報と登録情報がコンソールウィンドウに書き込まれます。

class Program {

   static void Main(string[] args) {

      using (var context = new UniContextEntities()) {

         context.Configuration.LazyLoadingEnabled = false;

         var student = (from s in context.Students where s.FirstMidName ==
            "Ali" select s).FirstOrDefault<Student>();

         string name = student.FirstMidName + " " + student.LastName;
         Console.WriteLine("ID: {0}, Name: {1}", student.ID, name);

         foreach (var enrollment in student.Enrollments) {
            Console.WriteLine("Enrollment ID: {0}, Course ID: {1}",
               enrollment.EnrollmentID, enrollment.CourseID);
         }

         Console.WriteLine();
         Console.WriteLine("Explicitly loaded Enrollments");
         Console.WriteLine();

         context.Entry(student).Collection(s ⇒ s.Enrollments).Load();
         Console.WriteLine("ID: {0}, Name: {1}", student.ID, name);

         foreach (var enrollment in student.Enrollments) {
            Console.WriteLine("Enrollment ID: {0}, Course ID: {1}",
               enrollment.EnrollmentID, enrollment.CourseID);
         }

         Console.ReadKey();
      }
   }
}

上記の例を実行すると、次の出力が表示されます。 最初に学生情報のみが表示され、登録エンティティを明示的にロードした後、学生とその関連する登録情報の両方が表示されます。

ID: 1, Name: Ali Alexander
Explicitly loaded Enrollments
ID: 1, Name: Ali Alexander
       Enrollment ID: 1, Course ID: 1050
       Enrollment ID: 2, Course ID: 4022
       Enrollment ID: 3, Course ID: 4041

理解を深めるために、上記の例を段階的に実行することをお勧めします。

Entity Framework-検証

この章では、モデルデータを検証するためにADO.NET Entity Frameworkで使用できる検証手法について学習します。 Entity Frameworkは、クライアント側の検証用のユーザーインターフェイスに実装したり、サーバー側の検証に使用したりできる、さまざまな検証機能を提供します。

  • Entity Frameworkでは、データ検証はアプリケーション内の不良データをキャッチするためのソリューションの一部です。
  • Entity Frameworkは、さまざまなデータ検証方法を使用して、デフォルトでデータベースに書き込まれる前にすべてのデータを検証します。
  • ただし、Entity Frameworkはユーザーインターフェイスのデータ検証の後に来ます。 そのため、その場合は、EFがスローしてすべての例外を処理し、一般的なメッセージを表示するエンティティ検証が必要です。
  • エラーチェックとエラーメッセージをユーザーに戻す方法を改善するためのデータ検証の手法がいくつかあります。

DbContextには、ValidateEntityと呼ばれるオーバーライド可能なメソッドがあります。 SaveChangesを呼び出すと、Entity Frameworkは、状態がUnchangedでないキャッシュ内の各エンティティに対してこのメ​​ソッドを呼び出します。 学生エンティティの次の例に示すように、ここに検証ロジックを直接配置できます。

public partial class UniContextEntities : DbContext {

   protected override System.Data.Entity.Validation
      .DbEntityValidationResult ValidateEntity(DbEntityEntry entityEntry,
      System.Collections.Generic.IDictionary<object, object> items) {

         if (entityEntry.Entity is Student) {

            if (entityEntry.CurrentValues.GetValue<string>("FirstMidName") == "") {

               var list = new List<System.Data.Entity
                  .Validation.DbValidationError>();

               list.Add(new System.Data.Entity.Validation
                  .DbValidationError("FirstMidName", "FirstMidName is required"));

               return new System.Data.Entity.Validation
                  .DbEntityValidationResult(entityEntry, list);
            }
         }

         if (entityEntry.CurrentValues.GetValue<string>("LastName") == "") {

            var list = new List<System.Data.Entity
               .Validation.DbValidationError>();

            list.Add(new System.Data.Entity.Validation
               .DbValidationError("LastName", "LastName is required"));

            return new System.Data.Entity.Validation
               .DbEntityValidationResult(entityEntry, list);
         }

         return base.ValidateEntity(entityEntry, items);
   }
}

上記のValidateEntityメソッドでは、いずれかのプロパティに空の文字列がある場合、StudentエンティティのFirstMidNameプロパティとLastNameプロパティがチェックされ、エラーメッセージが返されます。

次のコードに示すように、新しい生徒が作成されたが、生徒のFirstMidNameが空の文字列である簡単な例を見てみましょう。

class Program {

   static void Main(string[] args) {

      using (var context = new UniContextEntities()) {

         Console.WriteLine("Adding new Student to the database");
         Console.WriteLine();

         try {

            context.Students.Add(new Student() {
               FirstMidName = "",
               LastName = "Upston"
            });

            context.SaveChanges();
         } catch (DbEntityValidationException dbValidationEx) {

            foreach (DbEntityValidationResult entityErr in
               dbValidationEx.EntityValidationErrors) {

               foreach (DbValidationError error in entityErr.ValidationErrors) {
                  Console.WriteLine("Error: {0}",error.ErrorMessage);
               }
            }
         }

         Console.ReadKey();
      }
   }
}

上記の例をコンパイルして実行すると、コンソールウィンドウに次のエラーメッセージが表示されます。

Adding new Student to the database
Error: FirstMidName is required

理解を深めるために、上記の例を段階的に実行することをお勧めします。

Entity Framework-変更の追跡

Entity Frameworkは、エンティティとその関係に加えられた変更を追跡する機能を提供するため、コンテキストのSaveChangesメソッドが呼び出されると、データベースに対して正しい更新が行われます。 これは、Entity Frameworkの重要な機能です。

  • 変更追跡は、新しいレコードをエンティティコレクションに追加し、既存のエンティティを変更または削除するときに変更を追跡します。
  • その後、すべての変更はDbContextレベルで保持されます。
  • これらのトラックの変更は、DbContextオブジェクトが破棄される前に保存されないと失われます。
  • DbChangeTrackerクラスは、コンテキストによって追跡されている現在のエンティティに関するすべての情報を提供します。
  • コンテキストごとにエンティティを追跡するには、プライマリキープロパティが必要です。

Entity Frameworkでは、変更追跡はデフォルトで有効になっています。 DbContextのAutoDetectChangesEnabledプロパティをfalseに設定して、変更追跡を無効にすることもできます。 このプロパティがtrueに設定されている場合、Entity Frameworkはエンティティの状態を維持します。

using (var context = new UniContextEntities()) {
   context.Configuration.AutoDetectChangesEnabled = true;
}

学生とその登録がデータベースから取得される次の例を見てみましょう。

class Program {

   static void Main(string[] args) {

      using (var context = new UniContextEntities()) {

         context.Configuration.AutoDetectChangesEnabled = true;
         Console.WriteLine("Retrieve Student");

         var student = (from s in context.Students where s.FirstMidName ==
            "Ali" select s).FirstOrDefault<Student>();

         string name = student.FirstMidName + " " + student.LastName;
         Console.WriteLine("ID: {0}, Name: {1}", student.ID, name);
         Console.WriteLine();
         Console.WriteLine("Retrieve all related enrollments");

         foreach (var enrollment in student.Enrollments) {
            Console.WriteLine("Enrollment ID: {0}, Course ID: {1}",
               enrollment.EnrollmentID, enrollment.CourseID);
         }

         Console.WriteLine();

         Console.WriteLine("Context tracking changes of {0} entity.",
            context.ChangeTracker.Entries().Count());

         var entries = context.ChangeTracker.Entries();

         foreach (var entry in entries) {
            Console.WriteLine("Entity Name: {0}", entry.Entity.GetType().Name);
            Console.WriteLine("Status: {0}", entry.State);
         }

         Console.ReadKey();
      }
   }
}

上記の例をコンパイルして実行すると、次の出力が表示されます。

Retrieve Student
ID: 1, Name: Ali Alexander
Retrieve all related enrollments
       Enrollment ID: 1, Course ID: 1050
       Enrollment ID: 2, Course ID: 4022
       Enrollment ID: 3, Course ID: 4041
Context tracking changes of 4 entity.
Entity Name: Student
Status: Unchanged
Entity Name: Enrollment
Status: Unchanged
Entity Name: Enrollment
Status: Unchanged
Entity Name: Enrollment
Status: Unchanged

すべてのデータがデータベースからのみ取得されることがわかります。これが、すべてのエンティティのステータスが変更されない理由です。

ここで、もう1つの登録を追加し、データベースから1人の生徒を削除する別の簡単な例を見てみましょう。 以下は、新しい登録が追加され、1人の生徒が削除されるコードです。

class Program {

   static void Main(string[] args) {

      using (var context = new UniContextEntities()) {

         context.Configuration.AutoDetectChangesEnabled = true;

         Enrollment enr = new Enrollment() {
            StudentID = 1, CourseID = 3141
         };

         Console.WriteLine("Adding New Enrollment");
         context.Enrollments.Add(enr);
         Console.WriteLine("Delete Student");

         var student = (from s in context.Students where s.ID ==
            23 select s).SingleOrDefault<Student>();

         context.Students.Remove(student);
         Console.WriteLine("");

         Console.WriteLine("Context tracking changes of {0} entity.",
            context.ChangeTracker.Entries().Count());
         var entries = context.ChangeTracker.Entries();

         foreach (var entry in entries) {
            Console.WriteLine("Entity Name: {0}", entry.Entity.GetType().Name);
            Console.WriteLine("Status: {0}", entry.State);
         }

         Console.ReadKey();
      }
   }
}

上記の例をコンパイルして実行すると、次の出力が表示されます。

Adding New Enrollment
Delete Student
Context tracking changes of 2 entity.
Entity Name: Enrollment
Status: Added
Entity Name: Student
Status: Deleted

新しい登録が追加され、1人の学生がデータベースから削除されたため、登録エンティティのステータスが追加に設定され、学生エンティティのステータスが削除されたことがわかります。

理解を深めるために、上記の例を段階的に実行することをお勧めします。

Entity Framework-色付きエンティティ

Entity Frameworkでは、Colored Entityは主にデザイナーでエンティティの色を変更することで、開発者がVisual Studioデザイナーでエンティティの関連グループを簡単に識別できるようにします。 この機能は、Entity Framework 5.0で初めて導入されました。

  • この機能は、パフォーマンスの側面とは関係ありません。
  • 1つのedmxファイルに大規模なプロジェクトと多くのエンティティがある場合、この機能はエンティティを異なるモジュールに分けるのに非常に役立ちます。

edmxファイルを使用していて、デザイナーで開いている場合、色を変更するには、デザインウィンドウでエンティティを選択します。 次に、右クリックして[プロパティ]を選択します。

Edmx File Designer Edmx File

[プロパティ]ウィンドウで、[塗りつぶしの色]プロパティを選択します。

色プロパティウィンドウ

緑や有効なRGB(255、128、128)などの有効な色名を使用して色を指定するか、カラーピッカーから選択することもできます。

複数のエンティティの色を一度に変更するには、複数のエンティティを選択し、プロパティウィンドウを使用してすべてのエンティティの塗りつぶしの色を変更します。

複数のエンティティ

また、次のオプションのいずれかを選択して、プロパティの形式を変更することができます-

  • 表示名
  • 表示名とタイプ

デフォルトでは、表示名オプションが選択されています。 プロパティの形式を変更するには、デザイナーウィンドウを右クリックします。

プロパティ形式

[スカラープロパティフォーマット]→[表示名とタイプ]を選択します。

スカラープロパティ形式

タイプが名前とともに表示されることを確認できます。

エンティティフレームワーク-コードファーストアプローチ

Entity Frameworkには、エンティティモデルを作成するための3つのアプローチがあり、それぞれに長所と短所があります。

  • コードファースト
  • 最初のデータベース
  • モデルファースト

この章では、コードファーストアプローチについて簡単に説明します。 コードでDesignerを使用することを好む開発者もいれば、コードで作業するだけの開発者もいます。 これらの開発者向けに、Entity Frameworkには、コードファーストと呼ばれるモデリングワークフローがあります。

  • Code Firstモデリングワークフローは、存在しないデータベースを対象とし、Code Firstが作成します。
  • 空のデータベースがあり、Code Firstがデータベースに新しいテーブルを追加する場合にも使用できます。
  • Code Firstでは、C#またはVB.Netクラスを使用してモデルを定義できます。
  • オプションで、クラスおよびプロパティの属性を使用して、または流れるようなAPIを使用して、追加の構成を実行できます。

コードファーストアプローチ

なぜ最初のコードですか?

  • Code Firstは、本当にパズルのピースのセットで構成されています。 まず、ドメインクラスです。
  • ドメインクラスは、Entity Frameworkとは関係ありません。 それらはあなたのビジネスドメインの単なるアイテムです。
  • Entity Frameworkには、これらのクラスとデータベース間の相互作用を管理するコンテキストがあります。
  • コンテキストはCode Firstに固有のものではありません。 これは、Entity Frameworkの機能です。
  • Code Firstは、コンテキストが管理しているクラスを検査するモデルビルダーを追加してから、一連のルールまたは規則を使用して、それらのクラスと関係がモデルを記述する方法、およびそのモデルをデータベースにマップする方法を決定します。
  • これらはすべて実行時に行われます。 あなたはこのモデルを見ることは決してないだろう、それはただの記憶の中にある。
  • Code Firstには、必要に応じてそのモデルを使用してデータベースを作成する機能もあります。
  • また、Code First Migrationsと呼ばれる機能を使用して、モデルが変更された場合にデータベースを更新できます。

環境設定

EF Code Firstアプローチの使用を開始するには、システムに次のツールをインストールする必要があります。

  • Visual Studio 2013(.net framework 4.5.2)以降のバージョン。
  • MS SQL Server 2012以降。
  • NuGetパッケージを介したエンティティフレームワーク。

NuGetパッケージ経由でEFをインストールする

  • ステップ1 *-最初に、ファイル→新規→プロジェクト…からコンソールアプリケーションを作成します。
  • ステップ2 *-左ペインからWindowsを選択し、テンプレートペインからコンソールアプリケーションを選択します。

EFのインストール

  • ステップ3 *-名前としてEFCodeFirstDemoを入力し、[OK]を選択します。
  • ステップ4 *-ソリューションエクスプローラーでプロジェクトを右クリックし、[NuGetパッケージの管理…​]を選択します。

NuGetパッケージマネージャー

これにより、NuGetパッケージマネージャーが開き、EntityFrameworkが検索されます。 これにより、Entity Frameworkに関連するすべてのパッケージが検索されます。

  • ステップ5 *-EntityFrameworkを選択し、[インストール]をクリックします。 または、[ツール]メニューから[NuGetパッケージマネージャー]をクリックし、[パッケージマネージャーコンソール]をクリックします。 [パッケージマネージャーコンソール]ウィンドウで、コマンドInstall-Package EntityFrameworkを入力します。

Installed Entity Framework6

インストールが完了すると、出力ウィンドウに「EFCodeFirstDemoに「EntityFramework 6.1.2」が正常にインストールされました」というメッセージが表示されます。

次の図に示すように、インストール後、EntityFramework.dllがプロジェクトに含まれます。

Entity Framework dll

これで、Code Firstアプローチの作業を開始する準備ができました。

Entity Framework-最初の例

クラスを使用して非常に単純なモデルを定義しましょう。 Program.csファイルで定義しているだけですが、実際のアプリケーションでは、クラスを個別のファイルに分割し、場合によっては個別のプロジェクトに分割します。 以下は、Code Firstアプローチを使用して作成するデータモデルです。

クラスを使用したモデル

モデルを作成

Studentクラスの次のコードを使用して、Program.csファイルに次の3つのクラスを追加します。

public class Student {
   public int ID { get; set; }
   public string LastName { get; set; }
   public string FirstMidName { get; set; }
   public DateTime EnrollmentDate { get; set; }

   public virtual ICollection<Enrollment> Enrollments { get; set; }
}
  • IDプロパティは、このクラスに対応するデータベーステーブルの主キー列になります。
  • Enrollmentsプロパティはナビゲーションプロパティです。 ナビゲーションプロパティは、このエンティティに関連する他のエンティティを保持します。
  • この場合、StudentエンティティのEnrollmentsプロパティには、そのStudentエンティティに関連するすべてのEnrollmentエンティティが保持されます。
  • 通常、ナビゲーションプロパティは、遅延読み込みなどの特定のEntity Framework機能を利用できるように、仮想として定義されます。
  • ナビゲーションプロパティが複数のエンティティ(多対多または1対1の関係など)を保持できる場合、そのタイプは、ICollectionなどのエントリを追加、削除、更新できるリストである必要があります。

以下は、コースクラスの実装です。

public class Course {
   public int CourseID { get; set; }
   public string Title { get; set; }
   public int Credits { get; set; }

   public virtual ICollection<Enrollment> Enrollments { get; set; }
}

Enrollmentsプロパティはナビゲーションプロパティです。 コースエンティティは、任意の数の登録エンティティに関連付けることができます。

以下は、登録クラスと列挙型の実装です。

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; }
}
  • EnrollmentIDプロパティが主キーになります。
  • Gradeプロパティは列挙型です。 Gradeタイプ宣言の後の疑問符は、Gradeプロパティがnull許容であることを示します。
  • nullのグレードは、ゼログレードとは異なります。 Nullは、グレードが不明であるか、まだ割り当てられていないことを意味します。
  • StudentIDおよびCourseIDプロパティは外部キーであり、対応するナビゲーションプロパティはStudentおよびCourseです。
  • 登録エンティティは1つの学生と1つのコースエンティティに関連付けられているため、プロパティは1つの学生とコースエンティティのみを保持できます。

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

特定のデータモデルのEntity Framework機能を調整するメインクラスは、データのクエリと保存を可能にするデータベースコンテキストクラスです。 このクラスを作成するには、DbContextクラスから派生させ、モデル内の各クラスの型指定されたDbSetを公開します。 以下は、DbContextクラスから派生したMyContextクラスの実装です。

public class MyContext : DbContext {
   public virtual DbSet<Course> Courses { get; set; }
   public virtual DbSet<Enrollment> Enrollments { get; set; }
   public virtual DbSet<Student> Students { get; set; }
}

以下はProgram.csファイルの完全なコードです。

using System.ComponentModel.DataAnnotations.Schema;
using System.Data.Entity;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace EFCodeFirstDemo {

   class Program {
      static void Main(string[] args) {}
   }

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

   public class Student {
      public int ID { get; set; }
      public string LastName { get; set; }
      public string FirstMidName { get; set; }
      public DateTime EnrollmentDate { get; set; }

      public virtual ICollection<Enrollment> Enrollments { get; set; }
   }

   public class Course {
      public int CourseID { get; set; }
      public string Title { get; set; }
      public int Credits { get; set; }

      public virtual ICollection<Enrollment> Enrollments { get; set; }
   }

   public class MyContext : DbContext {
      public virtual DbSet<Course> Courses { get; set; }
      public virtual DbSet<Enrollment> Enrollments { get; set; }
      public virtual DbSet<Student> Students { get; set; }
   }

}

上記のコードは、データの保存と取得を開始するために必要なすべてです。 データを追加して取得します。 以下は、mainメソッドのコードです。

static void Main(string[] args) {

   using (var context = new MyContext()) {
     //Create and save a new Students
      Console.WriteLine("Adding new students");

      var student = new Student {
         FirstMidName = "Alain", LastName = "Bomer",
            EnrollmentDate = DateTime.Parse(DateTime.Today.ToString())
      };

      context.Students.Add(student);

      var student1 = new Student {
         FirstMidName = "Mark", LastName = "Upston",
            EnrollmentDate = DateTime.Parse(DateTime.Today.ToString())
      };

      context.Students.Add(student1);
      context.SaveChanges();

     //Display all Students from the database
      var students = (from s in context.Students
         orderby s.FirstMidName select s).ToList<Student>();

      Console.WriteLine("Retrieve all Students from the database:");

      foreach (var stdnt in students) {
         string name = stdnt.FirstMidName + " " + stdnt.LastName;
         Console.WriteLine("ID: {0}, Name: {1}", stdnt.ID, name);
      }

      Console.WriteLine("Press any key to exit...");
      Console.ReadKey();
   }
}

上記のコードが実行されると、次の出力が表示されます。

Adding new students
Retrieve all Students from the database:
ID: 1, Name: Alain Bomer
ID: 2, Name: Mark Upston
Press any key to exit...

ここで、頭に浮かぶ疑問は、どこにデータを追加し、データベースから取得したデータとデータベースがどこにあるかです。 慣例により、DbContextはデータベースを作成しました。

  • ローカルSQL Expressインスタンスが利用可能な場合、Code Firstはそのインスタンス上にデータベースを作成しました。
  • SQL Expressが利用できない場合、Code FirstはLocalDbを使用しようとします。
  • データベースには、派生コンテキストの完全修飾名にちなんで名前が付けられます。

この例では、SQL Expressインスタンスが使用可能であり、データベース名は次の画像に示すようにEFCodeFirstDemo.MyContextです。

SQL Expressインスタンス

  • これらはデフォルトの規則に過ぎず、Code Firstが使用するデータベースを変更するにはさまざまな方法があります。
  • 上の画像でわかるように、Student、Courses、およびEnrollmentsテーブルが作成され、各テーブルには適切なデータ型と長さの列が含まれています。
  • 列名とデータ型は、それぞれのドメインクラスのプロパティとも一致します。

データベースの初期化

上記の例では、Code Firstがデータベースを自動的に作成することを確認しましたが、データベースとサーバーの名前を変更する場合は、Code Firstがデータベースの初期化中にデータベース名とサーバーを決定する方法を見てみましょう。 次の図をご覧ください。

データベースの初期化

次の方法で、コンテキストクラスの基本コンストラクタを定義できます。

  • パラメータなし
  • データベース名
  • 接続文字列名

パラメータなし

上記の例に示すように、パラメーターなしでコンテキストクラスのベースコンストラクターを指定すると、エンティティフレームワークは、ローカルSQLEXPRESSサーバーに\ {Namespace}。\ {Context class name}という名前でデータベースを作成します。

上記の例では、自動的に作成されるデータベースの名前はEFCodeFirstDemo.MyContextです。 名前を見ると、次のコードに示すように、EFCodeFirstDemoがネームスペースであり、MyContextがコンテキストクラス名であることがわかります。

public class MyContext : DbContext {
   public MyContext() : base() {}

   public virtual DbSet<Course> Courses { get; set; }
   public virtual DbSet<Enrollment> Enrollments { get; set; }
   public virtual DbSet<Student> Students { get; set; }
}

データベース名

データベース名をパラメーターとしてコンテキストクラスの基本コンストラクターに渡すと、Code Firstは自動的にデータベースを自動的に作成しますが、今回はローカルSQLEXPRESSデータベースサーバーの基本コンストラクターにパラメーターとして渡された名前になります。

次のコードでは、MyContextDBがベースコンストラクターのパラメーターとして指定されています。 アプリケーションを実行すると、MyContextDB名のデータベースがローカルSQLサーバーに作成されます。

public class MyContext : DbContext {
   public MyContext() : base("MyContextDB") {}
   public virtual DbSet<Course> Courses { get; set; }
   public virtual DbSet<Enrollment> Enrollments { get; set; }
   public virtual DbSet<Student> Students { get; set; }
}

接続文字列名

これは、SQL ExpressまたはLocalDb以外のデータベースサーバーを使用するようDbContextに指示する簡単な方法です。 app.configファイルに接続文字列を挿入することもできます。

  • 接続文字列の名前がコンテキストの名前と一致する場合(名前空間修飾の有無にかかわらず)、パラメーターなしコンストラクターが使用されると、DbContextによって検出されます。
  • 接続文字列名がコンテキストの名前と異なる場合、接続文字列名をDbContextコンストラクターに渡すことにより、DbContextにこの接続をCode Firstモードで使用するように指示できます。
public class MyContext : DbContext {
   public MyContext() : base("name = MyContextDB") {}
   public virtual DbSet<Course> Courses { get; set; }
   public virtual DbSet<Enrollment> Enrollments { get; set; }
   public virtual DbSet<Student> Students { get; set; }
}
  • 上記のコードでは、コンテキストクラス接続文字列のスニペットが、ベースコンストラクターのパラメーターとして指定されています。
  • 接続文字列名は「name =」で始まる必要があります。それ以外の場合、データベース名と見なされます。
  • この形式により、接続文字列が設定ファイルで見つかることを明示的に示します。 指定された名前の接続文字列が見つからない場合、例外がスローされます。
<connectionStrings>
   <add name = "MyContextDB"
      connectionString = "Data Source =.;Initial Catalog = EFMyContextDB;Integrated Security = true"
      providerName = "System.Data.SqlClient"/>
</connectionStrings>
  • app.configの接続文字列のデータベース名は EFMyContextDB です。 CodeFirstは、新しい EFMyContextDB データベースを作成するか、ローカルSQL Serverにある既存の EFMyContextDB データベースを使用します。

ドメインクラス

これまでは、EFにデフォルトの規則を使用してモデルを検出させるだけでしたが、クラスが規則に従っていない場合があり、さらに設定を実行できるようにする必要があります。 ただし、EFに必要な情報を提供するようにドメインクラスを構成することにより、これらの規則をオーバーライドできます。 ドメインクラスを構成するには2つのオプションがあります-

  • データ注釈
  • 流暢なAPI

データ注釈

DataAnnotationsは、最も一般的に必要な構成を強調表示するクラスを構成するために使用されます。 DataAnnotationsは、ASP.NET MVCなどの多くの.NETアプリケーションでも理解されます。これにより、これらのアプリケーションは、クライアント側の検証に同じ注釈を活用できます。

以下は、学生クラスで使用されるデータ注釈です。

public class Enrollment {

   [Key]
   public int EnrollmentID { get; set; }
   public int CourseID { get; set; }
   public int StudentID { get; set; }
   public Grade? Grade { get; set; }

   [ForeignKey("CourseID")]
   public virtual Course Course { get; set; }

   [ForeignKey("ID")]
   public virtual Student Student { get; set; }
}

流暢なAPI

ほとんどのモデル構成は、単純なデータ注釈を使用して実行できます。 Fluent APIは、データ注釈では不可能な、より高度な構成に加えて、データ注釈で可能なすべてをカバーするモデル構成を指定する高度な方法です。 データ注釈と流れるようなAPIを一緒に使用できます。

Fluent APIにアクセスするには、DbContextのOnModelCreatingメソッドをオーバーライドします。 次のコードに示すように、学生テーブルの列名をFirstMidNameからFirstNameに変更します。

public class MyContext : DbContext {

   protected override void OnModelCreating(DbModelBuilder modelBuilder) {
      modelBuilder.Entity<Student>().Property(s ⇒ s.FirstMidName)
         .HasColumnName("FirstName");
   }

   public virtual DbSet<Course> Courses { get; set; }
   public virtual DbSet<Enrollment> Enrollments { get; set; }
   public virtual DbSet<Student> Students { get; set; }
}

Entity Framework-データ注釈

DataAnnotationsは、最も一般的に必要な構成を強調表示するクラスを構成するために使用されます。 DataAnnotationsは、ASP.NET MVCなどの多くの.NETアプリケーションでも理解されます。これにより、これらのアプリケーションは、クライアント側の検証に同じ注釈を活用できます。 DataAnnotation属性は、デフォルトのCodeFirst規則をオーバーライドします。

*System.ComponentModel.DataAnnotations* には、列のNULL可能性またはサイズに影響を与える次の属性が含まれています。
  • Key

  • タイムスタンプ

  • ConcurrencyCheck

  • 必須

  • 最小長

  • MaxLength

  • StringLength

    *System.ComponentModel.DataAnnotations.Schema* 名前空間には、データベースのスキーマに影響を与える次の属性が含まれています。
  • カラム

  • 索引

  • 外部キー

  • NotMapped

  • InverseProperty

Key

Entity Frameworkは、エンティティの追跡に使用するキー値を持つすべてのエンティティに依存しています。 Code Firstが依存する規則の1つは、どのプロパティがCode Firstクラスのキーであるかをどのように示唆するかです。

  • 慣例では、「Id」という名前のプロパティ、またはクラス名と「StudentId」などの「Id」を組み合わせたプロパティを検索します。
  • プロパティは、データベースの主キー列にマップされます。 *Student、Course、およびEnrollmentクラスはこの規則に従います。

次に、StudentクラスがIDではなくStdntIDという名前を使用したとします。 Code Firstがこの規則に一致するプロパティを見つけられない場合、キープロパティが必要であるというEntity Frameworkの要件のため、例外をスローします。 キーアノテーションを使用して、EntityKeyとして使用するプロパティを指定できます。

StdntIDを含むStudentクラスの次のコードを見てみましょう。ただし、デフォルトのCode First規則には準拠していません。 そのため、これを処理するために、キー属性を追加して、それを主キーにします。

public class Student {

   [Key]
   public int StdntID { get; set; }
   public string LastName { get; set; }
   public string FirstMidName { get; set; }
   public DateTime EnrollmentDate { get; set; }

   public virtual ICollection<Enrollment> Enrollments { get; set; }
}

アプリケーションを実行し、SQL Server Explorerでデータベースを調べると、主キーが学生テーブルのStdntIDになっていることがわかります。

プライマリキー

Entity Frameworkは複合キーもサポートしています。* 複合キー*は、複数のプロパティで構成される主キーでもあります。 たとえば、主キーがLicenseNumberとIssuingCountryの組み合わせであるDrivingLicenseクラスがあります。

public class DrivingLicense {

   [Key, Column(Order = 1)]
   public int LicenseNumber { get; set; }
   [Key, Column(Order = 2)]
   public string IssuingCountry { get; set; }
   public DateTime Issued { get; set; }
   public DateTime Expires { get; set; }
}

複合キーがある場合、Entity Frameworkでは、キープロパティの順序を定義する必要があります。 これを行うには、列注釈を使用して順序を指定します。

列注釈

タイムスタンプ

Code FirstはTimestampプロパティをConcurrencyCheckプロパティと同じように扱いますが、コードが最初に生成するデータベースフィールドがnull不可であることも保証します。

  • 並行性チェックにrowversionまたはtimestampフィールドを使用するのがより一般的です。
  • プロパティの型がバイト配列である限り、ConcurrencyCheckアノテーションを使用する代わりに、より具体的なTimeStampアノテーションを使用できます。
  • 指定されたクラスに含めることができるタイムスタンププロパティは1つだけです。

TimeStampプロパティをCourseクラスに追加して、簡単な例を見てみましょう-

public class Course {

   public int CourseID { get; set; }
   public string Title { get; set; }
   public int Credits { get; set; }
   [Timestamp]
   public byte[] TStamp { get; set; }

   public virtual ICollection<Enrollment> Enrollments { get; set; }
}

上記の例でわかるように、Timestamp属性はCourseクラスのByte []プロパティに適用されます。 そのため、Code FirstはCoursesテーブルにタイムスタンプ列「+ TStamp +」を作成します。

ConcurrencyCheck

ConcurrencyCheck注釈を使用すると、ユーザーがエンティティを編集または削除するときに、データベースでの同時実行チェックに使用する1つ以上のプロパティにフラグを付けることができます。 EF Designerで作業している場合、これはプロパティのConcurrencyModeをFixedに設定することと一致します。

ConcurrencyCheckがCourseクラスのTitleプロパティに追加されて動作する仕組みの簡単な例を見てみましょう。

public class Course {

   public int CourseID { get; set; }
   [ConcurrencyCheck]
   public string Title { get; set; }
   public int Credits { get; set; }
   [Timestamp, DataType("timestamp")]
   public byte[] TimeStamp { get; set; }

   public virtual ICollection<Enrollment> Enrollments { get; set; }
}

上記のコースクラスでは、ConcurrencyCheck属性が既存のTitleプロパティに適用されます。 これで、次のコードに示すように、コードファーストの更新コマンドにタイトル列が含まれ、オプティミスティックな同時実行性がチェックされます。

exec sp_executesql N'UPDATE [dbo].[Courses]
   SET [Title] = @0
   WHERE (([CourseID] = @1) AND ([Title] = @2))
   ',N'@0 nvarchar(max) ,@1 int,@2 nvarchar(max) ',@0=N'Maths',@1=1,@2=N'Calculus'
go

必要な注釈

Requiredアノテーションは、特定のプロパティが必須であることをEFに伝えます。 FirstMidNameプロパティにRequired idが追加されている次のStudentクラスを見てみましょう。 必須属性は、プロパティにデータがあることをEFに強制します。

public class Student {

   [Key]
   public int StdntID { get; set; }

   [Required]
   public string LastName { get; set; }

   [Required]
   public string FirstMidName { get; set; }
   public DateTime EnrollmentDate { get; set; }

   public virtual ICollection<Enrollment> Enrollments { get; set; }
}

上記の例に見られるように、必須属性はFirstMidNameとLastNameに適用されます。 したがって、次の図に示すように、Code Firstは、StudentsテーブルにNOT NULL FirstMidName列とLastName列を作成します。

Not Null

MaxLength

MaxLength属性を使用すると、追加のプロパティ検証を指定できます。 ドメインクラスの文字列または配列型のプロパティに適用できます。 EF Code Firstは、MaxLength属性で指定された列のサイズを設定します。

MaxLength(24)属性がTitleプロパティに適用されている次のコースクラスを見てみましょう。

public class Course {

   public int CourseID { get; set; }
   [ConcurrencyCheck]
   [MaxLength(24)]
   public string Title { get; set; }
   public int Credits { get; set; }

   public virtual ICollection<Enrollment> Enrollments { get; set; }
}

上記のアプリケーションを実行すると、Code Firstは次の画像に示すように、CourseIdテーブルにnvarchar(24)列のタイトルを作成します。

nvarchar Column

ユーザーが24文字を超えるタイトルを設定すると、EFはEntityValidationErrorをスローします。

最小長

MinLength属性を使用すると、MaxLengthで行ったように、追加のプロパティ検証を指定することもできます。 次のコードに示すように、MinLength属性はMaxLength属性とともに使用することもできます。

public class Course {

   public int CourseID { get; set; }
   [ConcurrencyCheck]
   [MaxLength(24) , MinLength(5)]
   public string Title { get; set; }
   public int Credits { get; set; }

   public virtual ICollection<Enrollment> Enrollments { get; set; }
}

MinLength属性で指定された長さより短いTitleプロパティの値を設定した場合、またはMaxLength属性で指定された長さより長い値を設定した場合、EFはEntityValidationErrorをスローします。

StringLength

StringLengthでは、MaxLengthなどの追加のプロパティ検証を指定することもできます。 唯一の違いは、StringLength属性はDomainクラスの文字列型プロパティにのみ適用できることです。

public class Course {

   public int CourseID { get; set; }
   [StringLength (24)]
   public string Title { get; set; }
   public int Credits { get; set; }

   public virtual ICollection<Enrollment> Enrollments { get; set; }
}

Entity Frameworkは、StringLength属性のプロパティの値も検証します。 ユーザーが24文字を超えるタイトルを設定すると、EFはEntityValidationErrorをスローします。

Default Code First規則は、クラス名に類似したテーブル名を作成します。 Code Firstにデータベースを作成させ、作成するテーブルの名前も変更したい場合。 その後-

  • 既存のデータベースでCode Firstを使用できます。 ただし、クラスの名前がデータベース内のテーブルの名前と一致しているとは限りません。
  • テーブル属性は、このデフォルトの規則をオーバーライドします。
  • EF Code Firstは、特定のドメインクラスのテーブル属性に指定された名前でテーブルを作成します。

クラスの名前がStudentである次の例を見てみましょう。慣例により、Code Firstは、これがStudentsという名前のテーブルにマッピングされることを前提としています。 そうでない場合は、次のコードに示すように、Table属性を使用してテーブルの名前を指定できます。

[Table("StudentsInfo")]
public class Student {

   [Key]
   public int StdntID { get; set; }
   [Required]
   public string LastName { get; set; }
   [Required]
   public string FirstMidName { get; set; }
   public DateTime EnrollmentDate { get; set; }

   public virtual ICollection<Enrollment> Enrollments { get; set; }
}

これで、Table属性がテーブルをStudentsInfoとして指定していることがわかります。 テーブルが生成されると、次の画像に示すようにテーブル名StudentsInfoが表示されます。

StudentsInfo

テーブル名のみを指定することはできませんが、次のコードに示すように、Table属性を使用してテーブルのスキーマを指定することもできます。

[Table("StudentsInfo", Schema = "Admin")]
public class Student {

   [Key]
   public int StdntID { get; set; }
   [Required]
   public string LastName { get; set; }
   [Required]
   public string FirstMidName { get; set; }
   public DateTime EnrollmentDate { get; set; }

   public virtual ICollection<Enrollment> Enrollments { get; set; }
}

上記の例でわかるように、テーブルはadminスキーマで指定されています。 次の図に示すように、Code FirstはAdminスキーマにStudentsInfoテーブルを作成します。

管理スキーマ

カラム

また、Table属性と同じですが、Table属性はテーブルの動作をオーバーライドしますが、Column属性はカラムの動作をオーバーライドします。 Default Code First規則は、プロパティ名に似た列名を作成します。 Code Firstにデータベースを作成させ、テーブルの列の名前を変更したい場合。 その後-

  • 列属性はデフォルトの規則をオーバーライドします。
  • EF Code Firstは、特定のプロパティのColumn属性に指定された名前の列を作成します。

プロパティの名前がFirstMidNameであり、慣例により、Code FirstがFirstMidNameという名前の列にマッピングされると想定している次の例を見てみましょう。

そうでない場合は、次のコードに示すように、Column属性を使用して列の名前を指定できます。

public class Student {

   public int ID { get; set; }
   public string LastName { get; set; }
   [Column("FirstName")]
   public string FirstMidName { get; set; }
   public DateTime EnrollmentDate { get; set; }

   public virtual ICollection<Enrollment> Enrollments { get; set; }
}

Column属性が列をFirstNameとして指定していることがわかります。 テーブルが生成されると、次の図に示すように、列名FirstNameが表示されます。

FirstName

索引

Index属性は、Entity Framework 6.1で導入されました。 以前のバージョンを使用している場合、このセクションの情報は適用されません。

  • IndexAttributeを使用して、1つ以上の列にインデックスを作成できます。
  • 1つ以上のプロパティに属性を追加すると、EFはデータベースを作成するときにデータベースに対応するインデックスを作成します。
  • ほとんどの場合、インデックスを使用すると、データをより高速かつ効率的に取得できます。 ただし、テーブルまたはビューにインデックスをオーバーロードすると、挿入や更新などの他の操作のパフォーマンスに不快な影響を与える可能性があります。
  • インデックス作成は、Entity Frameworkの新機能であり、データベースからデータをクエリするために必要な時間を短縮することにより、Code Firstアプリケーションのパフォーマンスを向上させることができます。
  • Index属性を使用してデータベースにインデックスを追加し、デフォルトのUniqueおよびClustered設定をオーバーライドして、シナリオに最適なインデックスを取得できます。
  • デフォルトでは、インデックスの名前はIX_ <プロパティ名>になります

クレジットのコースクラスにインデックス属性が追加されている次のコードを見てみましょう。

public class Course {
   public int CourseID { get; set; }
   public string Title { get; set; }
   [Index]
   public int Credits { get; set; }

   public virtual ICollection<Enrollment> Enrollments { get; set; }
}

Indexs属性がCreditsプロパティに適用されていることがわかります。 テーブルが生成されると、インデックスにIX_Creditsが表示されます。

IXクレジット

デフォルトでは、インデックスは一意ではありませんが、 IsUnique 名前付きパラメータを使用して、インデックスが一意であることを指定できます。 次の例では、次のコードに示すように一意のインデックスを紹介しています。

public class Course {
   public int CourseID { get; set; }
   [Index(IsUnique = true)]

   public string Title { get; set; }
   [Index]

   public int Credits { get; set; }
   public virtual ICollection<Enrollment> Enrollments { get; set; }
}

外部キー

Code First規約は、モデル内の最も一般的な関係を処理しますが、助けが必要な場合があります。 たとえば、Studentクラスのキープロパティの名前を変更すると、Enrollmentクラスとの関係に問題が発生します。

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

public class Student {
   [Key]
   public int StdntID { get; set; }
   public string LastName { get; set; }
   public string FirstMidName { get; set; }
   public DateTime EnrollmentDate { get; set; }

   public virtual ICollection<Enrollment> Enrollments { get; set; }
}

データベースの生成中、Code FirstはEnrollmentクラスのStudentIDプロパティを確認し、クラス名と「ID」を一致させるという規則により、Studentクラスの外部キーとして認識します。 ただし、StudentクラスにはStudentIDプロパティはありませんが、StdntIDプロパティはStudentクラスです。

これに対する解決策は、登録でナビゲーションプロパティを作成し、ForeignKey DataAnnotationを使用して、次のコードに示すように、Code Firstが2つのクラス間の関係を構築する方法を理解できるようにすることです。

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; }
   [ForeignKey("StudentID")]

   public virtual Student Student { get; set; }
}

これで、ForeignKey属性がナビゲーションプロパティに適用されていることがわかります。

ForeignKey Attribute

NotMapped

Code Firstのデフォルトの規則では、サポートされているデータ型で、ゲッターとセッターを含むすべてのプロパティがデータベースに表されます。 しかし、これは常にアプリケーションに当てはまるわけではありません。 NotMapped属性は、このデフォルトの規則をオーバーライドします。 たとえば、StudentクラスにFatherNameなどのプロパティがある場合でも、保存する必要はありません。 次のコードに示すように、データベースに列を作成しない場合、FatherNameプロパティにNotMapped属性を適用できます。

public class Student {
   [Key]
   public int StdntID { get; set; }
   public string LastName { get; set; }
   public string FirstMidName { get; set; }

   public DateTime EnrollmentDate { get; set; }
   [NotMapped]

   public int FatherName { get; set; }
   public virtual ICollection<Enrollment> Enrollments { get; set; }
}

NotMapped属性がFatherNameプロパティに適用されていることがわかります。 テーブルが生成されると、FatherName列はデータベースに作成されませんが、Studentクラスに存在することがわかります。

NotMapped Attribute

Code Firstは、StudentクラスのAddressおよびAgeプロパティの次の例に示すように、ゲッターまたはセッターを持たないプロパティの列を作成しません。

InverseProperty

InversePropertyは、クラス間に複数の関係がある場合に使用されます。 登録クラスでは、現在のコースと以前のコースを登録した人を追跡することができます。 Enrollmentクラスに2つのナビゲーションプロパティを追加しましょう。

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 CurrCourse { get; set; }
   public virtual Course PrevCourse { get; set; }
   public virtual Student Student { get; set; }
}

同様に、これらのプロパティによって参照されるCourseクラスにも追加する必要があります。 Courseクラスには、現在および以前のすべての登録を含むEnrollmentクラスに戻るナビゲーションプロパティがあります。

public class Course {

   public int CourseID { get; set; }
   public string Title { get; set; }
   [Index]

   public int Credits { get; set; }
   public virtual ICollection<Enrollment> CurrEnrollments { get; set; }
   public virtual ICollection<Enrollment> PrevEnrollments { get; set; }
}

上記のクラスに示されているように、外部キープロパティが特定のクラスに含まれていない場合、Code Firstは\ {Class Name} _ \ {Primary Key}外部キー列を作成します。 データベースが生成されると、次の外部キーが表示されます。

外部キー

ご覧のとおり、最初のコードは2つのクラスのプロパティを単独で一致させることができません。 登録のデータベーステーブルには、CurrCourseとPrevCourseにそれぞれ1つの外部キーが必要ですが、Code Firstは4つの外部キープロパティを作成します。

  • CurrCourse _CourseID
  • PrevCourse _CourseID
  • Course_CourseID、および
  • Course_CourseID1

これらの問題を修正するには、InversePropertyアノテーションを使用して、プロパティの配置を指定できます。

public class Course {

   public int CourseID { get; set; }
   public string Title { get; set; }
   [Index]

   public int Credits { get; set; }
   [InverseProperty("CurrCourse")]

   public virtual ICollection<Enrollment> CurrEnrollments { get; set; }
   [InverseProperty("PrevCourse")]

   public virtual ICollection<Enrollment> PrevEnrollments { get; set; }
}

ご覧のとおり、InverseProperty属性は、それが属するEnrollmentクラスの参照プロパティを指定することにより、上記のCourseクラスに適用されます。 これで、Code Firstはデータベースを生成し、次の図に示すように、Enrollmentsテーブルに2つの外部キー列のみを作成します。

外部キー列

理解を深めるために、上記の例を段階的に実行することをお勧めします。

Entity Framework-Fluent API

Fluent APIは、データアノテーションでは不可能な、より高度な構成に加えて、データアノテーションで可能なすべてをカバーするモデル構成を指定する高度な方法です。 データアノテーションとFluent APIは一緒に使用できますが、Code FirstはFluent API>データアノテーション>デフォルトの規則を優先します。

  • Fluent APIは、ドメインクラスを構成するもう1つの方法です。
  • Code First Fluent APIは、派生したDbContextでOnModelCreatingメソッドをオーバーライドすることで最も一般的にアクセスされます。 *Fluent APIは、DataAnnotationsよりも多くの構成機能を提供します。 Fluent APIは、次のタイプのマッピングをサポートしています。

この章では、次のコードに示すように、Student、Course、およびEnrollmentクラスとMyContext名を持つ1つのコンテキストクラスを含む簡単な例を続けます。

using System.Data.Entity;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace EFCodeFirstDemo {

   class Program {
      static void Main(string[] args) {}
   }

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

   public class Student {
      public int ID { get; set; }
      public string LastName { get; set; }
      public string FirstMidName { get; set; }

      public DateTime EnrollmentDate { get; set; }

      public virtual ICollection<Enrollment> Enrollments { get; set; }
   }

   public class Course {
      public int CourseID { get; set; }
      public string Title { get; set; }
      public int Credits { get; set; }

      public virtual ICollection<Enrollment> Enrollments { get; set; }
   }

   public class MyContext : DbContext {
      public virtual DbSet<Course> Courses { get; set; }
      public virtual DbSet<Enrollment> Enrollments { get; set; }
      public virtual DbSet<Student> Students { get; set; }
   }

}

Fluent APIにアクセスするには、DbContextのOnModelCreatingメソッドをオーバーライドする必要があります。 次のコードに示すように、学生テーブルの列名をFirstMidNameからFirstNameに変更する簡単な例を見てみましょう。

public class MyContext : DbContext {

   protected override void OnModelCreating(DbModelBuilder modelBuilder) {
      modelBuilder.Entity<Student>().Property(s ⇒ s.FirstMidName)
      .HasColumnName("FirstName");}

      public virtual DbSet<Course> Courses { get; set; }
      public virtual DbSet<Enrollment> Enrollments { get; set; }
      public virtual DbSet<Student> Students { get; set; }
}

DbModelBuilderは、CLRクラスをデータベーススキーマにマップするために使用されます。 これはメインクラスであり、すべてのドメインクラスを構成できます。 エンティティデータモデル(EDM)を構築するこのコード中心のアプローチは、コードファーストとして知られています。

Fluent APIには、エンティティとそのプロパティを構成して、さまざまなCode First規則をオーバーライドするための重要なメソッドが多数用意されています。 以下はその一部です。

Sr. No. Method Name & Description
1
  • ComplexType<TComplexType>*

型をモデルの複合型として登録し、複合型の構成に使用できるオブジェクトを返します。 このメソッドは、同じタイプに対して複数回呼び出して、複数行の構成を実行できます。

2

Entity<TEntityType>

エンティティタイプをモデルの一部として登録し、エンティティの構成に使用できるオブジェクトを返します。 このメソッドは、同じエンティティに対して複数回呼び出して、構成の複数の行を実行できます。

3

HasKey<TKey>

このエンティティタイプの主キープロパティを設定します。

4

HasMany<TTargetEntity>

このエンティティタイプから多くの関係を設定します。

5

HasOptional<TTargetEntity>

このエンティティタイプからオプションの関係を設定します。 エンティティタイプのインスタンスは、この関係を指定しなくてもデータベースに保存できます。 データベース内の外部キーはNULL可能です。

6

HasRequired<TTargetEntity>

このエンティティタイプから必要な関係を設定します。 この関係が指定されない限り、エンティティタイプのインスタンスはデータベースに保存できません。 データベース内の外部キーはNULL不可です。

7

Ignore<TProperty>

データベースからマップされないように、モデルからプロパティを除外します。 (StructuralTypeConfiguration <TStructuralType>から継承)

8

Property<T>

このタイプで定義されている構造体プロパティを設定します。 (StructuralTypeConfiguration <TStructuralType>から継承)

9

ToTable(String)

このエンティティタイプがマップされるテーブル名を設定します。

Fluent APIを使用すると、データベースへのマッピング方法や、それらの相互関係を変更するかどうかにかかわらず、エンティティまたはそのプロパティを構成できます。 構成を使用すると、さまざまなマッピングとモデリングに影響を与えることができます。 Fluent APIがサポートする主なマッピングタイプは次のとおりです-

  • エンティティマッピング
  • プロパティのマッピング

エンティティマッピング

エンティティマッピングは、クラスがデータベースにマップされる方法に関するEntity Frameworkの理解に影響を与える単なるいくつかの単純なマッピングです。 これらすべてをデータアノテーションで説明しましたが、ここではFluent APIを使用して同じことを実現する方法を説明します。

  • したがって、これらの構成を追加するためにドメインクラスに入るのではなく、コンテキスト内でこれを行うことができます。
  • 最初のことは、OnModelCreatingメソッドをオーバーライドして、modelBuilderを操作できるようにすることです。

デフォルトのスキーマ

データベースが生成されるときのデフォルトのスキーマはdboです。 DbModelBuilderのHasDefaultSchemaメソッドを使用して、すべてのテーブル、ストアドプロシージャなどに使用するデータベーススキーマを指定できます。

管理スキーマが適用される次の例を見てみましょう。

public class MyContext : DbContext {
   public MyContext() : base("name = MyContextDB") {}

   protected override void OnModelCreating(DbModelBuilder modelBuilder) {
     //Configure default schema
      modelBuilder.HasDefaultSchema("Admin");
   }

   public virtual DbSet<Course> Courses { get; set; }
   public virtual DbSet<Enrollment> Enrollments { get; set; }
   public virtual DbSet<Student> Students { get; set; }
}

エンティティをテーブルにマップ

デフォルトの規則では、Code Firstは、コース、登録、学生などのコンテキストクラスのDbSetプロパティの名前でデータベーステーブルを作成します。 ただし、別のテーブル名が必要な場合は、次のコードに示すように、この規則をオーバーライドして、DbSetプロパティとは異なるテーブル名を指定できます。

protected override void OnModelCreating(DbModelBuilder modelBuilder) {

  //Configure default schema
   modelBuilder.HasDefaultSchema("Admin");

  //Map entity to table
   modelBuilder.Entity<Student>().ToTable("StudentData");
   modelBuilder.Entity<Course>().ToTable("CourseDetail");
   modelBuilder.Entity<Enrollment>().ToTable("EnrollmentInfo");
}

データベースが生成されると、OnModelCreatingメソッドで指定されたテーブル名が表示されます。

OnModelメソッド

エンティティ分割(エンティティを複数のテーブルにマップ)

エンティティ分割を使用すると、複数のテーブルからのデータを単一のクラスに結合できます。また、それらは1対1の関係を持つテーブルでのみ使用できます。 学生情報が2つのテーブルにマッピングされている次の例を見てみましょう。

protected override void OnModelCreating(DbModelBuilder modelBuilder) {
  //Configure default schema
   modelBuilder.HasDefaultSchema("Admin");

  //Map entity to table
   modelBuilder.Entity<Student>().Map(sd ⇒ {
      sd.Properties(p ⇒ new { p.ID, p.FirstMidName, p.LastName });
      sd.ToTable("StudentData");
   })

   .Map(si ⇒ {
      si.Properties(p ⇒ new { p.ID, p.EnrollmentDate });
      si.ToTable("StudentEnrollmentInfo");
   });

   modelBuilder.Entity<Course>().ToTable("CourseDetail");
   modelBuilder.Entity<Enrollment>().ToTable("EnrollmentInfo");
}

上記のコードでは、Mapメソッドを使用して一部のプロパティをStudentDataテーブルに、一部のプロパティをStudentEnrollmentInfoテーブルにマッピングすることにより、Studentエンティティが次の2つのテーブルに分割されていることがわかります。

  • StudentData -学生のFirstMidNameと姓が含まれています。
  • StudentEnrollmentInfo -EnrollmentDateが含まれています。

データベースが生成されると、次の図に示すように、データベースに次のテーブルが表示されます。

エンティティ分割

プロパティのマッピング

Propertyメソッドは、エンティティまたは複合型に属する各プロパティの属性を構成するために使用されます。 Propertyメソッドは、特定のプロパティの構成オブジェクトを取得するために使用されます。 Fluent APIを使用して、ドメインクラスのプロパティをマップおよび構成することもできます。

主キーの構成

主キーのデフォルトの規則は次のとおりです-

  • クラスは、名前が「ID」または「Id」であるプロパティを定義します
  • クラス名の後に「ID」または「Id」が続く

クラスが次のStudentクラスのコードに示されている主キーのデフォルトの規則に従っていない場合-

public class Student {
   public int StdntID { get; set; }
   public string LastName { get; set; }
   public string FirstMidName { get; set; }
   public DateTime EnrollmentDate { get; set; }

   public virtual ICollection<Enrollment> Enrollments { get; set; }
}

その後、明示的にプロパティを主キーに設定するには、次のコードに示すようにHasKeyメソッドを使用できます-

protected override void OnModelCreating(DbModelBuilder modelBuilder) {
  //Configure default schema
   modelBuilder.HasDefaultSchema("Admin");

  //Configure Primary Key
   modelBuilder.Entity<Student>().HasKey<int>(s ⇒ s.StdntID);
}

列の構成

Entity Frameworkでは、デフォルトでCode Firstは同じ名前、順序、データ型を持つプロパティの列を作成します。 ただし、次のコードに示すように、この規則をオーバーライドすることもできます。

protected override void OnModelCreating(DbModelBuilder modelBuilder) {

  //Configure default schema
   modelBuilder.HasDefaultSchema("Admin");

  //Configure EnrollmentDate Column
   modelBuilder.Entity<Student>().Property(p ⇒ p.EnrollmentDate)

   .HasColumnName("EnDate")
   .HasColumnType("DateTime")
   .HasColumnOrder(2);
}

MaxLengthプロパティを構成する

次の例では、Course Titleプロパティは24文字以下にする必要があります。 ユーザーが24文字より長い値を指定すると、ユーザーはDbEntityValidationException例外を受け取ります。

protected override void OnModelCreating(DbModelBuilder modelBuilder) {
  //Configure default schema
   modelBuilder.HasDefaultSchema("Admin");
   modelBuilder.Entity<Course>().Property(p ⇒ p.Title).HasMaxLength(24);
}

NullまたはNotNullプロパティを構成する

次の例では、IsRequiredメソッドを使用してNotNull列を作成するために、Course Titleプロパティが必要です。 同様に、Student EnrollmentDateはオプションであるため、次のコードに示すように、IsOptionalメソッドを使用してこの列にnull値を許可します。

protected override void OnModelCreating(DbModelBuilder modelBuilder) {

  //Configure default schema
   modelBuilder.HasDefaultSchema("Admin");
   modelBuilder.Entity<Course>().Property(p ⇒ p.Title).IsRequired();
   modelBuilder.Entity<Student>().Property(p ⇒ p.EnrollmentDate).IsOptional();

  //modelBuilder.Entity<Student>().Property(s ⇒ s.FirstMidName)
  //.HasColumnName("FirstName");
}

関係の構成

データベースのコンテキストでは、リレーションシップは、1つのテーブルに他のテーブルのプライマリキーを参照する外部キーがある場合に、2つのリレーショナルデータベーステーブル間に存在する状況です。 Code Firstを使用する場合、ドメインCLRクラスを定義してモデルを定義します。 既定では、Entity FrameworkはCode First規則を使用して、クラスをデータベーススキーマにマップします。

  • Code First命名規則を使用する場合、ほとんどの場合、Code Firstを使用して、外部キーとナビゲーションプロパティに基づいてテーブル間の関係を設定できます。
  • それらの規則に合わない場合は、クラス間の関係に影響を与えるために使用できる構成、およびCode Firstで構成を追加するときにデータベースでそれらの関係がどのように実現されるかがあります。
  • それらの一部はデータアノテーションで使用でき、Fluent APIを使用してさらに複雑なものを適用できます。

1対1の関係を構成する

モデルで1対1の関係を定義する場合、各クラスで参照ナビゲーションプロパティを使用します。 データベースでは、両方のテーブルにリレーションシップの両側にレコードを1つだけ含めることができます。 各主キー値は、関連テーブル内の1つのレコードのみに関連付けられています(またはレコードがありません)。

  • 関連する列の両方が主キーであるか、一意の制約がある場合、1対1の関係が作成されます。
  • 1対1の関係では、主キーはさらに外部キーとして機能し、どちらのテーブルにも個別の外部キー列はありません。
  • この方法で関連するほとんどの情報はすべて1つのテーブルにあるため、このタイプの関係は一般的ではありません。

次の例を見てみましょう。ここでは、モデルに別のクラスを追加して、1対1の関係を作成します。

public class Student {
   public int ID { get; set; }
   public string LastName { get; set; }
   public string FirstMidName { get; set; }
   public DateTime EnrollmentDate { get; set; }

   public virtual StudentLogIn StudentLogIn { get; set; }
   public virtual ICollection<Enrollment> Enrollments { get; set; }
}

public class StudentLogIn {
   [Key, ForeignKey("Student")]
   public int ID { get; set; }
   public string EmailID { get; set; }
   public string Password { get; set; }

   public virtual Student Student { get; set; }
}

上記のコードからわかるように、KeyおよびForeignKey属性は、StudentLogInクラスのIDプロパティに使用され、主キーおよび外部キーとしてマークされます。

Fluent APIを使用してStudentとStudentLogInの間に1対0または1つの関係を構成するには、次のコードに示すようにOnModelCreatingメソッドをオーバーライドする必要があります。

protected override void OnModelCreating(DbModelBuilder modelBuilder) {

  //Configure default schema
   modelBuilder.HasDefaultSchema("Admin");

  //Configure ID as PK for StudentLogIn
   modelBuilder.Entity<StudentLogIn>()
   .HasKey(s ⇒ s.ID);

  //Configure ID as FK for StudentLogIn
   modelBuilder.Entity<Student>()

   .HasOptional(s ⇒ s.StudentLogIn)//StudentLogIn is optional
   .WithRequired(t ⇒ t.Student);//Create inverse relationship
}

ほとんどの場合、Entity Frameworkは、どのタイプが依存関係であり、どのタイプが関係のプリンシパルであるかを推測できます。 ただし、関係の両端が必要な場合、または両側がオプションの場合、Entity Frameworkは依存関係とプリンシパルを識別できません。 関係の両端が必要な場合、次のコードに示すようにHasRequiredを使用できます。

protected override void OnModelCreating(DbModelBuilder modelBuilder) {

  //Configure default schema
   modelBuilder.HasDefaultSchema("Admin");

  //Configure ID as PK for StudentLogIn
   modelBuilder.Entity<StudentLogIn>()
   .HasKey(s ⇒ s.ID);

  //Configure ID as FK for StudentLogIn
   modelBuilder.Entity<Student>()
   .HasRequired(r ⇒ r.Student)
   .WithOptional(s ⇒ s.StudentLogIn);
}

データベースが生成されると、次の図に示すような関係が作成されます。

作成された関係

1対多の関係を構成する

主キーテーブルには、関連テーブルのレコードに関連しないレコードが1つだけ含まれています。 これは、最も一般的に使用される関係のタイプです。

  • このタイプの関係では、テーブルAの行はテーブルBの多くの一致する行を持つことができますが、テーブルBの行はテーブルAの一致する行を1つだけ持つことができます。
  • 外部キーは、リレーションシップの多端を表すテーブルで定義されます。
  • たとえば、上の図では、StudentテーブルとEnrollmentテーブルには1対1の関係があり、各学生には多くの登録がありますが、各登録は1人の学生にのみ属します。

以下は、1対多の関係を持つStudentとEnrollmentです。ただし、Enrollmentテーブルの外部キーは、デフォルトのCode First規則に従っていません。

public class Enrollment {
   public int EnrollmentID { get; set; }
   public int CourseID { get; set; }

  //StdntID is not following code first conventions name
   public int StdntID { get; set; }
   public Grade? Grade { get; set; }

   public virtual Course Course { get; set; }
   public virtual Student Student { get; set; }
}

public class Student {
   public int ID { get; set; }
   public string LastName { get; set; }
   public string FirstMidName { get; set; }
   public DateTime EnrollmentDate { get; set; }

   public virtual StudentLogIn StudentLogIn { get; set; }
   public virtual ICollection<Enrollment> Enrollments { get; set; }
}

この場合、Fluent APIを使用して1対多の関係を構成するには、次のコードに示すようにHasForeignKeyメソッドを使用する必要があります。

protected override void OnModelCreating(DbModelBuilder modelBuilder) {

  //Configure default schema
   modelBuilder.HasDefaultSchema("Admin");

  //Configure FK for one-to-many relationship
   modelBuilder.Entity<Enrollment>()

   .HasRequired<Student>(s ⇒ s.Student)
   .WithMany(t ⇒ t.Enrollments)
   .HasForeignKey(u ⇒ u.StdntID);
}

データベースが生成されると、次の図に示すように関係が作成されていることがわかります。

HasRequiredメソッド

上記の例では、HasRequiredメソッドは、StudentナビゲーションプロパティがNullでなければならないことを指定しています。 したがって、登録を追加または更新するたびに、学生に登録エンティティを割り当てる必要があります。 これを処理するには、HasRequiredメソッドの代わりにHasOptionalメソッドを使用する必要があります。

多対多の関係を構成する

両方のテーブルの各レコードは、他のテーブルの任意の数のレコード(またはレコードなし)に関連付けることができます。

  • そのような関係を作成するには、ジャンクションテーブルと呼ばれる3番目のテーブルを定義します。このテーブルの主キーは、テーブルAとテーブルBの両方からの外部キーで構成されます。
  • たとえば、StudentテーブルとCourseテーブルには、多対多の関係があります。

以下は、StudentとCourseのナビゲーションプロパティであるStudentとCoursesがコレクションであるため、StudentとCourseが多対多の関係にあるStudentとCourseクラスです。 つまり、1つのエンティティに別のエンティティコレクションがあります。

public class Student {
   public int ID { get; set; }
   public string LastName { get; set; }
   public string FirstMidName { get; set; }
   public DateTime EnrollmentDate { get; set; }

   public virtual ICollection<Course> Courses { get; set; }
   public virtual ICollection<Enrollment> Enrollments { get; set; }
}

public class Course {
   public int CourseID { get; set; }
   public string Title { get; set; }
   public int Credits { get; set; }

   public virtual ICollection<Student> Students { get; set; }
   public virtual ICollection<Enrollment> Enrollments { get; set; }
}

学生とコース間の多対多の関係を構成するには、次のコードに示すようにFluent APIを使用できます。

protected override void OnModelCreating(DbModelBuilder modelBuilder) {

  //Configure default schema
   modelBuilder.HasDefaultSchema("Admin");

  //Configure many-to-many relationship
   modelBuilder.Entity<Student>()
   .HasMany(s ⇒ s.Courses)
   .WithMany(s ⇒ s.Students);
}

デフォルトのCode First規則は、データベースの生成時に結合テーブルを作成するために使用されます。 その結果、次の図に示すように、Course_CourseID列とStudent_ID列を持つStudentCoursesテーブルが作成されます。

結合テーブル

結合テーブル名とテーブル内の列の名前を指定する場合は、Mapメソッドを使用して追加の構成を行う必要があります。

protected override void OnModelCreating(DbModelBuilder modelBuilder) {

  //Configure default schema
   modelBuilder.HasDefaultSchema("Admin");

  //Configure many-to-many relationship
   modelBuilder.Entity<Student>()

   .HasMany(s ⇒ s.Courses)
   .WithMany(s ⇒ s.Students)

   .Map(m ⇒ {
      m.ToTable("StudentCoursesTable");
      m.MapLeftKey("StudentID");
      m.MapRightKey("CourseID");
   });
}

データベースが生成されると、上記のコードで指定されたとおりにテーブル名と列名が作成されることがわかります。

結合テーブル

理解を深めるために、上記の例を段階的に実行することをお勧めします。

Entity Framework-シードデータベース

Entity Frameworkでは、シードはEF 4.1で導入され、データベース初期化子で機能します。 *シードメソッド*の一般的な考え方は、Code Firstによって作成されたデータベースまたはMigrationsによって進化したデータベースにデータを初期化することです。 このデータは多くの場合テストデータですが、既知の学生、コースなどのリストなどの参照データでもあります。 データが初期化されると、次のようになります-

  • ターゲットデータベースが既に存在するかどうかを確認します。
  • 存在する場合、現在のCode Firstモデルがデータベースのメタデータに保存されているモデルと比較されます。
  • 現在のモデルがデータベース内のモデルと一致しない場合、データベースは削除されます。
  • データベースは、ドロップされたか、そもそも存在しなかった場合に作成されます。
  • データベースが作成された場合、初期化子のSeedメソッドが呼び出されます。

Seedメソッドはデータベースコンテキストオブジェクトを入力パラメーターとして受け取り、メソッド内のコードはそのオブジェクトを使用してデータベースに新しいエンティティを追加します。 データベースにデータをシードするには、Seedメソッドをオーバーライドする必要があります。 いくつかのデフォルトデータが内部クラスのデータベースに初期化される次の例を見てみましょう。

private class UniDBInitializer<T> : DropCreateDatabaseAlways<MyContext> {

   protected override void Seed(MyContext context) {

      IList<Student> students = new List<Student>();

      students.Add(new Student() {
         FirstMidName = "Andrew",
         LastName = "Peters",
         EnrollmentDate = DateTime.Parse(DateTime.Today.ToString())
      });

      students.Add(new Student() {
         FirstMidName = "Brice",
         LastName = "Lambson",
         EnrollmentDate = DateTime.Parse(DateTime.Today.ToString())
      });

      students.Add(new Student() {
         FirstMidName = "Rowan",
         LastName = "Miller",
         EnrollmentDate = DateTime.Parse(DateTime.Today.ToString())
      });

      foreach (Student student in students)
      context.Students.Add(student);
      base.Seed(context);
   }
}

上記のコードでは、studentテーブルが初期化されています。 次のコードに示すように、このDB初期化クラスをコンテキストクラスに設定する必要があります。

public MyContext() : base("name=MyContextDB") {
   Database.SetInitializer<MyContext>(new UniDBInitializer<MyContext>());
}

以下は、MyContextクラスの完全なクラス実装です。このクラスには、DB初期化クラスも含まれています。

public class MyContext : DbContext {

   public MyContext() : base("name=MyContextDB") {
      Database.SetInitializer<MyContext>(new UniDBInitializer<MyContext>());
   }

   public virtual DbSet<Course> Courses { get; set; }
   public virtual DbSet<Enrollment> Enrollments { get; set; }
   public virtual DbSet<Student> Students { get; set; }

   private class UniDBInitializer<T> : DropCreateDatabaseAlways<MyContext> {

      protected override void Seed(MyContext context) {

         IList<Student> students = new List<Student>();

         students.Add(new Student() {
            FirstMidName = "Andrew",
            LastName = "Peters",
            EnrollmentDate = DateTime.Parse(DateTime.Today.ToString())
         });

         students.Add(new Student() {
            FirstMidName = "Brice",
            LastName = "Lambson",
            EnrollmentDate = DateTime.Parse(DateTime.Today.ToString())
         });

         students.Add(new Student() {
            FirstMidName = "Rowan",
            LastName = "Miller",
            EnrollmentDate = DateTime.Parse(DateTime.Today.ToString())
         });

         foreach (Student student in students)
         context.Students.Add(student);
         base.Seed(context);
      }
   }
}

上記の例をコンパイルして実行すると、次の図に示すように、データベース内のデータを確認できます。

データベース内のデータ

理解を深めるために、上記の例を段階的に実行することをお勧めします。

Entity Framework-Code First Migration

Entity Framework 4.3には新しいCode First Migrations機能が含まれており、モデルの経時変化に応じてデータベーススキーマを段階的に進化させることができます。 ほとんどの開発者にとって、これは4.1および4.2リリースのデータベース初期化オプションに対する大きな改善であり、モデルの変更時にデータベースを手動で更新するか、削除して再作成する必要がありました。

  • Entity Framework 4.3より前、既にデータ(シードデータ以外)または既存のストアドプロシージャ、トリガーなどがある場合 データベースでは、これらの戦略はデータベース全体を削除して再作成するために使用されるため、データやその他のDBオブジェクトが失われます。
  • 移行により、既存のデータや他のデータベースオブジェクトを失うことなくモデルが変更されると、データベーススキーマが自動的に更新されます。
  • MigrateDatabaseToLatestVersionと呼ばれる新しいデータベース初期化子を使用します。

移行には2種類あります-

  • 自動移行
  • コードベースの移行

自動移行

自動移行は、Entity Framework 4.3で初めて導入されました。 自動移行では、コードファイルでデータベースの移行を手動で処理する必要はありません。 たとえば、変更ごとに、ドメインクラスも変更する必要があります。 ただし、自動移行では、パッケージマネージャーコンソールでコマンドを実行するだけでこれを実行できます。

自動移行の次の段階的なプロセスを見てみましょう。

Code Firstアプローチを使用する場合、アプリケーション用のデータベースはありません。

この例では、次のコードに示すように、Student、Course、Enrollmentなどの3つの基本クラスから始めます。

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

}

public class Student {
   public int ID { get; set; }
   public string LastName { get; set; }
   public string FirstMidName { get; set; }
   public DateTime EnrollmentDate { get; set; }

   public virtual ICollection<Enrollment> Enrollments { get; set; }

}

public class Course {
   public int CourseID { get; set; }
   public string Title { get; set; }
   [Index]
   public int Credits { get; set; }

   public virtual ICollection<Enrollment> Enrollments { get; set; }

}

以下はコンテキストクラスです。

public class MyContext : DbContext {
   public MyContext() : base("MyContextDB") {}
   public virtual DbSet<Course> Courses { get; set; }
   public virtual DbSet<Enrollment> Enrollments { get; set; }
   public virtual DbSet<Student> Students { get; set; }
}

アプリケーションを実行する前に、自動移行を有効にする必要があります。

  • ステップ1 *-ツール→NuGetパッケージマネージャー→パッケージマネージャーコンソールからパッケージマネージャーコンソールを開きます。
  • ステップ2 *-自動移行を有効にするには、パッケージマネージャーコンソールで次のコマンドを実行します。
PM> enable-migrations -EnableAutomaticMigrations:$true

コマンド

  • ステップ3 *-コマンドが正常に実行されると、次のコードに示すように、プロジェクトのMigrationフォルダーに内部の封印されたConfigurationクラスが作成されます。
namespace EFCodeFirstDemo.Migrations {

   using System;
   using System.Data.Entity;
   using System.Data.Entity.Migrations;
   using System.Linq;

   internal sealed class Configuration : DbMigrationsConfiguration<EFCodeFirstDemo.MyContext> {

      public Configuration() {
         AutomaticMigrationsEnabled = true;
         ContextKey = "EFCodeFirstDemo.MyContext";
      }

      protected override void Seed(EFCodeFirstDemo.MyContext 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" }
        // );
      }
   }
}
  • ステップ4 *-新しいDB初期化戦略MigrateDatabaseToLatestVersionを使用して、コンテキストクラスにデータベース初期化子を設定します。
public class MyContext : DbContext {

   public MyContext() : base("MyContextDB") {
      Database.SetInitializer(new MigrateDatabaseToLatestVersion<MyContext,
         EFCodeFirstDemo.Migrations.Configuration>("MyContextDB"));
   }

   public virtual DbSet<Course> Courses { get; set; }
   public virtual DbSet<Enrollment> Enrollments { get; set; }
   public virtual DbSet<Student> Students { get; set; }

}
  • ステップ5 *-自動移行を設定しました。 アプリケーションを実行すると、モデルを変更すると、自動的に移行が処理されます。

移行

  • ステップ6 *-ご覧のとおり、1つのシステムテーブルMigrationHistoryも他のテーブルと共にデータベースに作成されます。MigrationHistoryでは、自動移行によりデータベース変更の履歴が維持されます。
  • ステップ7 *-ドメインクラスとして別のエンティティクラスを追加してアプリケーションを実行すると、データベースにテーブルが作成されます。 次のStudentLogInクラスを追加しましょう。
public class StudentLogIn {
   [Key, ForeignKey("Student")]
   public int ID { get; set; }
   public string EmailID { get; set; }
   public string Password { get; set; }

   public virtual Student Student { get; set; }
}
  • ステップ8 *-次のコードに示すように、コンテキストクラスに上記のクラスのDBSetを追加することを忘れないでください。
public virtual DbSet<StudentLogIn> StudentsLogIn { get; set; }
  • ステップ9 *-アプリケーションを再度実行すると、StudentsLogInテーブルがデータベースに追加されます。

StudentsLogIn

上記の自動移行の手順は、エンティティに対してのみ機能します。 たとえば、別のエンティティクラスを追加したり、既存のエンティティクラスを削除したりすると、正常に移行されます。 ただし、エンティティクラスにプロパティを追加または削除すると、例外がスローされます。

  • ステップ10 *-プロパティの移行を処理するには、構成クラスコンストラクターでAutomaticMigrationDataLossAllowed = trueを設定する必要があります。
public Configuration() {
   AutomaticMigrationsEnabled = true;
   AutomaticMigrationDataLossAllowed = true;
   ContextKey = "EFCodeFirstDemo.MyContext";
}

コードベースの移行

新しいアプリケーションを開発すると、データモデルは頻繁に変更され、モデルが変更されるたびにデータベースとの同期が失われます。 データモデルを変更するたびにデータベースを自動的に削除して再作成するようにEntity Frameworkを構成しました。 コードベースの移行は、移行をさらに制御したい場合に便利です。

  • エンティティクラスを追加、削除、または変更するか、DbContextクラスを変更すると、次にアプリケーションを実行すると、既存のデータベースが自動的に削除され、モデルに一致する新しいデータベースが作成され、テストデータがシードされます。
  • Code First Migrations機能は、データベースを削除して再作成する代わりに、Code Firstがデータベーススキーマを更新できるようにすることで、この問題を解決します。 アプリケーションをデプロイするには、移行を有効にする必要があります。

データベース内の変更を移行するための基本的なルールは次のとおりです-

  • 移行を有効にする
  • 移行を追加
  • データベースを更新する

コードベースの移行の次の段階的なプロセスを見てみましょう。

コードファーストのアプローチを使用する場合、アプリケーション用のデータベースはありません。

この例では、次のコードに示すように、Student、Course、Enrollmentなどの3つの基本クラスから再び始めます。

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

}

public class Student {
   public int ID { get; set; }
   public string LastName { get; set; }
   public string FirstMidName { get; set; }
   public DateTime EnrollmentDate { get; set; }

   public virtual ICollection<Enrollment> Enrollments { get; set; }

}

public class Course {
   public int CourseID { get; set; }
   public string Title { get; set; }
   [Index]
   public int Credits { get; set; }

   public virtual ICollection<Enrollment> Enrollments { get; set; }

}

以下はコンテキストクラスです。

public class MyContext : DbContext {

   public MyContext() : base("MyContextDB") {
      Database.SetInitializer(new MigrateDatabaseToLatestVersion<
         MyContext, EFCodeFirstDemo.Migrations.Configuration>("MyContextDB"));
   }

   public virtual DbSet<Course> Courses { get; set; }
   public virtual DbSet<Enrollment> Enrollments { get; set; }
   public virtual DbSet<Student> Students { get; set; }

}
  • ステップ1 *-アプリケーションを実行する前に、移行を有効にする必要があります。
  • ステップ2 *-ツール→NuGetパッケージマネージャー→パッケージマネージャーコンソールからパッケージマネージャーコンソールを開きます。
  • ステップ3 *-移行はすでに有効になっています。次のコマンドを実行して、アプリケーションに移行を追加します。
PM> add-migration "UniDB Schema"
  • ステップ4 *-コマンドが正常に実行されると、次の画像に示すように、タイムスタンププレフィックスを付けてコマンドに渡したパラメーターの名前で、Migrationフォルダーに新しいファイルが作成されます。

タイムスタンププレフィックス

  • ステップ5 *-「update-database」コマンドを使用して、データベースを作成または更新できます。
PM> Update-Database -Verbose

「-Verbose」フラグは、コンソールでターゲットデータベースに適用されているSQLステートメントを表示することを指定します。

  • ステップ6 *-学生クラスにもう1つのプロパティ「年齢」を追加し、更新ステートメントを実行します。
public class Student {
   public int ID { get; set; }
   public string LastName { get; set; }
   public string FirstMidName { get; set; }
   public int Age { get; set; }
   public DateTime EnrollmentDate { get; set; }

   public virtual ICollection<Enrollment> Enrollments { get; set; }

}

PM→Update-Database –Verboseを実行すると、コマンドが正常に実行されると、データベースに新しい列Ageが追加されていることがわかります。

新しい列の時代。

理解を深めるために、上記の例を段階的に実行することをお勧めします。

Entity Framework-複数のDbContext

この章では、アプリケーションに複数のDbContextクラスがある場合に、データベースに変更を移行する方法を学習します。

  • 複数のDbContextは、Entity Framework 6.0で初めて導入されました。
  • 複数のコンテキストクラスは、単一のデータベースまたは2つの異なるデータベースに属する場合があります。

この例では、同じデータベースに対して2つのContextクラスを定義します。 次のコードには、StudentとTeacherの2つのDbContextクラスがあります。

public class Student {
   public int ID { get; set; }
   public string LastName { get; set; }
   public string FirstMidName { get; set; }
   public DateTime EnrollmentDate { get; set; }
}

public class MyStudentContext : DbContext {
   public MyStudentContext() : base("UniContextDB") {}
   public virtual DbSet<Student> Students { get; set; }
}

public class Teacher {
   public int ID { get; set; }
   public string LastName { get; set; }
   public string FirstMidName { get; set; }
   public DateTime HireDate { get; set; }
}

public class MyTeacherContext : DbContext {
   public MyTeacherContext() : base("UniContextDB") {}
   public virtual DbSet<Teacher> Teachers { get; set; }
}

上記のコードを見るとわかるように、「学生」と「教師」という2つのモデルがあります。 それぞれが特定の対応するコンテキストクラスに関連付けられます。つまり、StudentはMyStudentContextに関連付けられ、TeacherはMyTeacherContextに関連付けられます。

同じプロジェクト内に複数のContextクラスがある場合、データベースの変更を移行するための基本的なルールを次に示します。

  • enable-migrations -ContextTypeName <ネームスペース付きのDbContext-Name> MigrationsDirectory:<Migrations-Directory-Name>
  • Add-Migration -configuration <DbContext-Migrations-Configuration-Class-withNamespaces> <Migrations-Name>
  • Update-Database -configuration <DbContext-Migrations-Configuration-Class-withNamespaces> -Verbose

パッケージマネージャーコンソールで次のコマンドを実行して、MyStudentContextの移行を有効にしましょう。

PM→ enable-migrations -ContextTypeName:EFCodeFirstDemo.MyStudentContext

パッケージマネージャーコンソール

実行されたら、移行履歴にモデルを追加します。そのためには、同じコンソールでadd-migrationコマンドを実行する必要があります。

PM→ add-migration -configuration EFCodeFirstDemo.Migrations.Configuration Initial

ここで、データベース内のStudentテーブルとTeachersテーブルにデータを追加しましょう。

static void Main(string[] args) {

   using (var context = new MyStudentContext()) {

     ////Create and save a new Students
      Console.WriteLine("Adding new students");

      var student = new Student {
         FirstMidName = "Alain",
         LastName = "Bomer",
         EnrollmentDate = DateTime.Parse(DateTime.Today.ToString())
        //Age = 24
      };

      context.Students.Add(student);

      var student1 = new Student {
         FirstMidName = "Mark",
         LastName = "Upston",
         EnrollmentDate = DateTime.Parse(DateTime.Today.ToString())
        //Age = 30
      };

      context.Students.Add(student1);
      context.SaveChanges();

     //Display all Students from the database
      var students = (from s in context.Students orderby s.FirstMidName
         select s).ToList<Student>();

      Console.WriteLine("Retrieve all Students from the database:");

      foreach (var stdnt in students) {
         string name = stdnt.FirstMidName + " " + stdnt.LastName;
         Console.WriteLine("ID: {0}, Name: {1}", stdnt.ID, name);
      }

      Console.WriteLine("Press any key to exit...");
      Console.ReadKey();
   }

   using (var context = new MyTeacherContext()) {

     ////Create and save a new Teachers
      Console.WriteLine("Adding new teachers");

      var student = new Teacher {
         FirstMidName = "Alain",
         LastName = "Bomer",
         HireDate = DateTime.Parse(DateTime.Today.ToString())
        //Age = 24
      };

      context.Teachers.Add(student);

      var student1 = new Teacher {
         FirstMidName = "Mark",
         LastName = "Upston",
         HireDate = DateTime.Parse(DateTime.Today.ToString())
        //Age = 30
      };

      context.Teachers.Add(student1);
      context.SaveChanges();

     //Display all Teachers from the database
      var teachers = (from t in context.Teachers orderby t.FirstMidName
         select t).ToList<Teacher>();

      Console.WriteLine("Retrieve all teachers from the database:");

      foreach (var teacher in teachers) {
         string name = teacher.FirstMidName + " " + teacher.LastName;
         Console.WriteLine("ID: {0}, Name: {1}", teacher.ID, name);
      }

      Console.WriteLine("Press any key to exit...");
      Console.ReadKey();
   }
}

上記のコードを実行すると、次の図に示すように、2つの異なるモデルに対して2つの異なるテーブルが作成されていることがわかります。

実行されたコード

理解を深めるために、上記の例を段階的に実行することをお勧めします。

エンティティフレームワーク-ネストされたエンティティタイプ

Entity Framework 6より前は、Entity Frameworkは、他のエンティティまたは複合型内にネストされたエンティティまたは複合型を認識しませんでした。 Entity Frameworkがモデルを生成すると、ネストされた型が消えました。

Student、Course、およびEnrollmentの3つのエンティティを持つ基本モデルがある簡単な例を見てみましょう。

  • Person型であるIdentityプロパティを追加しましょう。 Personは別のエンティティであり、BirthDateプロパティとFatherNameプロパティが含まれます。
  • Entity Frameworkの用語では、IDを持たず、エンティティの一部であるため、Entity Frameworkの複合型であり、Entity Frameworkの最初のバージョン以降、実際に複合型をサポートしています。
  • 次のコードに示すように、Personタイプはネストされていません。
public class Student {
   public int ID { get; set; }
   public string LastName { get; set; }
   public string FirstMidName { get; set; }
   public DateTime EnrollmentDate { get; set; }
   public Person Identity { get; set; }

   public virtual ICollection<Enrollment> Enrollments { get; set; }
}

public class Person {

   public Person(string fatherName, DateTime birthDate) {
      FatherName = fatherName;
      BirthDate = birthDate;
   }

   public string FatherName { get; set; }
   public DateTime BirthDate { get; set; }
}

Entity Frameworkは、以前のバージョンでも使用されている場合、Person型を永続化する方法を知っています。

Entity Framework Power Toolを使用すると、Entity Frameworkがモデルをどのように解釈するかがわかります。 Program.csファイルを右クリックし、エンティティフレームワーク→エンティティデータモデルの表示(読み取り専用)を選択します

フレームワーク電動工具

これで、IdentityプロパティがStudentクラスで定義されていることがわかります。

IDプロパティ

このPersonクラスが他のエンティティによって使用されない場合、Studentクラス内にネストできますが、この以前のバージョンのEntity Frameworkはネストされた型を認識しません。

古いバージョンでは、型を再生成します。型が認識されないだけでなく、存在しないため、プロパティも存在しないため、Entity FrameworkはPerson型をまったく保持しません。

public class Student {
   public int ID { get; set; }
   public string LastName { get; set; }
   public string FirstMidName { get; set; }

   public DateTime EnrollmentDate { get; set; }
   public Person Identity { get; set; }

   public virtual ICollection<Enrollment> Enrollments { get; set; }

   public class Person {

      public Person(string fatherName, DateTime birthDate) {
         FatherName = fatherName;
         BirthDate = birthDate;
      }

      public string FatherName { get; set; }
      public DateTime BirthDate { get; set; }
   }
}

Entity Framework 6では、ネストされたエンティティと複合型が認識されます。 上記のコードでは、PersonがStudentクラス内にネストされていることがわかります。

Entity Framework Power Toolを使用して、今回のEntity Frameworkによるモデルの解釈方法を示すと、真のIdentityプロパティとPerson複合型があります。 したがって、Entity Frameworkはそのデータを永続化します。

ネストされたエンティティタイプ

これで、Identityはネストされたエンティティタイプであり、Entity Framework 6以前ではサポートされていなかったことがわかります。

理解を深めるために、上記の例を段階的に実行することをお勧めします。