Как сделать инъекцию зависимостей / зарегистрировать открытые универсальные типы - PullRequest
0 голосов
/ 10 сентября 2018

У меня есть архитектура микросервисов, использующая простой инжектор в каждом сервисе. Службы взаимодействуют через служебную шину Azure. В настоящее время я пытаюсь найти способ реализации универсального решения / библиотеки для взаимодействия с Azure Service Bus. Библиотека является базовой инфраструктурой служб и имеет издателя тем (для передачи событий / сообщений в Azure) и подписчика (для прослушивания сообщений из Azure). Кроме того, у меня есть общий интерфейс для событий / сообщений, содержащих идентификатор и метку времени для создания. У меня также есть универсальный интерфейс для обработчиков событий IEventHandler<T> where T : IEvent. Теперь моя проблема в том, как мне лучше сохранить корень композиции отделенным от остальной части кода, сохраняя при этом возможность регистрировать набор обработчиков для различных типов событий в данном сервисе?

Чтение документов для простого инжектора предполагает фабрику или что-то в этом роде, но мой интерфейс универсален, а фабрика не делает публичный IEventHandler GetHandler (Type eventType) недопустимым ...

ОБНОВЛЕНИЕ : добавлен код

Издательство:

   public interface IEventPublisher
    {
        Task PublishAsync(IEvent @event);
    }

    public class EventPublisher : IEventPublisher
    {
        private readonly ITopicClient topicClient;

        public EventPublisher(ITopicClient topicClient)
        {
            this.topicClient = topicClient;
        }

        public async Task PublishAsync(IEvent @event)
        {
            try
            {
                var json = JsonConvert.SerializeObject(@event);
                var message = new Message()
                {
                    Body = Encoding.UTF8.GetBytes(json),
                    PartitionKey = nameof(@event),
                    MessageId = @event.Id.ToString()
                };
                message.UserProperties.Add("Type", @event.GetType().FullName);
                await topicClient.SendAsync(message);
            }
            catch (Exception e)
            {
                //Handle error
            }
        }
    }

Обработка событий:

public interface IEventHandler<T> where T : IEvent
{
    void HandleEvent(T @event);
}

public interface IEventSubscriber
{
//Currently empty, might need some method for registration of handlers?
}

public class EventSubscriber : IEventSubscriber
{
    private readonly ISubscriptionClient subscriptionClient;

    public EventSubscriber(ISubscriptionClient subscriptionClient, )
    {
        this.subscriptionClient = subscriptionClient;

        var messageHandlerOptions = new MessageHandlerOptions(ExceptionReceivedHandler)
        {
            AutoComplete = false
        };

        this.subscriptionClient.RegisterMessageHandler(ProcessMessagesAsync, messageHandlerOptions);
    }

    private Task ExceptionReceivedHandler(ExceptionReceivedEventArgs exceptionReceivedEventArgs)
    {
        var context = exceptionReceivedEventArgs.ExceptionReceivedContext;
        logger.Error($"Message handler encountered an exception {exceptionReceivedEventArgs.Exception}.");
        logger.Error("Exception context for troubleshooting:");
        logger.Error($"- Endpoint: {context.Endpoint}");
        logger.Error($"- Entity Path: {context.EntityPath}");
        logger.Error($"- Executing Action: {context.Action}");
        return Task.CompletedTask;
    }

    private async Task ProcessMessagesAsync(Message message, CancellationToken cancellationToken)
    {
        JsonConvert.DeserializeObject<BankDataChangedEvent>(Encoding.UTF8.GetString(message.Body));
        // HERE I NEED SOME CODE TO FETCH/FIND THE RIGHT HANDLER FOR THE EVENT TYPE
        await subscriptionClient.CompleteAsync(message.SystemProperties.LockToken);
    }
}

Большинство из них и подписчик являются стандартным кодом из документации Microsoft для Azure Service Bus с .Net - только слегка изменены.

1 Ответ

0 голосов
/ 10 сентября 2018

Может быть, я знаю, что вы хотите.У вас есть что-то вроде этого?

internal sealed class CommonEventConsumer :
    IConsumer<Event1>,
    IConsumer<Event2>
{
    private readonly ISomeService _someService;

    public CommonEventConsumer(ISomeService someService)
    {
        _someService = someService;
    }

    public async Task HandleEventAsync(Event1 eventMessage)
    {
        await _someService.DoSomeThing1(eventMessage);
    }

    public async Task HandleEventAsync(Event2 eventMessage)
    {
        await _someService.DoSomeThing2(eventMessage);
    }
}

, где IConsumer<Tevent> - это глобальный интерфейс, который имеет метод HandleEventAsync(Tevent event message);, а CommonEventConsumer - средний потребитель в любом вашем микросервисе.

ТакжеТип публикации:

public sealed class EventPublisher : IEventPublisher
{
    public async Task PublishAsync<T>(T eventMessage)
    {
        var subscriptions = DependencyResolver.ResolveAll<IConsumer<T>>();

        foreach (var subscription in subscriptions)
        {
            await subscription.HandleEventAsync(eventMessage);
        }
    }
}

, где я разрешаю все подписчики событий и отправляю им сообщения.

Если да, то у меня такая же структура в моем приложении, и моя регистрация SimpleInjector для событий EventConsumersвыглядит так:

private static void RegisterConsumers(Container container)
    {
        container.Register<IEventPublisher, EventPublisher>(Lifestyle.Scoped);

        container.Collection.Register(typeof(IConsumer<>), new[] {
            typeof(CommonConsumer),
            typeof(MeasurementEventConsumer),
            typeof(PartRepairEventConsumer),
            typeof(OrderItemEventConsumer),
            typeof(OrderTaskEventConsumer),
            typeof(OrderItemWorkStatusEventConsumer),
            typeof(OrderItemTaskEventConsumer),
            typeof(OrderTaxEventConsumer)

       });
    }
...