Как инвертировать / внедрить зависимость - 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, входящий в систему ваших потребителей.