用Simple Injector在C#中实现域事件处理程序模式 [英] Implementing Domain Event Handler pattern in C# with Simple Injector

查看:174
本文介绍了用Simple Injector在C#中实现域事件处理程序模式的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我试图在C#中使用简单注入器实现域事件模式。



我已将代码简化为一个可作为控制台应用程序运行的文件,并排除了Simple Injector代码,以便为此问题保持清晰。



我遇到的问题是每个事件都可能有多个事件处理程序,并且可能会引发多个事件,但是我想限制我的Dispatcher只处理实现 IEvent的事件界面,所以我把这个约束放在我的Dispatch方法上。



这导致了如何从Simple Injector获取实例的问题, Dispatch 方法被称为 TEvent 是类型 IEvent (as我期望),但我需要获取传入的事件类型,以便从Simple Injector获取相关处理程序。



希望我的代码能够解释

 接口IEvent 
{
}

接口IEventHandler< T> T:IEvent
{
void Handle(T @ event);
}

class StandardEvent:IEvent
{
}

class AnotherEvent:IEvent
{
}

class StandardEventHandler:IEventHandler< StandardEvent>
{
public void Handle(StandardEvent @event)
{
Console.WriteLine(StandardEvent handled);
}
}

class AnotherEventHandler:IEventHandler< AnotherEvent>
{
public void Handle(AnotherEvent @event)
{
Console.WriteLine(AnotherEvent handled);


以下是我的调度程序:

  static class Dispatcher 
{
//我需要在这里获取@event的类型,以便我可以从
// IoC容器(SimpleInjector),但是TEvent类型为IEvent(如预期的那样)。
//我需要做的是从Simple Injector为每个
获取已注册的实例// Event类型,例如Container.GetAllInstances< IEventHandler< StandardEvent>>()
//和Container.GetAllInstances< IEventHandler< AnotherEvent>>()
public static void Dispatch< TEvent>(TEvent @event)where TEvent:IEvent
{
}
}

class PlainOldObject
{
public ICollection< IEvent> Events =新列表< IEvent>
{
New StandardEvent(),
New AnotherEvent()
};

$ b class StandAlone
{
static void Main(string [] args)
{
var poco = new PlainOldObject();
foreach(var @event in poco.Events)
{
Dispatcher.Dispatch(@event);
}
}
}

我已在Dispatch方法是我的问题是。有没有人有任何想法,我应该如何解决这个问题?



问候,
加里

解决方案

您需要的解决方案取决于 Dispatcher 的使用者如何调用事件。如果消费者在编译时总是知道事件的确切类型,那么可以使用上面显示的通用 Dispatch< TEvent>(TEvent)方法。在这种情况下, Dispatcher 的实现将非常简单。



另一方面,消费者可能不会总是知道确切的类型,但只需使用 IEvent 接口, Dispatch< TEvent>(TEvent)就变成了useles,你最好定义一个 Dispatch(IEvent)方法。这使得实现有点复杂,因为你需要使用反射来解决这个问题。



另外请注意,引入一个 IEventDispatcher 抽象。不要在代码中调用静态类。甚至Udi Dahan(最初描述了这样的静态类 a很久以前)现在认为这是一种反模式。相反,将 IEventDispatcher 抽象注入需要事件分派的类中。



如果所有使用者使用的事件类型在编译时已知,您的实现将如下所示:

  public interface IEventDispatcher 
{
void Dispatch< TEvent>(TEvent @event)where TEvent:IEvent;


private sealed class Dispatcher:IEventDispatcher
{
private readonly容器容器;
public Dispatcher(Container container){
this.container = container;

$ b $ public void Dispatch< TEvent>(TEvent @event)where TEvent:IEvent {
if(@event == null)throw new ArgumentNullException(event);

var handlers = this.container.GetAllInstances< IEventHandler< TEvent>>();

foreach(处理程序中的var处理程序){
handler.Handle(@event);



code $如果另一方面,事件类型是未知的,您可以使用以下代码:
$ b

public interface IEventDispatcher
{
void Dispatch(IEvent @event);


private sealed class Dispatcher:IEventDispatcher
{
private readonly容器容器;
public Dispatcher(Container container){
this.container = container;

$ b $ public void Dispatch(IEvent @event){
if(@event == null)throw new ArgumentNullException(event);

类型handlerType = typeof(IEventHandler<>)。MakeGenericType(@ event.GetType());

var handlers = this.container.GetAllInstances(handlerType);

foreach(处理程序中的动态处理程序){
handler.Handle((dynamic)@event);
}
}
}

请注意,使用与使用.NET反射API相比,动态关键字有一些不明显的优势。例如,当使用dynamic调用处理程序的 Handle 方法时,从句柄抛出的任何异常都会直接冒泡。另一方面,当使用 MethodInfo.Invoke 时,异常将被包裹在一个新的异常中。
$ b 您的事件处理程序可以注册如下:

  container.RegisterCollection(typeof(IEventHandler<>),listOfAssembliesToSearch); 


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 excluded the Simple Injector code to keep things clear for the purpose of this question.

The problem I am coming up against is that each event could have multiple event handlers and multiple events could be raised but I want to restrict my Dispatcher to only handle events that implement the IEvent interface so I put that restraint on my Dispatch method.

This caused problems as to how to get the instance from Simple Injector as each time the Dispatch method is called TEvent is of type IEvent (as I would expect) but I need to get the type of the event passed in so I can get the relevant handlers from Simple Injector.

Hopefully my code will explain this a little bit better:

interface IEvent 
{
}

interface IEventHandler<T> where T : IEvent
{
    void Handle(T @event);
}

class StandardEvent : IEvent
{
}

class AnotherEvent : IEvent
{
}

class StandardEventHandler : IEventHandler<StandardEvent>
{
    public void Handle(StandardEvent @event)
    {
        Console.WriteLine("StandardEvent handled");
    }
}

class AnotherEventHandler : IEventHandler<AnotherEvent>
{
    public void Handle(AnotherEvent @event)
    {
        Console.WriteLine("AnotherEvent handled");
    }
}

Here's my dispatcher:

static class Dispatcher
{
    // I need to get the type of @event here so I can get the registered instance from the
    // IoC container (SimpleInjector), however TEvent is of type IEvent (as expected). 
    // What I need to do here is Get the registered instance from Simple Injector for each
    // Event Type i.e. Container.GetAllInstances<IEventHandler<StandardEvent>>()
    // and Container.GetAllInstances<IEventHandler<AnotherEvent>>()
    public static void Dispatch<TEvent>(TEvent @event) where TEvent : IEvent
    {
    }
}

class PlainOldObject
{
    public ICollection<IEvent> Events = new List<IEvent>
    {
        new StandardEvent(),
        new AnotherEvent()
    };
}

class StandAlone
{
    static void Main(string[] args)
    {
        var poco = new PlainOldObject();
        foreach (var @event in poco.Events)
        {
            Dispatcher.Dispatch(@event);
        }
    }
}

I have commented in the Dispatch method what my issue is. Does anyone have any idea on how I should resolve this?

Regards, Gary

解决方案

The solution you need is a bit dependent on how the consumer of the Dispatcher call events. If the consumer always knows the exact type of the event at compile time, you can use the generic Dispatch<TEvent>(TEvent) method as you show above. In that case the Dispatcher's implementation will be really straightforward.

If on the other hand, consumers might not always know the exact type, but simply work with the IEvent interface, the generic type argument in Dispatch<TEvent>(TEvent) becomes useles, and you would be better of defining a Dispatch(IEvent) method. This makes the implementation a bit more sophisticated, because you will need to use reflection to solve this.

Also note that it would be good to introduce an IEventDispatcher abstraction. Don't call a static class from within your code. Even Udi Dahan (who initially described such static class a long time ago) now considers this an anti-pattern. Instead, inject an IEventDispatcher abstraction into classes that require event dispatching.

In case all consumers work with event types that are known at compile time, your implementation will look as follows:

public interface IEventDispatcher
{
    void Dispatch<TEvent>(TEvent @event) where TEvent : IEvent;
}

private sealed class Dispatcher : IEventDispatcher
{
    private readonly Container container;
    public Dispatcher(Container container) {
        this.container = container;
    }

    public void Dispatch<TEvent>(TEvent @event) where TEvent : IEvent {
        if (@event == null) throw new ArgumentNullException("event");

        var handlers = this.container.GetAllInstances<IEventHandler<TEvent>>();

        foreach (var handler in handlers) {
            handler.Handle(@event);
        }
    }
}

If on the other hand, event types are unknown, you can use the following code:

public interface IEventDispatcher
{
    void Dispatch(IEvent @event);
}

private sealed class Dispatcher : IEventDispatcher
{
    private readonly Container container;
    public Dispatcher(Container container) {
        this.container = container;
    }

    public void Dispatch(IEvent @event) {
        if (@event == null) throw new ArgumentNullException("event");

        Type handlerType = typeof(IEventHandler<>).MakeGenericType(@event.GetType());

        var handlers = this.container.GetAllInstances(handlerType);

        foreach (dynamic handler in handlers) {
            handler.Handle((dynamic)@event);
        }
    }
}

Do note that the use of the dynamic keyword has a few unobvious advantages over using the .NET reflection API. For instance, when calling the handler's Handle method using dynamic, any exception thrown thrown from the handle will bubble up directly. When using MethodInfo.Invoke on the other hand, the exception will be wrapped in a new exception. This makes catching and debugging harder.

Your event handlers can be registered as follows:

container.RegisterCollection(typeof(IEventHandler<>), listOfAssembliesToSearch);

这篇关于用Simple Injector在C#中实现域事件处理程序模式的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆