在WCF中的异常处理方法
在 WCF 中,客户端调用服务时,可能抛出的异常有以下三种类型。
1. 通讯异常。诸如网络错误,地址错误,服务器没有启动等等。这类异常多是 CommunicationException (或其具体继承类型)。
2. 状态异常。比如访问了已经关闭的代理对象,契约错误,以及安全设置错误等。常见的有 ObjectDisposedException。
3. 服务异常。由服务器触发,多是 FaultException。
针对服务异常,不同的实例管理方式会有不同的策略。
1. Pre-Call: 服务实例被释放,客户端抛出 FaultException,客户端代理对象无法继续使用。
2. Pre-Session: 服务实例被释放,会话终止。客户端抛出 FaultException,客户端代理对象无法继续使用。
3. Singleton: 服务实例依旧运行,会话终止。客户端抛出 FaultException,客户端代理对象无法继续使用。
基于平台中立和技术整合的需要,WCF 以标准 Soap Faults 方式传递异常信息。WCF 提供了 FaultException 和 FaultException<T> 两个类型来操控 Soap Faults。通过 FaultException<T> 我们可以向客户端传递一个错误信息(FaultReason)以及一个附加的详细信息(Detail)。理论上,这个附加信息是任何可以序列化的对象。
如果想传递一个附带元数据的自定义详细信息,可以使用 FaultContract。
当然,我们也可以直接抛出一个被称之为 "Unknown Faults" 的 FaultException 异常实例。还有另外一种情况,你已经写好了代码,有很多……很多……的代码,要是一个个修改会非常……非常……麻烦,那么怎么在不做大的代码修改情况下传递详细异常信息给客户端呢?
方法1: ServiceBehavior(IncludeExceptionDetailInFaults=true)]
方法2: ServiceDebugBehavior
这个比方法1要更方便一些,我们除了可以写代码外,还可以用配置文件。
看看这两种方法抛出的异常是什么样的。
不错,除了 Error Message,还有详细的 stack trace,方便调试。也正因为这样,此种方法也不适合在正式项目中使用,作为系统架构设计的一部分,我们应该事先设计好异常处理。
如果服务方法是 IsOneWay=true,因不接收返回消息,客户端也就不会触发异常了。而 Callback 无非是服务器和客户端掉换个身份而已,道理相同。
1. 通讯异常。诸如网络错误,地址错误,服务器没有启动等等。这类异常多是 CommunicationException (或其具体继承类型)。
2. 状态异常。比如访问了已经关闭的代理对象,契约错误,以及安全设置错误等。常见的有 ObjectDisposedException。
3. 服务异常。由服务器触发,多是 FaultException。
针对服务异常,不同的实例管理方式会有不同的策略。
1. Pre-Call: 服务实例被释放,客户端抛出 FaultException,客户端代理对象无法继续使用。
2. Pre-Session: 服务实例被释放,会话终止。客户端抛出 FaultException,客户端代理对象无法继续使用。
3. Singleton: 服务实例依旧运行,会话终止。客户端抛出 FaultException,客户端代理对象无法继续使用。
基于平台中立和技术整合的需要,WCF 以标准 Soap Faults 方式传递异常信息。WCF 提供了 FaultException 和 FaultException<T> 两个类型来操控 Soap Faults。通过 FaultException<T> 我们可以向客户端传递一个错误信息(FaultReason)以及一个附加的详细信息(Detail)。理论上,这个附加信息是任何可以序列化的对象。
throw new FaultException<int>(123, "abc");
throw new FaultException<Exception>(new Exception("abc"));
throw new FaultException<Exception>(new Exception("abc"));
如果想传递一个附带元数据的自定义详细信息,可以使用 FaultContract。
[DataContract]
public class ExceptionData
{
[DataMember]
public string Message;
}
[ServiceContract]
public interface IService
{
[OperationContract]
[FaultContract(typeof(ExceptionData))]
void Test();
}
public class Service : IService, IDisposable
{
public void Test()
{
ExceptionData d = new ExceptionData();
d.Message = "xxxxxx";
throw new FaultException<ExceptionData>(d, "abc");
}
public void Dispose()
{
Console.WriteLine("Dispose...");
}
}
public class ExceptionData
{
[DataMember]
public string Message;
}
[ServiceContract]
public interface IService
{
[OperationContract]
[FaultContract(typeof(ExceptionData))]
void Test();
}
public class Service : IService, IDisposable
{
public void Test()
{
ExceptionData d = new ExceptionData();
d.Message = "xxxxxx";
throw new FaultException<ExceptionData>(d, "abc");
}
public void Dispose()
{
Console.WriteLine("Dispose...");
}
}
当然,我们也可以直接抛出一个被称之为 "Unknown Faults" 的 FaultException 异常实例。还有另外一种情况,你已经写好了代码,有很多……很多……的代码,要是一个个修改会非常……非常……麻烦,那么怎么在不做大的代码修改情况下传递详细异常信息给客户端呢?
方法1: ServiceBehavior(IncludeExceptionDetailInFaults=true)]
[ServiceBehavior(IncludeExceptionDetailInFaults=true)]
public class Service : IService, IDisposable
{
public void Test()
{
throw new Exception("abc");
}
public void Dispose()
{
Console.WriteLine("Dispose...");
}
}
public class Service : IService, IDisposable
{
public void Test()
{
throw new Exception("abc");
}
public void Dispose()
{
Console.WriteLine("Dispose...");
}
}
方法2: ServiceDebugBehavior
这个比方法1要更方便一些,我们除了可以写代码外,还可以用配置文件。
ServiceHost host = new ServiceHost(typeof(Service), new Uri("http://localhost:8080/Service"));
host.AddServiceEndpoint(typeof(IService), new BasicHttpBinding(), "");
ServiceDebugBehavior debug = host.Description.Behaviors.Find<ServiceDebugBehavior>();
debug.IncludeExceptionDetailInFaults = true;
host.Open();
host.AddServiceEndpoint(typeof(IService), new BasicHttpBinding(), "");
ServiceDebugBehavior debug = host.Description.Behaviors.Find<ServiceDebugBehavior>();
debug.IncludeExceptionDetailInFaults = true;
host.Open();
看看这两种方法抛出的异常是什么样的。
未处理 System.ServiceModel.FaultException`1
Message="abc"
Source="mscorlib"
StackTrace:
Server stack trace:
在 ConsoleApplication1.localhost.IService.Test()
在 ConsoleApplication1.localhost.ServiceClient.Test() 位置 D:\...\localhost.cs:行号 60
在 ConsoleApplication1.Program.Main(String[] args) 位置 D:\...\Program.cs:行号 62
Message="abc"
Source="mscorlib"
StackTrace:
Server stack trace:
在 ConsoleApplication1.localhost.IService.Test()
在 ConsoleApplication1.localhost.ServiceClient.Test() 位置 D:\...\localhost.cs:行号 60
在 ConsoleApplication1.Program.Main(String[] args) 位置 D:\...\Program.cs:行号 62
不错,除了 Error Message,还有详细的 stack trace,方便调试。也正因为这样,此种方法也不适合在正式项目中使用,作为系统架构设计的一部分,我们应该事先设计好异常处理。
如果服务方法是 IsOneWay=true,因不接收返回消息,客户端也就不会触发异常了。而 Callback 无非是服务器和客户端掉换个身份而已,道理相同。
public interface ICallback
{
[OperationContract]
void DoCallback();
}
[ServiceContract(CallbackContract=typeof(ICallback))]
public interface IService
{
[OperationContract]
void Test();
}
[ServiceBehavior(ConcurrencyMode=ConcurrencyMode.Reentrant)]
public class Service : IService, IDisposable
{
public void Test()
{
try
{
OperationContext.Current.GetCallbackChannel<ICallback>().DoCallback();
}
catch (FaultException e)
{
Console.WriteLine(e);
}
}
public void Dispose()
{
Console.WriteLine("Dispose...");
}
}
{
[OperationContract]
void DoCallback();
}
[ServiceContract(CallbackContract=typeof(ICallback))]
public interface IService
{
[OperationContract]
void Test();
}
[ServiceBehavior(ConcurrencyMode=ConcurrencyMode.Reentrant)]
public class Service : IService, IDisposable
{
public void Test()
{
try
{
OperationContext.Current.GetCallbackChannel<ICallback>().DoCallback();
}
catch (FaultException e)
{
Console.WriteLine(e);
}
}
public void Dispose()
{
Console.WriteLine("Dispose...");
}
}
———————————————————————
任何美好的事物只有触动了人们的心灵才变的美好;
孤独的时候看看天空里的雨,其实流泪的不只是你。
人生只有走出来的美丽,没有等出来的辉煌!
———————————————————————