Adapt/Wrap 现有框架中的 MediatR 通知

Adapt/Wrap MediatR notifications in existing framework

TL;DR 如何调整现有的一组 events/notifications 和相关处理程序,以便 MediatR 使用它们而不必为每个现有类型实施额外的处理程序和通知?

长版: 我们目前有一个用于调度事件的系统,我们在其中使用总线实现来进行实际传输。

我目前正在试验一个进行中的变体。 与 MediatR 的 INotification 非常相似,我们有一个标记界面 IEvent 同样类似于 MediatR 的 INotificationHandler<in INotification> 我们有一个 IEventHandler<in IEvent>

我们使用 Autofac 来注册我们的 EventHandlers,然后我们在实际从总线上消费时进行一些反射。

我想做但又无法理解的是,实现某种通用的 adapter/wrapper 以便我可以保留现有的事件和事件处理程序,并在从 MediatR 发送通知时使用它们,基本上减少了我现有代码围绕设置容器的更改量。

就代码而言,我想让以下 class 表现得好像它在使用 MediatR 接口一样。

public class MyHandler : IEventHandler<SampleEvent>
{
    public Task Handle(SampleEvent input) => Task.CompletedTask;
}

有什么建议吗?

现状

所以现在您的代码如下所示:

public class SampleEvent : IEvent
{
}

public class SampleEventHandler : IEventHandler<SampleEvent>
{
    public Task Handle(SampleEvent input) => Task.CompletedTask;
}

使事件与 MediatR 兼容

我们需要 MediatR 识别的事件,这意味着他们需要实施 INotification

第一种方法是让您的 IEvent 接口实现 INotification。这样做的好处是几乎没有代码更改,它使您所有当前和新的事件都与 MediatR 兼容。可能不太好的事情是,您当前的 IEvent 实现所在的程序集需要依赖 MediatR。

public interface IEvent : INotification
{
}

如果这不可行,我看到的第二种方法是创建新的、特定于 MediatR 的 classes,它们继承现有的并实现 INotification。这意味着您需要为每个现有 class 创建一个适配器 class,但您可以从 MediatR 依赖项中释放现有项目。

// Lives in AssemblyA
public class ExistingEvent : IEvent
{
}

// Lives in AssemblyB that has a dependency on both
// AssemblyA and MediatR
public class MediatrExistingEvent : ExistingEvent, INotification
{
}

连接处理程序

无论您在上一步中采用哪种方式,您现在所处的状态是您有 class 实现了 IEventINotification,并且您有处理程序实施 IEventHandler<in T> where T : IEvent.

我们可以创建一个适配器 class 来满足 MediatR 的 API 并将工作委托给您现有的处理程序:

public class MediatrAdapterHandler<T> : INotificationHandler<T>
    where T : IEvent, INotification
{
    private readonly IEventHandler<T> _inner;

    public MediatrAdapterHandler(IEventHandler<T> inner)
    {
        _inner = inner;
    }

    public Task Handle(T notification) => _inner.Handle(notification);
}

最后一件事是在Autofac容器中注册这个class。鉴于您现有的处理程序已注册为 IEventHandler<T>,这很容易完成:

builder
    .RegisterGeneric(typeof(MediatrAdapterHandler<>))
    .As(typeof(INotificationHandler<>));

现在每次您向容器请求 INotificationHandler<T> 的实例时,它都会创建一个 MediatrAdapterHandler<T> 的实例,其中将注入您对 IEventHandler<T> 的原始实现。