1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

EFCoreのContextクラスコンストラクタについて

Posted at

はじめに

EFCoreのContextクラスコンストラクタは3種類あります。

  • DribedDbContext(DbContextOptions<DribedDbContext> options)
  • DribedDbContext()
  • 独自実装のDribedDbContext(string connectionString)など上記以外

どれを使うべきか謎になったのでメモ。
MySQL前提で書きましたが、他のRDBでも同じです。

前提条件

ASP.NET Core、その他ネイティブアプリどちらか一方でEFCoreを使う場合はあまり関係無い記事です。
場合によってはASP.NET Coreとそれ以外のアプリで同じコードのEFCoreを同時に使いたい時もあるでしょう。
こんなソリューション内容になるはずです。

  • ASP.NET Coreプロジェクト
  • コンソールアプリなどのプロジェクト
  • EFCore関係のクラスプロジェクト

これでASP.NET Coreプロジェクトとその他アプリのプロジェクト、双方からEFCoreのコードを使用できますが・・・。

いきさつ

DBファーストでスキャフォールディングすると、2個のコンストラクタ、オーバーライドのかけらが自動的に作られます。
コードファーストのサンプルコードでも同様の内容で書かれているはずです。

BookDbContext.cs
public partial class BookDbContext : DbContext
{
    public BookDbContext()
    {
    }

    public BookDbContext(DbContextOptions<BookDbContext> options)
       : base(options)
    {
    }
    
    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
        if (!optionsBuilder.IsConfigured)
        {
            optionsBuilder.UseMySql("スキャフォールディング時の接続文字列");
        }
    }
    //以降テーブル部分省略

何も考えないと接続文字列を引数にしたコンストラクタを作りたくなるでしょう。

BookDbContext.cs
//一応動くけど定石ではない
public partial class BookDbContext : DbContext
{
    private readonly string _connectionString;
    
    public BookDbContext(string connectionString)
    {
        _connectionString = connectionString;
    }
    
    public BookDbContext(DbContextOptions<BookContext> options)
       : base(options)
    {
    }
    
    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
        if (!optionsBuilder.IsConfigured)
        {
            optionsBuilder.UseMySql(_connectionString, 
                ServerVersion.AutoDetect(_connectionString));
        }
    }
    //以降テーブル部分省略

問題

ASP.NET Coreとその他アプリで使いたいコンストラクタが異なります。
コンストラクタの選択は下記となります。

  • ASP.NET CoreはBookDbContext(DbContextOptions<BookDbContext> options)
  • 他のアプリはBookDbContext(string connectionString)

後で実コードを出しますが、ASP.NET Coreは依存性注入(DI)サービスの一つIServiceCollection.AddDbContextFactory<T>()が推奨されており、他のアプリは普通にコンストラクタを使用します。

しかしBookDbContext(string connectionString)があるとASP.NET Coreで例外が発生します。

InvalidOperationException: Multiple constructors accepting all given argument types have been found in type 'Shared.Database.Model.BookContext'. There should only be one applicable constructor.
Microsoft.Extensions.DependencyInjection.ActivatorUtilities.TryFindMatchingConstructor(Type instanceType, Type[] argumentTypes, ref ConstructorInfo matchingConstructor, ref Nullable<int>[] parameterMap)

DribedDbContext(string connectionString)を使えない事になりますが、どうなってしまうのでしょう。

結論

  • DribedDbContext(DbContextOptions<DribedDbContext> options)が正解
  • DribedDbContext(string connectionString)は使えない
  • DribedDbContext()はあっても無くてもよい
  • OnConfiguringもあっても無くてもよい
  • コンストラクタ使用時はDbContextOptionsBuilder.Optionプロパティを使う

どうする

実際のコードを見てみましょう。

EFCoreのクラスプロジェクト

これだけでOKです。

BookDbContext.cs
public partial class BookDbContext : DbContext
{
    public BookDbContext(DbContextOptions<BookContext> options)
       : base(options)
    {
    }
    //以降テーブル部分省略

ASP.NET Core

こちらは公式の推奨通りIServiceCollection.AddDbContextFactory<T>()を使います。

Program.cs
var builder = WebApplication.CreateBuilder(args);
//中略
connectionString = builder.Configuration.GetConnectionString("Book") ?? string.Empty;
builder.Services.AddDbContextFactory<Shared.Database.Model.BookDbContext>(options =>
    options.UseMySql(connectionString, ServerVersion.AutoDetect(connectionString)));

ASP.NET Core の依存関係の挿入における DbContext
Razorコンポーネントのコードは上記リンクを読んでください。

ネイティブアプリ

ネイティブアプリにファクトリサービスなんてありません。
普通にコンストラクタを使いたいのですが、使い方について公式の見解が見当たりません。
これでいいようです。

private static BookDbContext GetDbContext()
{
    var builder = new DbContextOptionsBuilder<Shared.Database.BookDbContext>();
    var connectionString = "めんどいから自分で作って";
    builder.UseMySql(connectionString, ServerVersion.AutoDetect(connectionString));
    return new BookDbContext(builder.Options);
}

private void Tekitou()
{
    using (var context = GetDbContext())
    {
        //context.Database.EnsureCreated();とか
    }
}

おわりに

コンテキストのファクトリメソッドをサービスクラスとする手もありますが定石かは知らん。

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?