Structuremap ObjectFactory.GetAllInstances<IHandle <TEvent >> ()
Я испытываю трудности с реализацией событий в недавнем проекте.
Я проверил, что structmap сканирует правильно, собирает и добавляет EventHandlers
Scan(cfg =>
{
cfg.TheCallingAssembly();
cfg.IncludeNamespace("ABC.EventHandler");
cfg.ConnectImplementationsToTypesClosing(typeof(IHandle<>));
});
public class StructureMapEventDispatcher : IEventDispatcher
{
public void Dispatch<TEvent>(TEvent eventToDispatch) where TEvent : IDomainEvent
{
foreach (var handler in ObjectFactory.GetAllInstances<IHandle<TEvent>>())
{
handler.Handle(eventToDispatch);
}
}
}
Раньше я запускал Event из домена. Что-то вроде Dispatcher.RaiseEvent(new [domainEvent class](x,y,z));
и событие будет запущено. Мне пришлось изменить дизайн, где я сейчас собираю события в коллекции
_domainEvents = new Collection<IDomainEvent>();
и затем подняв его после того, как я сохранил домен в хранилище
public static void Raise(ICollection<IDomainEvent> domainEvents)
{
foreach (var domainEvent in domainEvents)
{
DomainEventDispatcher.Raise(domainEvent);
}
}
но сейчас
ObjectFactory.GetAllInstances<IHandle<TEvent>>()
возвращает 0 счетчиков обработчиков
если я буду наблюдать за
ObjectFactory.GetAllInstances<IHandle<DomainEventClass>>()
это возвращает коллекцию обработчиков должным образом (в настоящее время у меня есть 2, и это показывает 2 счета)
... Я предполагаю, что это как-то связано с событиями типа IDomainEvent
вместо фактического типа, и это усложняет структурную карту, чтобы решить это.
Как я могу решить эту проблему?
С Уважением,
Мар
-
Изменить 1:
Я подтвердил, что контейнер struturemap содержит все обработчики событий, отсканированные из сборки.
Редактировать 2
Я не знаю, как заставить этот вопрос привлечь больше внимания. Я добавляю награду за решение для достижения желаемых результатов. Если вопрос не понятен, пожалуйста, задавайте.
В основном я хочу ObjectFactory.GetAllInstances<IHandle<TEvent>>()
вернуть обработчики для TEvent
где TEvent
имеет тип IDomainEvent
, События, которые будут подняты, хранятся в коллекции IDomainEvent
и поднял после того, как Домен был сохранен (из уровня обслуживания).
Я думаю, что должен быть какой-то способ заставить структуру структуры знать, что событие возникло как IDomainEvent
на самом деле типа DomainEvent
var eventsToRaise= dealerr.EventsToRaise(); Добавление информации из окна отладки:
После того, как события были подняты в окне диспетчера
Изменить 3:
Eventhough eventToRaise отображается как "DealerName Changed" и "DealerCommunicationChanged"
typeof (TEvent) дает тип как Domain.IDomainEvent
Я догадываюсь, возможно ли получить возможность приводить к нужному типу (из окна наблюдения ВС, где появляется информация) проблема может быть решена
----- Результат ---
Оба подхода сработали. Я связал обоих с двумя другими членами в моей команде, и мы чувствовали, что это решение, не задумываясь, будет выбрано как правильный ответ.
Сегодня мы проведем тестирование с измененной реализацией и посмотрим, есть ли какие-либо проблемы с этим решением в решении.
Я проголосовал за решение, основанное на рефлексии, так как это также правильный ответ.
2 ответа
Вместо подхода, основанного на рефлексии, я бы рекомендовал использовать запись для хранения информации о типе. Что-то вроде этого:
interface IEventRecord
{
void Dispatch(IEventDispatcher dispatcher);
}
public class EventRecord<TEvent> : IEventRecord where TEvent : IDomainEvent
{
TEvent theEvent;
public EventRecord(TEvent theEvent)
{
this.theEvent = theEvent;
}
public void Dispatch(IEventDispatcher dispatcher)
{
dispatcher.Dispatch(theEvent);
}
}
Если вы находите создание экземпляров записей событий громоздким, помощник может вывести параметр типа следующим образом:
public static EventRecord<TEvent> CreateEventRecord<TEvent>(TEvent theEvent) where TEvent : IDomainEvent
{
return new EventRecord<TEvent>(theEvent);
}
Который позволил бы вам создавать экземпляры записей о событиях следующим образом:
var record = CreateEventRecord(myDomainEvent);
Тогда вместо того, чтобы держаться за коллекцию IDomainEvent
s, держись за коллекцию IEventRecords
которые содержат необходимые данные типа, чтобы поднять себя:
foreach (var eventRecord in Records)
{
eventRecord.Dispatch(myDispatcher);
}
Как вы говорите, проблема в том, что вы запрашиваете структуру карты для всех случаев IHandle<IDomainEvent>
и он не имеет ни одного из них, Structuremap имеет обработчики для конкретных событий. Вам нужно будет создать тип, используя фактический тип события, а затем запросить все обработчики этого события:
Type genericHandler = typeof(IHandle<>);
Type[] typeArgs = { eventToDispatch.GetType() };
Type neededHandler = genericHandler.MakeGenericType(typeArgs);
var handlers = ObjectFactory.GetAllInstances(neededHandler);
проблема в том, что вы в конечном итоге получаете IList объектов, и вам нужно привести их к правильному типу обработчика, и это становится немного сложнее.... возможное решение состоит в использовании отражения для вызова Handle()
метод:
var methodInfo = neededHandler.GetMethod("Handle");
object[] methodArgs = new object[] { eventToDispatch };
foreach (var h in handlers)
{
methodInfo.Invoke(h, methodArgs);
}