5

我在最近的一个项目中很难实现事件。

我已验证结构映射正在正确扫描组装并添加 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);

            }

        }

    }

在我曾经从域中触发事件之前。类似的东西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>>()返回处理程序TEventwhere TEventis of Type IDomainEvent。要引发的事件存储在 Collection of 中IDomainEvent,并在保存域(从服务层)之后引发。

我在想应该有某种方法让结构图知道引发的事件IDomainEvent实际上是类型DomainEvent

var eventsToRaise=dealer.EventsToRaise(); 从调试窗口添加信息:

在调度程序窗口中引发事件之后

在此处输入图像描述

编辑 3:尽管 eventToRaise 显示为“DealerName Changed”和“DealerCommunicationChanged”
typeof(TEvent) 将 Type 提供为 Domain.IDomainEvent

我猜想是否有可能转换为正确的类型(从 VS 监视窗口获取信息的任何地方)问题可能会得到解决

- - - 结果 - -

两种方法都有效。我将两者都联系到了我团队中的其他 2 名成员,我们认为没有反思的解决方案被选为正确答案。

今天我们将做一个改变实现的测试,看看这个解决方案在解决方案中是否有任何问题。

我赞成基于反射的解决方案,因为它也是正确的答案。


4

2 回答 2

4

正如您所说,问题在于您要为所有实例询问结构映射,IHandle<IDomainEvent>但它没有这些实例,结构映射具有具体事件的处理程序。您需要使用事件的实际类型构造类型,然后询问该事件的所有处理程序:

        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);
        }
于 2011-06-16T17:17:58.823 回答
1

我建议不要使用基于反射的方法,而是使用记录来为您保留类型信息。像这样的东西:

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);

然后,与其持有一个IDomainEvents 的集合,不如持有一个IEventRecords持有必要类型数据的集合来提升自己:

foreach (var eventRecord in Records)
{
    eventRecord.Dispatch(myDispatcher);
}
于 2011-06-16T18:09:34.650 回答