如何反转/注入依赖 - MassTransit 消费者

Jac*_*nes 3 c# reflection dependency-injection masstransit constructor-injection

我正在处理一个项目并且一切正常,但是我有一个紧密耦合的依赖项,我不知道如何反转/注入。

问题出在我的 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);
    }
}
Run Code Online (Sandbox Code Playgroud)

Task Consume是 MassTransit 的一部分,我无法修改 的签名,Consume因为这实际上IConsumerMassTransit.

我想我想要的是一种反转/注入该依赖项的方法,以便我的全局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);
    }
}
Run Code Online (Sandbox Code Playgroud)

通过使用反射并将此逻辑放入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();


    }
Run Code Online (Sandbox Code Playgroud)

但是Consumer由于我已经提到的限制以及我确定的健康无知,我无法对它应用类似的策略。

如果这是可能的,有人请指出我正确的方向吗?

更多信息:

项目:App.SubscriberConsole-> 参考App.Services.Subscriber

项目:App.Services.Subscriber-> 参考资料MyCompany.MQ.Services.Consumer

项目MyCompany.MQ.Services.Consumer-> 参考MassTransit-> 实现MassTransit.IConsumer

Ale*_*rev 5

我不确定您为什么考虑注入该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;
    }
}
Run Code Online (Sandbox Code Playgroud)

然后你像这样配置它:

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));
    });
});
Run Code Online (Sandbox Code Playgroud)

您可以在文档中的端点上找到更多消费者配置方法的重载。

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

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