はじめに
テストや環境を汚さないために In-Memory データベースを使用する際に初期データを投入する方法を調べました。
データベースコンテキストのOnModelCreatingメソッドにフックすることで初期化します。OnModelCreatingはデータベースコンテキストが初期化される際に1度だけ呼ばれます。
HasDataメソッドの初期化は、Entity Framework CoreによるIDの自動設定 が行われません。
そのため、自分でIDを取得する必要があります。
publicDbSet<Book>Books{get;set;}protectedoverridevoidOnModelCreating(ModelBuildermodelBuilder)=>modelBuilder.Entity<Book>().HasData(newBook{ID=1,Name="アンドロイドは電気羊の夢を見るか?"},newBook{ID=2,Name="幼年期の終り"},newBook{ID=3,Name="一九八四年"});サンプルアプリ In-Memory データベース を使った Web API
プロジェクトの雛形の作成
# テンプレートには Web API を選択$ dotnet new webapi -n SeedingInMemoryDb
$ cd SeedingInMemoryDb
# In-Memory データベースを使うためにライブラリをインストール$ dotnet add Package Microsoft.EntityFrameworkCore.InMemory
モデルとデータベースコンテキスト
Models/Books.cs
publicclassBook{publicintID{get;set;}publicstringName{get;set;}}// using Microsoft.EntityFrameworkCore;// using SeedingInMemoryDb.Models;publicclassSampleInMemoryDbContext:DbContext{publicSampleInMemoryDbContext(DbContextOptionsoptions):base(options){}publicDbSet<Book>Books{get;set;}protectedoverridevoidOnModelCreating(ModelBuildermodelBuilder)=>modelBuilder.Entity<Book>().HasData(newBook{ID=1,Name="アンドロイドは電気羊の夢を見るか?"},newBook{ID=2,Name="幼年期の終り"},newBook{ID=3,Name="一九八四年"});}Startupクラス
In-Memory データベースを使う設定をします。
いつも通りAddDbContextする際にオプションからUseInMemoryDatabaseメソッドを呼びます。UseInMemoryDatabaseメソッドの引数のデータベース名は必須みたいです。
Startup.cs
// using Microsoft.EntityFrameworkCore;// using Microsoft.Extensions.DependencyInjection;publicvoidConfigureServices(IServiceCollectionservices){services.AddDbContext<SampleInMemoryDbContext>(options=>options.UseInMemoryDatabase("sample_in_memory_db"););services.AddControllers();}エントリーポイント
In-Memory データベースを構築し初期データを投入します。
Program.csではまだ DI コンテナが設定前であるため、データベースコンテキストを DI できません。
サービスプロバイダーからインスタンスを取得しEnsureCreatedAsyncメソッドを呼んで DB を作成します。
Program.cs
// using Microsoft.AspNetCore.Hosting;// using Microsoft.Extensions.DependencyInjection;// using Microsoft.Extensions.Hosting;// using System.Threading.Tasks;publicclassProgram{publicstaticasyncTaskMain(string[]args){IHosthost=BuildHost(args);usingIServiceScopescope=host.Services.CreateScope();IServiceProviderprovider=scope.ServiceProvider;usingvarcontext=provider.GetRequiredService<SampleInMemoryDbContext>();awaitcontext.Database.EnsureCreatedAsync();host.Run();}publicstaticIHostBuildHost(string[]args)=>Host.CreateDefaultBuilder(args).ConfigureWebHostDefaults(web=>web.UseStartup<Startup>().Build();}コントローラークラス
In-Memory データベース内の書籍を全件返すエンドポイント/api/booksを作ってみます。
Controllers/BooksController.cs
// using Microsoft.AspNetCore.Mvc;// using Microsoft.EntityFrameworkCore;// using SeedingInMemoryDb.Models;// using System.Collections.Generic;// using System.Threading.Tasks;[Route("api/[controller]")]publicclassBooksController:Controller{privatereadonlySampleInMemoryDbContext_db;publicBooksController(SampleInMemoryDbContextdb)=>_db=db;[HttpGet]publicasyncTask<ActionResult<IEnumerable<Book>>>Get()=>await_db.Books.ToListAsync();}実行結果
実行しlocalhost:{ポート番号}/api/booksにアクセスします。
In-Memory データベースに初期データが投入され、そのデータを全件取得できることが確認できました。
実行環境は以下の通りです。
- Windows X64
- Visual Studio 2019 (v16.4)
- .NET Core SDK 3.1.101
- C# 8.0