Enterprise Library-WCF与Exception Handling AppBlock集成[下]

  在Enterprise Library-WCF与Exception Handling AppBlock集成[上]中, 我详细介绍了如何通过自定义ClientMessageInspector和ErrorHandler,实现WCF与微软企业库中的Exception Handling Application Block(EHAB)之间的集成。这个方案的基本思路就是:当异常从服务端抛出,利用EHAB针对某个配置好的异常处理策略进行处理;然后将处理有的异 常通过ServiceExceptionDetail对象进行封装,最后序列化置于Fault消息,最终被返回给客户端;客户端接收到该Fault消息 后,提取并创建ServiceExceptionDetail对象,并通过反射重建异常;最后将异常抛出,使客户端可以根据客户端配置的异常处理策略对该 异常进行进一步的处理。(Source Code从这里下载)

  为了实现WCF对ServiceExceptionDetail对象的序列化和反序列化,我们必须通过FaultContractAttribute特 性将类型定义成错误契约,相应的形式如下面的代码所示。在一般的情况下,如果你定义的服务是为他人所用,比如第三方服务消费者,该错误契约的定义是必须 的,因为相应的错误明细类型需要通过元数据的形式发布出来,指导客户端如何对接收到的消息进行反序列化。但是,如果服务仅供你自己的应用所用,那么你可以 在运行时动态地添加相应的错误描述,从而避免在服务契约的每一个服务操作方法上应用这么一个FaultContractAttribute

   1: [ServiceContract(Namespace = "http://www.artech.com/")]

   2: public interface ICalculator

   3: {

   4:     [OperationContract]

   5:     [ExceptionHandlingBehavior("myExceptionPolicy")]

   6:     [FaultContract(typeof(ServiceExceptionDetail), Action = "http://www.artech.com/fault")]

   7:     int Divide(int x, int y);

   8: }

  我们应用在操作方法上面的FaultContractAttribute特性,最终会转换成操作描述(OperationDescription)的错误描述(FaultDescription),如果我们在运行时能够为所有的操作描述添加相应的错误描述,就能避免在每个服务操作上面应用相同的FaultContractAttribute特性。不过,为了服务的重用,我不介意这样偷懒,所以这种方案仅仅作为研究、学习之用。

一、通过自定义ServiceHost的方式动态添加错误描述(服务端)

  首先需要在服务端为每一个服务操作添加基于ServiceExceptionDetail的错误描述,这可以通过自定ServiceHost来实现。由于服务描述需要在ServiceHost开启之前生成方才有效(具体的原因,相对比较复杂,大家可以在《WCF技术剖析目录》第7章关于服务寄宿的部分找到答案),所以我们将相关的逻辑定义在OnOpening方法之中。在下面的代码中,我定义了这样一个简单的ServiceHost:ExceptionHandlingServiceHost。

   1: using System;

   2: using System.ServiceModel;

   3: using System.ServiceModel.Activation;

   4: using System.ServiceModel.Description;

   5:  

   6: namespace Artech.EnterLibIntegration.WcfExtensions

   7: {

   8:     public class ExceptionHandlingServiceHost : ServiceHost

   9:     {

  10:         public ExceptionHandlingServiceHost(Type t, params Uri[] baseAddresses)

  11:             : base(t, baseAddresses)

  12:         { }

  13:  

  14:         protected override void OnOpening()

  15:         {

  16:             base.OnOpening();

  17:             foreach (ServiceEndpoint endpoint in this.Description.Endpoints)

  18:             {

  19:                 foreach (OperationDescription operation in endpoint.Contract.Operations)

  20:                 { 

  21:                     FaultDescription faultDescription = new FaultDescription(ServiceExceptionDetail.FaultAction);

  22:                     faultDescription.DetailType = typeof(ServiceExceptionDetail);

  23:                     operation.Faults.Add(faultDescription);

  24:                 }

  25:             }

  26:         }

  27:     }

  28: }

  逻辑相对比较简单:遍历所有终结点(ServiceEndpoint),为每一个终结点的契约(ContractDescription)的每一个操作(OperationDescription)添加错误明细类型为ServiceExceptionDetail的错误描述(FaultDescription),并指定预定义的Action。

  对于自定义的ServiceHost,可以直接用于不需要.svc文件进行访问的寄宿场景,也就是说对于除了IIS和WAS的服务寄宿,可以直接采用自定义的ServiceHost。服务需要在基于IIS和WAS的寄宿方式中采用自定义的ServiceHost,还需要为之创建相应的ServiceHostFactory(关于ServiceHostFactory作用和用法,同样可以参阅《WCF技术剖析(卷1)》第7章)。下面,我们为ExceptionHandlingServiceHost定义了一个简单的ServiceHostFactory:ExceptionHandlingServiceHostFactory。

   1: using System;

   2: using System.ServiceModel;

   3: using System.ServiceModel.Activation;

   4: using System.ServiceModel.Description;

   5:  

   6: namespace Artech.EnterLibIntegration.WcfExtensions

   7: {

   8:     public class ExceptionHandlingServiceHostFactory : ServiceHostFactory

   9:     {

  10:         protected override ServiceHost CreateServiceHost(Type serviceType, Uri[] baseAddresses)

  11:         {

  12:             return new ExceptionHandlingServiceHost(serviceType, baseAddresses);

  13:         }

  14:     }

  15: }

二、通过自定义ChannelFactory<TChanel>的方式动态添加错误描述(客户端)

  服务端需要为每一个操作添加基于ServiceExceptionDetail的错误描述,以便实现对该对象的序列化;同理,客户端同样需要这样一 个错误描述,以实现对该对象的反序列化。我们可以将这样的功能通过一个自定义ChannelFactory<TChannel>来实现。下面 定义的ExceptionHandlingChannelFactory就是这样一个自定的 ChannelFactory<TChannel>。对错误描述的添加实现在重写的CreateDescription方法中:

   1: using System.ServiceModel;

   2: using System.ServiceModel.Description;

   3:  

   4: namespace Artech.EnterLibIntegration.WcfExtensions

   5: {

   6:     public class ExceptionHandlingChannelFactory<TChannel>:ChannelFactory<TChannel>

   7:     {

   8:         public ExceptionHandlingChannelFactory(string endpointConfigurationName)

   9:             : base(endpointConfigurationName)

  10:         { }

  11:  

  12:         protected override ServiceEndpoint CreateDescription()

  13:         {  

  14:             ServiceEndpoint serviceEndpoint = base.CreateDescription();

  15:             foreach (OperationDescription operation in serviceEndpoint.Contract.Operations)

  16:             {

  17:                 FaultDescription faultDescription = new FaultDescription(ServiceExceptionDetail.FaultAction);

  18:                 faultDescription.DetailType = typeof(ServiceExceptionDetail);

  19:                 operation.Faults.Add(faultDescription);

  20:             }

  21:  

  22:             return serviceEndpoint;

  23:         }

  24:     }

  25: }

三、实例演示

  那么,对其我们给出的例子,我们就要使用我们上面创建的这两个组件了。首先,有了这两个组件的帮助,在服务契约中,我们再也不需要在繁琐地为每一个服务操作定义相同的FaultContractAttribute特性了。于是我们先将其拿掉。

   1: [ServiceContract(Namespace = "http://www.artech.com/")]

   2: public interface ICalculator

   3: {

   4:     [OperationContract]

   5:     [ExceptionHandlingBehavior("myExceptionPolicy")]

   6:     int Divide(int x, int y);

   7: }   

然后,再进行服务寄宿的时候,直接利用我们定义的ExceptionHandlingServiceHost就可以了。

   1: using System;

   2: using Artech.EnterLibIntegration.WcfExtensions;

   3: using Artech.WcfServices.Services;

   4: namespace Artech.WcfServices.Hosting

   5: {

   6:     public class Program

   7:     {

   8:         static void Main(string[] args)

   9:         {

  10:             using (ExceptionHandlingServiceHost host = new ExceptionHandlingServiceHost(typeof(CalculatorService)))

  11:             {

  12:  

  13:                 host.Open();

  14:                 Console.Read();

  15:             }

  16:         }

  17:     }

  18: }

  而客户端,我们可以借助于我们定义的ExceptionHandlingChannelFactory<TChannel>实现对服务代理的创建。

   1: using System;

   2: using Artech.EnterLibIntegration.WcfExtensions;

   3: using Artech.WcfServices.Contracts;

   4: namespace Artech.WcfServices.Clients

   5: {

   6:     class Program

   7:     {

   8:         static void Main(string[] args)

   9:         {

  10:             using (ExceptionHandlingChannelFactory<ICalculator> channelFactory = new ExceptionHandlingChannelFactory<ICalculator>(

  11:                "calculatorservice"))

  12:             {

  13:                 ICalculator calculator = channelFactory.CreateChannel();

  14:                 using (calculator as IDisposable)

  15:                 {

  16:                     try

  17:                     {

  18:                         int result = calculator.Divide(1, 0);

  19:                     }

  20:                     catch (CalculationException ex)

  21:                     {

  22:                         Console.WriteLine(ex.Message);

  23:                         Console.WriteLine("InnerException");

  24:                         Console.WriteLine("\tType:{0}", ex.InnerException.GetType());

  25:                         Console.WriteLine("\tMessage:{0}", ex.InnerException.Message);

  26:                     }

  27:                 }

  28:             }

  29:  

  30:             Console.Read();

  31:         }

  32:     }

  33: }

这样我们同样可以得到与Enterprise Library-WCF与Exception Handling AppBlock集成[上]一样的执行结果:

计算错误
InnerException
        Type:System.DivideByZeroException
        Message:试图除以零.

作者:Artech
出处:http://artech.cnblogs.com

加支付宝好友偷能量挖...


评论(0)网络
阅读(97)喜欢(0)Asp.Net/C#/WCF