一、传统的Exception Handling
我们沿用我们一直使用的Calculator的例子和简单的4层构架:

1. Service Contract- Artech.ExceptionHandling.Contract
usingSystem;
usingSystem.Collections.Generic;
usingSystem.Text;
usingSystem.ServiceModel;
namespaceArtech.ExceptionHandling.Contract

{
[ServiceContract]
publicinterfaceICalculator

{
[OperationContract]
doubleDivide(doublex,doubley);
}
}
定义了一个单一的进行除法运算的Operation。
2. Service:Artech.ExceptionHandling.Service. CalculatorService
usingSystem;
usingSystem.Collections.Generic;
usingSystem.Text;
usingArtech.ExceptionHandling.Contract;
namespaceArtech.ExceptionHandling.Service

{
publicclassCalculatorService:ICalculator

{
ICalculatorMembers#regionICalculatorMembers
publicdoubleDivide(doublex,doubley)

{
if(y==0)

{
thrownewDivideByZeroException("Dividebyzero");
}
returnx/y;
}
#endregion
}
}
如果被除数是零,抛出一个DivideByZeroException Exception。
3. Service Hosting
Configuration:
<?xmlversion="1.0"encoding="utf-8"?>
<configuration>
<system.serviceModel>
<behaviors>
<serviceBehaviors>
<behaviorname="calculatorServiceBehavior">
<serviceMetadatahttpGetEnabled="true"/>
</behavior>
</serviceBehaviors>
</behaviors>
<services>
<servicebehaviorConfiguration="calculatorServiceBehavior"name="Artech.ExceptionHandling.Service.CalculatorService">
<endpointbinding="basicHttpBinding"bindingConfiguration=""contract="Artech.ExceptionHandling.Contract.ICalculator"/>
<host>
<baseAddresses>
<addbaseAddress="http://localhost:8888/Calculator"/>
</baseAddresses>
</host>
</service>
</services>
</system.serviceModel>
</configuration>
Program
usingSystem;
usingSystem.Collections.Generic;
usingSystem.Text;
usingSystem.ServiceModel;
usingArtech.ExceptionHandling.Service;
namespaceArtech.ExceptionHandling.Hosting

{
classProgram

{
staticvoidMain(string[]args)

{
using(ServiceHostcalculatorHost=newServiceHost(typeof(CalculatorService)))

{
calculatorHost.Opened+=delegate

{
Console.WriteLine("TheCalculatorservicehasbeguntolistenviatheaddress:{0}",calculatorHost.BaseAddresses[0]);
};
calculatorHost.Open();
Console.Read();
}
}
}
}
4. Client
Configuration:
<?xmlversion="1.0"encoding="utf-8"?>
<configuration>
<system.serviceModel>
<client>
<endpointaddress=http://localhost:8888/Calculatorbinding="basicHttpBinding"contract="Artech.ExceptionHandling.Contract.ICalculator"
name="defualtEndpoint"/>
</client>
</system.serviceModel>
</configuration>
Program
usingSystem;
usingSystem.Collections.Generic;
usingSystem.Text;
usingArtech.ExceptionHandling.Contract;
usingSystem.ServiceModel;
namespaceArtech.ExceptionHandling.Client

{
classProgram

{
staticvoidMain(string[]args)

{
ChannelFactory<ICalculator>calculatorFactory=newChannelFactory<ICalculator>("defualtEndpoint");
ICalculatorcalculator=calculatorFactory.CreateChannel();
try

{
Console.WriteLine("TrytoinvokeDividemethod");
Console.WriteLine("x/y={2}whenx={0}andy={1}",2,0,calculator.Divide(2,0));
}
catch(Exceptionex)

{
Console.WriteLine("AnExceptionisthrown.\n\tExceptionType:{0}\n\tErrorMessage:{1}",ex.GetType(),ex.Message);
}
Console.Read();
}
}
}
把Service调用放在一个try/catch block中,看看Service端抛出的DivideByZeroException Exception能否被Catch。
我们运行这个程序,看看Client有怎样的输出:

我们发现Client catch住的不是我们Service端真正抛出的DivideByZeroException Exception,而是一个比较General的FaultException。Error message也是很general:
"Theserverwasunabletoprocesstherequestduetoaninternalerror.Formoreinformationabouttheerror,eitherturnonIncludeExceptionDetailInFaults(eitherfromServiceBehaviorAttributeorfromthe<serviceDebug>configurationbehavior)ontheserverinordertosendtheexceptioninformationbacktotheclient,orturnontracingaspertheMicrosoft.NETFramework3.0SDKdocumentationandinspecttheservertracelogs."
二、基于ServiceDebug的Exception Handling
很显然Client端Catch住的Exception对我们进行troubleshooting。为了利于我们进行有效的Debug,WCF提供了ServiceDebug Service Behavior。我们通过includeExceptionDetailInFaults属性设为true,那么如果Service抛出Exception,WCF会简单得包装这个Exception并把它置于Soap中Response到Service的访问者。介于此,我修改了Hosting的Configuration:
<?xmlversion="1.0"encoding="utf-8"?>
<configuration>
<system.serviceModel>
<behaviors>
<serviceBehaviors>
<behaviorname="calculatorServiceBehavior">
<serviceMetadatahttpGetEnabled="true"/>
<serviceDebugincludeExceptionDetailInFaults="true"/>
</behavior>
</serviceBehaviors>
</behaviors>
<services>
<servicebehaviorConfiguration="calculatorServiceBehavior"name="Artech.ExceptionHandling.Service.CalculatorService">
<endpointbinding="basicHttpBinding"bindingConfiguration=""contract="Artech.ExceptionHandling.Contract.ICalculator"/>
<host>
<baseAddresses>
<addbaseAddress="http://localhost:8888/Calculator"/>
</baseAddresses>
</host>
</service>
</services>
</system.serviceModel>
</configuration>
现在再次运行程序,看看现在的运行结果:

可以看到我们我们Catch的是一个FaultException< ExceptionDetail>Type的Exception,不是原来的FaultException。该Exception的Detail属性就是Service抛出的DivideByZeroException Exception。有兴趣的人可以自己测试一下。而且我们在Service端指定的Error Message也被Client获得。这种方式的Exception Handling方式确实比上面一种具有很强的指示性,对我们进行Debug确实很有帮助。但是这种方式确实不能正式用于我们最终发布的版本中,因为它会把Exception所有的信息返回到Client端,很容易泄露一些很敏感的信息。这也正是WCF把这个列入ServiceDebug Service Behavior的原因。
三、基于Fault Contract 的Exception Handling
既然上面通过定制ServiceDebug只能用于Debug阶段。我们必须寻求另外一种Exception Handling的方式。那就是我们现在将要介绍的基于FaultContract的解决方案。我们知道WCF采用一种基于Contract,Contract定义了进行交互的双方进行消息交换所遵循的准则和规范。Service Contract定义了包含了所有Operation的Service的接口,Data Contract定义了交互的数据的结构,而FaultContract实际上定义需要再双方之间进行交互的了异常、错误的表示。我们现在来看看如何来使用基于FaultContract的Exception Handling。
我们首先来定义一个表示Fault的类:MathError。考虑到这个类需要在Service 和Client使用,我把它定义在Artech.ExceptionHandling.Contract中:
usingSystem;
usingSystem.Collections.Generic;
usingSystem.Text;
usingSystem.Runtime.Serialization;
namespaceArtech.ExceptionHandling.Contract

{
[DataContract]
publicclassMathError

{
privatestring_operation;
privatestring_errorMessage;
publicMathError(stringoperation,stringerrorMessage)

{
this._operation=operation;
this._errorMessage=errorMessage;
}
[DataMember]
publicstringOperation

{
get
{return_operation;}
set
{_operation=value;}
}
[DataMember]
publicstringErrorMessage

{
get
{return_errorMessage;}
set
{_errorMessage=value;}
}
}
}
在MathError中定义了两个成员:表示出错操作的Operation和出错信息的ErrorMessage。由于该类的对象需要在Endpoint之间传递,所以必须是可序列化的,在WCF中,我们一般用两个不同的Serializer实现Object和XML的Serialization和Deserialization:Datacontract Serializer和XML Serializer。而对于Fault,只能使用前者。
定义了MathError,我们需要通过FaultContract将其运用到Service Contract中制定的Operation上面,我们通过下面的方式来实现:
usingSystem;
usingSystem.Collections.Generic;
usingSystem.Text;
usingSystem.ServiceModel;
namespaceArtech.ExceptionHandling.Contract

{
[ServiceContract]
publicinterfaceICalculator
本文介绍了在Windows通信基础(WCF)中处理异常的三种方法:传统异常处理、基于ServiceDebug的异常处理及基于FaultContract的异常处理。每种方法都有其适用场景,并详细展示了如何在WCF应用程序中实施。
170

被折叠的 条评论
为什么被折叠?



