MediatR中介者的一些理解以及处理事件排序

源码相关

MediaR是一个中介者库,里面实现了请求响应/发布订阅两种模式。

内部相当于一个容器,由源码可知,内部创建时注入了一个容器委托(从容器中获得对应的实现接口的实例)。

       // 这里注入了一个委托,用于是从服务容器中获取对应项实例。
        public Mediator(ServiceFactory serviceFactory)
        {
            _serviceFactory = serviceFactory;
        }
// 这一段有两个方法,一个是获得一个实例,一个是获得所有实现了这个接口的实例,这个是给发布订阅用的。    

public delegate object ServiceFactory(Type serviceType); public static class ServiceFactoryExtensions { public static T GetInstance<T>(this ServiceFactory factory) => (T) factory(typeof(T)); public static IEnumerable<T> GetInstances<T>(this ServiceFactory factory) => (IEnumerable<T>) factory(typeof(IEnumerable<T>)); }

获得所有的订阅者后,将在Mediator的方法Publish依次调用,如果途中取消,可以使用CancellationToken进行标记。

        protected virtual async Task PublishCore(IEnumerable<Func<INotification, CancellationToken, Task>> allHandlers, INotification notification, CancellationToken cancellationToken)
        {
            foreach (var handler in allHandlers)
            {
                await handler(notification, cancellationToken).ConfigureAwait(false);
            }
        }

发布订阅排序问题

问题:NOP中可以有顺序,那么问题来了,从上面看来,MediatR并没有给我们对订阅者排序的选择。那么我们如何才能对事件处理者排序呢?

目前有两种方式,第一种是修改MediatR的 源码,添加特性

namespace MediatR.Attributes
{
    [AttributeUsage(AttributeTargets.Class)]
    public sealed class EventOrderAttribute : Attribute
    {
        /// <summary>
        /// 排序值。
        /// </summary>
        public int Order { get; set; }
    }
}

总所周知,依赖注入一个接口多个实现的时候,可以通过IEnumerable获得所有的实现。

因此,我们可以在获取势力的时候,即上面的方法ServiceFactoryExtensions.IEnumerable<T> GetInstances<T>(this ServiceFactory factory)中处理

如下:

    public static class ServiceFactoryExtensions
    {
        public static T GetInstance<T>(this ServiceFactory factory)
            => (T)factory(typeof(T));

        public static IEnumerable<T> GetInstances<T>(this ServiceFactory factory)
        {
            var instanceList = (IEnumerable<T>)factory(typeof(IEnumerable<T>));

       // 反射进行排序。
if (instanceList.Any(o => o.GetType().GetCustomAttributes().Any(o => o.GetType().Equals(typeof(EventOrderAttribute))))) instanceList = instanceList .OrderBy(o => o.GetType().GetCustomAttribute<EventOrderAttribute>() == null ? 0 : o.GetType().GetCustomAttribute<EventOrderAttribute>().Order).ToList(); return instanceList; }

当然,以上方案是不推荐的,毕竟修改了核心源码,但我们依然可以利用容器的特性,安装一定顺序进行排序,然后依次注入,即可实现上面一样的效果。

首先依然保留原来的特性,这次我们把魔手伸向

MediatR.Extensions.Microsoft.DependencyInjection 这个项目中,一下只是为了快速参考,所以直接改这个,你也可以自己实现一个。

在这个项目中,主要的是ServiceRegistrar这个类,进行注册,里面是以此注入不同的接口。

关注:ServiceRegistrar.ConnectImplementationsToTypesClosing方法

private static void ConnectImplementationsToTypesClosing(Type openRequestInterface,
            IServiceCollection services,
            IEnumerable<Assembly> assembliesToScan,
            bool addIfAlreadyExists)
        {
            // 这个集合收集中保存的是处理者。
            var concretions = new List<Type>();

            // 这个集合保存的是处理者的接口。
            var interfaces = new List<Type>();
            foreach (var type in assembliesToScan.SelectMany(a => a.DefinedTypes).Where(t => !t.IsOpenGeneric()))
            {
                var interfaceTypes = type.FindInterfacesThatClose(openRequestInterface).ToArray();
                if (!interfaceTypes.Any()) continue;

                if (type.IsConcrete())
                {
                    concretions.Add(type);
                }

                foreach (var interfaceType in interfaceTypes)
                {
                    interfaces.Fill(interfaceType);
                }
            }

            // 这一部分是将接口与相应的实例者相匹配,然后注册到容器中。
            foreach (var @interface in interfaces)
            {
                var exactMatches = concretions.Where(x => x.CanBeCastTo(@interface)).ToList();
                if (addIfAlreadyExists)
                {
                    foreach (var type in exactMatches)
                    {
                        services.AddTransient(@interface, type);
                    }
                }
                else
                {
                    if (exactMatches.Count > 1)
                    {
                        exactMatches.RemoveAll(m => !IsMatchingWithInterface(m, @interface));
                    }

                    // 针对一个接口的注册,注入多个矗立着。
                    foreach (var type in exactMatches)
                    {
                        services.TryAddTransient(@interface, type);
                    }
                }

                if (!@interface.IsOpenGeneric())
                {
                    AddConcretionsThatCouldBeClosed(@interface, concretions, services);
                }
            }
        }

那么我们也只需要按照这种形式,先排序在一次注入到容器中。

如下:

        private static void ConnectImplementationsToTypesClosing(Type openRequestInterface,
            IServiceCollection services,
            IEnumerable<Assembly> assembliesToScan,
            bool addIfAlreadyExists)
        {
            // 这个集合收集中保存的是处理者。
            var concretions = new List<Type>();

            // 新增一个用于收集排序的集合。
            var concretionsOrder = new List<Type>();

            // 这个集合保存的是处理者的接口。
            var interfaces = new List<Type>();
            foreach (var type in assembliesToScan.SelectMany(a => a.DefinedTypes).Where(t => !t.IsOpenGeneric()))
            {
                var interfaceTypes = type.FindInterfacesThatClose(openRequestInterface).ToArray();
                if (!interfaceTypes.Any()) continue;

                if (type.IsConcrete())
                {
                    if (type.GetCustomAttributes().Any(o => o.GetType().Equals(typeof(EventOrderAttribute))))
                        concretionsOrder.Add(type);
                    else
                        concretions.Add(type);
                }

                foreach (var interfaceType in interfaceTypes)
                {
                    interfaces.Fill(interfaceType);
                }
            }

            // 排序后加入到服务中。
            concretionsOrder = OrderService(concretionsOrder);
            AddService(concretions);
            AddService(concretionsOrder);

            // 排序服务。
            List<Type> OrderService(List<Type> typeList)
            {
                if (typeList.Any())
                    return typeList.OrderBy(o => o.GetCustomAttribute<EventOrderAttribute>().Order).ToList();
                else
                    return typeList;
            }

            // 添加服务。
            void AddService(List<Type> typeList)
            {
                // 这一部分是将接口与相应的实例者相匹配,然后注册到容器中。
                foreach (var @interface in interfaces)
                {
                    var exactMatches = typeList.Where(x => x.CanBeCastTo(@interface)).ToList();
                    if (addIfAlreadyExists)
                    {
                        foreach (var type in exactMatches)
                        {
                            services.AddTransient(@interface, type);
                        }
                    }
                    else
                    {
                        if (exactMatches.Count > 1)
                        {
                            exactMatches.RemoveAll(m => !IsMatchingWithInterface(m, @interface));
                        }

                        // 针对一个接口的注册,注入多个矗立着。
                        foreach (var type in exactMatches)
                        {
                            services.TryAddTransient(@interface, type);
                        }
                    }

                    if (!@interface.IsOpenGeneric())
                    {
                        AddConcretionsThatCouldBeClosed(@interface, typeList, services);
                    }
                }
            }
        }

下面是ABC对应321.

    [EventOrder(Order = 3)]
    public class AClHandler : INotificationHandler<EventData>
    {
        public Task Handle(EventData notification, CancellationToken cancellationToken)
        {
            Console.WriteLine(nameof(AClHandler));
            return Task.CompletedTask;
        }
    }

    [EventOrder(Order = 2)]
    public class BClHandler : INotificationHandler<EventData>
    {
        public Task Handle(EventData notification, CancellationToken cancellationToken)
        {
            Console.WriteLine(nameof(BClHandler));
            return Task.CompletedTask;
        }
    }

    [EventOrder(Order = 1)]
    public class CClHandler : INotificationHandler<EventData>
    {
        public Task Handle(EventData notification, CancellationToken cancellationToken)
        {
            Console.WriteLine(nameof(CClHandler));
            return Task.CompletedTask;
        }
    }

看看结果:

  如果去掉排序

 第二种先排序再插入容器的方法,或许不适用所有的依赖注入容器,但是原生容器时没有问题的,Autofac应该也没有问题。

原文地址:https://www.cnblogs.com/yeqifeng2288/p/13216984.html