如何在EF7 / .NET Core中為多個數據庫實現DbContext繼承

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

我在ASP.NET Core 1.1中構建Web API。

我有許多不同的數據庫(針對不同的系統),這些數據庫具有配置項,例如配置,用戶和組(總共約25個表)的公共基本模式。我試圖通過繼承基類來避免重複模型共享部分的相當廣泛的EF配置,如圖所示。

我的DbContext繼承樹

但是,這不起作用,因為實體框架(EF)要求將DbContextOptions<DerivedRepository>作為參數傳遞給構造函數,其中DerivedRepository必須與調用構造函數的存儲庫的類型匹配。然後必須通過調用:base(param)將參數傳遞給基本DbContext

因此,當(例如)InvestContext使用DbContextOptions<InvestContext>初始化時,它調用base(DbContextOptions<InvestContext>)並且EF拋出錯誤,因為對ConfigurationContext構造函數的調用正在接收類型為DbContextOptions<InvestContext>的參數而不是所需的參數鍵入DbContextOptions<ConfigurationContext> 。由於DbContext上的選項字段定義為

    private readonly DbContextOptions _options;

我無法看到解決這個問題的方法。

定義共享模型一次並多次使用它的最佳方法是什麼?我想我可以創建一個輔助函數並從每個派生的上下文中調用它,但它不像繼承那樣乾淨或透明。

一般承認的答案

好的,我的工作方式仍然使用繼承層次結構,就像這樣(使用上面的InvestContext作為示例):

如上所述,InvestContext類接收類型為DbContextOptions<InvestContext>的構造函數參數,但必須將DbContextOptions<ConfigurationContext>傳遞給它的基礎。

我編寫了一個方法,用於從DbContextOptions變量中挖掘連接DbContextOptions ,並構建所需類型的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 Core代碼之後,我上面概述的方法看起來是安全的,並且是實施建議更改之前的最佳方法。


熱門答案

根據您的要求,您可以簡單地使用非類型特定版本的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合法嗎? 是的,了解原因