I have a piece of code a bit ugly because I have to use reflection to make it work. I can't help but wonder whether there is a better/cleaner way of achieving the same.
The scenario is the following: my code subscribes to an event store. Every time the event store records a new event my method Task Process(IResolvedEvent resolvedEvent)
is executed. At this time, I map (deserialize) the resolvedEvent
to its original IPersistableEvent
and I use its type to get from the DI container (Autofac) the registered IEventHandler<TEvent> where TEvent : IPersistableEvent
so that I can handle this event with it.
As you can see, the type of the event is only known at runtime and I have to use reflection twice. The first time to use the factory that retrieves the proper IEventHandler<TEvent>
from the DI container, and the second time to invoke the method Handle
on that instance.
The main issue I'd like to refactor is the fact that the method Invoke
returns an object, and I want to cast this object to a IEventHandler<TEvent>
where I know the TEvent
at runtime.
It would be great if I could do something like this is C#:
var eventHandler = (IEventHandler<?>)createMethodGeneric.Invoke(_eventHandlerFactory, null);
await eventHandler.Handle(persistableEvent);
Unfortunately, I cannot cast it to a generic class in that way. Is it possible to do the casting in a different way with or preferably without reflection? This is my current working code.
public class ResolvedEventDispatcher
: IResolvedEventDispatcher
{
private readonly IMapper<IResolvedEvent, IPersistableEvent> _persistableEventMapper;
private readonly IEventHandlerFactory _eventHandlerFactory;
public ResolvedEventDispatcher(
IMapper<IResolvedEvent, IPersistableEvent> persistableEventMapper,
IEventHandlerFactory eventHandlerFactory)
{
_persistableEventMapper = persistableEventMapper;
_eventHandlerFactory = eventHandlerFactory;
}
public async Task Process(IResolvedEvent resolvedEvent)
{
try
{
var persistableEvent = _persistableEventMapper.Map(resolvedEvent);
var persistableEventType = persistableEvent.GetType();
var eventHandlerFactoryType = _eventHandlerFactory.GetType();
var createMethod =
eventHandlerFactoryType
.GetMethods()
.First(x => x.IsGenericMethod);
var typeArguments =
new[]
{
persistableEventType
};
var createMethodGeneric = createMethod.MakeGenericMethod(typeArguments);
var eventHandler = createMethodGeneric.Invoke(_eventHandlerFactory, null);
var eventHandlerType = eventHandler.GetType();
var handleMethod = eventHandlerType.GetMethod("Handle");
var parameters =
new object[]
{
persistableEvent
};
await (Task) handleMethod.Invoke(eventHandler, parameters);
}
catch (Exception exception)
{
var foo = exception;
}
}
}
And this is the factory that I use to retrieve the proper IEventHandler<TEvent>
from the DI container:
public class EventHandlerFactory
: IEventHandlerFactory
{
private readonly IComponentContext _componentContext;
public EventHandlerFactory(IComponentContext componentContext)
{
_componentContext = componentContext;
}
public IEventHandler<TEvent> Create<TEvent>() where TEvent : class, IPersistableEvent
{
var eventType = typeof(TEvent);
var eventHandlerType = typeof(IEventHandler<>);
var typeArguments =
new[]{
eventType
};
var eventHandlerWithGenericType = eventHandlerType.MakeGenericType(typeArguments);
var isRegistered = _componentContext.IsRegistered(eventHandlerWithGenericType);
if (!isRegistered)
{
throw new EventHandlerNotRegisteredException($"Could not find any handler for event type {eventType}");
}
var eventHandler = _componentContext.Resolve(eventHandlerWithGenericType);
return (IEventHandler<TEvent>)eventHandler;
}
}
Finally, the handler I want to invoke is defined by:
public interface IEventHandler<in TEvent>
where TEvent : IPersistableEvent
{
Task Handle(TEvent @event);
}
Aucun commentaire:
Enregistrer un commentaire