C# 实体框架核心中的数据库独立性

C# 实体框架核心中的数据库独立性,c#,entity-framework-core,entity-framework-core-2.2,C#,Entity Framework Core,Entity Framework Core 2.2,我有一个使用postgres的简单WPF/EF Core 2.2.4应用程序。 我正在分析在SQL Server上迁移它的可能策略 在非ORM应用程序中,通过提供一种动态加载数据库驱动程序的方法(考虑JDBC模型),将特定于数据库的对连接字符串的引用限制在一起是很常见的。在该模型中,您必须解决编写跨数据库工作的SQL的问题 在这里,SQL编写的问题,实际上是主要的问题,从一开始就得到了解决。因此,我发现我们以助手方法的形式重新引入数据库依赖性是非常矛盾的 我的第一个问题是关于DbContext的

我有一个使用postgres的简单WPF/EF Core 2.2.4应用程序。
我正在分析在SQL Server上迁移它的可能策略

在非ORM应用程序中,通过提供一种动态加载数据库驱动程序的方法(考虑JDBC模型),将特定于数据库的对连接字符串的引用限制在一起是很常见的。在该模型中,您必须解决编写跨数据库工作的SQL的问题

在这里,SQL编写的问题,实际上是主要的问题,从一开始就得到了解决。因此,我发现我们以助手方法的形式重新引入数据库依赖性是非常矛盾的

我的第一个问题是关于DbContext的。OnConfigurang接收用于传递连接字符串的DbContextOptionsBuilder。 但是为了传递连接字符串,需要使用数据库提供程序作为扩展方法提供的特定于数据库的方法。 这是下面示例中的
选项builder.UseNpgsql(connstr)
。 我应该如何在独立于数据库的应用程序中解决这个问题

class MyDbContext: DbContext
{

    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {

    string connstr = 
            ConfigurationManager
            .ConnectionStrings["MYAPP_PROD"].ConnectionString;

    optionsBuilder.UseNpgsql(connstr);

    }

}
第二个问题是:如何以动态方式加载整个数据库包,以便能够对其进行配置,而不是对其进行编码? 实际上,我使用NuGet获取包:

Npgsql.EntityFrameworkCore.PostgreSQL
说我想使用:

Microsoft.EntityFrameworkCore.SqlServer

如何做到这一点?

使用策略模式根据外部配置注册相关数据库提供程序

interface IDbProvider {
    bool AppliesTo(string providerName);
    DbContextOptions<T> LoadProvider<T>();
}
接口提供程序{
bool AppliesTo(字符串提供程序名);
DbContextOptions加载提供程序();
}
公共类PostgresSqlProvider:IDbProvider{
公共bool AppliesTo(字符串提供程序名){
返回providerName.Equals(“Postgres”);
}
公共DbContextOptions加载提供程序(){
//加载提供程序。
}
}
var providers=new[]{
新的PostgresSqlProvider()
};
var selectedDbProvider=“”//从用户输入/配置加载
var selectedProvider=providers.SingleOrDefault(x=>x.AppliesTo(selectedDbProvider));
if(selectedProvider==null){
抛出新的NotSupportedException($“不支持数据库提供程序{selectedDbProvider}”);
}
var options=selectedProvider.LoadProvider();

使用策略模式根据外部配置注册相关数据库提供程序

interface IDbProvider {
    bool AppliesTo(string providerName);
    DbContextOptions<T> LoadProvider<T>();
}
接口提供程序{
bool AppliesTo(字符串提供程序名);
DbContextOptions加载提供程序();
}
公共类PostgresSqlProvider:IDbProvider{
公共bool AppliesTo(字符串提供程序名){
返回providerName.Equals(“Postgres”);
}
公共DbContextOptions加载提供程序(){
//加载提供程序。
}
}
var providers=new[]{
新的PostgresSqlProvider()
};
var selectedDbProvider=“”//从用户输入/配置加载
var selectedProvider=providers.SingleOrDefault(x=>x.AppliesTo(selectedDbProvider));
if(selectedProvider==null){
抛出新的NotSupportedException($“不支持数据库提供程序{selectedDbProvider}”);
}
var options=selectedProvider.LoadProvider();

EF Core已经涵盖了该场景。应使用接受生成器操作的任何方法在中配置提供程序

在最简单的情况下(最脏?),您可以根据来自配置系统本身的标志或值选择提供程序,例如:

public void ConfigureServices(IServiceCollection services)
{
    services.AddRazorPages();

    var connString=Configuration.GetConnectionString("SchoolContext");  
    var useSqlServer=Configuration.GetSection("MyDbConfig").GetValue<bool>("UseSqlServer");
    services.AddDbContext<SchoolContext>(options =>{
        if (useSqlServer)
        {
            options.UseSqlServer(connString);
        }
        else 
        {
            options.UseNpgsql(connString);
        }
    });
}
并使用:

var connString=Configuration.GetConnectionString("SchoolContext");  
var provider=Configuration.GetSection("MyDbConfig").GetValue<ProviderEnum>("Provider");
services.ConfigureContexts(provider,connString);
var connString=Configuration.GetConnectionString(“学校上下文”);
var provider=Configuration.GetSection(“MyDbConfig”).GetValue(“provider”);
services.ConfigureContexts(提供程序、连接字符串);
建筑工人拣选工

构建器、配置模式允许许多可以处理复杂场景的变体。例如,我们可以提前选择一个生成器方法:

var efBuilder= SelectBuilder(provider,connString);
services.AddDbContext<SchoolContext>(efBuilder);

...

Action<DbContextOptionsBuilder> SelectBuilder(ProviderEnum provider,string connString)
{
    switch (provider)
    {
        case ProviderEnum.SqlServer:
           return ConfigureSql;
        case ProviderEnum.Postgres :
           return ConfigurePostgres;
    }

    void ConfigureSqlServer(DbContextOptionsBuilder options)
    {
        options.UseSqlServer(connString);
    }

    void ConfigurePostgres(DbContextOptionsBuilder options)
    {
        options.UseNpgSql(connString);
    }
}
var efBuilder=SelectBuilder(provider,connString);
AddDbContext(efBuilder);
...
操作SelectBuilder(ProviderEnum提供程序,字符串连接字符串)
{
交换机(提供程序)
{
案例ProviderEnum.SqlServer:
返回ConfigureSql;
案例提供者编号。Postgres:
返回配置Postgres;
}
无效配置SQLServer(DbContextOptionsBuilder选项)
{
options.UseSqlServer(connString);
}
无效配置Postgres(DbContextOptionsBuilder选项)
{
options.UseNpgSql(connString);
}
}
在C#8中,这可以简化为:

Action<DbContextOptionsBuilder> SelectBuilder(ProviderEnum provider,string connString)
{
    return provider switch (provider) {
        ProviderEnum.SqlServer => ConfigureSql,
        ProviderEnum.Postgres => ConfigurePostgres
    };

    void ConfigureSqlServer(DbContextOptionsBuilder options)
    {
        options.UseSqlServer(connString);
    }

    void ConfigurePostgres(DbContextOptionsBuilder options)
    {
        options.UseNpgSql(connString);
    }
}
Action SelectBuilder(ProviderEnum提供程序,字符串连接字符串)
{
返回提供程序开关(提供程序){
ProviderEnum.SqlServer=>ConfigureSql,
ProviderEnum.Postgres=>ConfigurePostgres
};
无效配置SQLServer(DbContextOptionsBuilder选项)
{
options.UseSqlServer(connString);
}
无效配置Postgres(DbContextOptionsBuilder选项)
{
options.UseNpgSql(connString);
}
}
具体配置类

另一种可能是创建强类型配置类,并让它向生成器提供:

class MyDbConfig
{
    public ProviderEnum Provider {get;set;}
    ....
    public Action<DbContextOptionsBuilder> SelectBuilder(string connString)
    {
        return provider switch (provider) {
            ProviderEnum.SqlServer => ConfigureSql,
            ProviderEnum.Postgres => ConfigurePostgres
        };

        void ConfigureSqlServer(DbContextOptionsBuilder options)
        {
            options.UseSqlServer(connString);
        }

        void ConfigurePostgres(DbContextOptionsBuilder options)
        {
            options.UseNpgSql(connString);
        }
    }

}
类MyDbConfig
{
公共ProviderEnum提供程序{get;set;}
....
公共操作SelectBuilder(字符串连接字符串)
{
返回提供程序开关(提供程序){
ProviderEnum.SqlServer=>ConfigureSql,
ProviderEnum.Postgres=>ConfigurePostgres
};
无效配置SQLServer(DbContextOptionsBuilder选项)
{
options.UseSqlServer(connString);
}
无效配置Postgres(DbContextOptionsBuilder选项)
{
options.UseNpgSql(connString);
}
}
}
并使用它:

var dbConfig=Configuration.Get<MyDbConfig>("MyDbConfig");
var efBuilder=dbCongig.SelectBuilder(connString);
services.AddDbContext<SchoolContext>(efBuilder);
var dbConfig=Configuration.Get(“MyDbConfig”);
var efBuilder=dbCongig.SelectBuilder(connString);
AddDbContext(efBuilder);

EF Core已经涵盖了该场景。应使用接受生成器操作的任何方法在中配置提供程序

在最简单的情况下(最脏?),可以根据标志或值选择提供程序
Action<DbContextOptionsBuilder> SelectBuilder(ProviderEnum provider,string connString)
{
    return provider switch (provider) {
        ProviderEnum.SqlServer => ConfigureSql,
        ProviderEnum.Postgres => ConfigurePostgres
    };

    void ConfigureSqlServer(DbContextOptionsBuilder options)
    {
        options.UseSqlServer(connString);
    }

    void ConfigurePostgres(DbContextOptionsBuilder options)
    {
        options.UseNpgSql(connString);
    }
}
class MyDbConfig
{
    public ProviderEnum Provider {get;set;}
    ....
    public Action<DbContextOptionsBuilder> SelectBuilder(string connString)
    {
        return provider switch (provider) {
            ProviderEnum.SqlServer => ConfigureSql,
            ProviderEnum.Postgres => ConfigurePostgres
        };

        void ConfigureSqlServer(DbContextOptionsBuilder options)
        {
            options.UseSqlServer(connString);
        }

        void ConfigurePostgres(DbContextOptionsBuilder options)
        {
            options.UseNpgSql(connString);
        }
    }

}
var dbConfig=Configuration.Get<MyDbConfig>("MyDbConfig");
var efBuilder=dbCongig.SelectBuilder(connString);
services.AddDbContext<SchoolContext>(efBuilder);