WCF学习之旅—WCF中传统的异常处理(十六)

WCF学习之旅—基于ServiceDebug的异常处理(十七)

三、基于Fault Contract 的异常处理

第二个示例是通过定制ServiceDebug来获取服务端的异常,但是这种方式只能用于Debug阶段。在我们的WCF应用发布之后,这种获取异常的方式无法在我们的工作环境中使用。我们必须找到一种异常处理方式可以在客户端获取相应的异常提示信息。那就是我们接下来要介绍的基于FaultContract的解决方案。我们知道WCF采用一种基于 Contract,Contract定义了进行交互的双方进行消息交换所遵循的准则和规范。Service Contract定义了包含了所有Operation的Service的接口,Data Contract定义了交互的数据的结构,而FaultContract实际上定义需要再双方之间进行交互的了异常、错误的表示。现在我们来学习如何使用基于FaultContract的异常处理。

我们首先来定义一个表示Fault的类:SQLError。考虑到这个类需要在Service 和Client使用,我把它定义在SCF.Contracts中:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.Text;
using System.Threading.Tasks; namespace SCF.Contracts
{ [DataContract]
public class SQLError
{ private string _operation;
private string _errorMessage; public SQLError(string operation, string errorMessage)
{ this._operation = operation;
this._errorMessage = errorMessage;
} [DataMember]
public string Operation
{ get { return _operation; }
set { _operation = value; } } [DataMember]
public string ErrorMessage
{
get { return _errorMessage; }
set { _errorMessage = value; } }
} }

如果你出现如下图的错误信息,请引用一下System.Runtime.Serialization.dll。

在SQLError中定义了两个成员:表示出 错操作的Operation和出错信息的ErrorMessage。由于该类的对象需要在终结点之间传递,所以必须是可序列化的,在WCF中, 我们一般用两个不同的Serializer实现Object和XML的Serialization和 Deserialization:Datacontract Serializer和XML Serializer。而对于Fault,只能使用前者。

定义了SQLError,我们需要通过特性FaultContract将其添加到EditBook方法上面,我们把IBookService接口修改成如下。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.Text; namespace SCF.Contracts
{ // 注意: 使用“重构”菜单上的“重命名”命令,可以同时更改代码和配置文件中的接口名“IBookService”。 [ServiceContract]
public interface IBookService
{ [OperationContract]
string GetBook(string Id); [OperationContract]
string AddBook(string book); [OperationContract]
[FaultContract(typeof(SQLError))]
string EditBook(string book); [OperationContract]
string Search(string Category, string searchString);
}
}

我们在EditBook上运用了 FaultContract,并指定了封装了Fault对应的类型,那么最终这个基于SQLError类型的FaultContract会被写入 Service Description中,客户端通过获取该Service Description(一般是获取WSDL),它就被识别它,就会将从接收到的Soap中对该Fault的XML Mapping到具体的SQLError类型。

接着我们在服务端的出错处理中抛出Exception的方式植入这个SQLError对象:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.Text;
using System.Data.Entity;
using SCF.Contracts;
using SCF.Model;
using SCF.Common; namespace SCF.WcfService
{ // 注意: 使用“重构”菜单上的“重命名”命令,可以同时更改代码、svc 和配置文件中的类名“BookService”。 // 注意: 为了启动 WCF 测试客户端以测试此服务,请在解决方案资源管理器中选择 BookService.svc 或 BookService.svc.cs,然后开始调试。
public class BookService : IBookService
{ Entities db = new Entities();
public string AddBook(string mbook)
{
try
{ Books book = XMLHelper.DeSerializer<Books>(mbook);
db.Books.Add(book);
db.SaveChanges(); }
catch (Exception ex)
{
return ex.Message;
} return "true";
} public string EditBook(string mbook)
{ try
{
Books book = XMLHelper.DeSerializer<Books>(mbook);
db.Entry(book).State = EntityState.Added;
db.SaveChanges();
} catch (Exception ex)
{
//return ex.Message;
SQLError error = new SQLError("更新数据库操作", ex.Message);
string reason = string.Empty;
if (ex.InnerException != null)
{
reason = string.Format("{0}。{1}"ex.Message, ex.InnerException.Message);
}
else reason = ex.Message;
throw new FaultException<SQLError>(error, new FaultReason(reason), new FaultCode("Edit"));
}
return "true";
} public string GetBook(string Id)
{ int bookId = Convert.ToInt32(Id);
Books book= db.Books.Find(bookId);
string xml=XMLHelper.ToXML<Books>(book);
return xml;
//throw new NotImplementedException();
} public string Search(string Category, string searchString)
{ var cateLst = new List<string>(); var cateQry = from d in db.Books
orderby d.Category
select d.Category;
cateLst.AddRange(cateQry.Distinct()); var books = from m in db.Books
select m; if (!String.IsNullOrEmpty(searchString))
{ books = books.Where(s => s.Name.Contains(searchString));
} List<Books> list = null;
if (string.IsNullOrEmpty(Category))
{ list = books.ToList<Books>(); }
else
{
list = books.Where(x => x.Category == Category).ToList<Books>(); }
return XMLHelper.ToXML<List<Books>>(list);
} } }

在catch中,抛出FaultException<SQLError> Exception,并指定具体的SQLError对象,以及一个FaultCode(一般指明出错的来源)和FaultReason(出错的原因)。我们现在先不修改客户端的异常处理的相关代码,先运行Hosting,看看WSDL中什么特别之处,如下图:


    通 过上图,我们可以看到,在EditBook方法的WSDL定义了中多了一些节点。

弄清楚了Fault在WSDL中表示后,我们来修改我们客户端的代码,来有效地进行异常处理:

using SCF.Contracts;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.ServiceModel;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using SCF.Model;
using SCF.Common; namespace WinClient
{
public partial class FrmBook : Form
{
public FrmBook()
{
InitializeComponent();
} private void ShowBook()
{
Books book = XMLHelper.DeSerializer<Books>(textBoxMsg.Text);
txtBookId.Text = book.BookID.ToString();
txtAuthorID.Text = book.AuthorID.ToString();
textBoxName.Text = book.Name;
textBoxCategory.Text = book.Category.ToString();
textBoxPrice.Text = book.Price.ToString();
textBoxRating.Text = book.Rating.ToString();
textBoxNumberofcopies.Text = book.Numberofcopies.ToString();
dateTimePickerPublishDate.Text = book.PublishDate.ToString();
} private void btnSearch_Click(object sender, EventArgs e)
{ BookServiceRef.BookServiceClient bookSvrClient = new BookServiceRef.BookServiceClient();
textBoxMsg.Text = bookSvrClient.Search(string.Empty, string.Empty);
List<Books> books = XMLHelper.DeSerializer<List<Books>>(textBoxMsg.Text);
gridBooks.DataSource = books;
} private void buttonSave_Click(object sender, EventArgs e)
{
try
{
using (ChannelFactory<IBookService> channelFactory = new ChannelFactory<IBookService>("WSHttpBinding_IBookService"))
{
IBookService proxy = channelFactory.CreateChannel();
using (proxy as IDisposable)
{
if (string.IsNullOrEmpty(txtBookId.Text))
{
textBoxMsg.Text = proxy.AddBook(GetBookInfo());
}
else
textBoxMsg.Text = proxy.EditBook(GetBookInfo());
}
}
}
catch (FaultException<SQLError> ex)
{
SQLError error = ex.Detail;
textBoxMsg.Text = string.Format("抛出一个服务端错误。\r\n\t错误代码:{0}\n\t错误原因:{1}\r\n\t操作:{2}\r\n\t错误信息:{3}",
ex.Code, ex.Reason, error.Operation, error.ErrorMessage);
} catch (Exception ex)
{
if (ex.InnerException != null)
{
textBoxMsg.Text = ex.Message + ex.InnerException.Message;
}
else
textBoxMsg.Text = ex.Message;
}
}
public String GetBookInfo()
{
Books book = new Books();
book.AuthorID = NumberHelper.ToInt(txtAuthorID.Text);
book.BookID = NumberHelper.ToInt(txtBookId.Text);
book.Category = textBoxCategory.Text;
book.Name = textBoxName.Text;
book.Numberofcopies = NumberHelper.ToInt(textBoxNumberofcopies.Text);
book.Price = NumberHelper.ToDecimal(textBoxPrice.Text);
book.PublishDate = dateTimePickerPublishDate.Value;
book.Rating = textBoxRating.Text;
textBoxMsg.Text = XMLHelper.ToXML<Books>(book);
return textBoxMsg.Text;
}
}
}

执行“保存”操作之后,服务端抛出了如下错误信息:

WCF学习之旅—基于Fault Contract 的异常处理(十八)的更多相关文章

  1. WCF学习之旅—基于ServiceDebug的异常处理(十七)

    WCF学习之旅—WCF中传统的异常处理(十六) 二.基于ServiceDebug的异常处理 从前面的示例中,可以看到客户端捕获了异常,这是我们处理异常的前提.为了有利于我们进行有效的调试,WCF提供了 ...

  2. WCF学习之旅—HTTP双工模式(二十)

    WCF学习之旅—请求与答复模式和单向模式(十九) 四.HTTP双工模式 双工模式建立在上文所实现的两种模式的基础之上,实现客户端与服务端相互调用:前面介绍的两种方法只是在客户端调用服务端的方法,然后服 ...

  3. WCF学习之旅—实现REST服务(二十二)

    一.什么是REST 表述性状态转移(Representational State Transfer,REST),不是一种标准,而是一种软件架构风格. 基于REST的服务与基于SOAP的服务相比,性能. ...

  4. WCF学习之旅—TCP双工模式(二十一)

    WCF学习之旅—请求与答复模式和单向模式(十九) WCF学习之旅—HTTP双工模式(二十) 五.TCP双工模式 上一篇文章中我们学习了HTTP的双工模式,我们今天就学习一下TCP的双工模式. 在一个基 ...

  5. WCF学习之旅—实现支持REST服务端应用(二十三)

    在上一篇(WCF学习之旅—实现REST服务(二十二))文章中简单介绍了一下RestFul与WCF支持RestFul所提供的方法,本文讲解一下如何创建一个支持REST的WCF服务端程序. 四.在WCF中 ...

  6. WCF学习之旅—实现支持REST客户端应用(二十四)

    WCF学习之旅—实现REST服务(二十二) WCF学习之旅—实现支持REST服务端应用(二十三) 在上二篇文章中简单介绍了一下RestFul与WCF支持RestFul所提供的方法,及创建一个支持RES ...

  7. WCF学习之旅—WCF服务部署到IIS7.5(九)

    上接   WCF学习之旅—WCF寄宿前的准备(八) 四.WCF服务部署到IIS7.5 我们把WCF寄宿在IIS之上,在IIS中宿主一个服务的主要优点是在发生客户端请求时宿主进程会被自动启动,并且你可以 ...

  8. WCF学习之旅—WCF服务的批量寄宿(十三)

    上接    WCF学习之旅—WCF服务部署到IIS7.5(九) WCF学习之旅—WCF服务部署到应用程序(十) WCF学习之旅—WCF服务的Windows 服务程序寄宿(十一) WCF学习之旅—WCF ...

  9. WCF学习之旅—TcpTrace工具(二十六)

    止文(WCF学习之旅—TcpTrace工具(二十五))介绍了关于TcpTrance的一种使用方式,接下来介绍第二种使用方式. 三.通过ListenUri实现基于tcpTracer的消息路由 对于路由的 ...

随机推荐

  1. SQLSERVER聚集索引与非聚集索引的再次研究(上)

    SQLSERVER聚集索引与非聚集索引的再次研究(上) 上篇主要说聚集索引 下篇的地址:SQLSERVER聚集索引与非聚集索引的再次研究(下) 由于本人还是SQLSERVER菜鸟一枚,加上一些实验的逻 ...

  2. HTML文档声明

    前面的话   HTML文档通常以类型声明开始,该声明将帮助浏览器确定其尝试解析和显示的HTML文档类型.本文将详细介绍文档声明DOCTYPE 特点   文档声明必须是HTML文档的第一行.且顶格显示, ...

  3. 【Web动画】SVG 实现复杂线条动画

    在上一篇文章中,我们初步实现了一些利用基本图形就能完成的线条动画: [Web动画]SVG 线条动画入门 当然,事物都是朝着熵增焓减的方向发展的,复杂线条也肯定比有序线条要多. 很多时候,我们无法人工去 ...

  4. C#向PPT文档插入图片以及导出图片

    PowerPoint演示文稿是我们日常工作中常用的办公软件之一,而图片则是PowerPoint文档的重要组成部分,那么如何向幻灯片插入图片以及导出图片呢?本文我将给大家分享如何使用一个免费版Power ...

  5. [转载]Cookie/Session的机制与安全

    Cookie和Session是为了在无状态的HTTP协议之上维护会话状态,使得服务器可以知道当前是和哪个客户在打交道.本文来详细讨论Cookie和Session的实现机制,以及其中涉及的安全问题. 因 ...

  6. 通过自定义特性,使用EF6拦截器完成创建人、创建时间、更新人、更新时间的统一赋值(使用数据库服务器时间赋值,接上一篇)

    目录: 前言 设计(完成扩展) 实现效果 扩展设计方案 扩展后代码结构 集思广益(问题) 前言: 在上一篇文章我写了如何重建IDbCommandTreeInterceptor来实现创建人.创建时间.更 ...

  7. Node.js入门

    开始之前,安利一本正在看的书<站在两个世界的边缘>,作者程浩,上帝丢给他太多理想,却忘了给他完成理想的时间.OK,有兴趣的可以看一看. node.js如标题一样,我也是刚开始接触,大家一起 ...

  8. 拦截UIViewController的popViewController事件

    实现拦截UIViewController的pop操作有两种方式: 自定义实现返回按钮,即设置UIBarButtonItem来实现自定义的返回操作. 创建UINavigatonController的Ca ...

  9. Android中的多线程断点下载

    首先来看一下多线程下载的原理.多线程下载就是将同一个网络上的原始文件根据线程个数分成均等份,然后每个单独的线程下载对应的一部分,然后再将下载好的文件按照原始文件的顺序"拼接"起来就 ...

  10. [DJANGO] excel十几万行数据快速导入数据库研究

    先贴原来的导入数据代码: 8 import os os.environ.setdefault("DJANGO_SETTINGS_MODULE", "www.setting ...