【问题标题】:How to invert/inject dependency - MassTransit Consumer如何反转/注入依赖项 - MassTransit Consumer
【发布时间】:2017-12-01 00:34:09
【问题描述】:

我正在做一个项目,一切正常,但我有一个紧密耦合的依赖项,我不明白如何反转/注入。

问题出在我的 Consumer 类中,它将接收命令消息以启动进程,该进程是全局消息队列服务项目的一部分,例如MyCompany.MQ.Services,但对命令消息告诉它启动的进程具有紧密耦合的依赖关系,例如:

public Task Consume(ConsumeContext<MyMessageInterface> context)
{
    logger = new LoggerConfiguration()
        .WriteTo.Console()
        .CreateLogger();

    try
    {
        TightCoupleProcess tcp = new TightCoupleProcess(context);

        logger.Information("{Blah}, {Blah}, {Blah}", context.Message.exampleVar1, context.Message.exampleVar2, context.Message.exampleVar3);

        tcp.StartProcess();

        return Task.CompletedTask;

    }
    catch (Exception ex)
    {
        return Task.FromException(ex);
    }
}

Task Consume 是 MassTransit 的一部分,我无法修改 Consume 的签名,因为这实际上是 MassTransitIConsumer 接口的实现。

我想我想要的是一种反转/注入该依赖项的方法,以便我的全局 MQ.services 项目不依赖于调用它的项目。我想我对反转/注入有一些误解,但我不确定如何表达我的缺点。也许我想要的不可能。我知道我不能修改接口实现,但如果像下面这样的工作我会很酷,但由于 Consume 是 MassTransit 接口的实现,我认为我不能从我的调用类:

public Task Consume(ConsumeContext<MyMessageInterface> context, AnonFunc() func)
{ 
    try
    {
        func(context)

        logger.Information("{Blah}, {Blah}, {Blah}", context.Message.exampleVar1, context.Message.exampleVar2, context.Message.exampleVar3);

        return Task.CompletedTask;

    }
    catch (Exception ex)
    {
        return Task.FromException(ex);
    }
}

我已经设法绕过其他依赖项,例如消息类型定义,方法是使用反射并将此逻辑放入MQ.Services 项目中,这使我可以将所有与TightCoupleProcess 进程相关的代码保留在MQ.serives 之外项目,例如:

    public void PublishMessage(object msg)
    {
        MethodInfo method = this.GetType().GetMethod("InvokePublish");
        MethodInfo generic = method.MakeGenericMethod(msg.GetType());
        generic.Invoke(this, new object[] { msg });
    }

    public void InvokePublish<T>(object msg)
    {
        Task.Run(async () =>
        {
            await busControl.Publish(msg);
        }).Wait();


    }

但我不能对Consumer 应用类似的策略,因为我已经提到了一些限制,而且我敢肯定,这是一种健康的无知。

如果可能的话,有人请指点我正确的方向吗?

更多信息:

项目:App.SubscriberConsole -> 引用 App.Services.Subscriber

项目:App.Services.Subscriber -> 引用 MyCompany.MQ.Services.Consumer

项目MyCompany.MQ.Services.Consumer -> 引用MassTransit -> 实现MassTransit.IConsumer

【问题讨论】:

    标签: c# reflection dependency-injection masstransit constructor-injection


    【解决方案1】:

    我不确定您为什么要考虑注入Consume 方法。方法签名来自接口,不能更改。

    您应该在消费者类构造函数中注入。考虑注入工厂委托是正确的。

    public class MyMessageConsumer : IConsumer<MyMessage>
    {
        private readonly Func<IConsumeContext<MyMessage>, TightCoupleProcess> factory;
    
        public MyMessageConsumer(Func<IConsumeContext<MyMessage>, TightCoupleProcess> factory)
        {
            _factory = factory;
        }
    
        public Task Consume(ConsumeContext<MyMessage> context)
        {
            var tcp = _factory(context);
            tcp.StartProcess();
    
            return Task.CompletedTask;
        }
    }
    

    然后你像这样配置它:

    Func<IConsumeContext<MyMessage>, TightCoupleProcess> factory = c => new TightCoupleProcess(c);
    var busControl = Bus.Factory.CreateUsingRabbitMq(cfg =>
    {
        var host = cfg.Host(new Uri("rabbitmq://localhost/"), h =>
        {
            h.Username("guest");
            h.Password("guest");
        });
    
        cfg.ReceiveEndpoint(host, "customer_update_queue", e =>
        {
            e.Consumer<MyMessageConsumer>(() => new MyMessageConsumer(factory));
        });
    });
    

    您可以在端点in the documentation 上找到更多消费者配置方法的重载。

    还有一件事。您对 Serilog 有严重的问题。您为您使用的每条消息创建记录器配置。这个不对。您应该在应用程序入口点创建记录器配置一次

    然后,您可以注入您的记录器,或者使用全局 Log 对象,或者使用 MassTransit.SerilogIntegration 包并在您的消费者中使用 MassTransit 日志记录。

    【讨论】:

    • 嗨,Alexey,感谢您的回复。我知道我无法更改Consume 上的签名,但我不知道如何完成我所追求的目标。我不知道这是可能的e.Consumer&lt;MyMessageConsumer&gt;(() =&gt; new MyMessageConsumer(factory)); 所以在我的 Consumer 类中,我什至没有构造函数,只有默认值。
    • 关于 Serilog,我在调用应用程序中使用它,因为我无法让 MT.SerilogIntegration 轻松工作,并且在阅读 Github 中的代码之外没有找到任何文档,我只是使用了 Serilog本身,但计划在我开始使用 Saga 和状态机时使用 MT 包。无论如何,我明白你对每条消息的新配置的意思。我可以应用完全相同的策略来正确注入记录器吗?
    • 我想这可能是无知/误解发挥作用的地方。我刚刚完成了您的解决方案的实施,但似乎没有什么不同。我仍然必须引用其他项目才能找到构造函数中的类型和 Func。我如何反转/注入依赖项?
    • 实际上,我想我的组织可能很重要。我创建了一个MQ.Services 项目,其中有一个具有总线配置的MQHelper 类,例如CreateBus() / CreateInMemQuartzBus() 等。Consumer 类也在 MQ.Services 中,我现在认为这是糟糕的设计。我不知道我是否正确地传达了我正在尝试的内容,但我试图在 MassTransit 周围创建一个 MyCompanyWrapper 并让调用类传递它们的消费者实现和依赖项,如果这有意义的话。
    • 我添加了一个文档链接,您可以在其中阅读有关消费者配置的信息。从那里向下滚动,代码示例中列出了所有可能的配置选项。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-04-20
    • 1970-01-01
    • 1970-01-01
    • 2012-02-05
    • 2011-03-14
    相关资源
    最近更新 更多