EF Core多租户动态Schema迁移异常:运行时无法修改表Schema
多租户ASP.NET Core Web API中EF Core 8无法动态修改表Schema的问题
我正在开发一个多租户ASP.NET Core Web API项目,使用EF Core 8作为ORM框架,数据库采用SQL Server。项目需要支持运行时创建数据库,并将表的Schema从默认的dbo修改为巴西CPF文档编号。
目前迁移脚本已准备完成,运行Migrate()方法能正确创建数据库,但始终无法修改Schema——EF Core一直把表保留在dbo Schema下,完全不遵循指定的参数。这个问题我已经折腾了5个月,尝试各种方法都没解决,求可行的解决方案。
迁移执行代码
//Apply Migrations public static void Test(this IServiceProvider serviceProvider, IConfiguration configuration, TenantRequestViewModel tenantRequestViewModel) { using (var scope = serviceProvider.CreateScope()) { var currentTenantRepo = scope.ServiceProvider.GetRequiredService<ICurrentTenantRepository>(); if (!currentTenantRepo.SetTenantAsync(tenantRequestViewModel.DocumentNumber).Result) { { } } string dbName = DataBaseConst.DATABASE_NAME_ERP_VET + "-" + tenantRequestViewModel.DocumentNumber; string defaultConnectionString = configuration.GetConnectionString("DefaultConnection"); string connectionString = currentTenantRepo.ConnectionString ?? defaultConnectionString.Replace(DataBaseConst.DATABASE_NAME_ERP_VET, dbName); var appDbContextOptionsBuilder = new DbContextOptionsBuilder<AppDbContext>(); appDbContextOptionsBuilder.UseSqlServer(connectionString); var mediatorHandler = scope.ServiceProvider.GetRequiredService<IMediatorHandler>(); using (var appDbContext = new AppDbContext(appDbContextOptionsBuilder.Options, currentTenantRepo, mediatorHandler, currentTenantRepo.Schema)) { Console.WriteLine($"Current Schema: {currentTenantRepo.Schema}"); if (appDbContext.Database.GetPendingMigrations().Any()) { appDbContext.Database.Migrate(); } } } }
AppDbContext代码
// My Context public sealed class AppDbContext : DbContext, IUnitOfWork { private readonly IMediatorHandler _mediatorHandler; private readonly ICurrentTenantRepository _currentTenantRepository; public Guid CurrentTenantId { get; set; } public string? CurrentTenantConnectionString { get; set; } public string? CurrentSchema { get; set; } public AppDbContext(DbContextOptions<AppDbContext> options, ICurrentTenantRepository currentTenantRepository, IMediatorHandler mediatorHandler, string? schema = null) : base(options) { _mediatorHandler = mediatorHandler; _currentTenantRepository = currentTenantRepository; if (_currentTenantRepository is not null) { CurrentTenantId = _currentTenantRepository.TenantId; CurrentTenantConnectionString = _currentTenantRepository.ConnectionString; CurrentSchema = string.IsNullOrEmpty(schema) ? _currentTenantRepository.Schema : schema ; } //ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking; //ChangeTracker.AutoDetectChangesEnabled = false; } public DbSet<Customer> Customers { get; set; } protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.Ignore<ValidationResult>(); modelBuilder.Ignore<Event>(); modelBuilder.Ignore<CreateTenantRequest>(); modelBuilder.Ignore<TenantResponse>(); modelBuilder.Ignore<Tenant>(); modelBuilder.HasDefaultSchema(CurrentSchema); if (!string.IsNullOrEmpty(CurrentSchema)) { foreach (var entityType in modelBuilder.Model.GetEntityTypes()) { entityType.SetSchema(CurrentSchema); } } foreach (var property in modelBuilder.Model.GetEntityTypes().SelectMany( e => e.GetProperties().Where(p => p.ClrType == typeof(string)))) property.SetColumnType("varchar(100)"); //Schema name passed to table configuration modelBuilder.ApplyConfiguration(new CustomerMap(CurrentSchema)); base.OnModelCreating(modelBuilder); } // On Configuring -- dynamic connection string, fires on every request protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { string? tenantConnectionString = CurrentTenantConnectionString; if (!string.IsNullOrEmpty(tenantConnectionString)) // use tenant db if one is specified { _ = optionsBuilder.UseSqlServer(tenantConnectionString); } optionsBuilder.ReplaceService<IModelCacheKeyFactory, CustomModelCacheKeyFactory>(); base.OnConfiguring(optionsBuilder); } }
自定义Model缓存键工厂代码
// CustomModelCacheKeyFactory public class CustomModelCacheKeyFactory : IModelCacheKeyFactory { public object Create(DbContext context, bool designTime) { var contextWithSchema = context as AppDbContext; // 使用租户ID或特定Schema作为缓存键,结合设计时标识 return (contextWithSchema?.CurrentSchema, context.GetType(), designTime); } }
请求示例
{ "documentNumber": "03350593097", "workspaceName": "My_Work_1", "phoneNumber": "27996356704", "email": "jalberromano@msn.com", "password": "123456", "confirmPassword": "123456", "subscriptionPlanId": "0335a5fe-4754-4ed0-1434-08dd1937d5aa",//启动API后从表中获取ID "cultureCode": "pt-BR", "isolated": true }
内容的提问来源于stack exchange,提问作者rj-
相关产品推荐
相关产品推荐

