如何使用DbContext处理多源数据库并实现上下文仅管理对应聚合
解决方案
你可以按照以下步骤实现每个DbContext仅管理自身对应的聚合:
- 仅为每个DbContext显式声明负责的聚合DbSet
不要在所有DbContext中定义全部聚合的DbSet属性,每个上下文只保留自身管理的聚合对应的DbSet即可,示例如下:
// AppDbContext仅保留自己负责的聚合 public class AppDbContext : DbContext { private readonly IMediator? _mediator; public DbSet<User> Users { get; set; } public DbSet<Order> Orders { get; set; } public AppDbContext(DbContextOptions<AppDbContext> options, IMediator? mediator) : base(options) { _mediator = mediator; } } // AnalyzeDbContext仅保留自己负责的聚合 public class AnalyzeDbContext : DbContext { private readonly IMediator? _mediator; public DbSet<Report> Reports { get; set; } public DbSet<Statistics> Statistics { get; set; } public AnalyzeDbContext(DbContextOptions<AnalyzeDbContext> options, IMediator? mediator) : base(options) { _mediator = mediator; } }
- 限定实体配置的扫描范围
如果你使用IEntityTypeConfiguration做实体映射,不要使用全局扫描方法modelBuilder.ApplyConfigurationsFromAssembly(),该方法会加载当前程序集所有的实体配置,导致所有聚合都被加入到DbContext中。你可以改为两种注册方式:
- 逐个显式注册当前上下文需要的配置
protected override void OnModelCreating(ModelBuilder modelBuilder) { // 仅注册AppDbContext需要的实体配置 modelBuilder.ApplyConfiguration(new UserConfiguration()); modelBuilder.ApplyConfiguration(new OrderConfiguration()); }
- 按命名空间过滤扫描配置
protected override void OnModelCreating(ModelBuilder modelBuilder) { // 仅扫描当前上下文对应配置的命名空间 var configTypes = Assembly.GetExecutingAssembly() .GetTypes() .Where(t => t.Namespace == "WetaPayment.Infrastructure.Data.Configurations.App" && typeof(IEntityTypeConfiguration).IsAssignableFrom(t)); foreach (var configType in configTypes) { dynamic config = Activator.CreateInstance(configType); modelBuilder.ApplyConfiguration(config); } }
- 增加多余实体清理逻辑(可选,增强校验)
可以在OnModelCreating方法最后加入校验逻辑,自动移除不属于当前上下文的实体,避免误加:
protected override void OnModelCreating(ModelBuilder modelBuilder) { // 先执行配置注册逻辑 // ... // 清理非当前上下文管理的实体 var allowedTypes = new[] { typeof(User), typeof(Order) }; var invalidEntities = modelBuilder.Model.GetEntityTypes() .Where(e => !allowedTypes.Contains(e.ClrType)) .ToList(); foreach (var entity in invalidEntities) { modelBuilder.Model.RemoveEntityType(entity.ClrType); } }
- 独立配置各DbContext的迁移规则
为了避免多个上下文的迁移互相影响,注册时需要为每个DbContext指定独立的迁移历史表和迁移目录:
public static void AddAppDbContext(this IServiceCollection services, string connectionString) => services.AddDbContext<AppDbContext>(options => options.UseMySql(connectionString, new MySqlServerVersion(new Version(8, 0, 27)), opt => { opt.MigrationsAssembly("WetaPayment.Infrastructure.Data"); // 指定独立的迁移历史表 opt.MigrationsHistoryTable("__EFMigrationsHistory_App"); })); // 其他两个上下文也对应配置独立的迁移历史表 public static void AddAnalyzeDbContext(this IServiceCollection services, string connectionString) => services.AddDbContext<AnalyzeDbContext>(options => options.UseMySql(connectionString, new MySqlServerVersion(new Version(8, 0, 27)), opt => { opt.MigrationsAssembly("WetaPayment.Infrastructure.Data"); opt.MigrationsHistoryTable("__EFMigrationsHistory_Analyze"); }));
配置完成后执行迁移时依然指定对应Context即可:
Add-Migration init_app -Context AppDbContext Update-Database -Context AppDbContext
内容的提问来源于stack exchange,提问作者sos5020
相关产品推荐
相关产品推荐

