Implementing Domain Event Handler pattern in C# with Simple Injector

后端 未结 3 878
南方客
南方客 2021-02-02 04:20

I am trying to implement the Domain Event pattern in C# using Simple Injector.

I have simplified my code to be in one file that can be ran as a console app and have excl

3条回答
  •  孤街浪徒
    2021-02-02 04:52

    Here is a faster version using cached delegates. No dynamic and no reflection after the first call. Uses IServiceProvider from the default Microsoft DI but that can be easily changed. Expression trees can be used too, but they take more memory:

    public class EventDispatcherService : IEventDispatcher
    {
        private static readonly ConcurrentDictionary>> HandlersCache
            = new ConcurrentDictionary>>();
    
        private static readonly Type HandlerType = typeof(IEventHandler<>);
    
        private static readonly MethodInfo MakeDelegateMethod = typeof(EventDispatcherService)
            .GetMethod(nameof(MakeDelegate), BindingFlags.Static | BindingFlags.NonPublic);
    
        private static readonly Type OpenGenericFuncType = typeof(Func<,>);
    
        private static readonly Type TaskType = typeof(Task);
    
        private readonly IServiceProvider serviceProvider;
    
        public EventDispatcherService(IServiceProvider serviceProvider)
            => this.serviceProvider = serviceProvider;
    
        public async Task Dispatch(IDomainEvent domainEvent)
        {
            var eventHandlers = HandlersCache.GetOrAdd(domainEvent.GetType(), eventType =>
            {
                var eventHandlerType = HandlerType.MakeGenericType(eventType);
    
                var makeDelegate = MakeDelegateMethod.MakeGenericMethod(eventType);
    
                var funcType = OpenGenericFuncType.MakeGenericType(eventType, TaskType);
    
                return this.serviceProvider
                    .GetServices(eventHandlerType)
                    .Select(handler => handler
                        .GetType()
                        .GetMethod("Handle")
                        .CreateDelegate(funcType, handler))
                    .Select(handlerDelegateConcrete => (Func)makeDelegate
                        .Invoke(null, new object[] { handlerDelegateConcrete }))
                    .ToList();
            });
    
            foreach (var eventHandler in eventHandlers)
            {
                await eventHandler(domainEvent);
            }
        }
    
        private static Func MakeDelegate(Func action)
            => value => action((T)value);
    }
    

    And this is the event handler interface:

    public interface IEventHandler
        where TEvent : IDomainEvent
    {
        Task Handle(TEvent domainEvent);
    }
    

提交回复
热议问题