ASP.NET Core находит сервис в IServiceProvider, который соответствует UnderlyingSystemType - PullRequest
0 голосов
/ 18 мая 2019

У меня есть метод, который принимает список событий домена, которые реализуют интерфейс IDomainEvent.Я пытаюсь получить базовый тип IDomainEvent, а затем использовать IServiceProvider для получения службы, соответствующей базовому типу системы.

Например, это работает;

TicketCreatedEvent event = new TicketCreatedEvent();
var service = _serviceProvider.GetService(typeof(IEventHandler<TicketCreatedEvent>));
// Where _serviceProvider is ASP.NET Cores standard DI provider.

Но это не так, поскольку объект события создается как интерфейс, а не как конкретный тип.

IDomainEvent event = new TicketCreatedEvent();
var service = _serviceProvider.GetService(typeof(IEventHandler<TicketCreatedEvent>));

Я знаю, что могу получить базовый тип, используя event.GetType().UnderlyingSystemType, но не могу понять, какчтобы потом использовать это в _serviceProvider.GetService(), я подумал, что мог бы сделать следующее, но это недопустимо;

IDomainEvent event = new TicketCreatedEvent();
Type type = event.GetType().UnderlyingSystemType;
var service = _serviceProvider.GetService(typeof(IEventHandler<type>));

Любой совет о том, как это сделать, был бы оценен.

Извините, кажется, я пропустил некоторую важную информацию из своего вопроса.

В моем примере выше, event создается непосредственно перед использованием _serviceProvider, на самом деле событие передаетсяметод объекта EventRaiser, например:

public static Raise<T>(T args) {
    var service = (IEventHandler<T>) _locator.GetService(typeof(IEventHandler<T>));
}

TicketCreatedEvent event = new TicketCreatedEvent();
Raise(event); // Works

IDomainEvent event = new TicketCreatedEvent();
Raise(event); // Doesn't work as it cannot locate the service

Если T создается с использованием интерфейса, служба не может быть найдена, она работает, только если T напрямую относится к конкретной реализации.

Чтобы помочь решить проблему, вот мой полный DomainEventRaiser класс.Существует два важных метода, первый из которых Raise', which will raise an event right away without being deferred. The second is RaisedDeferred which accepts a list of events and handles them, RaisedDeferred` будет вызываться, когда необходимо отложить события, например, после сохранения объекта домена.

public class DomainEventsRaiser
    {
        [ThreadStatic] //so that each thread has its own callbacks
        private static List<Delegate> _actions;

        // ASP.NET Core DI Service Provider
        private static IServiceProvider _locator;

        /// <summary>
        /// Lets the event raiser know what DI provider to use to find event handlers
        /// </summary>
        /// <param name="provider"></param>
        public static void RegisterProvider(IServiceProvider provider)
        {
            _locator = provider;
        }

        public static void Register2<T>(Action<T> callback) where T : IDomainEvent
        {
            if (_actions == null)
            {
                _actions = new List<Delegate>();
            }

            _actions.Add(callback);
        }

        public static void ClearCallbacks()
        {
            _actions = null;
        }

        /// <summary>
        /// Accepts a list of events, usually deferred events
        /// </summary>
        /// <param name="events">list of events to handle</param>
        public static void RaiseDeferred(IList<IDomainEvent> events)
        {
            if (events != null)
            {
                foreach (IDomainEvent ev in events)
                {
                    Type eventType = ev.GetType().UnderlyingSystemType;
                    Type genericHandlerType = typeof(IEventHandler<>);
                    Type constructedHandlerType = genericHandlerType.MakeGenericType(eventType);
                    var service =  _locator.GetService(constructedHandlerType);
                    constructedHandlerType.GetMethod("Handle").Invoke(service, null);

                    if (service != null)
                    {
                        service.Handle(args); // Cannot resolve symbol 'Handle'
                    }
                }
            }
        }

        /// <summary>
        /// Handles an event right away
        /// </summary>
        /// <param name="args">event arguments</param>
        /// <typeparam name="T">the event to handle</typeparam>
        public static void Raise<T>(T args) where T : IDomainEvent
        {     
            if (_locator != null)
            {   
                var service = (IEventHandler<T>) _locator.GetService(typeof(IEventHandler<T>));
                if (service != null)
                {
                    service.Handle(args);
                }
            }

            if (_actions != null)
            {
                foreach (var action in _actions)
                {
                    if (action is Action<T>)
                    {
                        ((Action<T>) action)(args);
                    }
                }
            }
        }
    }

1 Ответ

1 голос
/ 18 мая 2019

Вы, вероятно, можете сделать что-то вроде этого:

IDomainEvent @event = new TicketCreatedEvent();
Type eventType = @event.GetType().UnderlyingSystemType;
Type genericHandlerType = typeof(IEventHandler<>);
Type constructedHandlerType = genericHandlerType.MakeGenericType(eventType);
var service = _serviceProvider.GetService(constructedHandlerType);

Я предполагаю, что вы регистрируете свои обработчики в Startup.cs как:

services.AddTransient<IEventHandler<TicketCreatedEvent>, TicketCreatedEventHandler>();

Для получения дополнительной информации см. Как: изучить и определить общие типы с помощью отражения .

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...