независимость от базы данных в Entity Framework Core - PullRequest
0 голосов
/ 08 ноября 2019

У меня есть простое приложение WPF / EF Core 2.2.4, которое использует postgres.
Я анализирую возможные стратегии для его миграции на SQL Server.

В приложениях, отличных от ORM, довольно часто ограничивают привязку базы данных к единому соединению строки подключения, предоставляя способ динамической загрузки драйвера базы данных (на примере модели JDBC). В этой модели вы должны решить проблему написания SQL, который работает между базами данных.

Здесь проблема написания SQL, на самом деле основная, решается с самого начала. Поэтому я нахожу довольно парадоксальным, что мы снова вводим зависимость от базы данных в виде вспомогательных методов.

Мой первый вопрос о DbContext. OnConfiguring получает DbContextOptionsBuilder, который используется для передачи строки подключения. Но чтобы передать строку подключения, вы используете специфичный для базы данных метод, который предоставляется как метод расширения поставщиком базы данных. Это optionsBuilder.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

Как это можно сделать?

Ответы [ 2 ]

1 голос
/ 08 ноября 2019

Этот сценарий уже охватывается EF Core. Конфигурирование провайдеров должно быть выполнено в Startup.ConfigureServices , используя любой из AddDbContext методов, которые принимают действие компоновщика.

В самом простом случае (самом грязном?) Выможет выбирать провайдеров на основе флага или значения, полученного из самой системы конфигурации, например:

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 provider=Configuration.GetSection("MyDbConfig").GetValue<ProviderEnum>("Provider");
services.AddDbContext<SchoolContext>(options =>{
    switch (provider)
    {
        case ProviderEnum.SqlServer:
             options.UseSqlServer(connString);
             break;
        case ProviderEnum.Postgres :
             options.UseNpgsql(connString);
             break;
        ...
    }
});

Этот флаг также может быть получен из конфигурации, например, изкомандная строка, переменные среды и т. д.

Рефакторинг на .... лоты

Метод расширения

Этот кодможет быть извлечен в метод расширения в IServiceCollection, аналогично другим контекстам, например:

public static ConfigureContexts(this IServiceCollection services,string connString, string provider)
{
    services.AddDbContext<SchoolContext>(options =>{
        switch (provider)
        {
            case ProviderEnum.SqlServer:
                 options.UseSqlServer(connString);
                 break;
            case ProviderEnum.Postgres :
                 options.UseNpgsql(connString);
                 break;
            ...
        }
    });
}

и использован:

var connString=Configuration.GetConnectionString("SchoolContext");  
var provider=Configuration.GetSection("MyDbConfig").GetValue<ProviderEnum>("Provider");
services.ConfigureContexts(provider,connString);

Выбор сборщика

Конструктор, шаблоны конфигурации допускают множество вариантов, которые могут обрабатывать сложные сценарии. Например, мы можем заранее выбрать метод конструктора:

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);
    }
}

В 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);
    }
}

Конкретный класс конфигурации

Еще одна возможность - создать строго типизированный класс конфигурации и it предоставить сборщику:

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);
1 голос
/ 08 ноября 2019

Используйте шаблон стратегии для регистрации соответствующего поставщика базы данных на основе внешней конфигурации.

interface IDbProvider {
    bool AppliesTo(string providerName);
    DbContextOptions<T> LoadProvider<T>();
}
public class PostgresSqlProvider : IDbProvider {

    public bool AppliesTo(string providerName) {
        return providerName.Equals("Postgres");
    }

    public DbContextOptions<T> LoadProvider<T>() {
        //load provider.
    }
}
var providers = new [] {
    new PostgresSqlProvider()
};
var selectedDbProvider = ""; //Load from user input / config

var selectedProvider = providers.SingleOrDefault(x => x.AppliesTo(selectedDbProvider));
if(selectedProvider == null) {
    throw new NotSupportedException($"Database provider {selectedDbProvider} is not supported.");
}

var options = selectedProvider.LoadProvider<DbContext>();
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...