ABP中的拦截器之EntityHistoryInterceptor

  今天我们接着之前的系列接着来写另外一种拦截器EntityHistoryInterceptor,这个拦截器到底是做什么的呢?这个从字面上理解是实体历史?这个到底是什么意思?带着这个问题我们来一步步去分析。

  整个拦截器的运行过程和前面几篇是一样的,这里就不再赘述,首先也是在AbpBootstrapper的构造函数中完成初始化过程。

   private void AddInterceptorRegistrars()
        {
            ValidationInterceptorRegistrar.Initialize(IocManager);
            AuditingInterceptorRegistrar.Initialize(IocManager);
            EntityHistoryInterceptorRegistrar.Initialize(IocManager);
            UnitOfWorkRegistrar.Initialize(IocManager);
            AuthorizationInterceptorRegistrar.Initialize(IocManager);
        }

  首先我们来从这个Initialize方法来进行说明,然后再来一步步分析。

 internal static class EntityHistoryInterceptorRegistrar
    {
        public static void Initialize(IIocManager iocManager)
        {
            iocManager.IocContainer.Kernel.ComponentRegistered += (key, handler) =>
            {
                if (!iocManager.IsRegistered<IEntityHistoryConfiguration>())
                {
                    return;
                }

                var entityHistoryConfiguration = iocManager.Resolve<IEntityHistoryConfiguration>();

                if (ShouldIntercept(entityHistoryConfiguration, handler.ComponentModel.Implementation))
                {
                    handler.ComponentModel.Interceptors.Add(new InterceptorReference(typeof(EntityHistoryInterceptor)));
                }
            };
        }
        
        private static bool ShouldIntercept(IEntityHistoryConfiguration entityHistoryConfiguration, Type type)
        {
            if (type.GetTypeInfo().IsDefined(typeof(UseCaseAttribute), true))
            {
                return true;
            }

            if (type.GetMethods().Any(m => m.IsDefined(typeof(UseCaseAttribute), true)))
            {
                return true;
            }

            return false;
        }
    }

  这个还是和之前一样去订阅IOC容器中的IocContainer.Kernel.ComponentRegistered这个事件,然后在整个ABP框架运行并将每一个接口对应的类型注册到IOC容器的视乎会调用上面订阅的方法。然后在订阅方法中首先判断IOC容器中是否注册过IEntityHistoryConfiguration这个接口,如果没有注册过那么就直接返回了?那么ABP框架到底有没有注册过这个接口呢?

  这个在ABP中的AbpBootstrapper的Initialize方法中就完成了注册,具体的过程就不再描述,重点的是下面这句代码。

 Component.For<IEntityHistoryConfiguration, EntityHistoryConfiguration>().ImplementedBy<EntityHistoryConfiguration>().LifestyleSingleton()

  这个在ABP系统中注册了唯一的一个单例实现EntityHistoryConfiguration这个类,我们来看看这个类的实现。

 internal class EntityHistoryConfiguration : IEntityHistoryConfiguration
    {
        public bool IsEnabled { get; set; }

        public bool IsEnabledForAnonymousUsers { get; set; }

        public IEntityHistorySelectorList Selectors { get; }

        public List<Type> IgnoredTypes { get; }

        public EntityHistoryConfiguration()
        {
            IsEnabled = true;
            Selectors = new EntityHistorySelectorList();
            IgnoredTypes = new List<Type>()
            {
                typeof(EntityChangeSet),
                typeof(EntityChange),
                typeof(EntityPropertyChange)
            };
        }
    }

  我们先不分析这个类到底为了配置些什么,我们接着看EntityHistoryInterceptorRegistrar这个类,在这个类中是否启用拦截,就看当前类型Type中是否定义了自定义属性 UseCaseAttribute,这个属性可以在类上面,也可以定义在类里面的方法中,如果这些类型或者类型里面的方法定义了这个自定义属性,那么就启动后面的拦截过程了,这里面这些类通常是定义在应用层或领域层中的具体和业务相关的类。在判断了这些之后就是添加我们的EntityHistoryInterceptor拦截器了。

  下面我们就来重点分析EntityHistoryInterceptor这个拦截器了。

 internal class EntityHistoryInterceptor : IInterceptor
    {
        public IEntityChangeSetReasonProvider ReasonProvider { get; set; }

        public EntityHistoryInterceptor()
        {
            ReasonProvider = NullEntityChangeSetReasonProvider.Instance;
        }

        public void Intercept(IInvocation invocation)
        {
            var methodInfo = invocation.MethodInvocationTarget;
            var useCaseAttribute = methodInfo.GetCustomAttributes(true).OfType<UseCaseAttribute>().FirstOrDefault()
                  ?? methodInfo.DeclaringType.GetCustomAttributes(true).OfType<UseCaseAttribute>().FirstOrDefault();

            if (useCaseAttribute?.Description == null)
            {
                invocation.Proceed();
                return;
            }

            using (ReasonProvider.Use(useCaseAttribute.Description))
            {
                invocation.Proceed();
            }
        }
    }

  在这个拦截器中,如果一个已拦截的方法在执行前首先会执行Intercept这个方法,在这个方法中我们会往其中的字段Description中添加描述,然后会执行ReasonProvider.Use这个方法,然后将之前的描述Description作为参数进行传递。

posted @ 2018-10-31 21:35  Hello——寻梦者!  阅读(1159)  评论(0编辑  收藏  举报