У меня есть метод, который принимает список событий домена, которые реализуют интерфейс 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);
}
}
}
}
}