【问题标题】:Dependency Injection in C# RabbitMQ EventHandlerC# RabbitMQ EventHandler 中的依赖注入
【发布时间】:2019-12-30 09:41:44
【问题描述】:

RabbitMQ 在收到消息时公开一个AsyncEventingBasicConsumer.Received EventHandler。

因为这是一个顶级事件,所以没有可用的依赖注入,除非为它创建了一个特定的范围并作为封闭变量传递。

#pragma warning disable CA2000 // Dispose objects before losing scope
var serviceScope = this.serviceProvider.CreateScope();
#pragma warning restore CA2000 // Dispose objects before losing scope

consumer.Received += async (object sender, BasicDeliverEventArgs eventArgs) => 
{
    try 
    {
         // Do something
    }
    finally
    {
        scope.Dispose();
    }
}

有没有更好的方法来处理像这样的顶级事件的 DI?

【问题讨论】:

  • 当您说“更好的方法”时,问题是“比什么更好?”你想在这里做什么,为什么需要在这个特定位置注入依赖项?
  • 我想知道在我的事件处理程序中是否存在使用注入服务的已知模式。这个解决方案在我看来更像是一种“黑客”,而不是一种将服务注入我的事件处理程序的真正方法。但是,按照我的事件处理程序内部的建议创建一个新的复合根并非易事。我还没想好怎么做。
  • 使用 DI 的原因是为了支持代码的可测试性。测试匿名函数是困难的或不可能的。因此,DI 在这里没有合法目的。这就是我试图了解你在做什么的原因。

标签: c# dependency-injection .net-core rabbitmq


【解决方案1】:

作为Received 处理程序的一部分运行的代码可以被视为您的Composition Root 的一部分。这是您将启动一个新的 DI 范围并从该范围解析您的服务并调用该服务的地方。

这类似于将 DI 应用于基于计时器触发的操作。我的书Dependency Injection Principles, Practices, and PatternsSection 8.3.3 包含一个这样的例子:

// Listing 8.14
public static class Program
{
    private static Composer composer;

    public static void Main(string[] args)
    {
        var money = new Money(    
            currency: new Currency(code: args[0]),    
            amount: decimal.Parse(args[1]));    

        composer = new Composer(LoadConnectionString());

        var timer = new Timer(interval: 60000);    
        timer.Elapsed += (s, e) => DisplayRates(money);  
        timer.Start();    

        Console.WriteLine("Press any key to exit.");
        Console.ReadLine();    
    }

    private static void DisplayRates(Money money)
    {
        CurrencyRateDisplayer displayer =
            composer.CreateRateDisplayer();    

        displayer.DisplayRatesFor(money);
    }

    private static string LoadConnectionString() { ... }
}

【讨论】:

  • 谢谢,这绝对证实我需要重新创建一个合成根。问题是我需要在代码的较低层执行此操作,并且需要弄清楚如何使用所有原始服务重新创建一个。是否有一种巧妙的方法可以使用 .NET DI 扩展来做到这一点,还是我需要重新定义所有服务?
  • 如果您不能将consumer.Received += 作为应用程序启动路径的一部分,解决方案是使用抽象。让consumer.Received += 处理程序依赖于抽象,让组合根提供正确的实现,允许创建消息处理程序并使用消息调用它们。
  • 听起来很有趣。我需要弄清楚如何使处理程序依赖于抽象。不确定我是否可以直接 DI 事件处理程序,但我会尝试这种方式或其他方式。谢谢!
  • 有趣的是,您正在直接使用 RabbitMQ,而不是使用 MassTransit 或 NServiceBus 等框架。它们提供了极大的灵活性和开箱即用的功能(例如自动重新连接、重试、DI 等),否则您必须手动构建和维护。
  • 不知道他们。一定会让他们看看;)谢谢
猜你喜欢
  • 2020-12-07
  • 2010-09-26
  • 2011-07-14
  • 1970-01-01
  • 1970-01-01
  • 2013-05-27
  • 1970-01-01
  • 2010-12-26
  • 2017-03-08
相关资源
最近更新 更多