具有多个数据库上下文的.net Core应用程序不会在所有DbContexts中生成表



我用.NET core 3.1制作了一个OData服务器。

在这个应用程序中,我有两个数据库上下文。一个用于应用程序本身(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()将不起作用。

处理这个问题有两种方法:

  1. 将数据库拆分为两个。这是EntityFramework最喜欢的解决方案。

但是,如果没有选项,请查看:

  1. 查看IRelationalDatabaseCreator.CreateTables()方法:

https://learn.microsoft.com/en us/dotnet/api/microsoft.entityframeworkcore.storage.irelationaldatabasecreator?view=efcore - 5.0

这是一个可能的解决方案:

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

最新更新