在RabbitMQ Consumer中使用DbContext(单例服务)

时间:2018-04-09 08:45:41

标签: c# .net asp.net-core rabbitmq

我有一个正常工作的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。这是实现这一目标的唯一途径吗?

2 个答案:

答案 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,例如。

**编辑:记得永远返回上下文的新实例。