实体框架核心2.1-多个提供程序

时间:2018-09-17 09:54:18

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

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

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

多个提供商的简便方法?

3 个答案:

答案 0 :(得分:5)

仅具有一个上下文的解决方案(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"); }
}

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

添加迁移InitialCreate

仅编辑“添加迁移”的每个输出并添加驱动程序特定的属性

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

        // ....
    }
}

现在可以更新数据库了

答案 1 :(得分:1)

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

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

Database.SqlServer.csproj <DefineConstants>SQLSERVER</DefineConstants>

Database.PostgreSql.csproj <DefineConstants>POSTGRESQL</DefineConstants>

如果要从VS中添加迁移,请打开Package Manager控制台,然后将迁移项目选择为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中指定派生类类型。提到的另一种方法需要人工避免。

答案 2 :(得分:0)

您可能要考虑使用AdaptiveClient之类的实用程序。 AdaptiveClient允许您使用服务的多个提供程序特定实现(MSSQL,MySQL,SQLite等)创建单个DbContext。 AdaptiveClient根据使用中的连接字符串注入正确的实现。

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

另请参阅:

AdaptiveClient.EntityFrameworkCore

Demo Application

AdaptiveClient可作为nuget package使用。

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

相关问题