具有多个数据库上下文的 .NET Core 应用不会在所有 DbContext 中生成表
.NET Core app with multiple database contextes does not generate tables in all DbContexts
我用 .NET core 3.1
制作了一个 OData 服务器。
在这个应用程序中,我有 2 个数据库上下文。其中之一用于应用程序本身 (AppDbContext),另一个用于帐户管理和身份验证 (AccountDbContext)。
我有一个 ServiceExtensions
这样的:
public static class ServiceExtensions
{
public static void ConfigureMySqlContext(this IServiceCollection services, IConfiguration configuration)
{
services.AddDbContext<AppDbContext>(options =>
{
options.UseMySql(configuration.GetConnectionString("MysqlConnection"),
mysqlOptions => {
mysqlOptions.ServerVersion(new ServerVersion(new Version(5, 4, 3), ServerType.MySql));
});
});
services.AddDbContext<AccountDbContext>(options =>
{
options.UseMySql(configuration.GetConnectionString("MysqlConnection"),
mysqlOptions => {
mysqlOptions.ServerVersion(new ServerVersion(new Version(5, 4, 3), ServerType.MySql));
});
});
}
}
在 startup.cs
中,我将按如下方式调用 ConfigureMySqlContext
函数:
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime.
// I use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.ConfigureMySqlContext(Configuration);
}
}
public static class Program
{
public static void Main(string[] args)
{
CreateWebHostBuilder(args)
.Build()
.CreateDbIfNotExists()
.Run();
}
private static IHost CreateDbIfNotExists(this IHost host)
{
using (var scope = host.Services.CreateScope())
{
var services = scope.ServiceProvider;
var accountDbContext = services.GetRequiredService<AccountDbContext>();
accountDbContext.Database.EnsureCreated();
var appDbContext = services.GetRequiredService<AppDbContext>();
appDbContext.Database.EnsureCreated();
}
return host;
}
}
此代码的目的是在数据库内的两个上下文中生成所需的表。
问题是,当我将这个应用程序发布到服务器时,当应用程序第一次 运行 时,它只会生成和启动 AppDbContext
中的表。为什么?因为放在AccountDbContext
.
之前
如果我按如下方式更改 ConfigureMySqlContext
函数并再次发布应用程序:
public static class ServiceExtensions
{
public static void ConfigureMySqlContext(this IServiceCollection services, IConfiguration configuration)
{
services.AddDbContext<AccountDbContext>(options =>
{
options.UseMySql(configuration.GetConnectionString("MysqlConnection"),
mysqlOptions => {
mysqlOptions.ServerVersion(new ServerVersion(new Version(5, 4, 3), ServerType.MySql));
});
});
services.AddDbContext<AppDbContext>(options =>
{
options.UseMySql(configuration.GetConnectionString("MysqlConnection"),
mysqlOptions => {
mysqlOptions.ServerVersion(new ServerVersion(new Version(5, 4, 3), ServerType.MySql));
});
});
}
}
然后就是AccountDbContext
里面的表,会在第一个运行之后在数据库中启动。
问题在于,EnsureCreated
的核心是语义非常简单,可以建模为:
if (!database.exists()) {
database.create();
}
其中 exists()
检查数据库是否存在,而不检查数据库中的表。
它的最终结果是,如果数据库没有完全清空,EnsureCreated()
将没有任何效果。
有两种方法可以解决这个问题:
- 将数据库一分为二。这是EntityFramework最喜欢的解决方案。
但是,如果不行,请查看:
- 查看
IRelationalDatabaseCreator.CreateTables()
方法:
这是一个可能的解决方案:
using Microsoft.EntityFrameworkCore.Storage;
using Microsoft.EntityFrameworkCore.Infrastructure;
public static class Program
{
public static void Main(string[] args)
{
CreateWebHostBuilder(args)
.Build()
.CreateDbIfNotExists()
.Run();
}
private static IHost CreateDbIfNotExists(this IHost host)
{
using (var scope = host.Services.CreateScope())
{
var services = scope.ServiceProvider;
try
{
var accountDbContext = services.GetRequiredService<AccountDbContext>();
accountDbContext.Database.EnsureCreated();
var accountDbCreator = accountDbContext.GetService<IRelationalDatabaseCreator>();
accountDbCreator.CreateTables();
}
catch (Exception e) { }
try
{
var appDbContext = services.GetRequiredService<AppDbContext>();
appDbContext.Database.EnsureCreated();
var appDbCreator = appDbContext.GetService<IRelationalDatabaseCreator>();
appDbCreator.CreateTables();
}
catch (Exception e) { }
}
return host;
}
}
我用 .NET core 3.1
制作了一个 OData 服务器。
在这个应用程序中,我有 2 个数据库上下文。其中之一用于应用程序本身 (AppDbContext),另一个用于帐户管理和身份验证 (AccountDbContext)。
我有一个 ServiceExtensions
这样的:
public static class ServiceExtensions
{
public static void ConfigureMySqlContext(this IServiceCollection services, IConfiguration configuration)
{
services.AddDbContext<AppDbContext>(options =>
{
options.UseMySql(configuration.GetConnectionString("MysqlConnection"),
mysqlOptions => {
mysqlOptions.ServerVersion(new ServerVersion(new Version(5, 4, 3), ServerType.MySql));
});
});
services.AddDbContext<AccountDbContext>(options =>
{
options.UseMySql(configuration.GetConnectionString("MysqlConnection"),
mysqlOptions => {
mysqlOptions.ServerVersion(new ServerVersion(new Version(5, 4, 3), ServerType.MySql));
});
});
}
}
在 startup.cs
中,我将按如下方式调用 ConfigureMySqlContext
函数:
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime.
// I use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.ConfigureMySqlContext(Configuration);
}
}
public static class Program
{
public static void Main(string[] args)
{
CreateWebHostBuilder(args)
.Build()
.CreateDbIfNotExists()
.Run();
}
private static IHost CreateDbIfNotExists(this IHost host)
{
using (var scope = host.Services.CreateScope())
{
var services = scope.ServiceProvider;
var accountDbContext = services.GetRequiredService<AccountDbContext>();
accountDbContext.Database.EnsureCreated();
var appDbContext = services.GetRequiredService<AppDbContext>();
appDbContext.Database.EnsureCreated();
}
return host;
}
}
此代码的目的是在数据库内的两个上下文中生成所需的表。
问题是,当我将这个应用程序发布到服务器时,当应用程序第一次 运行 时,它只会生成和启动 AppDbContext
中的表。为什么?因为放在AccountDbContext
.
如果我按如下方式更改 ConfigureMySqlContext
函数并再次发布应用程序:
public static class ServiceExtensions
{
public static void ConfigureMySqlContext(this IServiceCollection services, IConfiguration configuration)
{
services.AddDbContext<AccountDbContext>(options =>
{
options.UseMySql(configuration.GetConnectionString("MysqlConnection"),
mysqlOptions => {
mysqlOptions.ServerVersion(new ServerVersion(new Version(5, 4, 3), ServerType.MySql));
});
});
services.AddDbContext<AppDbContext>(options =>
{
options.UseMySql(configuration.GetConnectionString("MysqlConnection"),
mysqlOptions => {
mysqlOptions.ServerVersion(new ServerVersion(new Version(5, 4, 3), ServerType.MySql));
});
});
}
}
然后就是AccountDbContext
里面的表,会在第一个运行之后在数据库中启动。
问题在于,EnsureCreated
的核心是语义非常简单,可以建模为:
if (!database.exists()) {
database.create();
}
其中 exists()
检查数据库是否存在,而不检查数据库中的表。
它的最终结果是,如果数据库没有完全清空,EnsureCreated()
将没有任何效果。
有两种方法可以解决这个问题:
- 将数据库一分为二。这是EntityFramework最喜欢的解决方案。
但是,如果不行,请查看:
- 查看
IRelationalDatabaseCreator.CreateTables()
方法:
这是一个可能的解决方案:
using Microsoft.EntityFrameworkCore.Storage;
using Microsoft.EntityFrameworkCore.Infrastructure;
public static class Program
{
public static void Main(string[] args)
{
CreateWebHostBuilder(args)
.Build()
.CreateDbIfNotExists()
.Run();
}
private static IHost CreateDbIfNotExists(this IHost host)
{
using (var scope = host.Services.CreateScope())
{
var services = scope.ServiceProvider;
try
{
var accountDbContext = services.GetRequiredService<AccountDbContext>();
accountDbContext.Database.EnsureCreated();
var accountDbCreator = accountDbContext.GetService<IRelationalDatabaseCreator>();
accountDbCreator.CreateTables();
}
catch (Exception e) { }
try
{
var appDbContext = services.GetRequiredService<AppDbContext>();
appDbContext.Database.EnsureCreated();
var appDbCreator = appDbContext.GetService<IRelationalDatabaseCreator>();
appDbCreator.CreateTables();
}
catch (Exception e) { }
}
return host;
}
}