65.9K
CodeProject 正在变化。 阅读更多。
Home

在 WCF 中处理带和不带 FaultContract 属性的错误。

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.79/5 (5投票s)

2014年10月27日

CPOL

8分钟阅读

viewsIcon

21637

downloadIcon

148

处理 WCF 中的错误。

引言

在本文中,我将讨论WCF (Windows Communication Foundation) 中的错误处理。每个人都想要简单的事情,我的意思是他们如何能够轻松理解或如何以简单的方式高效地完成它。因此,如果我们谈论 WCF 服务中的故障(Faults),它们不能在没有 FaultException 类帮助的情况下直接在 SOAP 消息中发送到客户端。因此,客户端可以在浏览器本身中看到 WCF 故障,但与故障相关的错误消息应该简单,或者客户端可以轻松理解。因此,WCF FaultContract 属性可以轻松处理它。所以我们需要同时使用 FaultException 类向客户端抛出故障,并使用 FaultContract 属性使故障消息尽可能简单。

背景

基本上,本文是为新手准备的。在本文中,我将详细阐述 WCF 中 FaultContract 属性的概念。所以在这里我将提供一些示例,您也可以在一个 zip 文件格式中下载相同的应用程序。

程序员/读者应该对 WCF 服务有一些基本想法或知识,例如如何创建 WCF 服务应用程序、如何在客户端应用程序中消耗 WCF 服务以及如何在客户端应用程序中调用它等等。

FaultContract 属性基本上用作方法中的属性。通过使用这些属性,我们可以为 WCF 故障添加更多信息。FaultContract 在属性中定义为类类型。所以如果我们向 FaultContract 添加一个类类型,我们将能够抛出 FaultContract 类型的 FaultException。所以稍后在这篇文章中,我们将向您展示带类型的 FaultContract 的完整示例。

所以让我们开始我们在 WCF 服务中的错误处理之旅。

使用代码

如上文所述,本文完全是关于 WCF 服务中的故障/错误处理。因此,在我们接下来的示例中,我们将使用FaultException 类来处理错误。但是,为了在故障中添加更多信息,我们在方法中使用 FaultContract 属性。所以让我们开始...

在这里,我们将根据我们的要求检查两个示例。

A. 在第一个示例中,我们将处理 WCF 故障,而不使用 FaultContract 属性。

B. 在第二个示例中,我们将处理 WCF 故障,并使用 FaultContract 属性。

A. 在第一个示例中,我们将处理 WCF 故障,而不使用 FaultContract 属性。

步骤:1

在您的 Visual Studio 框架中创建一个 WCF 服务应用程序,并将其命名为“HandleFaultInWCF”。添加服务应用程序后,将创建一些文件,如 IService1.cs 和 Service1.svc.cs

步骤:2

添加 WCF 服务应用程序后,将创建两个文件,如 IService1.cs 和 Service1.svc.cs。这个文件 IService1.cs 作为服务契约。服务契约只不过是将所有契约封装起来,并根据客户端的要求公开其契约。实际上它是一个接口,因此根据接口定义,它只包含所有契约的声明部分。然后将这些代码添加到您的 cs 文件中,如 IService1.cs 文件。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.ServiceModel.Web;
using System.Text;

namespace HandleFaultInWCF
{
   
    [ServiceContract]
    public interface IService1
    {
        // Here we are handling the Fault without FaultContract attribute
        [OperationContract]
        string GetData(string value);

    }
}

在上面的代码中,我只是在 IService1 接口中声明了一个方法/操作契约 "GetData()",并添加了一个属性 [OperationContract]。

步骤:3

然后在第 3 步中,我们将像 Service1svc.cs 文件一样在 svc.cs 文件中定义 GetData() 方法/操作契约。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.ServiceModel.Web;
using System.Text;
using System.Data.SqlClient;

namespace HandleFaultInWCF
{
    public class Service1 : IService1
    {
      

        // Return the  Value what ever entered in the client end
        public string GetData(string value)
        {
            try
            {
                // Assign a conection string
                SqlConnection conObj = new SqlConnection("Server=localhost; Database=OnlineShopping ; Integrated Security=True");
             
                // Open the connection
                conObj.Open();

                return value;
            }
            catch (Exception ex)
            {
                throw new FaultException(ex.Message);
            }
        }
    }
}

GetData() 方法中,我们尝试打开一个数据库,如果我们无法打开数据库,则将在 catch 块中由 Faultexception 类抛出异常。catch 块只会将异常消息传递给客户端。所以客户端将直接看到系统生成的错误消息,没有用户友好的消息,客户端可以轻松理解。所以这对于客户端/最终用户来说会有点令人困惑,不知道该怎么做。我们将在文章的后面检查该方法的输出。

由于我们在上述场景中无法向客户端/最终用户提供/显示用户友好的消息。但在我们的下一个示例中,我们将使用带 FaultContract 属性的 FaultException,我们将能够向客户端/最终用户发送任何类型的消息或消息的详细信息。

B. 在第二个示例中,我们将处理 WCF 故障,并使用 FaultContract 属性。

步骤 1

我们可以在同一个服务中使用示例 B,然后将这些代码添加到您的 cs 文件中,如 IService1.cs 文件中的 ServiceContract。现在新代码看起来像这样

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.ServiceModel.Web;
using System.Text;

namespace HandleFaultInWCF
{
   
    [ServiceContract]
    public interface IService1
    {

        // Handling the Fault with FaultContract attribute as Strong Type 
        [OperationContract]
        [FaultContract(typeof(SetErrorDetails))]
        int DividedByZero(int firstNumber,int secondNumber);

          // Here we are handling the Fault without FaultContract attribute
        [OperationContract]
        string GetData(string value);
    }

    // Declared the Data Contract for handling the fault details
    [DataContract]
    public class SetErrorDetails
    {
        public string erroName;
        public string errorDetails;

        [DataMember]
        public string ErrorName
        {
            get
            {
                return erroName;
            }

            set
            {
                erroName = value;
            }
        }

        [DataMember]
        public string ErrorDetails
        {
            get
            {
                return errorDetails;
            }

            set
            {
                errorDetails = value;
            }
        }

    }

}
 

在上面的代码中,我们声明了一个方法“DividedByZero()”和一个类 SetErrorDetails class

此方法 DividedByZero() 有两个属性 [OperationContract] 和 [FaultContract(typeof(SetErrorDetails))]。通过使用此 FaultContract 属性,我们可以向最终用户/客户端发送一些用户可理解的消息。[FaultContract(typeof(SetErrorDetails))] 意味着 FaultContract 是 SetErrorDetails 类类型,我们还声明了一些属性的类。

这里我声明了类 SetErrorDetails,它有两个属性,如 ErrorNameErroDetails。我们将在 FaultContract 类型中使用此类。所以在这个例子中,属性 ErrorName 我们将设置错误的名称,而另一个属性 ErrorDetails 用于设置错误的详细信息。但如果我们比较示例 A,那里没有这样的设施可以设置 FaultContract 属性中的任何消息详细信息。

步骤 2

然后在第 3 步中,我们将像 Service1svc.cs 文件一样在 svc.cs 文件中定义 DividedByZero() 方法。添加代码后,文件看起来像这样

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.ServiceModel.Web;
using System.Text;
using System.Data.SqlClient;

namespace HandleFaultInWCF
{
    public class Service1 : IService1
    {
     // Return the  Value what ever entered in the client end
         public string GetData(string value)
        {
            try
            {
                // Assign a conection string
                SqlConnection conObj = new SqlConnection("Server=localhost; Database=OnlineShopping ; Integrated Security=True");
             
                // Open the connection
                conObj.Open();

                return value;
            }
            catch (Exception ex)
            {
                throw new FaultException(ex.Message);
            }
        }

        // Checking the Fault as strong Type
        public int DividedByZero(int FirstNumber, int SecondNumber)
        {
            try 
	        {	 
             return (FirstNumber / SecondNumber);
	        }

	        catch (Exception ex)
	        {
                // Creating the ErrorDetails Class Object 
                 SetErrorDetails errorObj = new SetErrorDetails();

                 if (SecondNumber == 0)
                {
                    // Here we can set the message what ever we want to show in Client side
                    errorObj.ErrorName = "The second number is Zero.";
                    errorObj.ErrorDetails = ex.Message + " " + "Second number must be greater than Zero"; 
                }
                 throw new FaultException(errorObj);
	        }

        }
    }
}

这里我们声明了方法/操作契约 DividedByZero()。在此方法中,我们只是尝试在除以两个整数后返回一个整数值。这里我们只是处理第二个数字为零 (0) 的错误。

如何在 FaultContract 中设置更多信息

在上面的方法中,我们处理 catch 块中的故障,如果 secondNumber = 0。在 Catch 块中,我们创建 SetErrorDetails 类的对象,并为 SetErrorDetails 类的属性分配一些用户可理解的消息。在此示例中,我已分配如下

errorObj.ErrorName = "The second number is Zero.";

errorObj.ErrorDetails = ex.Message + " " + "Second number must be greater than Zero";

您可以在此类的属性中设置任何文本,我将通过此语句throw new FaultException(errorObj) 抛出 FaultException。所以这里errorObj 是类SetErrorDetails 对象。所以客户端也必须使用 SetErrorDetails 类型来处理 FaultException。

在客户端应用程序中消耗服务

要在客户端应用程序中消耗服务,首先构建服务应用程序,然后在客户端应用程序中添加服务引用,例如“ClientApp”。在客户端应用程序中添加一个页面,如 WebForm1.aspx,并将 html 代码放入 WebForm1.aspx 页面中。

<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="WebForm1.aspx.cs" Inherits="ClientApp.WebForm1" %>

<!DOCTYPE html>

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title></title>
</head>
<body>
    <form id="form1" runat="server">
    <div>  
          <h2>WCF Fault Without FaultContract</h2>
      <h3> Please Enter some string :</h3> <asp:TextBox ID="txtEnter" runat="server"></asp:TextBox>
         <asp:Button ID="btnResult1"  runat="server" Text="GetData() Method" OnClick="btnResult1_Click" />
         <h4>Result:</h4>
        <asp:Label ID="lblResult1" runat="server" ForeColor="Red" ></asp:Label>
         
         <h2>WCF Fault With FaultContract</h2>
        
        Enter First Number : <asp:TextBox ID="txtFirst" runat="server" Text="1"></asp:TextBox>
        Enter Second Number : <asp:TextBox ID="txtSecond" runat="server" Text="0"></asp:TextBox>
        <asp:Button ID="btnCheckResult"  runat="server" Text="DividedByZero() method" OnClick="btnCheckResult_Click" />
        
         <h4>Result:</h4>
        <asp:Label ID="lblResult2" runat="server"></asp:Label><br />

        <asp:Label ID="lblError1" runat="server" ForeColor="Red" ></asp:Label>
        <asp:Label ID="lblError2" runat="server" ForeColor="Red" ></asp:Label>

    </div>
    </form>
</body>
</html>

将代码放在 WebForm1.aspx.cs 文件的代码隐藏文件中

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.ServiceModel;
using ClientApp.ServiceReference1;

namespace ClientApp
{
    public partial class WebForm1 : System.Web.UI.Page
    {
        protected void Page_Load(object sender, EventArgs e)
        {

        }

        protected void btnCheckResult_Click(object sender, EventArgs e)
        {
            ServiceReference1.Service1Client serviceobj = new ServiceReference1.Service1Client();
            int result = 0;

            try
            {
               
                result = serviceobj.DividedByZero(Convert.ToInt16(txtFirst.Text),
                                                  Convert.ToInt16(txtSecond.Text));

                lblResult2.Text = Convert.ToString(result);

            }
            catch (FaultException exObj)
            {
                 lblError1.Text = Convert.ToString(exObj.Detail.ErrorName);
                 lblError2.Text = Convert.ToString(exObj.Detail.ErrorDetails);  
            }
        }

        protected void btnResult1_Click(object sender, EventArgs e)
        {
            ServiceReference1.Service1Client serviceobj = new ServiceReference1.Service1Client();

            try
            {
                lblResult1.Text = serviceobj.GetData(txtEnter.Text.Trim());
                
            }
            catch (FaultException ex)
            {
                lblResult1.Text = ex.Message;
            }
        }
    }
}

我在同一个代码隐藏文件中添加了两个示例 A(在第一个示例中,我们将处理 WCF 故障,而不使用 FaultContract 属性。)和示例 B(在第二个示例中,我们将处理 WCF 故障,并使用 FaultContract 属性。)的代码。现在运行服务消耗的客户端应用程序。运行应用程序后,输出将如下所示

让我们描述一下输出

在输出窗口中将有两个示例。

检查示例 A 的步骤(不带 FaultContract 的 WCF 故障)

1. 在文本框“Please Enter some string”中输入一些文本。

2. 然后点击第一个按钮“GetData() Method”,您将看到示例 A 的输出。

输出将是

"无法打开登录请求的数据库“OnlineShopping”。登录失败。用户“ram\ram1”登录失败。" 这是系统生成的异常消息。在此示例中,我们没有从服务发送任何消息。

检查示例 B 的步骤(带 FaultContract 的 WCF 故障)

1. 假设在第一个文本框“Enter First Number”中输入整数值“1”。

2. 假设在第二个文本框“Enter Second Number”中输入整数值“0”。

注意

请在第二个示例中提供零 (0) 来检查异常。

然后,要查看示例 B 的输出,您需要单击第二个按钮“DividedByZero() method”。一开始,我为相应的文本框提供了一些值,例如“1”和“0”,例如“Enter First Number”“Enter Second Number”。输出将是

 第二个数是零。 尝试除以零。第二个数必须大于零

在此消息中,我们添加了一些用户友好的消息,以使最终用户清楚故障,我们只是使用 FaultException 类中的 FaultContract 属性完成了此操作。

所以最终我们可以说,如果您想向最终用户提供有关故障的一些信息,那么最好使用带 FaultException 类的 FaultContract 属性。

希望您喜欢这篇文章。我将带来一些关于 WCF 的新文章。

© . All rights reserved.