如何配置EF Core单DbContext并管理多数据库提供程序迁移?
问题描述
生产环境中有一个托管于ASP.NET Web API的.NET 6 Blazor WASM项目,采用标准C#分层架构(客户端、API、逻辑、数据及共享项目)。DbContext注入仓储后服务于逻辑层,请求完成后服务与DbContext会自动释放,运行状态稳定。
现需创建配套WinUI 3桌面应用,复用现有逻辑与数据层,但切换为SQLite数据提供程序。尝试继承现有DbContext创建新类后,仓储无法确定应使用的DbContext实例;参考官方「多提供程序单上下文」文档配置后,未生成SQLite对应的ModelSnapshot,执行update-database时仍生成SQL Server语法的SQL代码。
现有架构与配置
项目架构
采用分层架构,核心项目包括:
- 客户端:Blazor WASM项目
- API:ASP.NET Web API项目
- Logic:业务逻辑层项目
- Data:数据访问层(包含DbContext与仓储实现)
- Data.Migrations:独立的数据库迁移项目
- WinUI 3:配套桌面应用项目
Data.Migrations.csproj(仅用于迁移)
- Migrations:SQL Server提供程序对应的迁移文件目录
- Migrations/Sqlite:预留的SQLite提供程序迁移文件目录
Data.csproj(DbContext与仓储实现)
public class Context : DbContext { protected readonly IConfiguration Configuration; public Context() { } public Context(DbContextOptions<Context> options) : base(options) { } protected override void OnModelCreating(ModelBuilder modelBuilder) { // 实体模型配置代码 } // DbSet<T> 定义... } public interface IRepository<T> where T : class { // 仓储接口方法定义 } public class Repository<T> : IRepository<T> where T : class { private DbSet<T> entities; public Repository() { } // 仓储接口方法实现 } public class DbContextFactory : IDesignTimeDbContextFactory<Context> { public Context CreateDbContext(string[] args) { var optionsBuilder = new DbContextOptionsBuilder<Context>(); if (DbContextHelper.Provider == "SqlServer") optionsBuilder.UseSqlServer(DbContextHelper.ConnectionString, x => x.MigrationsAssembly("Data.Migrations")); else if (DbContextHelper.Provider == "Sqlite") optionsBuilder.UseSqlite(DbContextHelper.ConnectionString, x => x.MigrationsAssembly("Data.Migrations")); else throw new InvalidOperationException("未指定数据库提供程序"); return new Context(optionsBuilder.Options); } }
Logic.csproj(业务逻辑层)
public interface ICustomerService { // 业务服务接口方法定义 } public class CustomerService : ICustomerService { protected readonly IRepository<Customer> _customerRepository; public CustomerService(IRepository<Customer> customerRepository) { _customerRepository = customerRepository; } // 业务服务接口方法实现 }
Api.csproj(Program类配置)
builder.Services.AddDbContext<Context>(options => options.UseSqlServer(builder.Configuration.GetConnectionString("Context"), x => x.MigrationsAssembly("Data.Migrations")) ); builder.Services.AddTransient<ICustomerService, CustomerService>(); builder.Services.AddScoped(typeof(IRepository<>), typeof(Repository<>)); builder.Services.AddScoped<DbContext, Context>();
WinUi.csproj(App.xaml.cs配置)
public App() { services.AddDbContext<InSpecContext, Context>(options => options.UseSqlite(DbContextHelper.ConnectionString, x => x.MigrationsAssembly("Data.Migrations")) ); }
解决方案
1. 生成SQLite专属迁移与ModelSnapshot
通过指定提供程序和输出目录,强制生成SQLite对应的迁移文件与ModelSnapshot:
- 生成SQLite初始迁移:
Add-Migration InitialCreate_Sqlite -Context Context -OutputDir Migrations/Sqlite -Provider Microsoft.EntityFrameworkCore.Sqlite
- 执行SQLite数据库更新:
Update-Database -Context Context -Provider Microsoft.EntityFrameworkCore.Sqlite
2. 消除仓储的DbContext注入歧义
无需继承DbContext,通过依赖注入的配置差异实现多提供适配:
- 修改WinUI项目的DbContext注册代码,直接注册
Context而非继承类:
// WinUi.csproj App.xaml.cs services.AddDbContext<Context>(options => options.UseSqlite(DbContextHelper.ConnectionString, x => x.MigrationsAssembly("Data.Migrations")) );
- 调整仓储类构造函数,直接注入
Context实例,避免依赖DbContext基类:
// Data.csproj 中修改Repository<T> public class Repository<T> : IRepository<T> where T : class { private readonly DbSet<T> _entities; public Repository(Context context) { _entities = context.Set<T>(); } // 仓储接口方法实现 }
API项目注入SQL Server版本的Context,WinUI项目注入SQLite版本的Context,仓储会自动获取对应实例,无需额外区分。
3. 优化设计时DbContextFactory
修改DbContextFactory,通过命令行参数指定提供程序,避免依赖静态状态:
public class DbContextFactory : IDesignTimeDbContextFactory<Context> { public Context CreateDbContext(string[] args) { var provider = args.Length > 0 ? args[0] : "SqlServer"; var configuration = new ConfigurationBuilder() .SetBasePath(Directory.GetCurrentDirectory()) .AddJsonFile("appsettings.json") .Build(); var optionsBuilder = new DbContextOptionsBuilder<Context>(); string connectionString = configuration.GetConnectionString($"{provider}Context"); if (provider == "SqlServer") optionsBuilder.UseSqlServer(connectionString, x => x.MigrationsAssembly("Data.Migrations")); else if (provider == "Sqlite") optionsBuilder.UseSqlite(connectionString, x => x.MigrationsAssembly("Data.Migrations")); else throw new InvalidOperationException($"不支持的数据库提供程序:{provider}"); return new Context(optionsBuilder.Options); } }
生成SQLite迁移时可通过参数指定提供程序:
Add-Migration NewMigration_Sqlite -Context Context -OutputDir Migrations/Sqlite -Provider Microsoft.EntityFrameworkCore.Sqlite -Args "Sqlite"
内容的提问来源于stack exchange,提问作者A. Dixon
相关产品推荐
相关产品推荐

