Как инвертировать / внедрить зависимость - MassTransit Consumer

Я работаю над проектом, и все работает, но у меня есть одна тесно связанная зависимость, которую я не понимаю, как инвертировать / ввести.

Проблема в моем классе 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 так как это на самом деле реализация IConsumer интерфейс MassTransit,

Я думаю, что я хочу, это способ инвертировать / внедрить эту зависимость, чтобы мой глобальный 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

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));
    });
});

Вы можете найти больше перегрузок для метода конфигурации потребителя на конечной точке в документации.

Еще одна вещь. У вас серьезные проблемы с Serilog. Вы создаете конфигурацию логгера для каждого сообщения, которое вы потребляете. Это неправильно. Вы должны создать конфигурацию логгера один раз в точке входа вашего приложения.

Затем вы либо вводите свой логгер, либо используете глобальный Log объект или использование MassTransit.SerilogIntegration Упакуйте и используйте MassTransit, входящий в систему ваших потребителей.

Другие вопросы по тегам