c# - DbContext 在BackgroundService 中释放

2023-12-13

我有一个 WebAPI,它也应该从 RabbitMQ 接收消息。我用了this教程,因为我知道有时 IIS 喜欢终止长时间运行的任务(但尚未在服务器上测试它,也许它不起作用)。我有一个处理通过 RabbitMQ 接收的消息的服务。我遇到的第一个问题 - 我无法将其注入BackgroundService类,所以我用了IServiceScopeFactory。现在,我必须使用来自两个队列的消息,据我了解,最佳实践是为此使用两个通道。但处理是在一项服务中完成的。后台服务:

public class ConsumeRabbitMQHostedService : BackgroundService
{
    private IConnection _connection;
    private IModel _firstChannel;
    private IModel _secondChannel;
    private RabbitConfigSection _rabbitConfig;
    public IServiceScopeFactory _serviceScopeFactory;

    public ConsumeRabbitMQHostedService(IOptions<RabbitConfigSection> rabbitConfig, IServiceScopeFactory serviceScopeFactory)
    {
        _rabbitConfig = rabbitConfig.Value;
        _serviceScopeFactory = serviceScopeFactory;
        InitRabbitMQ();
    }

    private void InitRabbitMQ()
    {
        var factory = new ConnectionFactory { HostName = _rabbitConfig.HostName, UserName = _rabbitConfig.UserName, Password = _rabbitConfig.Password };

        
        _connection = factory.CreateConnection();

        
        _firstChannel = _connection.CreateModel();

        _firstChannel.ExchangeDeclare(_rabbitConfig.DefaultExchange, ExchangeType.Topic);
        _firstChannel.QueueDeclare(_rabbitConfig.Queues.ConsumeQueues.FirstItemsConsumeQueue, true, false, false, null);
        _firstChannel.QueueBind(_rabbitConfig.Queues.ConsumeQueues.FirstItemsConsumeQueue, _rabbitConfig.DefaultExchange, "*.test.queue", null);
        _firstChannel.BasicQos(0, 1, false);

        _secondChannel = _connection.CreateModel();

        _secondChannel.ExchangeDeclare(_rabbitConfig.DefaultExchange, ExchangeType.Topic);
        _secondChannel.QueueDeclare(_rabbitConfig.Queues.ConsumeQueues.SecondItemsConsumeQueue, true, false, false, null);
        _secondChannel.QueueBind(_rabbitConfig.Queues.ConsumeQueues.SecondItemsConsumeQueue, _rabbitConfig.DefaultExchange, "*.test.queue", null);
        _secondChannel.BasicQos(0, 1, false);

        _connection.ConnectionShutdown += RabbitMQ_ConnectionShutdown;
    }
    protected override Task ExecuteAsync(CancellationToken stoppingToken)
    {
        stoppingToken.ThrowIfCancellationRequested();

        var firstConsumer = new EventingBasicConsumer(_firstChannel);
        var secondConsumer = new EventingBasicConsumer(_secondChannel);
        using (var scope = _serviceScopeFactory.CreateScope())
        {
            IIntegrationService scoped = scope.ServiceProvider.GetRequiredService<IIntegrationService>();
            firstConsumer.Received += (ch, ea) =>
            {
                // received message  
                var content = System.Text.Encoding.UTF8.GetString(ea.Body.ToArray());

                // handle the received message  
                HandleFirstMessage(content, scoped);
                _firstChannel.BasicAck(ea.DeliveryTag, false);

            };
            firstConsumer.Shutdown += OnConsumerShutdown;
            firstConsumer.Registered += OnConsumerRegistered;
            firstConsumer.Unregistered += OnConsumerUnregistered;
            firstConsumer.ConsumerCancelled += OnConsumerConsumerCancelled;
            _firstChannel.BasicConsume(_rabbitConfig.Queues.ConsumeQueues.FirstItemsConsumeQueue, false, firstConsumer);
        }
        using (var scope = _serviceScopeFactory.CreateScope())
        {
            IIntegrationService scoped = scope.ServiceProvider.GetRequiredService<IIntegrationService>();
            secondConsumer.Received += (ch, ea) =>
            {
                // received message  

                var content = System.Text.Encoding.UTF8.GetString(ea.Body.ToArray());

                // handle the received message  
                HandleSecondMessage(content, scoped);
                _secondChannel.BasicAck(ea.DeliveryTag, false);
            };


            secondConsumer.Shutdown += OnConsumerShutdown;
            secondConsumer.Registered += OnConsumerRegistered;
            secondConsumer.Unregistered += OnConsumerUnregistered;
            secondConsumer.ConsumerCancelled += OnConsumerConsumerCancelled;

            _secondChannel.BasicConsume(_rabbitConfig.Queues.ConsumeQueues.SecondItemsConsumeQueue, false, secondConsumer);
        }
        return Task.CompletedTask;
    }

    private void HandleFirstMessage(string content, IIntegrationService integrationService)
    {
        List<StockImportDto> dataToImport = JsonConvert.DeserializeObject<List<StockImportDto>>(content);
        integrationService.ImportFirst(dataToImport);
    }

    private void HandleSecondMessage(string content, IIntegrationService integrationService)
    {
        List<Import901Data> importData = JsonConvert.DeserializeObject<List<Import901Data>>(content);
        integrationService.ImportSecond(importData);
    }

    private void OnConsumerConsumerCancelled(object sender, ConsumerEventArgs e) { }
    private void OnConsumerUnregistered(object sender, ConsumerEventArgs e) { }
    private void OnConsumerRegistered(object sender, ConsumerEventArgs e) { }
    private void OnConsumerShutdown(object sender, ShutdownEventArgs e) { }
    private void RabbitMQ_ConnectionShutdown(object sender, ShutdownEventArgs e) { }

    public override void Dispose()
    {
        _firstChannel.Close();
        _connection.Close();
        base.Dispose();
    }
}

在服务中我得到

System.ObjectDisposeException:“无法访问已处置的上下文实例。导致此错误的一个常见原因是处置从依赖项注入解析的上下文实例,然后尝试在应用程序的其他位置使用相同的上下文实例。如果您在上下文实例上调用“Dispose”或将其包装在 using 语句中,则可能会发生这种情况。如果您使用依赖项注入,则应该让依赖项注入容器负责处理上下文实例。 对象名称:“IntegrationDbContext”。

DbContext被注入IIntegrationService。如果我了解发生了什么,服务的两个实例(甚至一个)共享DbContext,当其中一个完成时,它会处置DbContext。我尝试不创建两个实例(所有代码都在一个实例中)using),尝试制作IIntegrationService瞬态,尝试异步执行所有操作(这是初始版本,使其同步测试)-仍然是相同的错误。我应该在这里做什么?这是正确的方法吗?

更新1。 ConfigureServices in Startup:

        public void ConfigureServices(IServiceCollection services)
    {
        var rabbitConfigSection =
            Configuration.GetSection("Rabbit");
        services.Configure<RabbitConfigSection>(rabbitConfigSection);
        services.AddDbContext<SUNDbContext>(options =>
               options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));

        services.AddCors();
        services.AddSwaggerGen(c =>
        {
            c.SwaggerDoc("v1", new OpenApiInfo
            {
                Title = "My API",
                Version = "v1"
            });
        });
        services.AddRabbit(Configuration);
        services.AddHostedService<ConsumeRabbitMQHostedService>();
        services.AddControllers();
        services.AddTransient<IIntegrationService, IntegrationService>();// it's transient now, same error with scoped
    }

该问题是由以下事实引起的:外部scope由...制作_serviceScopeFactory.CreateScope()在每个 using 语句之后被释放,而每条消息仍然尝试依赖现在释放的范围和附加的上下文来处理消息。

解决方案是在消息处理程序中为每条消息创建一个新范围:

private void HandleFirstMessage(string content)
{
    using (var scope = _serviceScopeFactory.CreateScope())
    {
        IIntegrationService integrationService = scope.ServiceProvider.GetRequiredService<IIntegrationService>();
        List<StockImportDto> dataToImport = JsonConvert.DeserializeObject<List<StockImportDto>>(content);
        integrationService.ImportFirst(dataToImport);
    }
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

c# - DbContext 在BackgroundService 中释放 的相关文章

随机推荐

  • 什么是最好/非常好的元数据阅读器库?

    现在 我对从 MP3 文件 ID3 标签 读取数据特别感兴趣 但它能做的越多 例如图像中的 EXIF 就越好 而且不会影响 ID3 标签的读取能力 我有兴趣制作一个脚本来遍历我的媒体 现在是我的音乐文件 并确保文件名和目录路径与文件的元数据
  • F# 中什么是柯里化? [复制]

    这个问题已经存在了 可能的重复 函数式编程 柯里化 我正在这里阅读免费的 F Wikibook http en wikibooks org wiki F Sharp Programming 有一节解释了什么是偏函数 它说使用 F 你可以部分
  • Javascript 键码冲突:“右箭头”和“单引号”

    以下脚本执行其应该执行的操作 即它对 向左箭头 和 向右箭头 键做出反应 但是 由于键码冲突 它也会对单引号做出反应 它使得无法将该字符输入到输入字段中 对此可以采取什么措施吗
  • 使用Spring Batch在不同的数据源中写入

    对于一个项目 我需要处理一个表中的项目 并为 3 个不同的表生成 3 个不同的项目 所有 3 个项目都位于与第一个项目不同的第二个数据源中 该实施是通过 Oracle DB 使用 Spring Batch 完成的 我认为这question有
  • 矩阵和数组有什么区别?

    更广义的术语是什么 那么为什么 MATLAB 被命名为矩阵实验室呢 矩阵是表示维度空间线性变换的实用方法n到一个维度空间m以一个形式nxm标量值数组 以非常系统的方式进行线性代数运算也非常实用 可以在计算机上实现 例如如果矩阵A表示线性变换
  • 如何检测对 HTML5“下载”属性的支持?

    HTML5 中实现的新功能之一是download锚标记的属性 此属性的好处是 它为用户提供了下载在客户端应用程序中创建的内容的方法 例如图像 例如从画布转换而来 目前 对此功能的支持很差 所以我想知道如何检测浏览器中对此功能的支持 Use
  • Knockout JS“uniqueName”绑定 - 两个字段同名

    我正在使用 Knockout JS 创建一个编辑器 我正在使用 foreach 属性循环模型中的列表 tbody 我正在使用 JQuery 不显眼的验证 它需要一个 name 属性来验证 我想为两个字段分配相同的名称 以便能够输出验证消息
  • 在 C# 中执行 SQL 语句?

    大家好 我想执行我的 SQL 语句 但我遇到语法问题 有人可以帮助我理解我做错了什么吗 谢谢 阿什 public void AddToDatabase string WordArray int Good int Bad int Remove
  • 如何在 IIS 上使用 django 获取静态 CSS 文件?

    我在 IIS8 上安装了 django 现在我想将其配置为提供静态文件 我一直在关注本教程 我已将其添加到设置 py STATIC URL 静态 STATIC ROOT C my django project NZtracker stati
  • UIScrollView 内的 UIDatePicker 与页面

    我有一个包含 2 个页面的 UIScrollView 我可以在它们之间水平滚动 但是 在我的其中一个页面上 我有一个 UIDatePicker 并且滚动视图正在拦截垂直触摸事件 因此我无法再操作日期选择器 除非通过单击或点击 有没有办法告诉
  • 如何在 Mac 上使用 Visual Studio Code 将 Azure 发布设置文件导入到 Angular 项目?

    我已经使用 Visual Studio Code 构建了一个 Angular 应用程序 并被指示使用提供的 publishsettings 文件将其部署到 Azure 我实现这一目标的尝试失败了 除了 publishsettings 文件和
  • PHP - 无法访问外部 URL

    由于流量很大 我最近升级了网站的服务器 在新服务器上 PHP 的某些方面似乎被破坏了 我有一个非常具体的代码 但不起作用 但是 由于版权原因 我只能向您展示非机密的等效内容 这段代码有效绝对完美在升级之前 现在这里或那里的一些奇怪的设置阻止
  • 如何在 C# 中将图像覆盖或叠加到视频上

    我试图弄清楚如何在 Visual C 中将图像覆盖在 保存的文件 视频上 然后重新保存它 在过去的四个小时里 我一直在努力做到这一点 到处寻找 所以任何帮助将不胜感激 DirectShow NET 是 DirectShow 的包装器 Dir
  • UIViewController 自由格式尺寸问题

    我正在使用这个神奇的 PopupViewController https github com martinjuhasz MJPopupViewController 控制 我的应用程序使用故事板 我已经按照开发人员的指示创建了一个自定义 S
  • 提取双引号之间的字符串

    我正在阅读来自期刊或论文来源的响应 并且我将 html 响应作为字符串 如下所示 一些人认为 梦表达了 人格的深刻方面 Foulkes 184 尽管其他人不同意 我的目标只是从给定字符串中提取所有引号并将它们保存到一个列表中 我的方法是 m
  • 如何使用JAVA向COM PORT发送数据? [复制]

    这个问题在这里已经有答案了 可能的重复 Windows 上的 Java 串行通信 朋友们 我想在JAVA中连接并传输数据到COM PORT 虚拟的或原始的 这个问题已经被问过并回答过很多次了 使用Java从串口读取文件 Java读取串口 J
  • 如何编译Boost多线程程序?

    我安装了boost库 没有多线程 一切都很顺利 我如何编译这个测试程序 include
  • iOS 应用程序在 PushViewController 上冻结

    我的导航控制器在按下时会间歇性冻结 它似乎将新的视图控制器添加到堆栈中 但动画从未发生 我还有另外两个容器 它们在屏幕上保存视图控制器 并且在导航控制器冻结后我可以与它们进行良好的交互 真正有趣的是 如果我尝试将另一个视图控制器推送到导航控
  • Python 最小值/最大值的关键字函数

    我试图理解这是如何工作的 my dict a 2 b 1 min my dict key my dict get produces b 这是一项非常酷的功能 我想更好地了解它 基于文档 分钟 可迭代 键 返回可迭代中的最小项或两个或多个参数
  • c# - DbContext 在BackgroundService 中释放

    我有一个 WebAPI 它也应该从 RabbitMQ 接收消息 我用了this教程 因为我知道有时 IIS 喜欢终止长时间运行的任务 但尚未在服务器上测试它 也许它不起作用 我有一个处理通过 RabbitMQ 接收的消息的服务 我遇到的第一