【ASP.NET】Database用プロジェクトのみでマイグレーションを実行したい

## はじめに

本記事は「ASP.NET MVCにデータベースを接続する」の派生記事です。本記事内でのコードは元記事で記載したものがベースとなっています。

https://qiita.com/shironana/items/18d38933324a3e4276ee

## 実現したいこと

多くの記事ではアプリケーション側の依存性注入でマイグレーションを行う手法が採られているようですが、複数アプリケーションを用意する場合などはDatabase用プロジェクトで完結させることが望ましい場合もあります。

そこで、いろいろと試行錯誤して上手くいく方法を考えてみました。先行事例やより良い手法があれば、コメントいただければ幸いです。

本記事の全体像は以下画像のようになります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2779418/baf33409-1e04-cc37-84f7-d14c6beceb0f.png)


## Entity Framework Core ToolsをDatabaseプロジェクトのみにインストール

「Entity Framework Core Tools」はマイグレーション実行時のスタートアッププロジェクトに指定されたプロジェクトで必要になるため、「Database」プロジェクトのみにインストールします。

![image1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2779418/0678b69a-5347-d870-696a-6100682de789.png)

## 接続文字列をDatabaseプロジェクト内の設定ファイルに定義

対象Contextの接続文字列が固定である場合、各アプリケーション内に別々に定義するのではなくDatabaseプロジェクトに独自の設定ファイルを作成し、このファイルに定義します。（設定ファイル名は任意）

なお、独自ファイルを作成した場合は実行フォルダ内に含める設定を明示的に行う必要があるため、プロパティ内の「出力ディレクトリにコピー」を「新しい場合はコピーする」または「常にコピーする」に設定します。
※今回の追加ファイルは動的に変更されるものではないため「新しい場合はコピーする」で問題ありません。

![image2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2779418/8260c70a-ef45-c3a4-1d5c-3a8c38b3d4cc.png)


## コンテキストクラスで接続文字列を直接受け取る

Databaseプロジェクトで設定ファイルの読み取りを行うため、Webプロジェクトでは依存関係により同梱されていた以下2パッケージを、NugetでDatabaseプロジェクトに対してインストールする必要があります。

```
Microsoft.Extensions.Configuration
Microsoft.Extensions.Configuration.Json
```

![image3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2779418/a5ce6d9a-63ff-18c2-465d-07a4b631a3c3.png)

コンテキストクラスの `OnConfiguring` メソッド内に設定値の取得処理と接続文字列設定処理を追加します。
なお、設定ファイルの取得で `Path.GetFullPath ~` としている理由は、マイグレーション以外の場合には別プロジェクトの設定ファイルを参照するという処理になり、明示的にDatabaseプロジェクトの設定ファイルを読み込む必要があるためです。

例．WebプロジェクトでDBアクセスを行うと、`Directory.GetCurrentDirectory();`ではWebの実行ファイルのパスが返却され、Web配下に設定ファイルがないとして例外が発生する。

```diff_c_sharp:GourmetDbContext
  using Database.Entities;
  using Microsoft.EntityFrameworkCore;
+ using Microsoft.Extensions.Configuration;

  namespace Database
  {
      public class GourmetDbContext : DbContext
      {
-         // 依存性の注入用コンストラクタ
-         public GourmetDbContext(DbContextOptions<GourmetDbContext> options) : base(options) { }
-
           // エンティティ定義（作成したエンティティと名前が揃うように設定）
          public DbSet<Gourmet> Gourmet { get; set; }
          public DbSet<Prefecture> Prefecture { get; set; }

+         protected override void OnConfiguring(DbContextOptionsBuilder options)
+         {
+             /* 起動時の設定など */
+
+             // 設定ファイルから設定値を取得（）
+             var config = new ConfigurationBuilder()
+                 .SetBasePath(Path.GetFullPath(Path.Combine("../", typeof(GourmetDbContext).Namespace!)))
+                 .AddJsonFile("dbconfig.json")
+                 .Build();
+
+             // DBの接続文字列設定
+             options.UseSqlServer(config.GetConnectionString(nameof(GourmetDbContext)));
+         }

          protected override void OnModelCreating(ModelBuilder modelBuilder)
          {
              /* エンティティの追加設定など */
          }
      }
  }
```

## Web側のDI部分での接続文字列設定を削除

コンテキストクラス側で接続文字列の設定を行ったのでコンテキストのDIでは接続文字列設定が不要になります。（`AddDbContext`の際にDbContextの`OnConfiguring`が実行されるため、接続文字列が設定できる）
設定ファイルの読み込み処理は必要なければ削除してください。

```diff_c_sharp:Program.cs
  var builder = WebApplication.CreateBuilder(args);

- // 設定ファイルから設定値を取得
- var config = new ConfigurationBuilder()
-     .AddJsonFile("appsettings.json")
-     .Build();
-
  // Add services to the container.
  builder.Services.AddControllersWithViews();
- builder.Services.AddDbContext<GourmetDbContext>(options => {
-     options.UseSqlServer(config.GetConnectionString(nameof(GourmetDbContext)));
- });
+ builder.Services.AddDbContext<GourmetDbContext>();

  var app = builder.Build();

  (... 省略 ...)
```

## マイグレーション実行

1．スタートアッププロジェクト、既定のプロジェクトを両方とも「Database」プロジェクトに設定します。

![image4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2779418/3b856291-a12c-f92e-f4d0-41aee69b3cae.png)

2．`Add-Migration`コマンドを入力してマイグレーションを実行

```
Add-Migration {任意の名前}
```
![image5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2779418/5f01fc0b-d82f-1b70-b462-2b2252e3a22b.png)

3．`Update-Database`コマンドを入力してマイグレーションファイルの内容をDBに反映させる。

```
Update-Database
```

![image6.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2779418/92e7308e-3335-c95b-06c8-a7b114fea3c0.png)