Blazor Server.NET7:切换环境时如何重建DI实例更新EF Core连接?
解决Blazor Server环境切换后数据库连接不更新的问题
问题背景
基于.NET7开发Blazor Server应用,使用内置依赖注入,核心配置如下:
... builder.Services.AddRazorPages(); builder.Services.AddServerSideBlazor(); // 环境服务:Scoped生命周期,存储当前选中环境 builder.Services.AddScoped<IEnvironment, ApplicationLogic.Models.Environment>(); // 数据库相关服务:Transient生命周期 builder.Services.AddTransient<IDbConnectionString, DbConnectionString>(); builder.Services.AddTransient<IIMP_GG_Context, IMP_GG_Context>(); ...
应用支持切换Live/Test/Development三种环境,当前环境存储在IEnvironment实例中。架构层级为:UI组件注入IProductStatusController → 控制器注入IProductStatusRepository → 仓储注入EF Core上下文IIMP_GG_Context → 上下文通过IDbConnectionString根据当前环境获取连接字符串。
当前问题:IProductStatusController随UI组件初始化创建(同组件为Scoped生命周期),环境切换后控制器不会自动重建,导致仓储和上下文仍使用旧环境的连接字符串。
尝试创建新作用域解决时,新作用域内的IEnvironment会被重置为默认Test环境,代码如下:
private void GetAllProductStatus() { using (var scope = _ServiceProvider.CreateScope()) { var environment = scope.ServiceProvider.GetRequiredService<IEnvironment>(); environment = _Environment; var productStatusController = scope.ServiceProvider.GetRequiredService<IProductStatusController>(); var productStatus = productStatusController.GetAllProductStatus(); ... } }
问题原因
environment = _Environment只是将局部变量指向了原Scoped的IEnvironment实例,并未修改新作用域内创建的IEnvironment实例的属性。控制器获取EF上下文时,依然使用新作用域中IEnvironment的默认Test环境值。
解决方案
方案1:修改新作用域内IEnvironment实例的属性
直接修改新作用域中IEnvironment实例的环境属性,确保后续创建的控制器、仓储、上下文都使用选中的环境:
private void GetAllProductStatus() { using (var scope = _ServiceProvider.CreateScope()) { // 获取新作用域内的IEnvironment实例 var scopedEnv = scope.ServiceProvider.GetRequiredService<IEnvironment>(); // 将其环境属性设置为当前选中的环境 scopedEnv.CurrentEnvironment = _Environment.CurrentEnvironment; // 从新作用域获取控制器,此时依赖链上的服务都会使用更新后的环境 var productStatusController = scope.ServiceProvider.GetRequiredService<IProductStatusController>(); var productStatus = productStatusController.GetAllProductStatus(); // 后续业务逻辑处理 } }
配套要求
- 确保
IEnvironment接口和实现类的环境属性支持修改:
public enum EnvironmentType { Live, Test, Development } public interface IEnvironment { EnvironmentType CurrentEnvironment { get; set; } } public class Environment : IEnvironment { // 默认环境设为Test public EnvironmentType CurrentEnvironment { get; set; } = EnvironmentType.Test; }
- 确保
IDbConnectionString每次获取连接字符串时读取最新环境状态:
public class DbConnectionString : IDbConnectionString { private readonly IEnvironment _environment; public DbConnectionString(IEnvironment environment) { _environment = environment; } public string GetConnectionString() { return _environment.CurrentEnvironment switch { EnvironmentType.Live => "你的Live环境连接字符串", EnvironmentType.Development => "你的开发环境连接字符串", _ => "你的Test环境连接字符串" }; } }
- EF Core上下文动态获取连接字符串:
public class IMP_GG_Context : DbContext, IIMP_GG_Context { private readonly IDbConnectionString _connectionStringProvider; public IMP_GG_Context(IDbConnectionString connectionStringProvider) { _connectionStringProvider = connectionStringProvider; } protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { // 每次创建上下文时都会获取最新环境的连接字符串 optionsBuilder.UseSqlServer(_connectionStringProvider.GetConnectionString()); } // 定义业务DbSet public DbSet<ProductStatus> ProductStatuses { get; set; } }
方案2:重构控制器为Transient并按需获取
将IProductStatusController注册为Transient生命周期:
builder.Services.AddTransient<IProductStatusController, ProductStatusController>();
在UI组件中,环境切换后主动从DI获取新的控制器实例:
// 组件中注入IServiceProvider [Inject] private IServiceProvider ServiceProvider { get; set; } private void OnEnvironmentChanged() { // 获取新的控制器实例,自动使用当前环境的连接字符串 var newController = ServiceProvider.GetRequiredService<IProductStatusController>(); var productStatus = newController.GetAllProductStatus(); }
内容的提问来源于stack exchange,提问作者dns_nx
相关产品推荐
相关产品推荐

