[原创]Enterprise Library Policy Injection Application Block 之四:如何控制CallHandler的执行顺序

一、为什么CallHandler需要进行排序

PIAB为我们提供了一个很好地实现AOP的方式。AOP旨在实现Business LogicNon-Business Infrastructure Logic的分离。通过PIAB,我们将这些业务无关的逻辑定义在一个个的CallHandler中,然后通过Attribute或者Configuration的方式,将我们所需的CallHandler运用到相应的目标对象中。从这个意义上讲,PIAB具有很好的FlexibilityExtensibility。但是,就我看来PIAB也具有一些不足之处,其最大的局限性在于:不能控制运用到某个Method的多个方法的执行顺序。而让CallHandler按照我们希望的顺序进行调用是非常有必要的。

举个例子,假设我们将以下3CallHandler运用到某个方法中:

  • ValidationHandler:用于参数参数的验证,比如是否为null, stringLength是否超出长度等等。
  • TransactionEnlistHandler: 用于将操作自动纳入到一个Transaction中,从而保证数据的一致性。
  • AuditLoggingHandler:当时操作成功执行后进行Audit Log

很显然,正常的执行顺序应该是这样的:在最开始调用ValidationHandler进行参数的验证;Audit Log需要和目标方法一起纳入同一个Transaction中,所以TransactionEnlistHandler的调用紧随其后,最后才是AuditLoggingHandler

Microsoft提供的原生的PIAB是无法实现的,好在Enterprise Library是开源的,我们可以修改PIABSource Code来使其实现我们的目标。而仅仅是一个很小的改动。接下来我们就来讨论一下如何来实现可被排序的CallHandler Pipeline

二、如何创建Sequential CallHandler Pipeline

如果要了解我们这个Sequential CallHandler Pipeline的实现,需要对PIAB的是实现机制有一定的了解。在本系列的第二部分里,我对PIAB的实现机制进行了详细的阐述,在这里我仅仅简单介绍一个PIAB是如何实现AOP的。

PIABAOP的实现原理可以用一个词来概括:Method Interception。具体的做法做法是:通过PIAB Factory创建基于Target TypeReal Proxy,然后通过这个Real Proxy创建Transparent Proxy,并通过该Transparent Proxy调用Target Instance。在创建Real Proxy中,将运用到该Type的所有CallHandler缓存起来。当进行调用的时候,Transparent Proxy调用Real ProxyInvoke方法。在该方法中,在将运用到当前MethodCallHandler构成一个Handler Pipeline。在真正调用Target Instance之前,按照Pipeline的先后顺序依次调用每个CallHandler

而我们实现的切入点就是:CallHandler Pipeline创建之后,再根据我们希望的顺序将所有的CallHander重新排序

三、Sequential CallHandler Pipeline的实现

实现一个Sequential CallHandler Pipeline的一个前提就是,如何确定一个CallHandlerPipeline的位置。为此,我们需要我们的Custom CallHandler有一个额外的属性:Ordinal,表明在Pipeline的序号,序号小的在前,大的在后。如何没有该属性,比如PIAB提供的所有CallHandler,我们将其放在最后。

我们仅仅需要修改两个PIAB Class: Microsoft.Practices.EnterpriseLibrary.PolicyInjection. HandlerPipelineMicrosoft.Practices.EnterpriseLibrary.PolicyInjection. RemotingInterception. InterceptingRealProxy

对于HandlerPipeline,添加了一个新的PropertyHandlers,用于在InterceptingRealProxy中能够获得组成Pipeline的所有CallHandler以利于排序。

public class HandlerPipeline

    {

        private List<ICallHandler> handlers;

        ///<summary>

        /// This property is added to get the CallHandler(s).

        ///</summary>

        public List<ICallHandler> Handlers

        {

            get { return handlers; }

            set { handlers = value; }

        }
      。。。。。。
}

在才中,添加一个新的方法:ResortHandlers,将所有CallHandler按照Ordinal的大小进行重新排序(通过Reflection得到Ordinal的值)。

public HandlerPipeline ResortHandlers(HandlerPipeline pipeline)

        {

            HandlerPipeline sequentialPipeline = new HandlerPipeline();

            IDictionary<ICallHandler,int> handlerOrdinalPairList = new Dictionary<ICallHandler,int>();

            ICallHandler[] handlers = Array.CreateInstance(typeof(ICallHandler), pipeline.Handlers.Count) as ICallHandler[];

            int[] ordinals = Array.CreateInstance(typeof(int), pipeline.Handlers.Count) as int[];

            for (int i = 0; i < pipeline.Handlers.Count; i++ )

            {

                ICallHandler handler = pipeline.Handlers[i];

                handlers[i] = handler;

                Type handlerType = handler.GetType();

                MemberInfo[] memberInfos = handlerType.GetMember("Ordinal");

                if (memberInfos.Length == 0)

                {

                    ordinals[i] = int.MaxValue;

                    continue;

                }

                PropertyInfo propertyInfo = memberInfos[0] as PropertyInfo;

                if (propertyInfo == null)

                {

                    ordinals[i] = int.MaxValue;

                    continue;

                }

                int ordinal = (int)propertyInfo.GetValue(handler, null);

                ordinals[i] = ordinal;

            }

            ICallHandler swapHandler;

            int swapOrdinal;

            for (int i = 0; i < pipeline.Handlers.Count - 1; i++)

            {

                for (int j = i + 1; j < pipeline.Handlers.Count; j++)

                {

                    if (ordinals[i] > ordinals[j])

                    {

                        swapOrdinal = ordinals[i];

                        ordinals[i] = ordinals[j];

                        ordinals[j] = swapOrdinal;

                        swapHandler = handlers[i];

                        handlers[i] = handlers[j];

                        handlers[j] = swapHandler;

                    }

                }

            }

            return new HandlerPipeline(handlers);
        }

注:采用Reflection的方式获得Ordinal并不是一种很好的方式,最好是定义一个Abstract CallHandler BaseClass,并将Ordinal Property定义在这个BaseClass中。

该方法将在OrdinalInvoke中调用:

public override IMessage Invoke(IMessage msg)

        {

            IMethodCallMessage callMessage = (IMethodCallMessage)msg;

            HandlerPipeline pipeline;

            if (memberHandlers.ContainsKey(callMessage.MethodBase))

            {

                pipeline = memberHandlers[callMessage.MethodBase];

                //Added by Jiang Jin Nan

                pipeline = ResortHandlers(pipeline);

            }

            else

            {

                pipeline = new HandlerPipeline();

            }

            RemotingMethodInvocation invocation = new RemotingMethodInvocation(callMessage, target);

            IMethodReturn result =

                pipeline.Invoke(

                    invocation,

                    delegate(IMethodInvocation input, GetNextHandlerDelegate getNext)

                    {

                        try

                        {

                            object returnValue = callMessage.MethodBase.Invoke(target, invocation.Arguments);

                            return input.CreateMethodReturn(returnValue, invocation.Arguments);

                        }

                        catch (TargetInvocationException ex)

                        {

                            // The outer exception will always be a reflection exception; we want the inner, which is

                            // the underlying exception.

                            return input.CreateExceptionMethodReturn(ex.InnerException);

                        }

                    });

            return ((RemotingMethodReturn)result).ToMethodReturnMessage();
        }

这就是所有需要的改动,为了验证是否有效,我们照例写一个测试程序。

四、如何使用Sequential CallHandlerPIAB

为了验证我们上所做的能否实现我们的目标:让运用到某个Method上的CallHandler按照我们希望的顺序来执行,我们创建了两个Custom CallHandler: CustomHandlerA CustomHandlerB

namespace Artech.SequentialCallHandlers

{

    public class CustomHandlerA: ICallHandler

    {

        public int Ordinal

        { get; set; }

        public CustomHandlerA()

        {

            this.Ordinal = int.MaxValue;

        }

        #region ICallHandler Members

        public IMethodReturn Invoke(IMethodInvocation input, GetNextHandlerDelegate getNext)

        {

            Console.WriteLine("Artech.SequentialCallHandlers.CustomHandlerA is invoked!");

            return getNext()(input, getNext);

        }

        #endregion

    }

}

namespace Artech.SequentialCallHandlers

{

    public class CustomHandlerB:ICallHandler

    {

        public int Ordinal

        { get; set; }

        public CustomHandlerB()

        {

            this.Ordinal = int.MaxValue;

        }

        #region ICallHandler Members

        public IMethodReturn Invoke(IMethodInvocation input, GetNextHandlerDelegate getNext)

        {

            Console.WriteLine("Artech.SequentialCallHandlers.CustomHandlerB is invoked!");

            return getNext()(input, getNext);

        }

        #endregion

    }

}

下面是两个对应的HandlerAttribute

namespace Artech.SequentialCallHandlers

{

   public class ACustomHandlerAttribute:HandlerAttribute

    {

       public int Ordinal

       { get; set; }

        public override ICallHandler CreateHandler()

        {

            return new CustomHandlerA() { Ordinal = this.Ordinal };

        }

    }

}

namespace Artech.SequentialCallHandlers

{

    [AttributeUsage(AttributeTargets.Method | AttributeTargets.Class)]

    public class BCustomHandlerAttribute:HandlerAttribute

    {

        public int Ordinal

        { get; set; }

        public override ICallHandler CreateHandler()

        {

            return new CustomHandlerB() { Ordinal = this.Ordinal };

        }

    }

}

注:如何定义Custom CallHandler,在本系列的第三部分有详细的介绍。

然后,我们将这连个Attribute运用到同一个方法中:

    class PolicyInjectionType:MarshalByRefObject

    {

        [BCustomHandlerAttribute(Ordinal = 1)]

        [ACustomHandlerAttribute(Ordinal = 2)]

        public void DoSomething()

        {

            Console.WriteLine("The target object is invoked!");

        }

}

我们在一个Console ApplicationMain()种调用这个DoSomething()方法:

class Program

    {

        static void Main(string[] args)

        {

            PolicyInjectionType proxy = PolicyInjection.Create<PolicyInjectionType>();

            proxy.DoSomething();

        }

}

由于CustomHandlerAOrdinal2CustomHandlerBOrdinal1,所以他们正确的执行顺序为:CustomHandlerB-CustomHandlerA。输出的结果证实了这一点:


我们来改变一下他们的顺序:

    class PolicyInjectionType:MarshalByRefObject

    {

        [BCustomHandlerAttribute(Ordinal = 2)]

        [ACustomHandlerAttribute(Ordinal = 1)]

        public void DoSomething()

        {

            Console.WriteLine("The target object is invoked!");

        }

}

这样的话,两个CallHandler的顺序将变成:CustomHandlerA-CustomHandlerB。我们再来看看输出的结果:



这正是我们所希望的。

· [原创]Enterprise Library Policy Injection Application Block 之一: PIAB Overview
· [原创]Enterprise Library Policy Injection Application Block 之二: PIAB设计和实现原理
· [原创]Enterprise Library Policy Injection Application Block 之三: PIAB的扩展—创建自定义CallHandler(提供Source Code下载)

· [原创]Enterprise Library Policy Injection Application Block 之四:如何控制CallHandler的执行顺序

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Enterprise Library是一个开源的.NET应用程序库,提供了一系列可重用的软件组件和工具,用于简化企业级应用程序的开发。.NET 6可以使用Enterprise Library,只需要将它添加到项目引用中即可使用。 要使用Enterprise Library,您需要进行以下步骤: 1. 下载并安装Enterprise Library。您可以从NuGet或GitHub上下载最新版本的Enterprise Library。 2. 在Visual Studio中打开您的.NET项目,并在“引用”中添加Enterprise Library的程序集。 3. 在代码中引用Enterprise Library的命名空间,并使用其中的类和方法。 例如,您可以使用Enterprise Library中的数据访问组件来连接数据库和执行查询。以下是一个示例代码片段: ```csharp using Microsoft.Practices.EnterpriseLibrary.Data; using System.Data.Common; public class DataAccess { private readonly Database db; public DataAccess() { db = DatabaseFactory.CreateDatabase(); } public DbCommand GetCommand(string sql) { return db.GetSqlStringCommand(sql); } public void ExecuteNonQuery(DbCommand cmd) { db.ExecuteNonQuery(cmd); } public object ExecuteScalar(DbCommand cmd) { return db.ExecuteScalar(cmd); } public DbDataReader ExecuteReader(DbCommand cmd) { return db.ExecuteReader(cmd); } } ``` 在上面的示例中,我们使用Enterprise Library的Database组件来创建数据库连接,并使用它来执行查询和操作。您也可以使用其他的Enterprise Library组件,例如日志记录、缓存、安全等等。 总之,Enterprise Library是一个非常有用的.NET库,可为企业级应用程序开发提供许多便利,您可以在.NET 6项目中使用它来简化开发过程。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值