这是我的设置:使用 .NET 7 的 Blazor 服务器应用程序。
我正在使用 .NET 的内置依赖注入。
...
builder.Services.AddRazorPages();
builder.Services.AddServerSideBlazor();
//Models
builder.Services.AddScoped<IEnvironment, ApplicationLogic.Models.Environment>();
//Database
builder.Services.AddTransient<IDbConnectionString, DbConnectionString>();
builder.Services.AddTransient<IIMP_GG_Context, IMP_GG_Context>();
...
我使用 Entity Framework Core 连接到数据库。 现在我遇到的情况是,在我的应用程序中,用户可以在不同的环境(“实时”、“测试”、“开发”)之间进行选择。 环境的改变应该改变正在使用的数据库的连接。
所选环境保存在
Environment
对象中,并且只有一个属性 enum
。环境设置是Scoped
。
builder.Services.AddScoped<IEnvironment, ApplicationLogic.Models.Environment>();
这是我的应用程序的当前结构,基于一个示例 (
ProductStatus
)。
UI 已注入一个控制器对象。这是一个例子(参见_ProductStatusController
):
public partial class Filter
{
...
[Inject] private IProductStatusController _ProductStatusController { get; set; }
protected override void OnInitialized()
{
base.OnInitialized();
GetAllProductStatus();
}
private void GetAllProductStatus()
{
var productStatus = _ProductStatusController.GetAllProductStatus();
...
}
...
}
控制器已注入存储库。
public class ProductStatusController : IProductStatusController
{
private IProductStatusRepository _ProductStatusRepository;
public ProductStatusController(IProductStatusRepository productStatusRepository)
{
_ProductStatusRepository = productStatusRepository;
}
public List<Vorlauf> GetAllProductStatus()
{
return _ProductStatusRepository.GetAll();
}
}
存储库 (
ProductStatusRepository
) 看起来像这样。这是实体框架核心上下文对象 (_Context
)(也已注入)。
public class ProductStatusRepository : IProductStatusRepository
{
private IIMP_GG_Context _Context;
public ProductStatusRepository(IIMP_GG_Context impGgContext)
{
_Context = impGgContext;
}
public List<Vorlauf> GetAll()
{
return _Context.VorlaufTable.Where((vorlauf) => vorlauf.Art == 62).ToList();
}
}
使用哪个连接取决于对象
Environment
。
这是 Context
类:
public class IMP_GG_Context : DbContext, IIMP_GG_Context
{
public DbSet<Vorlauf> VorlaufTable { get; set; }
... //more tables
DbConnection _Connection;
IDbConnectionString _DbConnectionString;
IEnvironment _Environment;
public IMP_GG_Context(IDbConnectionString dbConnectionString)
{
_DbConnectionString = dbConnectionString;
Database.SetCommandTimeout(600);
}
public IMP_GG_Context(DbConnection connection, IDbConnectionString dbConnectionString)
{
_DbConnectionString = dbConnectionString;
_Connection = connection;
}
~IMP_GG_Context()
{
}
private void OnEnvironmentChanged(object sender, PropertyChangedEventArgs e)
{
OnConfiguring(new DbContextOptionsBuilder());
}
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
if (_Connection == null)
{
string connection = _DbConnectionString.GetImpGgConnectionString();
optionsBuilder.UseSqlServer(connection);
}
else
{
optionsBuilder.UseSqlServer(_Connection);
}
}
}
_DbConnectionString
属性提供了一种根据所选环境获取正确连接字符串的方法。
我认为,我现在遇到的问题是,在创建 UI 组件时,
_ProductStatusController
对象就被创建了一次。因此数据库连接只会创建一次,并且不会在环境发生变化时更新。有什么办法解决这个问题吗?我可以做什么来重新创建 _ProductStatusController
对象?
我尝试了 Svyatoslav Danyliv 提供的解决方案。但它实际上将我选择的环境重置为默认值(=“测试”)。
这是我在过滤器 UI 中使用的新代码:
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();
...
}
}
如您所见,此阶段环境设置正确(=“Dev”):
但是当我执行下一步时
var productStatusController = scope.ServiceProvider.GetRequiredService<IProductStatusController>();
...环境正在重置为“测试”:
我在这里做错了什么吗?
您可以自己控制示波器的使用寿命。
制作所有内容,而不是
Filter
范围:
//Models
builder.Services.AddScoped<IEnvironment, ApplicationLogic.Models.Environment>();
//Database
builder.Services.AddScoped<IDbConnectionString, DbConnectionString>();
builder.Services.AddScoped<IIMP_GG_Context, IMP_GG_Context>();
//Filter
builder.Services.AddTransient<IFilter, Filter>();
注入您的服务
IServiceProvider
public class Filter
{
IServiceProvider _serviceProvider;
public Filter(IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider
}
public void GetAllProductStatus()
{
using (var scope = _serviceProvider.CreateScope())
{
// istantiate IProductStatusController manually
var statusController = scope.ServiceProvider.GetRequiredService<IProductStatusController>();
var productStatus = statusController.GetAllProductStatus();
...
// after disposing Scope - IProductStatusController, IIMP_GG_Context, IProductStatusRepository ect. will be disposed automatically
}
}
}
无论如何,看起来您还有另一个与 Blazor 无关的问题 - 如何正确配置
DbContext
。自己打电话给OnConfiguring
没有任何帮助。
添加适当的构造函数。其他和
OnConfiguring
覆盖应删除。
public class IMP_GG_Context: DbContext, IIMP_GG_Context
{
public IMP_GG_Context(IDbConnectionString dbConnectionString) : base(GetOptions(dbConnectionString))
{
}
private static DbContextOptions<IMP_GG_Context> GetOptions(IDbConnectionString dbConnectionString)
{
return new DbContextOptionsBuilder<IMP_GG_Context>()
.UseSqlServer(dbConnectionString.GetImpGgConnectionString())
.Options;
}
}
我现在已经可以工作了。这是我最终的解决方案:
首先我对我的
DbContext
做了一些改变:
public class IMP_GG_Context : DbContext, IIMP_GG_Context
{
public DbSet<Vorlauf> VorlaufTable { get; set; }
... //more tables
IDbConnectionString _DbConnectionString;
[ActivatorUtilitiesConstructor]
public IMP_GG_Context(DbContextOptions<IMP_GG_Context> options, IDbConnectionString dbConnectionString) : base(options)
{
_DbConnectionString = dbConnectionString;
}
~IMP_GG_Context()
{
}
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlServer(_DbConnectionString.GetImpGgConnectionString());
}
}
然后我更改了
Program.cs
中的配置以使用AddDbContextFactory
:
...
builder.Services.AddDbContextFactory<IMP_GG_Context>(options =>
{
var environment = new ApplicationLogic.Models.Environment();
DbConnectionString dbConnectionString = new DbConnectionString(environment);
options.UseSqlServer(dbConnectionString.GetImpGgConnectionString());
}, ServiceLifetime.Scoped);
...
...最后,我在加载数据时更改了存储库以在
IDbContextFactory
的帮助下获得“新鲜”实例:
public class ProductStatusRepository : IProductStatusRepository
{
private IIMP_GG_Context _Context;
private IDbContextFactory<IMP_GG_Context> _ContextFactory;
public ProductStatusRepository(IDbContextFactory<IMP_GG_Context> dbContextFactory)
{
_ContextFactory = dbContextFactory;
}
public List<Vorlauf> GetAll()
{
_Context = _ContextFactory.CreateDbContext();
return _Context.VorlaufTable.Where((vorlauf) => vorlauf.Art == 62).ToList();
}
}
通过这些更改,将使用正确的环境。 我实际上不知道这是否是使用不同数据库服务器/环境的推荐方式。