データ ポイント

Entity Framework 7 に備える

Julie Lerman

Julie Lerman次期バージョンの Entity Framework の開発はかなり進んでいます。2014 年の TechEd North America で、EF チームのプログラム マネージャー Rowan Miller が Entity Framework 7 (EF7) の目標と、ごく初期の例を一部紹介し、ほんの一部ですが、同チームが取り組んでいることを初めて明らかにしました。

これは本稿執筆の 5 か月前のことですが、EF7 は現在も初期アルファ版で、長い間この状態が続いています。今回は、EF7 が開発者にもたらすメリット、EF7 について下す決断の背後にある理由、EF6 以前のバージョンを使用する既存のアプリと EF7 の関係について紹介します。また、わずかですが、コードの一部がどのようになるかについても示します。

オープン ソースでも管理は GitHub に移行

EF7 について把握しておくべき最初の点は、EF6 と同様オープン ソースであることです。ただし、EF7 は CodePlex で開発されているのではなく、ASP.NET の今後リリース予定の残りのバージョンと共に、GitHub で開発されています。EF7 開発の URL は github.com/aspnet/EntityFramework (英語) です。EF6 と同様、EF7 の進展を詳しく確認できます。ソースだけでなく分岐やコミットによる進捗の確認、ディスカッションのフォロー、問題提起、ソースの分岐、チームへの要望提示、コード ベースのテスト、コード ベースに対するコミットなどが可能です。

しばらくはサポートされる EF6

ご心配なく。EF7 に無理やり移行させられるわけではありません。ADO.NET DataSets と DataReaders を思い出してください。ASP.NET Web フォームとよく似ています。依然としてサポートされ、たまに行われる改定によってメリットを得られることもあります。ADO.NET もいまだに Microsoft .NET Framework に含まれています。EF が長年の間 .NET のデータ アクセスの主要テクノロジの地位を確保しているにもかかわらずです。このようなテクノロジが強化されることはほとんどありませんが、いまなお現役で、(筆者のコードを含め) 数々のレガシー コードをサポートし続けています。他のテクノロジを上回る EF6 の大きなメリットの 1 つはオープン ソースであることです。マイクロソフトの社内チームが EF6 に大きな改訂を行わなくても、コミュニティがそれを行います。EF チームは EF6 への熱心な取り組みを続けます。今後も EF6 の調整、要望の精査、更新を続けることになります。チームは総力を挙げて 2014 年の大半を EF7 に取り組んでいましたが、その一方で EF6 も更新しました。バージョン 6.1.0 を 2014 年 の 2 月に、6.1.1 を 6 月に、6.1.2 を 12 月にリリースしました。当初は古いアプリを使い続けることに不安がありましたが、現在はほとんど不安を感じていません。唯一不安があるとすれば、.NET Framework 3.5、ObjectContext などと一緒に EF を使用していた初期のアプリです。ただし、長い間 EF に加えられた大きな強化点をすべて利用するようにアプリを更新していなければ、EF7 についてもそれほど心配することはありません。NuGet では EF 4.1.10311 までさかのぼって過去のパッケージをすべて探すこともできます。

EF7: 簡単な一覧

以下は EF7 の魅力的な部分についての大まかな一覧です。

  • 非リレーショナル データ ストアと、テスト用にインメモリ データをサポートする。
  • 完全版の .NET Framework を使用していないコンピューターやデバイスをサポートする。つまり、Windows Phone や Windows ストア アプリだけでなく、Mono が動作する Linux コンピューターや Macintosh コンピューターでも EF7 を使用できる。
  • 開発者が要請している多くの機能をサポートする。ただし既存のコード ベースでは実現されない。
  • Windows Presentation Foundation やその他のクライアント アプリケーションなど、完全版の .NET Framework を使用するアプリケーションへのサポートを継続する。
  • EF7 を ASP.NET 5 と同じ方式で配布し、ASP.NET 5 アプリと併用できるようにする。

使い慣れたコーディング サーフェイスと新しいコード ベース

EF の各バージョンは、新機能の追加や、パフォーマンスと API の微調整を行いながら、フレームワークを進化させてきました。ここまでに説明してきた内容に加え、2013 年 12 月号の「Entity Framework 6: 上級者向けエディション」(https://msdn.microsoft.com/ja-jp/magazine/dn532202.aspx) でも解説したように、EF の最新バージョンは、ユーザーが要望していたデータベースの非同期実行、クエリ パイプラインの活用、Code First のカスタム規約など、多くの機能をフレームワークにもたらし、、EF を新たな段階に引き上げています。これらの機能については、Pluralsight コースの「Entity Framework 6: 上級者向けエディション: EF6 の新機能とは」(bit.ly/PS-EF6、英語) で詳しく解説しています。

他にも開発者が必要としていて、マイクロソフトが実装しようとしていた機能がありましたが、コード ベースが10 数年経過している EF は ObjectContext に依存し続けているうえ、コーディング パターンの柔軟性が乏しいため、そのような新たな段階の機能に対応しづらくなっています。チームは Entity Framework を根本から作り直す、つまり、多くの開発者が間違いなくレガシー ソフトウェアに立ち向かわざるを得なくなるという、難しい決断を下しました。

EF7 ではデータ アクセス用に新しいフレームワークを作成していません。代わりに、何年間も EF で利用してきた機能やワークフローだけでなく、それらをはるかに上回る機能やワークフローに対応するため、長く使える新しいベースを構築しています。このベースを次の EF とするか、新しいデータ アクセス テクノロジとするかについてチームは悩みました。ある時点では、「EF Light」になるかもしれないとも思いました。しかし、EF の中核機能はそのままです。多くの考察が行われた後、このベースが Entity Framework の次期バージョンになるのは妥当だと考えます。チームのブログ記事「EF7 – v1 or v7?」(EF7 – v1 か v7 か?、bit.ly/1EFEdRH、英語) には、これに関してより詳しいディスカッションが行われています。

優秀な部分は残し、一部のレガシ機能を廃止

他にも EF7 に関するニュースがあります。それは一部の開発者が気にしている点です。EF の一般的なクラス、パターン、ワークフローの大半は手を加えられずに残りますが、あまり使用されていない一部のものが廃止されます。心配されるといけないので、この点について少し説明しておきましょう。

EF7 の重要な目標は、開発者が使い慣れたパターンをそのまま使い続けられるようにし、かなりの数の既存コードを EF7 に移植できるようにすることでした。DbContext、DbSet、LINQ クエリ、SaveChanges、および長い間 EF の一部だった対話手段の多くはそのまま使用できます。

以下は EF7 で定義した DbContext クラスです。

public class BreweryContext : DbContext {
  public DbSet<Brewery> Breweries { get; set; }
  public DbSet<Beer> Beers { get; set; }
}

そして、以下は EF7 での簡単な更新ですが、EF6 で行うのと同じです。同期保存を使用していますが、すべての非同期メソッドも同様に存在しています。

public void StoreBeers(List<Beer> beers) {
  using (var context = new BreweryContext()) {
    context.Beers.AddRange(beers);
    context.SaveChanges();
  }
}

以下は簡単なクエリです。

using (var context = new BreweryContext()) {
       return context.Breweries.Where(b=>b.Location.Contains("Vermont"));
}

ここで使用している EF7 は、バージョン beta2-11616 のパッケージに含まれているものです。現時点の EF7 は実際にはベータ版ではありませんが、"beta2" は NuGet でのパッケージ名決定に関係しています。本稿公開までに EF7 はさらに進化する可能性があるため、こうしなければならないのはなく、一例と考えてください。

これまでとまったく同じように DbContext を用意し、DbSets を定義します。今回は使っていませんが、OnModelCreating もこれまでどおり使用できます。

EF4.1 では、一般的な EF の使い方を一層重視し、DbContext API を導入しました。この API は、表面下では依然としてオリジナルの ObjectContext に依存しています。この ObjectContext が、データベースとの対話を提供し、トランザクションを管理し、オブジェクトの状態を追跡します。EF4.1 以降、DbContext が既定のクラスとして使われるようになり、たまに ObjectContext を操作したい場合は、低レベルの API まで落とし込むことになります。EF7 ではこのように肥大化した ObjectContext を廃止し、DbContext だけを残します。ただし、ObjectContext を利用する一部のタスクには、依然としてアクセス可能です。

非常に複雑でサポートが難しく、あまり使われないマッピングは一部 EF7 から削除されます。前述のブログ記事では、「たとえば、TPH、TPT、TPC の各マッピングを組み合わせた継承階層と、Entity Splitting をすべて同じ階層内に含めることができる」という投稿があります。MetadataWorkspace API を使って直接作業しようとして、悲鳴を上げて逃げ出してしまった開発者ならば、この種の柔軟性に対応できて便利だとしても、それが難解かつ複雑であることがわかっています。しかし、この複雑さのために、チームはユーザーから要請される他のシナリオに対応できなくなっています。マッピングの可能性を単純化することで、MetadataWorkspace API もシンプルになり、柔軟性が大幅に高まります。EF7 では DbContext API からモデル スキーマに関するメタデータを簡単に取得できます。その結果、低レベルの機能によって高度な手法を実行できるようになります。このとき、自由に使える低レベルの ObjectContext を用意する必要はありません。

EDMX の削除と Database First の継続

Entity Framework には、モデルを記述する方法が現状 2 つあります。1 つはデザイナーで EDMX を使用する方法で、もう 1 つは Code First API で使用されている DbContext クラスとマッピングが関係します。EDMX とデザイナーを使用している場合、EF は実行時に EDMX の背後にある XML からメモリ内モデルを作成します。Code First の手法を選択する場合、EF は指定された DbContext クラスとマッピングを読み取って、同じメモリ内モデルを作成します。その時点から、モデルを記述する方法とは無関係に、EF は同じ動作を行います。EDMX とデザイナーのワークフローでは、コード内で操作するために、POCO クラスと DbContext も取得します。しかし、EDMX が存在するため、メモリ内モデルの作成にその 2 つが使われることはありません。この点は、EF7 がデザイナーベースの EDMX モデルをサポートしないことを理解するために重要です。EF7 には、実行時に EDMX XML を読み取って、メモリ内モデルを作成する機能はありません。使用されるのは Code First ワークフローのみです。

チームがこの件をブログに投稿したときは、開発者の間で混乱が起きました。この混乱の原因になったのは、開発者の多くがデータベースを POCO クラス、DbContext、およびマッピングにリバース エンジニアリングできることを知らなかったことにあります。言い換えれば、データベースから始めて Code First モデルにすることができます。これは 2011 年初頭に初めてリリースされた EF Power Tools Beta の時点から可能でした。EF Power Tools は EF6.1 デザイナーによってサポートされ、EF7 向けにも確実にサポートされる予定です。ここまで何度も「Code First」という呼び名を使っていますが、これは少々ややこしく、誤解を生みやすい呼称です。これは本来「Code Only」と呼ばれていましたが、「Database First」や「Model First」と歩調を合わせるために、「Code First」という呼称に変えられました。

そのため、既存のデータベースから始める場合は、デザイナー (EDMX) は必要ありません。

しかし、既に EDMX モデルがあるため、デザイナーが使えなくなると困る場合はどうすればよいでしょう。その場合、LLBLGen Pro Designer などの、既に EF Code First (bit.ly/11OLlN2、英語) や Devart Entity Developer (bit.ly/1yHWbB2、英語) をサポートする Entity Framework 対応のサードパーティ製デザイナーがあります。上記ツールや、EF7 へのデザイナー サポートを提供する可能性のあるツールを探してみてください。

さらに、EF6 も忘れずに目を向けておきましょう。

小さなフットプリントに、多くのデバイスと OS

また、マイクロソフトは EF API の配布の効率向上を目指しています。EF6.1.1 の NuGet パッケージ フォルダーはおよそ 22MB です。このうち 5.5MB は .NET Framework 4.5 のアセンブリで、.NET Framework 4 を使用している場合はアセンブリが異なります。EF7 では、今までよりも DLL の数が増え、1 つ 1 つが小さくなります。ワークフローのサポートに必要な DLL のみを組み合わせることになります。たとえば SQL Server をターゲットにする場合は、核となる EntityFramework.dll、SQL Server 用の DLL、およびリレーショナル データ ストア共通の API を使用することになります。移行を使用する場合は、単独のアセンブリのため、組み合わせを省略できます。それ以外の場合は、Package Manager Console から移行を作成して実行することができます。コマンド用の API があります。NuGet パッケージ マネージャーを使用すると、依存関係を考慮して適切なパッケージが特定されてダウンロードされるため、詳細について深刻に悩む必要がなくなります。

このようなことから、エンドユーザーの PC やデバイスに配布される EF7 のフットプリントが最小限に抑えられます。このことはデバイスの場合に特に重要になります。ASP.NET も同じ方法を採用しています。これらのテクノロジは、どちらも完全版の .NET Framework への依存度を低くしています。代わりに、特定のアプリのタスクを処理するのに必要な DLL のみを配布します。つまり、Windows Phone アプリや Windows ストア アプリによって使用される、既に効率が向上したバージョンの .NET から EF7 を使用できます。

完全版の .NET Framework ではなく、Mono を使用する OS (OS X や Linux など) も、クライアント側の Entity Framework をサポートできるようになります。

リレーショナルを超える

Entity Framework をはじめて発表したとき、マイクロソフトはこれがさまざまなデータ ストアで使用されると考えていましたが、その時点ではリレーショナル データベースに重点を置いていました。現在では NoSQL データベース (特にドキュメント データベース) が非常に普及しています。当時も非リレーショナル データベースは存在していましたが、広く使われてはいませんでした。

EF はオブジェクト リレーショナル マッパー (ORM) ですが、それを使う開発者は、同じ構造を使って非リレーショナル データベースを操作することを考えます。EF7 ではこうした操作に高いレベルのサポートを用意する予定ですが、その本当の意味に注意してください。リレーショナル データベースと非リレーショナル データベースには大きな違いがあり、EF ではそれらの違いをマスクする試みは行われません。ただし、基本的なクエリや更新に関しては、これまで使い慣れたパターンを利用できます。

図 1 は、非リレーショナル ドキュメント データベースの Microsoft Azure Table Storage をターゲットにするサンプル アプリのコードです。このサンプルは github.com/rowanmiller/Demo-EF7 (英語) で EF Program Manager の Rowan Miller が公開しています。このサンプルは、EF7 alpha 夜間ビルドのバージョン 11514 で動作します。

図 1 Azure Table Storage を操作するために定義した DbContext

public class WarrantyContext : DbContext
{
  public DbSet<WarrantyInfo> Warranties { get; set; }
  protected override void OnConfiguring(DbContextOptions options) {
    var connection =
      ConfigurationManager.ConnectionStrings["WarrantyConnection"]
                        .ConnectionString;
    options.UseAzureTableStorage(connection);
  }
  protected override void OnModelCreating(ModelBuilder builder) {
    builder.Entity<WarrantyInfo>()
           .ForAzureTableStorage()
           .PartitionAndRowKey(w => w.BikeModelNo, w => w.BikeSerialNo);
  }
}

OnConfiguring は新しいメソッドです。このメソッドは、実行時に EF が DbContext を構成する方法に影響します。現在の DbConfiguration クラスを使って行う方法に似ています。今回のプロジェクトには EntityFramework.AzureTableStorage パッケージもインストールしているため、builder.UseAzureTableStorage 拡張メソッドを使っているのがわかります。

EF7 はさまざまなプロバイダーにこのパターンを使います。以下は SQLite をターゲットにするプロジェクト内の DbContext クラスの OnConfiguring メソッドです。

protected override void OnConfiguring(DbContextOptions builder) {
  string dir = ApplicationData.Current.LocalFolder.Path;
  string connection = "Filename=" + Path.Combine(dir, "VermontBrewery.db");
  builder.UseSQLite(connection);
}

このプロジェクトには EntityFramework.SQLite パッケージがインストールされているため、代わりに UseSQLite 拡張メソッドを使用することになります。

図 1 の WarrantyContext クラスに戻ります。DbContext 用になじみのある OnModelCreating オーバーライドを使用し、ある程度特殊なマッピングを行っているのがわかります。繰り返しになりますが、メソッドは EntityFramework.AzureTableStorage NuGet パッケージによって提供します。必要な機能を基にパッケージを選択しています。Azure Table Storage は一意 ID にキーと値のペアを利用し、テーブルのパーティション分割をサポートします。データを取得または格納するためには、PartitionKey と RowKey に使用する値を把握しておくことが重要です。そのため API は、プロパティを適切なキーにマッピングできるようにするメソッド (PartitionAndRowKey) を提供しています。この考え方は、リレーショナル データベースの主キーにマッピングするプロパティを指定するために、Fluent API や Data Annotations を使用できたこととまったく変わりません。

このマッピングによって、使い慣れた LINQ クエリを作成して一部のデータを取得できるようになります。

var warranty = _context.Warranties
          .Where(w =>
            w.BikeModelNo == modelNo
            && w.BikeSerialNo == serialNo)
          .SingleOrDefault();

上記は代表的な LINQ クエリですが、これは現在のリレーショナル データベースで行うのとまったく同じように、Azure Table Storage データ ストアに対して実行されています。

同じデモでは、保証オブジェクトを更新し、DbSet.Add を使って新しいオブジェクトを作成および挿入して、DbContext.SaveChanges を使用してデータ ストアにすべてを保存しています。これは現在の EF6 や、以前の EF を使って行ってきたことと同じです。

他にも興味深い点があります。Entity Framework はリレーショナル データベースにマッピングするための標準機能のセットを常にサポートしてきましたが、こうした機能をターゲットにするデータベース向けに変換する方法を指定するのはデータベース プロバイダーの役割でした。EF7 では、リレーショナルと非リレーショナルのデータ ストアのどちらからも理解される、高いレベルの標準機能のセットをを用意する予定です。リレーショナル データベースに焦点を当てた低レベルの機能セットもあり、EntityFramework.Relational アセンブリにカプセル化されています。すべてのリレーショナル データベース プロバイダーはこうした機能を利用することになります。データベース操作のプロバイダー固有の処理方法は、現在と同様に、プロバイダー固有の API (コラムで以前使用した EntityFramework.SQLite など) で管理される予定です。Relational API にある AsRelational メソッドから分離した拡張メソッドはプロバイダーで見つかります。それが DbContext の拡張メソッドです。

メモリ内データ ストア プロバイダーも存在します。これは単体テスト用で、テストするロジックに関連するデータベース操作を避けるために使用します。通常、このようなシナリオでは、データベース操作を偽装するために、フェイクやモック作成フレームワークを使用します。

クエリ実行やデータベースの更新を行うテストをセットアップしている場合は、データベースのインスタンスを作成する次のようなコードが必要になります。

using (var context = new BreweryContext()) {
  // Perform some action against the context
}

まず、entityframework.InMemory パッケージをテスト プロジェクトにインストールし、InMemoryStore 用に DbContextOption を定義してから、コンテキストがそのオプションを使用するように指定することで、メモリ内ストアに簡単に切り替えることができます。前述のように、これが可能になるのは、この API が拡張メソッドを提供しているためです。

var options = new DbContextOptions().UseInMemoryStore();
using (var context = new BreweryContext(options)){
  // Perform some action against the context
}

機能の増加、能力の向上、柔軟性の大幅な強化

ここまでの説明で、拡張メソッドが提供する柔軟性、OnConfiguring オーバーロードを使って Entity Framework パイプラインに影響を与える機能など、EF7 の新しいコード ベースのメリットをお分かりいただけたと思います。この新しいコードベースには、、EF7 を変化させるだけでなく、EF7 に独自のロジックを簡単にプラグインできるようにする拡張ポイントが至るところに存在します。

この新しいコード ベースは、EF チームが昔からある問題の一部を解決するチャンスを提供します。たとえば、今手元にあるバージョンでは、リレーショナル データベース既定の一括更新がサポートされています。このバージョンで作成したコードでは、厄介な「Entity Framework cannot translate this method into SQL.」(Entity Framework ではこのメソッドを SQL に変換できません) というメッセージが表示されずに、独自のメソッドを LINQ クエリ中にインラインで使用できます。それどころか、EF とプロバイダーは、クエリが SQL になる部分とクライアント上でローカルに実行される部分を解析して切り分けることができます。コラムでは、このような特定の機能で生じる可能性のある問題に対する保護や、こうした問題を回避するためのガイダンスを用意する予定です。

チームは、長い間要請されていた、モデル用の一意外部キー機能を追加しました。また、テーブル値関数のサポートや、非接続型データを処理する明確な方法についても細かく検討しています。後者は Entity Framework について長年コラムで注目してきたものです。これは Entity Framework に限らず、非接続型アプリケーションにかかわる共通の問題であり、すべてのシナリオで一貫して機能するアルゴリズムを作成するのは容易なことではありません。そのため、新しいアプローチが確実に必要になります。

EF7 はここで取り上げた以上に魅力にあふれています。blogs.msdn.com/adonet の ADO.NET チームのブログ投稿 (英語) を詳しく確認することを強くお勧めします。前半にリンクで示した資料以外にも、Rowan Miller が、EF7 でデザイナーへのサポートを廃止した決断について詳しく述べた投稿「EF7 - What Does ‘Code First Only’ Really Mean」(EF7 - "Code First Only" の本当の意味とは、英語) も bit.ly/1sLM3Ur で確認してください。GitHub プロジェクトだけでなく、このブログからも目を離さないことをお勧めします。GitHub の Wiki (bit.ly/1viwqXu、英語) では、夜間ビルドへのアクセス方法、ソース コードのダウンロード、コンパイルおよびデバッグの方法、チュートリアル、設計ミーティングのメモへのリンクが掲載されています。チームは皆さんからのフィードバックを心待ちにしており、要望が送られてくると盛り上がります。

軽々しくできない決断

本コラムにとって重要なことは、EF7 についての情報を提示し、この大きな変化によって生じる不安を和らげることです。アプリケーションに組み込んだ既存の EF 機能の中に EF7 に移行されないものもあるといった噂が不安をあおります。すべてが事実無根というわけではありません。チームも本コラムもこうした噂を軽く捉えてはいません。しかし、EF6 が消え去ることはなく、コミュニティの貢献によって発展し続けるという点を理解しておくのは重要です。先に進むことでメリットを得られると考える場合は、難しい決断をいくつか下すことになるでしょう。巨大なアプリケーションは簡単にはアップグレードできません。選択肢を慎重に比較検討すべきです。アプリケーションを分割して、EF7 のメリットを生かせる特定の部分だけを作り直すこともできます。

かさねて、ここまで述べてきたように、EF7 はいまだに初期の段階にあり、本稿が公開される時点までにどれほど進化するかはわかりません。ただし、現在利用可能なソースと NuGet パッケージがあり、調査、実験、フィードバックの提供が可能です。EF チームは、核となる API を更新するときは必ず、すべてのプロバイダー API (Redis、SQLite など) を最新状態に保ちます。bit.ly/1ykagF0 の投稿「EF7 - Priorities, Focus and Initial Release,」(EF7 - 優先順位、重点事項、および初期リリース、英語) によると、EF7 の最初のリリースは ASP.NET 5 との互換性に重点が置かれ、その後のリリースで多くの機能が追加される予定です。EF7 を使ったアプリケーションの開発に着手するには、EF 7 はまだ十分安定していませんが、前もって計画を始めるには適切なタイミングであることは間違いありません。


Julie Lerman  は、バーモント ヒルズ在住の Microsoft MVP、.NET の指導者、およびコンサルタントです。世界中のユーザー グループやカンファレンスで、データ アクセスなどの .NET トピックについてプレゼンテーションを行っています。彼女のブログは thedatafarm.com/blog で、彼女は O'Reilly Media から出版されている『Programming Entity Framework』(2010 年) および『Code First』版 (2011 年)、『DbContext』版 (2012 年) を執筆しています。彼女の Twitter (twitter.com/julielerman) をフォローして、juliel.me/PS-Videos で彼女の Pluralsight コースをご覧ください。

この記事のレビューに協力してくれたマイクロソフト技術スタッフの Rowan Miller に心より感謝いたします。

本稿は、Entity Framework 7 アルファ版に基づいています。記載している内容は変更される可能性があります。