zoukankan      html  css  js  c++  java
  • 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应该也没有问题。

  • 相关阅读:
    Educational Codeforces Round 75 (Rated for Div. 2)
    Codeforces Round #596 (Div. 2, based on Technocup 2020 Elimination Round 2)
    Codeforces Round #594 (Div. 2)
    Codeforces Round #597 (Div. 2)
    Codeforces Round #599 (Div. 2)
    数学笔记
    模板
    模板
    win7如何更改语言教程
    cmd-net命令详解
  • 原文地址:https://www.cnblogs.com/yeqifeng2288/p/13216984.html
Copyright © 2011-2022 走看看