DI っぽく EF Core 1.0 + SQLite を Full .NET と .NET Core のコンソールアプリケーションで使ってみる [Entity Framework Core]

Pocket

しばやん御大の Entity Framework Core についての以下の記事を読んで、


コンソールアプリでも、 ソースコードに 接続文字列 や ログの設定を書かずに、設定ファイルから Dependency Injection (依存性の注入: 以下DI) するにはどうしたらよいのかな? と思ったので、 ASP.NET Core の流儀を参考にしながら やってみようと思う。

データベースは、扱いが簡単な SQLite にする。

記事の最後に、 Visual Studio 2015 ですぐに使えるサンプルプロジェクトを用意しているので、 手っ取り早く結果を見たければ、 そのサンプルプロジェクトを見てみてほしい。

実現すること

まずは、何を実現させるのかをハッキリさせておこう。

  1. 接続文字列と ログ表示の設定を、外のファイルから指定すること
  2. マイグレーションなどを行うため、 EF Tools からも、上記設定が利用されるようにすること

ここで言う EF Tools とは、Entity Framework Core の コマンドラインツール のことだ。
このツールを使うと、 パッケージマネージャーコンソールから Add-Migration とか Update-Database と実行したり、 dotnet.exe から dotnet ef コマンドを 実行することで、 コード生成やマイグレーションなど を利用することができる。

EF Tools でデータベースを取り扱う際、その接続文字列は DbContext に設定されたものが使用される。
コード内に接続文字列を書いてしまうと、マイグレーションするためのデータベースファイルが決め打ちになってしまい、変更ができなくなる。
このため、 EF Tools を実行した際も、依存性の注入が行えるようにしたい。

前準備

まず、 Visual Studio を使って、 .NET Framework 4.5.1 以降か、 .NET Core の どちらかの コンソールアプリのプロジェクトを作成しよう。

パッケージマネージャーコンソールで、以下のパッケージとその依存パッケージをインストールする。

Install-Package Microsoft.Extensions.Logging.Console -Pre
Install-Package Microsoft.Extensions.Configuration.Json -Pre
Install-Package Microsoft.EntityFrameworkCore.Tools -Pre
Install-Package Microsoft.EntityFrameworkCore.Sqlite -Pre

DbContext への依存性の注入の方法

実際にコードを書いていく。

設定ファイルからの読み取りについては、 ASP.NET のサンプルに倣って、 ConfigurationBuilderAddJsonFile を使ったものにしよう。

さて、本題の 依存性 の注入については、 IServiceCollection に対して、 フレームワークが提供する拡張メソッド を使いながら 依存性を定義してゆき、 ActivatorUtilities などを使って依存関係が解決されたインスタンスを取得するのが、おおざっぱな流れとなる。

DbContext を例に、もうちょっと具体的に説明すると、

  • new Microsoft.Extensions.DependencyInjection.ServiceCollection() して IServiceCollection を得る
  • IServiceCollection.AddDbContext() の拡張メソッドを利用して、依存性を定義する
  • EF のモデリングに使う DbContext (以降 BloggingContext とする) には、 DbContextOptions<BloggingContext> options を引数にしたコンストラクタを用意し、 基底クラスに options を引き渡すことで、設定された依存性を反映させる。
  • ActivatorUtilities.CreateInstance(serviceProvider) メソッドを使って、 BloggingContext のインスタンスを取得する…

    となる。

コードにしてみると、以下のようになる。

EF Tools はどのように DbContext への注入を行うのか

さて、上記のような処理を Main 関数に書いたとしても、 EF Tools で Add-Migration とか Update-Database と実行した際に、

No parameterless constructor was found on 'BloggingContext'. Either add a parameterless constructor to 'BloggingContext' or add an implementation of 'IDbContextFactory' in the same assembly as 'BloggingContext'.

とエラーになってしまうだろう。

これは、 EF Tool が Main 関数を実行せずに BloggingContext を初期化しようとするため、 BloggingContext のコンストラクタに DbContextOptions<BloggingContext> options を指定する」 という依存性が定義されないまま BloggingContext を初期化してしまうためだ。

では、 EF Tools が実行してくれるように 依存性を注入の定義を行う ためにはどうしたらよいのだろうか?

その答えは、 EF Tools の 「初期化クラスっぽい名前のクラスに、 依存性の注入を行っていそうな名前のメソッドがあると、 それを実行する」 という機能を利用するのだ。
…えーと、どういうこと?

もうちょっと具体的に言うと、

↑この StartupInvoker が、

  • $"Startup{environment}", "Startup", "Program", "App" の順番で、アセンブリからクラスを探す
  • 上記のうち最初に見つかったクラスから、 "ConfigureServices", $"Configure{environment}Services" メソッドを探す
  • このメソッドが static メソッドなら そのまま呼び出し、 そうでなければ コンストラクタで初期化してから呼び出す
  • このメソッドが IServiceProvider を返せばそれを、 返さなかったら メソッドの引数に指定した IServiceCollectionIServiceCollection.BuildServiceProvider() したものを、 これらのどちらか使って、 DbContext の依存性の解決を行う

といった動きをする。 (ASP.NET Core RC2, Tools Preview 1 の場合)

つまるところ、 ConfigureServices という名前のメソッドを持った、 "Startup" とか "Program" という名前のクラス用意して、 その中に DI のコードを書いてやれば良いのだ。

上記を満たす書き方はいろいろあるが、たとえば 先ほどの Program クラスを、以下のように書き換えてみよう。

こうすることで、 EF Tools も使用でき、 DB のマイグレなどがコンソールアプリでも行えるようになる。
試しに、 パッケージマネージャーコンソールで Add-Migration InitialMigration と実行して正しくマイグレーションコードが作成されることと、 Update-Database と実行して appsettings.json で指定した接続文字列の通りのファイルに DB が初期化されることを確認してみよう。

サンプルコード

今回使用したプロジェクトは、そのまま GitHub にも上げておいたので、良かったら適当に clone して使ってほしい。
Visual Studio 2015 でソリューションを開き、 .NET 4.5.1 用、 .NET Core 用のそれぞれのプロジェクトでパッケージを復元すれば、利用できるようになるはずだ。

それぞれのプロジェクトで、 Add-Migration <マイグレーション名>, Update-Database と実行し、データベースファイルを作成した後、 コンソールアプリをデバッグ実行してみよう。

DI っぽく EF Core 1.0 + SQLite を Full .NET と .NET Core のコンソールアプリケーションで使ってみる [Entity Framework Core]」への3件のフィードバック

  1. ピンバック: DbContext.OnConfiguring(EF Core)で接続文字列を書きたくない時 | 株式会社システムキューブ

  2. ピンバック: System.Data.SQLite の NuGet パッケージ のうち どれをインストールするべきか | Aqua Ware つぶやきブログ

  3. ピンバック: System.Data.SQLite でどれをインストールするべきか | Aqua Ware つぶやきブログ

コメントを残す

メールアドレスが公開されることはありません。

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください