我有一个正常工作的RabbitMQ Singleton,但是当消息到达时它依赖于作用域服务:
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作为依赖的问题。
&#34;协议&#34;在DI中用于在单例服务中调用dbContext?
我可以使用using
语句来确保它被丢弃,但是我必须使用DI来传递DbContextOptions。这是实现这一目标的唯一途径吗?
答案 0 :(得分:8)
一种方法是自己创建范围。通常,asp.net核心在请求启动时为您创建范围,并在请求结束时关闭范围。但在您的情况下 - rabbitmq消息消费与http请求完全无关。但是,您可以说,每个消息处理都代表了自己的范围。
在这种情况下,请将IServiceProvider
注入RabbitMqListener
(在下方表示为_provider
私有字段)然后:
private void OnMessageReceived(Message message) {
using (var scope = _provider.CreateScope()) {
var handler = scope.ServiceProvider.GetRequiredService<IHandler>();
handler.ProcessResourceObject(message);
}
}
替代方案可以是在容器中注册ApplicationDbContext
factory (除常规范围注册外)。 Factory将返回ApplicationDbContext
的新实例,并且调用者将负责处理它。例如:
services.AddSingleton<Func<ApplicationDbContext>>(() =>
{
var optionsBuilder = new DbContextOptionsBuilder<ApplicationDbContext>();
optionsBuilder.UseSqlServer(Configuration.GetConnectionString("DefaultConnection"));
return new ApplicationDbContext(optionsBuilder.Options);
});
然后你可以将IHandler
注册为单身(并且不像现在一样),并在其构造函数中注入Func<ApplicationDbContext>
:
private readonly Func<ApplicationDbContext> _appDbContextFactory;
public ResourcesHandler(Func<ApplicationDbContext> appDbContextFactory)
{
_appDbContextFactory = appDbContextFactory;
}
然后,只要您需要在处理程序中处理消息 - 您自己管理上下文:
using (var context = _appDbContextFactory()) {
// do stuff
}
答案 1 :(得分:1)
我认为如果你创建一个ContextFactory并向它询问Context是一个很好的方法。
您可以注册新工厂
services.AddSingleton<ContextFactory>();
并注入处理程序的构造函数。
然后你可以: _yourService.GetContext()
;并使用它。
您的工厂应该具有关于如何创建上下文的逻辑,并且将与其余部分隔离。任何时候你需要使用上下文,你应该打电话给工厂。
请记住,只要是一个单身人士,就不应该使用状态内容。
如果您想使用状态,只需注册为Transient,例如。
**编辑:记得永远返回上下文的新实例。