Entity Framework 核心 2.1 - 多个提供商
Entity Framework Core 2.1 - Multiple Providers
与多个提供商合作的正确方式是什么?
我的例子:
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; }
}
多个提供商的简单方法?
只有一个上下文的解决方案(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
仅编辑 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; }
// ....
}
}
现在准备更新数据库
您可能需要考虑 AdaptiveClient 这样的实用程序。 AdaptiveClient 允许您使用服务的多个特定于提供者的实现(MSSQL、MySQL、SQLite 等)创建单个 DbContext。 AdaptiveClient 根据正在使用的连接字符串注入正确的实现。
AdaptiveClient 还允许您注入特定于传输的服务实现。例如,许多应用程序 运行 在本地(与数据库服务器相同的 LAN)和远程(使用 WCF 或 REST)。当 运行 在本地 AdaptiveClient 将注入一个直接与数据库对话的服务实现。这提供了大约 10 倍的性能提升。当 运行ning 远程 AdaptiveClient 注入 WCF 或 REST 实现时。
另请参阅:
AdaptiveClient.EntityFrameworkCore
AdaptiveClient 可用作 nuget package。
免责声明:我是 AdaptiveClient 的作者。
Seimann 的回答很好,但我发现处理迁移很痛苦。我想要很少或根本不需要手动工作来让它工作。我发现最简单的方法是为每个提供程序创建一个单独的程序集并添加 IDesignTimeDbContextFactory
.
的实现
另一种解决方案是创建设计时程序集,但 select 事实证明要使用哪个提供程序进行迁移很困难,至少在实现此功能之前 here。我尝试了在执行迁移之前设置环境变量的建议方法,但我发现使用编译器常量 select 正确的提供者更容易。
我通过创建一个供所有提供者使用的共享项目来组织这个。这是一个提取主要项目配置设置的示例实现。 class 将支持上述两种方法,因此可以根据您的需要进行简化。
#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 中添加迁移时,打开程序包管理器控制台并select 迁移项目作为 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 所述,还有另一种建议的方法来完成此操作,但我发现创建派生 classes 意味着迁移将仅适用于派生 class 的实例,而不适用于基地class。因此,您需要在 AddDbContext 中指定派生的 class 类型。提到的另一种方法需要我想避免的手动工作。
与多个提供商合作的正确方式是什么? 我的例子:
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; }
}
多个提供商的简单方法?
只有一个上下文的解决方案(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
仅编辑 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; }
// ....
}
}
现在准备更新数据库
您可能需要考虑 AdaptiveClient 这样的实用程序。 AdaptiveClient 允许您使用服务的多个特定于提供者的实现(MSSQL、MySQL、SQLite 等)创建单个 DbContext。 AdaptiveClient 根据正在使用的连接字符串注入正确的实现。
AdaptiveClient 还允许您注入特定于传输的服务实现。例如,许多应用程序 运行 在本地(与数据库服务器相同的 LAN)和远程(使用 WCF 或 REST)。当 运行 在本地 AdaptiveClient 将注入一个直接与数据库对话的服务实现。这提供了大约 10 倍的性能提升。当 运行ning 远程 AdaptiveClient 注入 WCF 或 REST 实现时。
另请参阅:
AdaptiveClient.EntityFrameworkCore
AdaptiveClient 可用作 nuget package。
免责声明:我是 AdaptiveClient 的作者。
Seimann 的回答很好,但我发现处理迁移很痛苦。我想要很少或根本不需要手动工作来让它工作。我发现最简单的方法是为每个提供程序创建一个单独的程序集并添加 IDesignTimeDbContextFactory
.
另一种解决方案是创建设计时程序集,但 select 事实证明要使用哪个提供程序进行迁移很困难,至少在实现此功能之前 here。我尝试了在执行迁移之前设置环境变量的建议方法,但我发现使用编译器常量 select 正确的提供者更容易。
我通过创建一个供所有提供者使用的共享项目来组织这个。这是一个提取主要项目配置设置的示例实现。 class 将支持上述两种方法,因此可以根据您的需要进行简化。
#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 中添加迁移时,打开程序包管理器控制台并select 迁移项目作为 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 所述,还有另一种建议的方法来完成此操作,但我发现创建派生 classes 意味着迁移将仅适用于派生 class 的实例,而不适用于基地class。因此,您需要在 AddDbContext 中指定派生的 class 类型。提到的另一种方法需要我想避免的手动工作。