我有一个工作正常的 RabbitMQ 单例,但每当消息到达时都依赖于作用域服务:
consumer.Received += _resourcesHandler.ProcessResourceObject; //Scoped Service
我的服务是这样注册的:
services.AddScoped<IHandler, Handler>();
services.AddSingleton<RabbitMqListener>();
作用域服务构造函数使用 DI 作为 Db 上下文:
private readonly ApplicationDbContext _appDbContext;
public ResourcesHandler(ApplicationDbContext appDbContext)
{
_appDbContext = appDbContext;
}
此作用域服务调用 Db 上下文,以便在收到消息时将属性插入数据库。
但是,由于作用域服务的生命周期不同,因此启动失败。
有更好的方法吗?我可以将作用域服务设为单例,但随后我会遇到使用 DbContext 作为依赖项的问题。
DI 中用于在单例服务中调用 dbContext 的“协议(protocol)”是什么?
我可以使用 using
语句来确保它已被处置,但是我必须改为使用 DI 来传递 DbContextOptions。这是实现这一目标的唯一方法吗?
最佳答案
一种方法是自己创建作用域。通常 asp.net core 在请求开始时为您创建范围,并在请求结束时关闭范围。但在你的情况下 - rabbitmq 消息消费与 http 请求根本无关。不过,您可以说,每个消息处理都代表其自己的范围。
在这种情况下,注入(inject) IServiceProvider
至 RabbitMqListener
(在下面表示为 _provider
私有(private)字段)然后:
private void OnMessageReceived(Message message) {
using (var scope = _provider.CreateScope()) {
var handler = scope.ServiceProvider.GetRequiredService<IHandler>();
handler.ProcessResourceObject(message);
}
}
另一种方法是注册 ApplicationDbContext
容器中的工厂(除了常规范围内的注册)。工厂将返回 ApplicationDbContext
的新实例这将是调用者的责任来处理它。例如:
services.AddSingleton<Func<ApplicationDbContext>>(() =>
{
var optionsBuilder = new DbContextOptionsBuilder<ApplicationDbContext>();
optionsBuilder.UseSqlServer(Configuration.GetConnectionString("DefaultConnection"));
return new ApplicationDbContext(optionsBuilder.Options);
});
然后就可以注册了IHandler
作为单例(并且不像现在那样限定范围)并注入(inject) Func<ApplicationDbContext>
在其构造函数中:
private readonly Func<ApplicationDbContext> _appDbContextFactory;
public ResourcesHandler(Func<ApplicationDbContext> appDbContextFactory)
{
_appDbContextFactory = appDbContextFactory;
}
然后,每当您需要在处理程序中处理消息时——您自己管理上下文:
using (var context = _appDbContextFactory()) {
// do stuff
}
关于c# - 在 RabbitMQ Consumer(单例服务)中使用 DbContext,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49728884/