问题描述
我有一个RabbitMQ Singleton可以正常工作,但是每当消息到达时就依赖于作用域服务:
I have a RabbitMQ Singleton that is working fine, but has a dependency on a scoped service whenever a message arrives:
consumer.Received += _resourcesHandler.ProcessResourceObject; //Scoped Service
我的服务注册如下:
services.AddScoped<IHandler, Handler>();
services.AddSingleton<RabbitMqListener>();
作用域服务构造函数将DI用于Db上下文:
The scoped services constructors uses DI for the Db Context:
private readonly ApplicationDbContext _appDbContext;
public ResourcesHandler(ApplicationDbContext appDbContext)
{
_appDbContext = appDbContext;
}
此作用域服务调用Db上下文,以便在收到消息时将属性插入数据库.
This scoped service calls the Db Context in order to insert properties to the database on receipt of a message.
但是,由于作用域服务的生存期不同,因此启动失败.
However, because the scoped service has a different lifetime, startup is failing.
是否有更好的方法可以做到这一点?我可以将范围服务设置为单例,但随后会遇到使用DbContext作为依赖项的问题.
Is there a better way to do this? I could make the scoped service a singleton, but then I'd have the problem of using DbContext as a dependancy.
DI中在单例服务中调用dbContext的协议"是什么?
What's the "protocol" in DI for calling the dbContext in singleton services?
我可以使用 using
语句来确保将其处置,但随后必须使用DI传递DbContextOptions.这是实现这一目标的唯一方法吗?
I could use a using
statement to make sure its disposed, but then I'd have to pass the DbContextOptions using DI instead. Is this the only way to achieve this?
推荐答案
一种方法是自己创建作用域.通常,asp.net核心在请求开始时为您创建范围,并在请求结束时关闭范围.但是,在您的情况下-Rabbitmq消息的使用与HTTP请求根本无关.但是,您可以说,每个消息处理都代表其自己的作用域.
One way is to create scope yourself. Usually asp.net core creates scope for you when request starts and closes scope when request ends. But in your case - rabbitmq message consumption is not related to http requests at all. You can say though, that every message processing represents its own scope.
在这种情况下,将 IServiceProvider
注入到 RabbitMqListener
(以下表示为 _provider
私有字段),然后:
In such case, inject IServiceProvider
to RabbitMqListener
(represented as _provider
private field below) and then:
private void OnMessageReceived(Message message) {
using (var scope = _provider.CreateScope()) {
var handler = scope.ServiceProvider.GetRequiredService<IHandler>();
handler.ProcessResourceObject(message);
}
}
替代方法可能是在容器中注册 ApplicationDbContext
factory (除了常规范围内的注册).Factory将返回 ApplicationDbContext
的新实例,这将由调用方负责处置.例如:
Alternative could be to register ApplicationDbContext
factory in container (in addition to regular scoped registration). Factory will return new instance of ApplicationDbContext
and that will be callers responsibility to dispose it. For example:
services.AddSingleton<Func<ApplicationDbContext>>(() =>
{
var optionsBuilder = new DbContextOptionsBuilder<ApplicationDbContext>();
optionsBuilder.UseSqlServer(Configuration.GetConnectionString("DefaultConnection"));
return new ApplicationDbContext(optionsBuilder.Options);
});
然后,您可以将 IHandler
注册为单例(并且不再像现在那样作用域),并将 Func< ApplicationDbContext>
注入其构造函数中:
Then you can register IHandler
as singleton (and not scoped like now) and inject Func<ApplicationDbContext>
in its constructor:
private readonly Func<ApplicationDbContext> _appDbContextFactory;
public ResourcesHandler(Func<ApplicationDbContext> appDbContextFactory)
{
_appDbContextFactory = appDbContextFactory;
}
然后,每当需要在处理程序中处理消息时,您就可以自己管理上下文:
Then whenever you need to process message in handler - you manage context yourself:
using (var context = _appDbContextFactory()) {
// do stuff
}
这篇关于在RabbitMQ使用者(单服务)中使用DbContext的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!