【问题标题】:Entity Framework Core 2.1 - Multiple ProvidersEntity Framework Core 2.1 - 多个提供者
【发布时间】:2018-09-17 09:54:17
【问题描述】:

与多个提供商合作的正确方法是什么? 我的例子:

appsettings.json

{
  "ConnectionStrings": {
    "Sqlite": "Data Source=database.db"
  }
}

Startup.cs

public void ConfigureServices(IServiceCollection services)
{
     services.AddDbContext<DatabaseContext>(options =>
                    options.UseSqlite(Configuration.GetConnectionString("Sqlite")));
}

DatabaseContext.cs

public class DatabaseContext : DbContext
{
    public DatabaseContext(DbContextOptions<DatabaseContext> options) : base(options) { }
    public DbSet<TestModel> TestModel{ get; set; }
}

多个提供商的简单方法?

【问题讨论】:

  • 它们代表相同的数据结构吗?那么您不需要需要多个 DbContext 实现。您只需要根据您要实现的提供者有条件地调用 .UseSqlite/.UseSqlServer/etc 注册,最好通过 appsettings 中的附加配置选项来解决.json
  • 我不会将Provider 设置添加到ConnectionStrings,因为ConnectionStrings 具有特殊含义:它具有configuration.GetConnectionString("MyConnection") (docs) 方法作为configuration["ConnectionStrings:MyConnection"] 的简写
  • 但是 Add-Migration 不允许我只使用一个 DbContext?
  • 没关系。迁移不应包含特定于提供者的信息(如数据类型)。即不要在您的迁移文件、模型和配置中使用VAR(255)NVAR(256)。通过这种方式,它创建了一个与提供程序无关的迁移文件,并在应用迁移时生成迁移脚本(SQL 等)。其他选项是将 DbContext 的(流利的)配置移到上下文本身之外。有一个 github 问题,午餐后需要检查
  • 即使用IEntityTypeConfiguration&lt;TModel&gt; 就像在anthonygiretti.com/2018/01/11/… 中一样,但这需要您为每个提供者复制所有流畅的配置。我强烈建议创建与任何提供者无关的实体和上下文配置(不要使用仅存在于特定数据库中的特殊类型,如 ENUM、SET、NVAR、ROWVERSION 等。

标签: c# asp.net-core ef-core-2.1


【解决方案1】:

只有一个上下文的解决方案(SQLite + MySQL + MSSQL + PostgreSQL(或其他)的示例):

appsettings.json

{
  // Add Provider and ConnectionStrings for your EFC drivers
  // Providers: SQLite, MySQL, MSSQL, PostgreSQL, or other provider...
  "Provider": "SQLite",
  "ConnectionStrings": {
    "SQLite": "Data Source=mydatabase.db",
    "MySQL": "server=localhost;port=3306;database=mydatabase;user=root;password=root",
    "MSSQL": "Server=(localdb)\\mssqllocaldb;Database=mydatabase;Trusted_Connection=True;MultipleActiveResultSets=true",
    "PostgreSQL": "Host=localhost;Database=mydatabase;Username=root;Password=root"
  }
}

单个DatabaseContext.cs

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

        // add Models...
}

Startup.cs

public void ConfigureServices(IServiceCollection services)
{
    // Check Provider and get ConnectionString
    if (Configuration["Provider"] == "SQLite")
    {
        services.AddDbContext<DatabaseContext>(options =>
            options.UseSqlite(Configuration.GetConnectionString("SQLite")));
    }
    else if (Configuration["Provider"] == "MySQL")
    {
        services.AddDbContext<DatabaseContext>(options =>
            options.UseMySql(Configuration.GetConnectionString("MySQL")));
    }
    else if (Configuration["Provider"] == "MSSQL")
    {
        services.AddDbContext<DatabaseContext>(options =>
            options.UseSqlServer(Configuration.GetConnectionString("MSSQL")));
    }
    else if (Configuration["Provider"] == "PostgreSQL")
    {
        services.AddDbContext<DatabaseContext>(options =>
            options.UseNpgsql(Configuration.GetConnectionString("PostgreSQL")));
    }
    // Exception
    else
    { throw new ArgumentException("Not a valid database type"); }
}

现在我们可以进行单一迁移

添加迁移初始创建

仅编辑 Add-Migration 的每个输出并添加驱动程序特定属性

    protected override void Up(MigrationBuilder migrationBuilder)
    {
        migrationBuilder.CreateTable(
        name: "Mytable",
        columns: table => new
        {
            Id = table.Column<int>(nullable: false)
            // Add for SQLite
            .Annotation("Sqlite:Autoincrement", true)
            // Add for MySQL
            .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn)
            // Add for MSSQL
            .Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn)
            // Add for PostgreSQL
            .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.SerialColumn),
            // Or other provider...
            Name = table.Column<string>(maxLength: 50, nullable: false),
            Text = table.Column<string>(maxLength: 100, nullable: true)
        },
        constraints: table =>
        {
            table.PrimaryKey("PK_Mytable", x => x.Id);
        });
    }

编辑: 或者您使用字符串 ID“DatabaseGenerated” 因此您不必编辑 migrationBuilder 并且添加迁移是多个提供者能够在没有“.Annotation”的情况下

示例模型:

using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;

namespace WebApplication.Models
{
    public class Mytable
    {
        // This generate a String ID
        // No ID modification needed for providers
        [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
        public string Id { get; set; }

        // ....
    }
}

现在可以更新数据库了

【讨论】:

  • 在使用带有[DatabaseGenerated(DatabaseGeneratedOption.Identity)] 的第二个示例时,我看不到如何为不同的提供者生成注释。
【解决方案2】:

您可能需要考虑像AdaptiveClient 这样的实用程序。 AdaptiveClient 允许您创建单个 DbContext,其中包含多个特定于提供者的服务实现(MSSQL、MySQL、SQLite 等)。 AdaptiveClient 根据正在使用的连接字符串注入正确的实现。

AdaptiveClient 还允许您注入特定于传输的服务实现。例如,许多应用程序在本地(与数据库服务器相同的 LAN)和远程(使用 WCF 或 REST)都运行。在本地运行时,AdaptiveClient 将注入一个直接与数据库对话的服务实现。这使性能提高了约 10 倍。远程运行时,AdaptiveClient 会注入 WCF 或 REST 实现。

另见:

AdaptiveClient.EntityFrameworkCore

Demo Application

AdaptiveClient 以nuget package 的形式提供。

免责声明:我是 AdaptiveClient 的作者。

【讨论】:

    【解决方案3】:

    Seimann 的回答很好,但我发现使用迁移很痛苦。我想要很少或不需要手动工作来让它工作。我发现最简单的方法是为每个提供程序创建一个单独的程序集并添加一个 IDesignTimeDbContextFactory 的实现。

    另一个解决方案是创建一个设计时程序集,但结果证明选择使用哪个提供程序进行迁移很困难,至少在实现此功能之前here。我尝试了在执行迁移之前设置环境变量的建议方法,但我发现使用编译器常量来选择正确的提供程序更容易。

    我通过创建一个供所有提供商使用的共享项目来组织这一点。这是一个提取您的主要项目配置设置的示例实现。该类将支持上述两种方法,因此可以根据您的需要进行简化。

    #if DEBUG
    using Microsoft.EntityFrameworkCore;
    using Microsoft.EntityFrameworkCore.Design;
    using Microsoft.Extensions.Configuration;
    using System;
    using System.IO;
    
    namespace Database.DesignTime
    {
        public class ApplicationDbContextDesignTimeFactory : IDesignTimeDbContextFactory<ApplicationDbContext>
        {
            public ApplicationDbContext CreateDbContext(string[] args)
            {
                var configuration = new ConfigurationBuilder()
                     .SetBasePath(Path.GetFullPath(@"..\MainProjectDirectory"))
                     .AddJsonFile("appsettings.json")
                     .AddJsonFile("appsettings.Development.json")
                     .Build();
    
                // Determine provider from environment variable or use compiler constants below
                var databaseProvider = Environment.GetEnvironmentVariable("DatabaseProvider");
    
    #if SQLSERVER
                databaseProvider = "SqlServer";
    #endif
    #if POSTGRESQL
                databaseProvider = "PostgreSql";
    #endif
    
                var connectionString = configuration.GetConnectionString($"{databaseProvider}Connection");
    
                var contextBuilder = new DbContextOptionsBuilder<ApplicationDbContext>();
    
                switch (databaseProvider)
                {
    #if SQLSERVER
                    case "SqlServer":
                        contextBuilder.UseSqlServer(connectionString, dbOptions =>
                        {
                            dbOptions.MigrationsAssembly("Database.SqlServer");
                        });
                        break;
    #endif
    
    #if POSTGRESQL
                    case "PostgreSql":
                        contextBuilder.UseNpgsql(connectionString, dbOptions =>
                        {
                            dbOptions.MigrationsAssembly("Database.PostgreSql");
                        });
                        break;
    #endif
    
                    default:
                        throw new NotSupportedException(databaseProvider);
                }
    
                return new ApplicationDbContext(contextBuilder.Options);
            }
        }
    }
    
    #endif
    

    然后在您的数据库迁移项目中为每个提供程序添加编译器常量。例如:

    数据库.SqlServer.csproj &lt;DefineConstants&gt;SQLSERVER&lt;/DefineConstants&gt;

    数据库.PostgreSql.csproj &lt;DefineConstants&gt;POSTGRESQL&lt;/DefineConstants&gt;

    如果您想从 VS 中添加迁移,请打开包管理器控制台并选择迁移项目作为 Default project。执行命令时,需要指定要使用的包含IDesignTimeDbContextFactory实现的项目。

    Add-Migration Initial -StartupProject "Database.SqlServer"

    现在您可以切换回主项目并正常使用它。仅供参考,这是我相关的 appsettings.json 和启动代码。

    {
      "DatabaseProvider": "SqlServer",
      "ConnectionStrings": {
        "SqlServerConnection": "Server=(localdb)\\mssqllocaldb;Database=DatabaseName;Trusted_Connection=True;MultipleActiveResultSets=true",
        "PostgreSqlConnection": "Host=host;Database=DatabaseName;User ID=Test;Password=secrectPass"  
    }
    
                services.AddDbContext<ApplicationDbContext>(options =>
                {
                    switch (Configuration["DatabaseProvider"])
                    {
                        case "SqlServer":
                            options.UseSqlServer(Configuration.GetConnectionString("SqlServerConnection"), dbOptions =>
                            {
                                dbOptions.MigrationsAssembly("Database.SqlServer");
                            });
                            break;
    
                        case "PostgreSql":
                            options.UseNpgsql(Configuration.GetConnectionString("PostgreSqlConnection"), dbOptions =>
                            {
                                dbOptions.MigrationsAssembly("Database.PostgreSql");
                            });
                            break;
                    }
                });
    

    正如here 所解释的,还有另一种建议的方法来完成此任务,但我发现创建派生类意味着迁移仅适用于派生类的实例,而不适用于基类。因此,您需要在 AddDbContext 中指定派生类类型。提到的另一种方法需要手动工作,我想避免。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2018-09-29
      • 2018-12-01
      • 2019-04-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-10-18
      相关资源
      最近更新 更多