WCF 异常处理、FaultExceptions 和 FaultContracts 入门教程






4.88/5 (26投票s)
本文讨论了 WCF 服务中的异常处理、FaultExceptions,并介绍了 FaultContract。
引言
在本文中,我们将讨论如何在 WCF
服务中执行异常处理,以便客户端能够获取异常信息并采取纠正措施。我们将了解如何将异常信息传递给 WCF
客户端。
背景
在常规的 .Net 应用程序中,使用 try catch 块处理异常非常简单。通知用户的方面也同样简单,因为我们可以在处理完异常后直接将错误消息显示给用户。根据这些信息,用户可以采取纠正措施。
从 WCF
服务的角度来看,WCF
服务中的所有常规异常都可以像处理普通 .NET 异常一样进行处理。事实上,它们就是普通的 .NET 异常。服务面临的主要挑战是将异常信息传播给用户。这是因为服务用户最有可能以文档和 SOAP 消息的形式使用该服务,其次,服务消费者可能是其他技术,向他们发送 .NET 特定的异常没有任何意义。
将异常信息发送给用户永远不是一个好主意。所以,当我说是将异常信息发送给用户时,这里的用户实际上是指尝试使用该服务的应用程序/开发人员,而不是最终用户。最终用户应该始终看到用户友好的消息,而不是异常。另一方面,消耗该服务的应用程序开发人员需要了解应用程序的详细信息,以便他们能够理解哪里出了问题以及应该采取哪些纠正措施。
在基于服务的应用程序场景中,当应用程序以文档(即 SOAP 消息)进行通信时,来自服务到服务消费者的错误和异常报告也以 SOAP 元素的形式进行。SOAP 故障是用于将异常从服务传播到客户端应用程序的方式。
这是否意味着服务开发人员每次遇到异常时都必须手动创建 SOAP 故障?答案是否定的。WCF
服务以 FaultException
的形式提供了非常好的抽象,服务可以使用 FaultException
和 FaultContract
轻松地将异常信息传播给客户端。
使用代码
现在让我们看看在没有 FaultExceptions
的情况下,为什么无法获取异常详细信息。此外,让我们尝试了解如何使用 FaultContracts
和 FaultExceptions
在异常情况下更好地与客户端应用程序进行通信。
创建一个简单的服务
让我们从创建一个包含两个方法的示例服务开始。一个用于两个数字的乘法,另一个用于两个数字的除法。在乘法方法中,如果任何一个数字为 0,我们将抛出一个自定义异常。在除法函数中,框架将抛出 DivideByZeroException
。我们将尝试将这两个异常传播给客户端。
注意:本文假设读者至少了解如何创建和使用简单的 WCF
服务。如果不是,请在继续阅读本文之前参考以下文章:WCF 入门教程[^]
示例服务的服务契约将如下所示:
[ServiceContract]
public interface ITestService
{
[OperationContract]
int Multiply(int num1, int num2);
[OperationContract]
int Divide(int num1, int num2);
}
服务的服务实现将如下所示:
public class TestServiceImplementation : ITestService
{
public int Multiply(int num1, int num2)
{
if (num1 == 0 || num2 == 0)
{
throw new Exception("Please pass only non zero numbers");
}
return num1 * num2;
}
public int Divide(int num1, int num2)
{
try
{
return num1 / num2;
}
catch (DivideByZeroException ex)
{
throw ex;
}
}
}
创建服务客户端
对于客户端,让我们创建一个简单的控制台客户端。首先,让我们尝试向服务传递非零值。
using (ServiceReference1.TestServiceClient client = new ServiceReference1.TestServiceClient())
{
int num1 = 6;
int num2 = 3;
int resultMulitply = client.Multiply(num1, num2);
int resultDivide = client.Divide(num1, num2);
Console.WriteLine("Multiplication: {0}, Division: {1}", resultMulitply, resultDivide);
}

简单的异常处理
让我们尝试将乘法函数的第二个参数传递为 0。我们将收到一个带有消息的异常:Unhandled Exception: System.ServiceModel.FaultException: The server was unable to process the request due to an internal error.
如果我们将除法函数的第二个参数也传递为 0,情况也是如此。

现在,既然我们已经得到了异常,我们就知道出了问题。但是我们收到的异常并没有提供太多有用信息。要从异常中获取有用的信息,我们需要将异常的原因传播给客户端应用程序。这正是 FaultException
发挥作用的地方。
处理 FaultExceptions
现在,让我们使用一个简单的 FaultException
并从我们服务的 Divide 方法中抛出它。
public int Divide(int num1, int num2)
{
try
{
return num1 / num2;
}
catch (DivideByZeroException ex)
{
throw new FaultException(ex.Message);
}
}
这将负责在 SOAP 消息中创建故障元素,并让客户端应用程序了解详细的异常信息。客户端应用程序现在必须捕获来自服务的所有 FaultExceptions
。
try
{
using (ServiceReference1.TestServiceClient client = new ServiceReference1.TestServiceClient())
{
int num1 = 6;
int num2 = 3;
int resultMulitply = client.Multiply(num1, num2);
int resultDivide = client.Divide(num1, 0);
Console.WriteLine("Multiplication: {0}, Division: {1}", resultMulitply, resultDivide);
}
Console.ReadLine();
}
catch (FaultException ex)
{
Console.WriteLine(ex.Message);
}
现在,当我们尝试将第二个参数为 0 的 Divide 函数调用时,我们不仅会收到异常,还会收到异常的详细原因。

FaultContract 和强类型 FaultExceptions
因此,使用故障异常使得服务能够将异常消息传播给客户端。那么,如果我们想在服务中创建自己的类型来对异常进行分类并提供更详细的信息,而不是简单的字符串消息,该怎么办?
WCF
服务还提供了一种方式让我们在故障异常中抛出自定义对象。这些自定义对象将包含异常的详细信息。这些对象必须作为 FaultContract
从服务中公开。
现在,让我们尝试创建一个简单的对象,称为 MyExceptionContainer
,它将包含异常的详细信息(基于应用程序需求)。
[DataContract]
public class MyExceptionContainer
{
[DataMember]
public string Messsage { get; set;}
[DataMember]
public string Description { get; set; }
}
我们将把它作为 Multiply 函数的故障契约公开。
[OperationContract]
[FaultContract(typeof(MyExceptionContainer))]
int Multiply(int num1, int num2);
现在,需要更改此 Mulitply
函数的实现,以在异常中抛出此 FaultContract
对象。让我们看看如何做到这一点。
public int Multiply(int num1, int num2)
{
if (num1 == 0 || num2 == 0)
{
MyExceptionContainer exceptionDetails = new MyExceptionContainer();
exceptionDetails.Messsage = "Business Rule violatuion";
exceptionDetails.Description = "The numbers should be non zero to perform this operation";
throw new FaultException<MyExceptionContainer>(exceptionDetails);
}
return num1 * num2;
}
现在,这个强类型的 FaultException
会将此异常传播到客户端。让我们看看如何在我们的客户端应用程序中处理它。
try
{
using (ServiceReference1.TestServiceClient client = new ServiceReference1.TestServiceClient())
{
int num1 = 6;
int num2 = 3;
int resultMulitply = client.Multiply(num1, 0);
int resultDivide = client.Divide(num1, num2);
Console.WriteLine("Multiplication: {0}, Division: {1}", resultMulitply, resultDivide);
}
Console.ReadLine();
}
catch (FaultException<ServiceReference1.MyExceptionContainer> ex)
{
Console.WriteLine(ex.Detail.Messsage);
Console.WriteLine(ex.Detail.Description);
}
catch (FaultException ex)
{
Console.WriteLine(ex.Message);
}
现在,当 Multiply 函数被调用且任何参数为 0 时,将抛出强类型异常,客户端应用程序可以使用 FaultException<T>
类型的异常来处理它。

现在我们有了一个能够将异常消息发送给客户端应用程序的示例应用程序。拥有适当的 FaultContracts
并将其传递给客户端应用程序始终是一个好主意,因为发送简单的故障有时对客户端应用程序来说可能帮助不大。
关注点
在本文中,我们研究了如何使用普通的 .NET 异常处理技术在 WCF 服务内部执行异常处理。我们还了解了如何在发生异常时将适当的消息和数据传播给客户端应用程序。本文是从初学者的角度编写的,因此只讨论了 FaultExceptions
和 FaultContracts
。人们遵循一些非常好的模式和实践来指定 FaultContracts
和抛出 FaultExceptions
。这些在本文中没有涵盖。希望本文有所帮助。
历史
- 2013 年 3 月 2 日:初版