如何配置EF Core连接上下文以解耦程序包依赖关系?

时间:2018-09-12 13:25:07

标签: c# .net-core entity-framework-core

我制作了一个使用Entity Framework Core处理持久性的库。 面向SQL Server数据库的Web API使用它。 此外,我针对SQLite数据库进行了测试(单元测试)。

我使用配置文件以简单的方式完成了该任务。因此,在扩展DBContext的类中,我使用以下内容实现了OnConfiguring:

if(isSqlServer) //don't mind where it comes from, it works fine
    optionsBuilder.UseSqlServer(connectionString);
else
    optionsBuilder.UseSqlite(connectionString);

它似乎工作正常,但效果不理想。假设我有这个项目:

  1. PersistenceLayer
  2. PersistenceLayerTests
  3. WebAPI

由于UseSqlite方法是Microsoft.EntityFrameworkCore.Sqlite包的类中的静态扩展方法,并且类似地,方法UseSqlServer是Microsoft.EntityFrameworkCore.SqlServer包的类中的静态扩展方法,有两件事很麻烦我。

首先,我必须在三个项目中包括两个依赖项,以避免在运行时出现错误“ System.IO.FileNotFoundException:无法加载文件或程序集Microsoft.EntityFrameworkCore.SqlServer(...)”。

第二,如果需要支持新的DBMS(Oracle,MySQL),则必须在这三个项目中包括新的依赖项。

我希望建立一个方案,其中项目PersistentLayer将不具有那些依赖关系,而PersistentLayerTests将仅依赖于Microsoft.EntityFrameworkCore.Sqlite和WebAPI将仅依赖于Microsoft.EntityFrameworkCore.SqlServer。

还有另一种方法来配置连接上下文以解耦这些程序包依赖性吗?

注意:

  1. 我使用NHibernate做到了这一点,后者为每个DBMS使用不同的驱动程序实现。
  2. 我尝试使用反射,但是并不优雅,我发现如果扩展方法具有不同的参数,它将无法正常工作。
  3. 我不需要完整的解决方案。如果有人可以用另一种方式指向我,那将非常有帮助。

2 个答案:

答案 0 :(得分:2)

您只需要让 DbContext 的用户(PersistenceLayerTests和WebApi项目)负责配置它,而不是 DbContext 本身。

DbContext provides a constructor接受 DbContextOptions 。您需要公开此构造函数:

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

    // ...
}

然后,您可以执行以下操作:

var optionsBuilder = new DbContextOptionsBuilder<MyDbContext>();

if (isSqlServer) //don't mind where it comes from, it works fine
    optionsBuilder.UseSqlServer(connectionString);
else
    optionsBuilder.UseSqlite(connectionString);

using (var context = new MyDbContext(optionsBuilder.Options))
{
    // ...
}

答案 1 :(得分:2)

这似乎有些矫kill过正,但是,在这种情况下,基本策略是放置一个公共界面,并将其分隔在不同的项目中。不确定这是否是这种情况的最佳解决方案。

然后,不同的项目是独立的(除了通用界面之外),并且随着时间的推移可以轻松扩展。

关键部分是在您的主项目中仅引用接口组件,而不引用其中之一。如果要避免使用FileNotFoundException,则必须在运行时加载混凝土(装配体):此外:您需要一种机制来拾取合适的(通常称为factory)。

让我们考虑一下其中有3种(sql,sqllite和oracle)的情况:

要做的第一件事是创建一个通用接口来与适当的Db enginge通信,例如:

//separate assembly
public interface IDbEningeSelector
{
    //added the option builder for simplicity: one could do better.
    void Configure(string connectionString,IOptionsBuilder optionsBuilder);
}

接下来,只需为具体的实现创建3个独立的项目,一个sql-lite,一个sql和一个oracle。

下一步:在三个项目中创建一个实现此目的的类:

//3 of these.
public class SqlEnging : IDbEngineSelector
{
    public void Configure(string connectionString,IOptionsBuilder optionsBuilder)
    {
         optionsBuilder.UseSqlServer(connectionString);
    }
}

好的,现在您可以选择其中之一。

//the "thing"
IDbEngineSelector selector = null;

selector = //resolve through factory, possibly based on a flag. 

selector.Configure(connectionString, optionsBuilder);

基本上,您已经完成了可扩展的,动态引用的数据库提供程序系统。

对于动态程序集解析器,您需要按照本文中的描述进行加载: https://www.codeproject.com/Articles/1194332/Resolving-Assemblies-in-NET-Core

这实际上将在运行时加载依赖项,为此,我建议保留一个特殊路径以放置这些程序集。

我省略了工厂实现的详细信息,我需要首先检查在.net Core中执行该操作的正确方法,等等...