Skip to content

MS_EntityFramework

nishi_74322014 edited this page Aug 18, 2026 · 2 revisions

Entity Framework

概要

.NET Framework 3.5 SP1 で追加されたデータベース アクセス テクノロジー。

  • Entity Framework は、TableAdapter と比べ、より高次の機能を提供し、
    高い生産性を誇る RAD ツールです。
  • データアクセス部分を隠ぺいするため、
    データベースの種類や構造を意識することなくプログラミングできます。
  • 反面、実際に発行される SQL 文の構築は Entity Framework に委ねられます。
  • このため、チューニングや、柔軟なデータの参照・更新が課題になりやすく、
    ミッション クリティカルなエンタープライズ システム開発には適合しないことがあります。

補足(最新化:EF6 と EF Core): 本ページが扱っているのは
**EF6(Entity Framework 6.x)**である。現在の位置付けは以下。

EF6 EF Core
対象 .NET Framework(.NET Core 3.0 以降でも動作) .NET(クロスプラットフォーム)
開発 保守のみ(新機能なし) 現行。年次でメジャー更新
EDMX / デザイナ あり なし
開発スタイル モデル / DB / コード ファースト Code First と scaffold のみ
名前空間 System.Data.Entity Microsoft.EntityFrameworkCore

新規開発では EF Core を選ぶ。
本ページの EDM / CSDL / MSL / SSDL に関する記述は
EF6 固有であり、EF Core には該当する概念が存在しない。

概念モデルに対してプログラミング

Entity Framework は、インピーダンス・ミスマッチ問題を解決するため、
エンティティを定義し、エンティティ経由でデータベースへアクセスするための手段を提供する技術。

  • 概念モデルである Entity Data Model(EDM) を開発者が柔軟にモデリング可能。
    RDB に対して直接プログラミングする代わりに、EDM に対してプログラミングする。

    • これにより、テーブルとオブジェクトを 1 対 1 に対応させる O/R マッパとは異なり、
      「結合(Join)」という RDB 固有の事情に振り回されることなく、
      より直感的に必要な情報を取得することが可能になる。

    • RDB 側のスキーマ変更(特にテーブルの分割/統合のような変化)に対して耐性がある。

  • Entity は ADO.NET 非接続型と同様に、多階層のアプリケーションを開発する場合、
    オブジェクトとして渡すことが可能であり DTO(Data Transfer Object) として利用できる。

Entity Data Model(EDM)

CSDL、SSDL、および MSL 仕様
https://learn.microsoft.com/ja-jp/dotnet/framework/data/adonet/ef/language-reference/csdl-ssdl-and-msl-specifications

  • 概念スキーマ定義言語 (CSDL) <---> マッピング仕様言語 (MSL) <---> ストア スキーマ定義言語 (SSDL) は
    XML ベースの言語で、それぞれ概念モデル、モデル間のマッピング、
    ストレージ モデルについて記述する。

  • Entity Data Model(EDM) の定義は、Entity Data Model デザイナーにより、
    設計時にモデルとマッピングの情報を *.edmx ファイルに保存する。

  • Entity Data Model デザイナーは、ビルド時に EDM ファイル(*.edmx)の情報を使用して、
    Entity Framework が実行時に必要とする *.csdl*.ssdl、および *.msl ファイルを作成する。

CSDL

CSDL(Conceptual Schema Definition Language:概念スキーマ定義言語)
概念スキーマ(概念モデル)

MSL

MSL(Mapping Schema Language:マッピング・スキーマ言語)
CSDL と SSDL(モデル間)のマッピング

SSDL

SSDL(Storage Schema Definition Language:ストア・スキーマ定義言語)
ストレージ・スキーマ(ストレージ モデル)

xxx ファースト

  • エンタープライズではほぼ、DB ファーストを採用する。

  • 単純なテーブル(データストア)しか使用しない
    EUC などの世界では、モデルファーストもマッチする可能性がある。

モデルファースト

モデル(EDM)から DBMS のスキーマを生成する方法。

DBファースト

DBMS のスキーマからモデル(EDM)を生成する方法。

コードファースト

補足(最新化:EF Core での対応関係): EF Core には
モデルファースト(デザイナ)が無いため、選択肢は 2 つになる。

元ページの区分 EF Core での対応
モデルファースト 無し
DB ファースト dotnet ef dbcontext scaffold で既存 DB からリバース
コードファースト dotnet ef migrations adddotnet ef database update

エンタープライズで DBA が DB を設計する場合も、
scaffold で生成したコードを起点に手で調整する運用になる。
なお、本番環境へのスキーマ適用は database update を直接叩かず、
dotnet ef migrations script で SQL を出力してレビューするのが安全。

CRUD処理

ここでは、「context」が DbContext を継承したコンテキストを指している。
サンプルプログラム次第で、context, db, dbContext などの名称が使用される。

仕組み

Entity States

Entity は以下の Entity States を持っており、

  • Unchanged 状態
    追跡開始後に変更されていない状態

  • Added 状態
    追加された状態。

  • Modified 状態
    編集された状態。

  • Deleted 状態
    削除された状態。

  • Detached 状態
    追跡が開始されていない状態

SaveChanges()メソッド

SaveChanges() メソッドを呼び出した際に、
コレを更新系 SQL に変えて実行すると言う、ある種、
RowState + TableAdapter 的な仕組みで動作している。

補足(変更追跡と更新列): 変更追跡により、SaveChanges() が生成する
UPDATE 文には変更されたプロパティのみが含まれる。
ただし、AsNoTracking() で読んだエンティティや、
別階層から戻ってきた(Detached な)エンティティを Update() した場合は
全列が更新対象になる点に注意。
Web API のように「読み取りと更新でリクエストが分かれる」構成では、
この違いが同時実行制御(後述のロストアップデート)に直結する。

補足(同時実行制御): EF には楽観的同時実行制御の仕組みがあり、
更新競合を検出できる。

  • SQL Server: rowversion 型の列を用意し、
    [Timestamp] 属性(または IsRowVersion())を付ける
  • 競合時は DbUpdateConcurrencyException が発生する

これを入れないと、後勝ちでロストアップデートが発生する。

参照系

LINQは遅延評価

LINQ は遅延評価なので、結果が必要になるまでは SQL 文が発行されない。

ToArray() メソッドや Load() メソッド(Entity Framework の懸念)を使うと、
即時実行されて結果をメモリに起こす事ができる。

オブジェクト参照

オブジェクト参照により SQL が実行される。

using (var context = new XXXXXContext())
{
    foreach (var YYYYY in context.YYYYYs)
    {
        Console.WriteLine(YYYYY.Name);
    }
}

単一要素取得のLINQメソッド

  • Find() メソッド

    • 主キーを使用して検索する。
      • DbContext にキャッシュがあれば、それを検索する。
      • キャッシュがない or キャッシュ内にデータが無い場合、SQL で検索する。
  • Single() メソッド

    • 0 件、複数件の結果が返ったらエラーを返す。
    • DBMS の TOP ヒントなどを使用する(Select Top 2 * From YYYYYs)ので性能はいい。
    context.YYYYYs.Single(x => x.name == "hogehoge");
  • SingleOrDefault() メソッド

    • 取得した結果が 0 件の場合、既定値を返す。1 件の場合、その要素を返す。
    • 取得した結果が 0 件 or 1 件以外の結果が返ったらエラーを返す。
    • DBMS の TOP ヒントなどを使用する(Select Top 2 * From YYYYYs)ので性能はいい。
    context.YYYYYs.SingleOrDefault(x => x.name == "hogehoge");
  • ,etc.

移行メモ(正誤): 元ページの SingleOrDefault() の例は
コードが Single() のままだったため、SingleOrDefault() に修正した。

補足(SingleFirst の使い分け): 「一意であることを表明したい」なら
Single 系、「先頭 1 件でよい」なら First 系を使う。
First 系は Select Top 1 になるため、
Single 系(Top 2 を取って 2 件目の有無を確認する)より軽い。
一意性が制約で保証されているなら First 系で十分なことが多い。

LINQ to Object

以下の様な感じ。

  • ToArray() メソッドで確定させた結果に対して LINQ を実行する。

    var YYYYYs = context.YYYYYs.ToArray();
    YYYYYs.Where(x => x.Name == "hogehoge")
  • Load() メソッドでキャッシュした後に、
    Local プロパティに対して LINQ を実行する。

    context.YYYYYs.Load()
    context.YYYYYs.Local.OrderBy(x => x.Name);

詳しくは、LINQを参照。

LINQ to Entities

以下の様な感じ。

context.YYYYYs.Where(x => x.Id == "1");
context.YYYYYs.Where(x => x.Name == "hogehoge").ToArray();

補足(IQueryableIEnumerable の境界): この 2 つの節の違いは
どこで SQL に変換されるか」であり、性能上もっとも重要な論点である。

  • IQueryable(LINQ to Entities): 式ツリーが SQL に翻訳され、
    DB 側で絞り込まれる
  • IEnumerable(LINQ to Objects): 先に全件をメモリに読み込み、
    アプリ側で絞り込む

ToArray() / ToList() / AsEnumerable() を挟んだ時点で
境界を越えるため、以下はテーブル全件を読み込んでから絞り込む。

// NG: 全件フェッチしてからメモリ上で Where
var bad = context.YYYYYs.ToArray().Where(x => x.Name == "hogehoge");

// OK: WHERE 句として SQL に翻訳される
var good = await context.YYYYYs
    .Where(x => x.Name == "hogehoge")
    .ToArrayAsync();

注意

全体的に実行される SQL がブラックボックスで性能を意識し難い
Entity Framework の懸念)。

補足(発行 SQL を見る): ブラックボックス性は、
ログを出せば大幅に緩和できる。

特に N+1 問題は、ログを見れば一目で分かる。
関連エンティティは Include でまとめて取得する。

更新系

Insert

using (var context = new XXXXXContext())
{
    // YYYYY Entity の Statesが
    // Unchanged状態→Added状態へ。
    context.YYYYYs.Add(new YYYYY
    {
        プロパティ名1 = 値1,
        プロパティ名2 = 値2,
    });

    // SaveChanges→Added状態の
    // YYYYY EntityがInsert文に替えられる。
    context.SaveChanges();
}

移行メモ(正誤): コメントの「Unchanged 状態 → Added 状態へ」は、
新規生成した Entity は追跡されていないため
正しくは「Detached 状態 → Added 状態へ」である。

Update

using (var context = new XXXXXContext())
{
    var yyyyy = context.YYYYYs.Single(x => x.Name == "hogehoge");
    yyyyy.Name = "Aiueo";
    context.SaveChanges();
}

Delete

  • Select ---> Delete
using (var context = new XXXXXContext())
{
    var yyyyy = context.YYYYYs.Single(x => x.Name == "hogehoge");
    context.YYYYYs.Remove(yyyyy);
    context.SaveChanges();
}
  • 直接 Delete
using (var context = new XXXXXContext())
{
    var toRemoveYyyyy = new YYYYY { Id = 1 };
    context.YYYYYs.Attach(toRemoveYyyyy);
    context.YYYYYs.Remove(toRemoveYyyyy);
    context.SaveChanges();
}

補足(最新化:一括更新・削除): 上記はいずれも
1 行ごとに UPDATE / DELETE を発行するため、
大量件数のバッチでは破綻する。
EF Core 7 以降は、SQL 1 発で処理する API が追加されている。

// 1 回の DELETE 文になる(SaveChanges は不要)
await context.YYYYYs
    .Where(x => x.CreatedAt < threshold)
    .ExecuteDeleteAsync();

// 1 回の UPDATE 文になる
await context.YYYYYs
    .Where(x => x.Name == "hogehoge")
    .ExecuteUpdateAsync(s => s.SetProperty(x => x.Name, "Aiueo"));

さらに大量の挿入は SqlBulkCopy を使うほうが桁違いに速い。

参考

LINQ to Entities

Entity Framework Core

Entity Framework Core

@IT

  • .NET の新データアクセス・テクノロジ「ADO.NET Entity Framework」 - @IT
    http://www.atmarkit.co.jp/fdotnet/special/vs2008sp1ef/vs2008sp1ef_01.html

  • 連載:ADO.NET Entity Framework 入門 - @IT
    http://www.atmarkit.co.jp/fdotnet/ef4basic/index/index.html

    • 第1回 最新 DB アクセス・フレームワークの基本的な考え方
    • 第2回 EDM における多対多関係と Entity Framework でのデータの取得/保存
    • 第3回 Entity Framework におけるクエリと更新
    • 第4回 データベースからの Entity Data Model 生成
    • 第5回 POCO によるエンティティ・クラス
    • 第6回 EF4 による N 層アーキテクチャと自己追跡エンティティ【前編】
    • 第7回 EF4 による N 層アーキテクチャと自己追跡エンティティ【後編】

Microsoft Docs

MSDN

magazine

  • データ ポイント
    • Dapper、Entity Framework、およびハイブリッド アプリ
    • EF Core の変更追跡の動作: 変更なし、変更済み、および追加
    • .NET Framework と .NET Core の両方での EF Core の実行
    • EF Core 1.1: お気に入りをいくつか
    • EF Core と InMemory プロバイダーを使ったテスト作成のヒント
    • DDD に最適な EF Core 2.0
    • DDD に最適な EF Core 2.0 (第 2 部)

補足(InMemory プロバイダについて): 上記記事にある
InMemory プロバイダは、現在テスト用途に推奨されていない
リレーショナル DB の制約(一意制約、外部キー、トランザクション)を
再現しないため、テストが通っても本番で落ちる。
現在は SQLite の in-memory モードか、
Testcontainers で実 DBMS を立てる方式が推奨されている。

blogs

  • 雲のごとく - Site Home - MSDN Blogs
    • .NET Framework 4.5 におけるデータ アクセスの概要 その1
    • .NET Framework 4.5 におけるデータ アクセスの概要 その3 ~ ADO.NET Entity Framework
    • Entity Framework Core 1.0 正式リリース – 雲のごとく

比較関連


Tags: 移行, .NET開発, データアクセス, ADO.NET, Entity Framework

NetDevInfraWiki

マイクロソフト系技術情報 Wiki
Open 棟梁 Wiki

(未着手)

開発基盤部会 Wiki

移行管理: DONETODO

Clone this wiki locally