[原创]Enterprise Library Policy Injection Application Block 之三:PIAB的扩展—创建自定义CallHandler(提供Source Code下载)

本系列的第一部分PIAB使用场景进行了简单的介绍,作中阐述了通过PIPolicy Injection)的方式实现了Business LogicNon-Business Infrastructure Logic的分离,从而实现了AOPAspect Oriented Programming)。在第二部分中详细介绍PIAB的实现机制:通过自定义RealProxy的方式实现了Method Injection。通过这几天接收到的网友的留言,觉得很多人对在具体的项目开发中如何使用PIAB还有很多困惑,对PIAB的价值还不是很了解。为此,在本系列的第三篇文章中,我将以Walk through的方式定义一个Custom CallHandler,并通过两种不同的方式:AttributeConfiguration将其运用到所以得Method上。你可以这里从下载Source Code.

场景描述:本Demo模拟一个简单的场景:订单处理,我们将订单处理之前对订单的验证通过PI的方式提供。我们假设需要进行如何的验证:

  • Order Date必须早于当前日期。
  • 必须有具体的Product
  • 供应商必须是制定的几家合法供应商(可选)。
  • Order的总价必须是所有Product价格之和(可选)。

其中前两个验证规则为必须的,后两个未可选项,可以通过AttributeConfiguration进行相应的配置。

Step I 创建Solution


如上图,整个
Solution由两个Project组成,一个Class Library和一个Console Application。所有与Custom CallHandlerClass都定义在Artech.CustomCallHandler.ClassLibrary中,而Artech.CustomCallHandler.ConsoleApp重在演示对Custom CallHandler的使用。在Artech.CustomCallHandler.ClassLibrary中,添加如下3Dll Reference,你可以在安装Enterprise Library V3 .1的目录中找到。

  • Microsoft.Practices.EnterpriseLibrary.Common
  • Microsoft.Practices.EnterpriseLibrary.PolicyInjection
  • Microsoft.Practices.ObjectBuilder

Step II定义辅助类:OrderOrderItemOrderValidationException

namespace Artech.CustomCallHandlers

{

    public class Order

    {

        public Order()

        {

this.Items = new List<OrderItem>();

        }

        public Guid OrderNo

        { get; set; }

        public DateTime OrderDate

        { get; set; }

        public string Supplier

        { get; set; }

        public IList<OrderItem> Items

        { get; set; }

        public double TotalPrice

        { get; set; }

    }
}

namespace Artech.CustomCallHandlers

{

    public class OrderItem

    {

        public Guid ProductID

        { get; set; }

        public string ProductName

        { get; set; }

        public double UnitPrice

        { get; set; }

        public int Quantity

        { get; set; }

    }
}

namespace Artech.CustomCallHandlers

{

    public class OrderValidationException:ApplicationException

    {

        public OrderValidationException(string message)

            : base(message)

        { }

    }

}

注:Demo通过VS2008创建,上面广泛使用了C# 3.0的一个新特性:Automatically Implemented Property

Step III 定义Custom CallHandler:  OrderValidationCallHandler

namespace Artech.CustomCallHandlers

{

   public class OrderValidationCallHandler:ICallHandler

    {

       private static IList<string> _legalSuppliers;

       public static IList<string> LegalSuppliers

       {

           get

           {

               if (_legalSuppliers == null)

               {

                   _legalSuppliers = new List<string>();

                   _legalSuppliers.Add("Company AAA");

                   _legalSuppliers.Add("Company BBB");

                   _legalSuppliers.Add("Company CCC");

               }

               return _legalSuppliers;

           }

       }

       #region Public Properties

       public bool ValidateTotalPrice

       { get; set; }

       public bool ValidateSupplier

       { get; set; }

       #endregion

       #region ICallHandler Members

       public IMethodReturn Invoke(IMethodInvocation input, GetNextHandlerDelegate getNext)

        {

            if (input.Inputs.Count == 0)

            {

                return getNext()(input, getNext);

            }

            Order order = input.Inputs[0] as Order;

            if (order == null)

            {

                return getNext()(input, getNext);

            }

            if (order.OrderDate > DateTime.Today)

            {

               return input.CreateExceptionMethodReturn(new OrderValidationException("The order date is later than the current date!"));

            }

            if(order.Items.Count == 0)

            {

               return input.CreateExceptionMethodReturn(new OrderValidationException("There are not any items for the order!"));

            }

            if (this.ValidateSupplier)

            {

                if (!LegalSuppliers.Contains<string>(order.Supplier))

                {

                    return input.CreateExceptionMethodReturn(new OrderValidationException("The supplier is inllegal!"));

                }

            }

            if(this.ValidateTotalPrice)

            {

                double totalPrice = 0;

                foreach (OrderItem item in order.Items)

                {

                    totalPrice += item.Quantity * item.UnitPrice;

                }

                if (totalPrice != order.TotalPrice)

                {

                    return input.CreateExceptionMethodReturn(new OrderValidationException("The sum of the order item is not equal to the order total price!"));

                }

            }

            return getNext()(input, getNext);

        }

        #endregion

    }

}

OrderValidationCallHandler实现了InterfaceMicrosoft.Practices.EnterpriseLibrary.PolicyInjection. ICallHandlerICallHandler仅仅有一个方法成员:

namespace Microsoft.Practices.EnterpriseLibrary.PolicyInjection

{

    public interface ICallHandler

    {

        IMethodReturn Invoke(IMethodInvocation input, GetNextHandlerDelegate getNext);

    }

}

参数input代表对方法的调用,你可以通过他们获得方法调用的参数、ContextMethodBaseTarget Object。上本系列的第二部分已经详细介绍了,运用到某一个Method上的Policy可能包含一个或多个CallHandler,这些Handler在初始化的时候串成一个Pipeline。在一般的情况下在完成某一个Handler的操作之后会调用后一个Handler或者是Target Object(如何改Handler是最后一个Handler)。但是在一些特殊的场合,比如:验证错误;在执行当前Handler的操作时抛出Exception;对于某些特殊的输入有固定的返回值,那么就没有必要再将接力棒向后传递了。在这个时候我们可能直接抛出Exception或者返回一个特设的返回值。这个时候可以调用CreateExceptionMethodReturnCreateMethodReturn来实现。

namespace Microsoft.Practices.EnterpriseLibrary.PolicyInjection

{

    public interface IMethodInvocation

    {

        IParameterCollection Arguments { get; }

        IParameterCollection Inputs { get; }

        IDictionary InvocationContext { get; }

        MethodBase MethodBase { get; }

        object Target { get; }

        IMethodReturn CreateExceptionMethodReturn(Exception ex);

        IMethodReturn CreateMethodReturn(object returnValue, params object[] outputs);

    }

}

而第二个参数getNext是一个Delegate,代表对CallHandler Pipeline后面CallHandler或者是Target Object的调用,这也在第二部分有提到。

我们在回到Invoke的具体定义。我们假设我们具体调用的Method的第一个参数必须是我们定义的Order对象:先验证方法的调用是否含有输入参数(如何没有直接调用后面的CallHandler或者Target Object);返回获得第一个输入参数并验证其类型(如果不是Order类型直接调用后面的CallHandler或者Target Object

if (input.Inputs.Count == 0)

{

    return getNext()(input, getNext);

}

Order order = input.Inputs[0] as Order;

if (order == null)

{

    return getNext()(input, getNext);

}

然后我们再验证Order对象是否满足我们在上面提出的验证规则,先看看必须的验证规则:对Order DateOrder Item Count的验证。

 if (order.OrderDate > DateTime.Today)

 {

    return input.CreateExceptionMethodReturn(new OrderValidationException("The order date is later than the current date!"));

 }

 if(order.Items.Count == 0)

 {

    return input.CreateExceptionMethodReturn(new OrderValidationException("There are not any items for the order!"));

 }

以及对可选的规则的验证:Total PriceSupplier。是否需要对其进行验证由两个Property来决定: ValidateSupplierValidateTotalPrice

if (this.ValidateSupplier)

{

    if (!LegalSuppliers.Contains<string>(order.Supplier))

    {

        return input.CreateExceptionMethodReturn(new OrderValidationException("The supplier is inlegal!"));

    }

}

if(this.ValidateTotalPrice)

{

    double totalPrice = 0;

    foreach (OrderItem item in order.Items)

    {

        totalPrice += item.Quantity * item.UnitPrice;

    }

    if (totalPrice != order.TotalPrice)

    {

        return input.CreateExceptionMethodReturn(new OrderValidationException("The sum of product unit price * quantity is not equal to the order total price!"));

    }

}

最有将接力棒向后传递:

return getNext()(input, getNext);

到此为止,我们的OrderValidationCallHandler就定义完毕。但这仅仅完成了一半而已。因为我们最终需要通过Attribute或者Configuration的方式将我们的CallHandler运用到具体的Method上。我们先来看看使用Attribute的清况。我们需要在定一个Custom Attribute: OrderValidationCallHandlerAttribute.

namespace Artech.CustomCallHandlers

{

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

    public class OrderValidationCallHandlerAttribute : HandlerAttribute

    {

        #region Public Properties

        public bool ValidateTotalPrice

        { get; set; }

        public bool ValidateSupplier

        { get; set; }

        #endregion

        public override ICallHandler CreateHandler()

        {

            return new OrderValidationCallHandler() { ValidateSupplier = this.ValidateSupplier, ValidateTotalPrice = this.ValidateTotalPrice };

        }

    }

}

这是一个派生Microsoft.Practices.EnterpriseLibrary.PolicyInjection. HandlerAttribute自得特殊的Custom AttributeHandlerAttribute是一个Abstract Class,继承自该Class通过其OrverrideCreateHandler来创建所需要的CallHandler,在这里当然是创建我们定义的OrderValidationCallHandler。由于对Total PriceSupplier的验证时可选的,所以我们定义了两个对应的Property来供Developer进行自由地配置,这两个Property用于初始化CallHandler

注:Demo通过VS2008创建,上面广泛使用了C# 3.0的一个新特性:Object Initializer

Step IV
通过Attribute运用OrderValidationCallHandler

我想到此为止,我们已经迫不及待地想将我们定义的OrderValidationCallHandler应用到我们程序中了。我们就通过一个Console Application来演示如何通过Attibute的方式来运用OrderValidationCallHandler到我们所需的Method 上。

现在定义以一个处理OrderClass: OrderProcessor

public class OrderProcessor : MarshalByRefObject

{

        [OrderValidationCallHandlerAttribute]

        public void ProcessOrder(Order order)

        {

            Console.WriteLine("The order has been processed!");

        }

        public static Order CreateOrder(DateTime orderDate, string supplier)

        {

            Order order = new Order() { OrderNo = Guid.NewGuid(), OrderDate = orderDate, Supplier = supplier, TotalPrice = 10000 };

            order.Items.Add(new OrderItem() { ProductID = Guid.NewGuid(), UnitPrice = 6000, Quantity = 1, ProductName = "PC" });

            order.Items.Add(new OrderItem() { ProductID = Guid.NewGuid(), UnitPrice = 5000, Quantity = 2, ProductName = "Print"});

            return order;

        }

 }

CreateOrder用于创建Order对象。而我们将我们的OrderValidationCallHandlerAttribute运用到ProcessOrder Method上。现在我们就可以在Main方法上调用这个方法了:

class Program

    {

        static void Main(string[] args)

        {

            OrderProcessor orderProcessor = PolicyInjection.Create<OrderProcessor>();

            Order order;

            try

            {

                order = OrderProcessor.CreateOrder(DateTime.Today.AddDays(1), " Company AAA");

                Console.WriteLine("Proceed to process an order with an invalid order date!");

                orderProcessor.ProcessOrder(order);

            }

            catch (OrderValidationException ex)

            {

                Console.WriteLine("Error: {0}"n",ex.Message);

            }

            try

            {

                order = OrderProcessor.CreateOrder(DateTime.Today.AddDays(-1), " Company DDD");

                Console.WriteLine("Proceed to process an order with an illegal supplier!");

                orderProcessor.ProcessOrder(order);

            }

            catch (OrderValidationException ex)

            {

                Console.WriteLine("Error: {0}"n", ex.Message);

            }

            try

            {

                order = OrderProcessor.CreateOrder(DateTime.Today.AddDays(-1), " Company AAA");

                Console.WriteLine("Proceed to process an order with incorrect total price!");

                orderProcessor.ProcessOrder(order);

            }

            catch (OrderValidationException ex)

            {

                Console.WriteLine("Error: {0}"n", ex.Message);

            }

        }

    }


我们看出,
Order Date 的验证正常执行,而对于Total PriceSupplier的验证却没有起作用。因为这两个是可选的(默认为不进行验证),我们可以通过修改运用在ProcessOrder MethodOrderValidationCallHandlerAttribute来进行有效的配置。比如:

[OrderValidationCallHandlerAttribute(ValidateSupplier = true, ValidateTotalPrice = true)]

public void ProcessOrder(Order order)

{

     Console.WriteLine("The order has been processed!");

}

这样将会出现如下的结果:


Step V
定义HandlerDataCallHandlerAssembler

在上面我们实现了通过Attribute的方式使用CallHandler的方式,我们现在来看看另一种运用CallHandler的方式:Configuration。为此我们需要定义两个额外的Class: HandlerDataCallHandlerAssembler。前者用于定义Configuration相关的Property,而后者则通过Configuration创建并初始化相应的CallHandler

下面是HandlerData的定义:

namespace Artech.CustomCallHandlers

{

    [Assembler(typeof(OrderValidationCallHandlerAssembler))]

    public class OrderValidationCallHandlerData:CallHandlerData

    {

        [ConfigurationProperty("validateSupplier", DefaultValue = false)]

        public bool ValidateSupplier

        {

            get

            {

                return (bool)base["validateSupplier"];

            }

            set

            {

                base["validateSupplier"] = value;

            }

        }

        [ConfigurationProperty("validateTotalPrice", DefaultValue = false)]

        public bool ValidateTotalPrice

        {

            get

            {

                return (bool)base["validateTotalPrice"];

            }

            set

            {

                base["validateTotalPrice"] = value;

            }

        }

    }

}

这和ConfigurationProperty相同,唯一的区别是在Class上运用了一个Assembler Attribute,并制定了一个CallHandlerAssembler typeOrderValidationCallHandlerAssemblerOrderValidationCallHandlerAssembler定义如下:

namespace Artech.CustomCallHandlers

{

    public class OrderValidationCallHandlerAssembler : IAssembler<ICallHandler, CallHandlerData>

    {

        #region IAssembler<ICallHandler,OrderValidationCallHandlerData> Members

        public ICallHandler Assemble(Microsoft.Practices.ObjectBuilder.IBuilderContext context, CallHandlerData objectConfiguration, Microsoft.Practices.EnterpriseLibrary.Common.Configuration.IConfigurationSource configurationSource, ConfigurationReflectionCache reflectionCache)

        {

            OrderValidationCallHandlerData handlerData = objectConfiguration as OrderValidationCallHandlerData;

            return new OrderValidationCallHandler(){ ValidateSupplier = handlerData.ValidateSupplier, ValidateTotalPrice = handlerData.ValidateTotalPrice};

        }

        #endregion

    }

}

OrderValidationCallHandlerAssembler派生自IAssembler<ICallHandler, CallHandlerData>,实现了Assemble方法。该方法用于收集的Configuration来创建所需的CallHandler

到此为止,任务尚未结束,我们还需要将我们定义的CallHandlerHandlerData之间建立一个Mapping关系。这主要通过在CallHandler Class上运用ConfigurationElementType Attribute来实现。为此我们将此Attribute加在OrderValidationCallHandler上面:

namespace Artech.CustomCallHandlers

{

   [ConfigurationElementType(typeof(OrderValidationCallHandlerData))]

   public class OrderValidationCallHandler:ICallHandler

    {

        。。。。。。

    }

}

Step VI 通过Configuration来使用CallHandler

现在我们就可以采用Configuration的方式来讲我们定义的OrderValidationCallHandler运用到我们所需的Method上。我们先去掉OrderProcessor. ProcessOrder上的OrderValidationCallHandlerAttribute。然后添加一个Application Configuration 文件,并进行如下配置:

<configuration>

            <configSections>

                        <sectionname="policyInjection"type="Microsoft.Practices.EnterpriseLibrary.PolicyInjection.Configuration.PolicyInjectionSettings, Microsoft.Practices.EnterpriseLibrary.PolicyInjection, Version=3.1.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />

            </configSections>

            <policyInjection>

                        <policies>

                                    <addname="Policy">

                                                <matchingRules>

                                                            <addtype="Microsoft.Practices.EnterpriseLibrary.PolicyInjection.MatchingRules.MemberNameMatchingRule, Microsoft.Practices.EnterpriseLibrary.PolicyInjection, Version=3.1.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" name="Member Name Matching Rule">

                                                                        <matches>

                                                                                    <addmatch="ProcessOrder"ignoreCase="false" />

                                                                        </matches>

                                                            </add>

                                                </matchingRules>

                                                <handlers>

                                                            <addtype="Artech.CustomCallHandlers.OrderValidationCallHandler, Artech.CustomCallHandlers"name="OrderValidationCallHandler"validateSupplier="true"validateTotalPrice="true"/>

                                                </handlers>

                                    </add>

                        </policies>

            </policyInjection>

</configuration>

policyInjection Configuration Section中添加了一个PolicyPolicy=Matching Rule + Call Handler, 对于Matching Rule,我们采用的是基于Method NameMicrosoft.Practices.EnterpriseLibrary.PolicyInjection.MatchingRules.MemberNameMatchingRule。而我们定义的OrderValidationCallHandler则加在<handlers> element下,两个属性validateSuppliervalidateTotalPrice直接置于其中。

我们再次运行我们的程序,我们的输出结果和上面的没有任何区别:

· [原创]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
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值