October 2016

Volume 31 Number 10

データ ポイント - .NET Framework と .NET Core の両方での EF Core の実行

Julie Lerman

Julie Lerman以前、Entity Framework 7 (EF7) と呼ばれていたテクノロジは、2016 年前半に Entity Framework Core (EF Core) という名前に変わりました。EF Core 1.0.0 には新しくすばらしい機能がいくつか導入されていますが、EF6 に比べて機能の数は全体的に少なくなっています。だからと言って、EF が .NET Core でしか実行できなくなったというわけではありません。完全版の .NET Framework を必要とする API やアプリケーションでも、クロスプラットフォームの .NET Core のみをターゲットにした API やアプリケーションでも、EF Core を使用することができます。今回は、2 つのプロジェクトを使って、こうしたオプションについて説明します。目標は、「Core」という呼称から生じるであろう、「EF Core は .NET Core でしか実行できないのではないか」という心配を取り除くことです。同時に、それぞれソリューションを作成する手順についても説明します。

完全版 .NET プロジェクトでの EF Core

最初に取り上げるのは、完全版の .NET Framework をターゲットとするプロジェクトです。Visual Studio 2015 で EF Core を使用するには、Visual Studio 2015 Update 3、最新の Microsoft ASP.NET、および Web ツールが必要です。本稿執筆時点 (2016 年 8 月) で、これらのインストール方法を説明している最良のガイドは、bit.ly/2bte6Gu (英語) です。

データを利用するアプリケーションが何であれ、データ アクセスを分離するために、独自のクラス ライブラリ内でプロジェクトを作成します。図 1 を見ると、.NET Core クラス ライブラリを明確にターゲットにしたテンプレートがあることがわかります。しかし、常に利用可能で .NET をターゲットにできる標準オプションの [クラス ライブラリ] を選択します。作成されるプロジェクト (図 2) も「標準」です。project.json ファイルや、.NET Core プロジェクト アセットは何も含まれていません。すべて、いつものクラス ライブラリです。

完全版 .NET API 用クラス ライブラリの作成
図 1 完全版 .NET API 用クラス ライブラリの作成

単純な従来の (見慣れた) .NET クラス ライブラリ
図 2 単純な従来の (見慣れた) .NET クラス ライブラリ

これまでのところ、なんらかの形で EF に結びつけるものはありません。この時点で EF6 または EF Core を選ぶこともできますが、今回は後ほど EF Core をプロジェクトに追加します。いつもどおり、NuGet パッケージ マネージャーを使用して EF Core を検索して選択することも、Package Manager Console ウィンドウを使用することもできます。今回は Package Manager Console を使用します。「entityframework」パッケージは EF6 用なので注意してください。EF Core を入手するには、Microsoft.EntityFrameworkCore パッケージのいずれかをインストールする必要があります。ここでは SqlServer パッケージを使用します。このパッケージには、EF が SqlServer と通信するために必要なものが含まれています。

install-package Microsoft.EntityFrameworkCore.SqlServer

このパッケージはメイン Microsoft.EntityFrameworkCore パッケージと Microsoft.EntityFramework­Core.Relational パッケージを利用しているため、NuGet はそれらのパッケージも同時にインストールします。また、EF Core パッケージが利用するパッケージもインストールされます。このプロセスでは合計 3 個の EF Core パッケージに加え、EF Core が利用する 23 個の新しく構成しやすい .NET パッケージが追加されます。少数の大きなパッケージを入手するのではなく、多数の小さなパッケージの中から利用します。つまり、アプリケーションに必要なパッケージのみを入手します。これらのパッケージはすべて、プロジェクト既存の標準 .NET ライブラリと適切に連携します。

次に、シンプルなドメイン クラス (Samurai.cs) と DbContext (SamuraiContext.cs) を追加して、EF Core からデータをデータベースに永続化できるようにします (図 3 参照)。EF Core には、EF6 に用意されている接続文字列を推論する魔法のような機能はありません。そのため、使用するプロバイダーと接続文字列を知らせる必要があります。シンプルにするために、今回はそれを DbContext の新しい仮想メソッド OnConfiguring で行います。また、コンストラクターのオーバーロードを作成して、必要に応じてプロバイダーなどの詳細を渡せるようにもしました。これは、この後すぐに利用します。

図 3 Samurai クラスと SamuraiContext DbContext クラス

public class Samurai
  {
    public int Id { get; set; }
    public string Name { get; set;}
  }
public class SamuraiContext : DbContext
  {
    public DbSet<Samurai> Samurais { get; set; }
    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) {
      {
        if (optionsBuilder.IsConfigured == false) {
          optionsBuilder.UseSqlServer(
         @"Data Source=(localdb)\\mssqllocaldb;Initial Catalog=EFCoreFullNet;
                       Integrated Security=True;");
        }
        base.OnConfiguring(optionsBuilder);
      }
    }
    public SamuraiContext(DbContextOptions<SamuraiContext> options)
      : base(options) { }
  }

完全版の .NET を使用している (つまり、完全な Windows をターゲットにしている) ため、Windows PowerShell を利用できます。つまり、add-migration、update-database など、いつもの移行コマンドを使用できることになります。新しいコマンドもいくつかあります。EF Core の移行コマンドの詳細については、2016 年 1 月のコラム (msdn.com/magazine/mt614250) をご覧ください。先ほど、小さく構成しやすいパッケージと記しましたが、 移行を使用する場合は、そのコマンドを含むパッケージを追加する必要があります。本稿執筆時点で、このツールはまだプレビュー段階なので、-pre パラメーターを使用する必要があります。パッケージを追加したら、以下のように新しい移行を追加します。

install-package Microsoft.EntityFrameworkCore.Tools –pre
add-migration init

このコマンドは、これまでと同じように機能します。つまり、新しい [Migrations] フォルダーと移行ファイルが作成されます (図 4 参照)。EF Core では、モデルのスナップショットの格納方法が変わりました。詳細については、先ほどの 2016 年 1 月号のコラムをお読みください。

完全版の .NET クラス ライブラリでの EF Core の移行
図 4 完全版の .NET クラス ライブラリでの EF Core の移行

移行の準備ができたら、update-database コマンドを実行します。正常にコマンドが実行されると、SQL Server localdb に新しい EFCoreFullNet データベースが作成されます。

最後に、Visual Studio でいつも使用しているのと同じ単体テスト プロジェクト テンプレートのテスト プロジェクトをソリューションに追加します。その後、EFCoreFullNet クラス ライブラリに参照を追加します。EF Core が機能しているかどうかを確認するために、テスト プロジェクトでデータベースを使用したくはありません。そのため、SqlServer パッケージを使用するのではなく、新しいテスト プロジェクトに対して次の NuGet コマンドを実行します。

install-package microsoft.EntityFrameworkCore.InMemory

InMemory プロバイダーは、EF Core をテストするうえで非常に便利な存在です。InMemory プロバイダーは、インメモリ データを使用して、データベースと EF キャッシュを表現します。EF Core は、データベースに対してデータの追加、削除、更新などの操作をする場合とほぼ同じ方法で、このキャッシュを操作します。

本稿前半の SamuraiContext で、追加のコンストラクターを作成しました。 図 5 の TestEFCoreFullNet テストでは、これを利用します。テスト クラスのコンストラクターで、SamuraiContext の DbContext­Options ビルダーを作成し、InMemory プロバイダーを使用するように指定します。その後、SamuraiContext のインスタンスを作成するときに、それらのオプションを渡します。SamuraiContext の OnConfiguring メソッドは、オプションが構成済みかどうかチェックするように設計しています。構成済みの場合、そのオプション (今回は InMemory プロバイダー) を使用します。未構成の場合、ハードコーディングした SqlServer と接続文字列を使用して設定を行います。

図 5 EFCore を使ったテスト

using EFCoreFullNet;
using Microsoft.EntityFrameworkCore;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System.Linq;
namespace Tests
{
  [TestClass]
  public class TestEFCoreFullNet
  {
    private DbContextOptions<SamuraiContext> _options;
    public TestEFCoreFullNet() {
      var optionsBuilder = new DbContextOptionsBuilder<SamuraiContext>();
      optionsBuilder.UseInMemoryDatabase();
      _options = optionsBuilder.Options;
    }
    [TestMethod]
    public void CanAddAndUpdateSomeData() {
      var samurai = new Samurai { Name = "Julie" };
      using (var context = new SamuraiContext(_options)) {
        context.Add(samurai);
        context.SaveChanges();
      }
      samurai.Name += "San";
      using (var context = new SamuraiContext(_options)) {
        context.Samurais.Update(samurai);
        context.SaveChanges();
      }
      using (var context = new SamuraiContext(_options)) {
        Assert.AreEqual("JulieSan", context.Samurais.FirstOrDefault().Name);
      }
    }
  }
}

このテスト メソッドは、EF6 には存在しない EF Core 固有の機能をいくつか利用しています。EF Core 固有の機能や、その他の変更追跡機能については、2016 年 8 月のデータ ポイント コラム (msdn.com/magazine/mt767693) で取り上げています。たとえば、新しい samurai オブジェクトを作成した後、DbContext.Add メソッドを使用してそのオブジェクトをコンテキストに追加します。オブジェクトをどの DbSet に結び付ける必要があるかの判断は、EF に任せます。その後、オブジェクトをデータ ストアに保存します。今回は、InMemory プロバイダーが管理しているインメモリのなんらかの型のリストに保存します。次に、samurai オブジェクトを変更して、DbContext の新しいインスタンスを作成します。そして、新しい EF Core Update コマンドを使用して、新しいオブジェクトを作成するのではなく、保存されている samurai オブジェクトが SaveChanges によって更新されるようにします。最後に、コンテキストにその samurai オブジェクトをクエリし、Assert を使用してコンテキストが本当に更新後の名前を返すことを確認します。

ただし、使用している特定の機能が重要なのではありません。重要なのは、Windows の「単純な従来の .NET」プロジェクトで EF Core を使用してこの作業すべてを行っていることです。

CoreCLR 用 EF Core: 同じコード、異なる依存関係

引き続き Windows と Visual Studio 2015 Update 3 環境で、同じ EF Core API、同じコード、および同じテストを使用して CoreCLR ランタイムをターゲットにする方法を示すこともできるのですが、これでは Windows をターゲットにする場合と見た目が似通いすぎています。そのため、正反対の環境を使用し、手順を説明しながら、CoreCLR のバリエーションを MacBook で作成します。

.NET Core は、Windows やそのツールに依存しません。Visual Studio 2015 以外にも使用できるツールがあります。Visual Studio ではない人気のエディターの中でも古いのは Emacs でしょう。しかし、コードを記述するだけでなく、デバッグや Git のサポートなどの機能も利用できるクロスプラットフォーム IDE がいくつかあるので、そちらも選択できます。たとえば、MSDN マガジンの 2016 年 8 月号では、Alessandro Del Sole が Visual Studio Code を使用して ASP.NET Core Web サイトを作成する方法を説明しました (msdn.com/magazine/mt767698 参照)。このコラムでは、スクリーンショットから Windows を使用していることがわかりますが、Windows ではなく、Mac を使用しても基本的に操作は同じです。

別のクロスプラットフォーム オプションとしては、JetBrains 製の Rider があります。Rider は特に C# 向けに設計されており、最もわかりやすく説明するなら、「ReSharper を組み込んだ JetBrains 製 IDE」です。

既に Windows と OS X で Visual Studio Code を使用したことがあります (C# だけでなく、Node.js も使用しました)。これを使用して、CoreCLR をターゲットにしたアプリケーション作成における EF Core をお見せします。実は、このソリューションを OS X で作成しているため、CoreCLR をターゲットにするのが唯一の選択肢なのです。ライブラリで利用可能な数々の API は、より限定されます。ただし、EF Core は、最初のプロジェクトの完全版の .NET ライブラリで使用したときと同じ API セットです。

作業のほとんどは、CoreCLR をターゲットとする場合固有のプロジェクトと依存関係の設定に必要になります。しかし、前のプロジェクトと同じ SamuraiContext を使用して EF Core データを定義でき、同じ CanAddAndUpdateSomeData テスト メソッドを使用して同じ処理を実行できます。ターゲットとするランタイムは限定され、.NET Core しか使用できない環境で作業していますが、コードは同じです。

.NET クラス ライブラリに似たライブラリの作成

フォルダーを 1 つ作成しています。このフォルダーには、Library プロジェクトと Test プロジェクトの両方を含めるので、各プロジェクト用のサブフォルダーも用意します。Library サブフォルダー内で、dotnet new を呼び出して、Library プロジェクトを作成できます。図 6 にそのコマンドを示します。プロジェクトが作成されたことを示す確認メッセージも表示されています。フォルダーの内容を一覧すると、ファイルが 2 つだけ作成されていることが示されます。最も重要なのが、project.json です。このファイルには、必要な NuGet パッケージと他の関連プロジェクトの詳細のリストが含まれています。Library.cs ファイルは、単なる空のクラス ファイルなので、削除します。

dotnet コマンドによる CoreCLR ライブラリの新規作成
図 6 dotnet コマンドによる CoreCLR ライブラリの新規作成

次に、Visual Studio Code でこの新しいライブラリ プロジェクトを開きます。それには、ターミナルで「code」と入力するだけです。これで、Library をターゲット フォルダーとして Visual Studio Code が開きます。自動的に、json ファイルに記載されたパッケージが認識され、未解決の依存関係の解決を行う dotnet restore が実行されます。ありがたく dotnet restore の実行を受け入れます。

project.json ファイルは図 7 のコードのようになっています。

図 7 project.json ファイル

{
  "version": "1.0.0-*",
  "buildOptions": {
    "debugType": "portable"
  },
  "dependencies": {},
  "frameworks": {
    "netstandard1.6": {
      "dependencies": {
        "NETStandard.Library": "1.6.0"
      }
    }
  }
}

非常にシンプルです。ライブラリに必要なのは、これまで使用していた ASP.NET Core に関するものすべてではなく、NETStandard ライブラリだけです。.NET Standard ライブラリには、現在 .NET を実行可能なさまざまな場所に共通のものがカプセル化されています。.NET Standard ドキュメント (bit.ly/2b1JoHJ、英語) には、「.NET Standard は、すべての .NET ランタイムで利用可能にすることを目的とした .NET API の正式仕様です」と書かれています。 そのため、この作成中のライブラリは、.NET Core、ASP.NET Core に加え、.NET 4.5 以降の .NET アプリケーションでも使用できます。ドキュメント ページの互換性の表を確認します。

次に、EF Core をプロジェクトに追加します。Mac を使用しているので、SqlServer は使用できません。代わりに、EF Core 用の PostgreSQ プロバイダーを使用します。これを、project.json の現在空白の dependencies セクションで指定します。

"dependencies": {
    "Npgsql.EntityFrameworkCore.PostgreSQL": "1.0.0-*"   
  },
  "tools": {
    "Microsoft.EntityFrameworkCore.Tools": "1.0.0-preview2-final"
  },

前と同じように、移行を行う予定です。通常であれば、このライブラリの完全版の .NETバージョンに対して行ったのと同じように、コマンドを含む Microsoft.EntityFrameworkCore.Tools パッケージへの依存関係も追加します。しかし、現在、この Preview 2 ツールには制限があるため、その作業はプロセス後半の手順まで延期します。ただし、このライブラリ フォルダーからコマンドにアクセスできる必要があるため、上述のコードに示すように、project.json の特別な「tools」セクションにこのパッケージを追加します。

パッケージを復元すると、これら 2 つのパッケージだけではなく、その依存関係も取り込まれます。作成される project.lock.json ファイルを確認すると、Microsoft.EntityFrameworkCore と Microsoft.EntityFramework­Core.Relational (最初の .NET ソリューションで追加したパッケージと同じパッケージ) を含む、すべてのパッケージを確認できます。

次に、Samurai.cs と SamuraiContext.cs ファイルをコピーします。SQL Server ではなく、PostgreSQL とその接続文字列を使用するように OnConfiguring クラスを変更します。その部分のコードは次のようになります。

optionsBuilder.UseNpgsql(
  "User ID=julie;Password=12345;Host=localhost;Port=5432;Database=EFCoreCoreCLR;
    Pooling=true;");

移行を実行するところまできましたが、ここで、現在 Preview2 バージョンである EFCore ツールを Visual Studio 以外で利用する場合の既知の制限事項に直面します。つまり、重要なアセットを見つけるには、実行可能なプロジェクトが必要です。初めて直面するときは少し大変ですが、余計な手間はあまりかかりません。詳細については、bit.ly/2btm4OW (英語) を参照してください。

テスト プロジェクトの作成

テスト プロジェクトを追加します。このテスト プロジェクトは、実行可能なプロジェクトとして移行で使用します。コマンド ラインに戻り、先ほど作成した Oct2016DataPointsMac/Test サブフォルダーに移動して、次のコマンドを実行します。

dotnet new -t xunittest

Visual Studio Code で Test フォルダーに新しい project.json が表示されます。このプロジェクトは実行可能な EF コマンド ラインを確認する役割があるので、EF Core Tools パッケージへの参照を依存関係に追加する必要があります。さらに、テスト プロジェクトには Library への参照も必要です。そのため、それも project.json の依存関係に追加します。これらの依存関係を追加した後の dependencies セクションは次のようになります。

"dependencies": {
    "System.Runtime.Serialization.Primitives": "4.1.1",
    "xunit": "2.1.0",
    "dotnet-test-xunit": "1.0.0-rc2-192208-24",
    "Library": "1.0.0",
    "Microsoft.EntityFrameworkCore.Tools": "1.0.0-preview2-final"
  },

これで、Library フォルダーから EF Core コマンドにアクセスできるようになります。図 8 のコマンドでは、Test フォルダー内のプロジェクトを --startup-project パラメーターを使用してポイントしています。これを、移行コマンドのそれぞれと一緒に使用します。

実行可能なプロジェクトを利用して Library で EF 移行コマンドを使用できるようにする
図 8 実行可能なプロジェクトを利用して Library で EF 移行コマンドを使用できるようにする

.NET ライブラリから EF モデルに対する移行の実行

このコラムで EFCore 移行について説明しましたが、dotnet ef migrations コマンドは PowerShell コマンドとは見た目が違いました。しかし、2 つのコマンドが実行するのは、移行 API の同じロジックです。

まず、以下を使用して移行を作成します。

dotnet ef --startup-project ../Test migrations add init

これを実行すると、移行と移行のスナップショットが追加された新しい Migrations フォルダーが作成され、.NET ソリューションと同じ結果になります。

次に、以下を使用してデータベースを作成できるようになります。

dotnet ef --startup-project ../Test database update

その後、PostgreSQL データベース、テーブル、およびリレーションシップが作成されたことを確認します。そのために OS X で使用できるツールはたくさんあります。筆者の Mac では、データベース IDE として JetBrains 製のクロスプラットフォーム DataGrip を使用しています。

CoreCLR でのテストの実行

最後に、最初のソリューションの TestEFCoreFull­Net クラスを Test フォルダーにコピーします。ここでも、MS Test の代わりに xUnit を使用するよう、インフラストラクチャの変更を加えます。いくつか名前空間の変更、TestClass 属性の削除、TestMethod 属性を Fact に置換、Assert.AreEqual を Assert.Equal に置換などです。もちろん、クラス名も変更して TestEFCoreCoreClr にします。

project.json にも InMemory プロバイダーについて通知する必要があります。そのため、以下を dependencies セクションに追加します。

"Microsoft.EntityFrameworkCore.InMemory": "1.0.0"

その後、「dotnet restore」をさらにもう一度実行します。

xUnit テスト プロジェクトでは、xUnit コマンド ライン テスト ランナーを使用します。そのため、ターミナル ウィンドウに戻り、dotnet test コマンドを使用してテストを実行します。図 9 にテスト実行後の出力を示します。テストは大成功です。ただし、このコマンド ライン テスト ランナーでは、テストに合格しても合格を示す出力が緑色になることはありません。

テストの合格を示す xUnit テストの出力
図 9 テストの合格を示す xUnit テストの出力

.NET または CoreCLR: 同じ API、同じコード

完全版の .NET Framework を使用して Windows 環境のみで実行するソフトウェアをターゲットにする場合でも、サポート対象のすべての環境 (Linux、OS X、Windows) で CoreCLR で実行するソフトウェアをターゲットにする場合でも、EF Core 関連のコードとアセンブリは同じです。Windows PC で Visual Studio 2015 を使用して両方のデモを行えたら良かったのですが、完全版の .NET Framework をまったく利用できない環境で CoreCLR を使う作業に注目すれば、EF API と EF 関連のコードがどちらの環境でもまったく同一のものであることを示す、非常にすばらしいデモになると考えました。大きな違いは、ターゲット プラットフォーム (.NET か CoreCLR か) に関連するもので、追加作業もすべて、ターゲット プラットフォーム関連です。MacBook で EF Core 1.0.0 を使用して完全な ASP.NET Core Web API を作成する方法については、EF Core 1.0 の概要に関するビデオ (bit.ly/2cmblqE、英語) をご覧ください。同じビデオの簡潔で楽しいデモ部分は、bit.ly/2ci7q0T (英語) から、DotNetFringe セッションのビデオをご覧ください。


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

この記事のレビューに協力してくれたマイクロソフト技術スタッフの Jeff Fritz に心より感謝いたします。
Jeffrey T. Fritz は、マイクロソフトの ASP.NET チームで Web フォームと ASP.Net Core に携わるシニア プログラム マネージャーです。長年、Web 開発者として、さまざまな業界において、さまざまな規模のアプリケーションに関する経験を積み、パフォーマンスと実用性を実現する開発方法について熟知しています。