如何在EF7/.NET Core中为多个数据库实现DbContext继承

Pet*_*ter 8 entity-framework entity-framework-core .net-core asp.net-core

我正在ASP.NET Core 1.1中构建Web API.

我有许多不同的数据库(针对不同的系统),这些数据库具有配置项,例如配置,用户和组(总共约25个表)的公共基本模式.我试图通过继承基类来避免为模型的共享部分复制相当广泛的EF配置,如图所示.

我的DbContext继承树

但是,这不起作用,因为实体框架(EF)要求DbContextOptions<DerivedRepository>作为参数传递给构造函数,其中DerivedRepository必须匹配调用构造函数的存储库的类型.然后必须通过DbContext调用将参数传递给基数:base(param).

因此,当(例如)InvestContext初始化时DbContextOptions<InvestContext>,它调用base(DbContextOptions<InvestContext>)并且EF抛出错误,因为对ConfigurationContext构造函数的调用正在接收类型的参数DbContextOptions<InvestContext>而不是所需的类型DbContextOptions<ConfigurationContext>.由于DbContext上的选项字段定义为

    private readonly DbContextOptions _options;
Run Code Online (Sandbox Code Playgroud)

我无法看到解决这个问题的方法.

定义共享模型一次并多次使用它的最佳方法是什么?我想我可以创建一个辅助函数并从每个派生的上下文中调用它,但它不像继承那样干净或透明.

Mar*_*ark 45

我想把这篇来自 OP 的 GitHub issue 的帖子引起大家的注意:

通过提供一个使用 DbContextOptions 而没有任何类型的受保护构造函数,我能够在没有黑客的情况下解决这个问题。保护第二个构造函数确保它不会被 DI 使用。

public class MainDbContext : DbContext {
    public MainDbContext(DbContextOptions<MainDbContext> options)
        : base(options) {
    }

    protected MainDbContext(DbContextOptions options)
        : base(options) {
    }
}

public class SubDbContext : MainDbContext {
    public SubDbContext (DbContextOptions<SubDbContext> options)
        : base(options) {
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 这应该是公认的答案。看起来这将作为本例中的建议模式添加到官方文档中。https://github.com/aspnet/EntityFramework.Docs/issues/594 (3认同)
  • 经过几个小时的调试和尝试不同的事情后,这救了我。奇迹般有效 (2认同)

Pet*_*ter 16

好的,我的工作方式仍然使用继承层次结构,如下所示(以InvestContext上面的例子为例):

如上所述,InvestContext类接收类型的构造函数参数DbContextOptions<InvestContext>,但必须传递DbContextOptions<ConfigurationContext>给它的基础.

我编写了一个方法,用于从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;
    }
Run Code Online (Sandbox Code Playgroud)

和InvestContext构造函数调用此更改:

  public InvestContext(DbContextOptions<InvestContext> options):base(options)
Run Code Online (Sandbox Code Playgroud)

对此:

  public InvestContext(DbContextOptions<InvestContext> options):base(ChangeOptionsType<ConfigurationContext>(options))
Run Code Online (Sandbox Code Playgroud)

到目前为止,InvestContext和ConfigurationContext都适用于简单的查询,但它似乎有点像黑客,可能不是EF7的设计者所想到的.

当我尝试复杂的查询,更新等时,我仍然担心EF会陷入困境.看来这不是问题,见下文)

编辑:我已经记录了此问题,因为与EF7队的问题在这里,和团队成员曾建议更改了EF核心的核心如下:

"我们应该更新检查以允许TContext成为从当前上下文类型派生的类型"

这样可以解决问题.

在与该团队成员(您可以在问题上看到)进行进一步交互并进行一些挖掘EF Core代码之后,我上面概述的方法看起来是安全的,并且是实施建议更改之前的最佳方法.