EF7 / .NETコアで複数のデータベースに対してDbContext継承を実装する方法

.net-core asp.net-core entity-framework entity-framework-core

質問

私はASP.NET Core 1.1でWeb APIを構築しています。

私は構成、ユーザー、グループ(約25テーブル)などの構成項目のための共通の基本スキーマを持つ数多くの異なるデータベース(異なるシステム用)を持っています。私は、図に示すように、基本クラスから継承して、モデルの共有部分の非常に広範なEF構成を複製することを避けようとしています。

私のDbContext継承ツリー

ただし、 DbContextOptions<DerivedRepository>をコンストラクターにパラメーターとして渡すEntity Framework(EF)要件のため、これは機能しません。コンストラクターが呼び出されるリポジトリの型にDerivedRepositoryが一致する必要があります。このパラメータは、 :base(param)呼び出すことによってベースのDbContext渡されなければなりません。

したがって、InvestContextがDbContextOptions<InvestContext>で初期化されたDbContextOptions<InvestContext>ConfigurationContextコンストラクタへの呼び出しが必須ではなくDbContextOptions<InvestContext>型のパラメータを受け取っているため、 base(DbContextOptions<InvestContext>)を呼び出し、EFがエラーをスローします。 DbContextOptions<ConfigurationContext>ます。 DbContextのoptionsフィールドは次のように定義されているため

    private readonly DbContextOptions _options;

私はこれを回避する方法を見ることができません。

共有モデルを一度定義して複数回使用する最善の方法は何ですか?私はヘルパー関数を作成し、それが派生したすべてのコンテキストから呼び出すことができると思いますが、継承と同じくらいきれいではありません。

受け入れられた回答

これで、上の例のようなInvestContextを使用して、継承階層を使用する方法でこれを動作させました。

前述のように、InvestContextクラスはDbContextOptions<InvestContext>型のコンストラクタパラメータを受け取りますが、 DbContextOptions<ConfigurationContext>をそのベースに渡す必要があります。

私は、 DbContextOptions変数からconnectionstringを掘り起こし、必要な型のDbContextOptionsインスタンスを構築するメソッドを作成しました。 InvestContextは、このメソッドを使用して、base()を呼び出す前にoptionsパラメータを正しい型に変換します。

変換方法は次のようになります。

    protected static DbContextOptions<T> ChangeOptionsType<T>(DbContextOptions options) where T:DbContext
    {
        var sqlExt = options.Extensions.FirstOrDefault(e => e is SqlServerOptionsExtension);

        if (sqlExt == null)
            throw (new Exception("Failed to retrieve SQL connection string for base Context"));

        return new DbContextOptionsBuilder<T>()
                    .UseSqlServer(((SqlServerOptionsExtension)sqlExt).ConnectionString)
                    .Options;
    }

InvestContextのコンストラクタ呼び出しは次のように変更されます。

  public InvestContext(DbContextOptions<InvestContext> options):base(options)

これに:

  public InvestContext(DbContextOptions<InvestContext> options):base(ChangeOptionsType<ConfigurationContext>(options))

これまでのところ、InvestContextとConfigurationContextの両方が簡単なクエリで動作しますが、ハックのように思えますし、EF7の設計者が心に留めていたものではないかもしれません。

私はまだ複雑なクエリ、更新などを試みるとEFが結び目になると懸念していますが、これは問題ではないようです(下記参照)

編集:私はここでEF7チームの問題としてこの問題を記録しました。チームメンバーはEFコアコアの変更を次のように提案しました。

"TContextを現在のコンテキストタイプから派生した型にするためのチェックを更新する必要があります"

これは問題を解決するだろう。

そのチームメンバー(これについては問題がわかります)とのやりとりやEFコアコードの掘り下げが行われた後、上記で概説したアプローチは安全であり、提案された変更が実装されるまでは最良のアプローチです。


人気のある回答

要件に応じて、DbContextOptionsの非型固有バージョンを使用できます。

これらを変更してください:

public ConfigurationContext(DbContextOptions<ConfigurationContext> options):base(options)    
public InvestContext(DbContextOptions<InvestContext> options):base(options)

これに:

public ConfigurationContext(DbContextOptions options):base(options) 
public InvestContext(DbContextOptions options):base(options)

次に、ConfigurationContextを最初に作成した場合、それを継承するクラスは同じ構成を持つように見えます。また、異なるコンテキストを初期化する順序によっても異なる場合があります。

編集:私の動作例:

public class QueryContext : DbContext
{
    public QueryContext(DbContextOptions options): base(options)
    {
    }
}

public class CommandContext : QueryContext
{
    public CommandContext(DbContextOptions options): base(options)
    {
    }
}

そしてStartup.cs

services.AddDbContext<CommandContext>(options =>
                 options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));

services.AddDbContext<QueryContext>(options =>
                options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));

あるいは、テストクラスでは:

    var connectionString = "Data Source=MyDatabase;Initial Catalog=MyData;Integrated Security=SSPI;";

    var serviceProvider = new ServiceCollection()
        .AddDbContext<QueryContext>(options => options.UseSqlServer(connectionString))
        .BuildServiceProvider();

    _db = serviceProvider.GetService<QueryContext>();


Related

ライセンスを受けた: CC-BY-SA with attribution
所属していない Stack Overflow
このKBは合法ですか? はい、理由を学ぶ
ライセンスを受けた: CC-BY-SA with attribution
所属していない Stack Overflow
このKBは合法ですか? はい、理由を学ぶ