WCF学习之旅—基于Fault Contract 的异常处理(十八)
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 的异常处理(十八)的更多相关文章
- WCF学习之旅—基于ServiceDebug的异常处理(十七)
WCF学习之旅—WCF中传统的异常处理(十六) 二.基于ServiceDebug的异常处理 从前面的示例中,可以看到客户端捕获了异常,这是我们处理异常的前提.为了有利于我们进行有效的调试,WCF提供了 ...
- WCF学习之旅—HTTP双工模式(二十)
WCF学习之旅—请求与答复模式和单向模式(十九) 四.HTTP双工模式 双工模式建立在上文所实现的两种模式的基础之上,实现客户端与服务端相互调用:前面介绍的两种方法只是在客户端调用服务端的方法,然后服 ...
- WCF学习之旅—实现REST服务(二十二)
一.什么是REST 表述性状态转移(Representational State Transfer,REST),不是一种标准,而是一种软件架构风格. 基于REST的服务与基于SOAP的服务相比,性能. ...
- WCF学习之旅—TCP双工模式(二十一)
WCF学习之旅—请求与答复模式和单向模式(十九) WCF学习之旅—HTTP双工模式(二十) 五.TCP双工模式 上一篇文章中我们学习了HTTP的双工模式,我们今天就学习一下TCP的双工模式. 在一个基 ...
- WCF学习之旅—实现支持REST服务端应用(二十三)
在上一篇(WCF学习之旅—实现REST服务(二十二))文章中简单介绍了一下RestFul与WCF支持RestFul所提供的方法,本文讲解一下如何创建一个支持REST的WCF服务端程序. 四.在WCF中 ...
- WCF学习之旅—实现支持REST客户端应用(二十四)
WCF学习之旅—实现REST服务(二十二) WCF学习之旅—实现支持REST服务端应用(二十三) 在上二篇文章中简单介绍了一下RestFul与WCF支持RestFul所提供的方法,及创建一个支持RES ...
- WCF学习之旅—WCF服务部署到IIS7.5(九)
上接 WCF学习之旅—WCF寄宿前的准备(八) 四.WCF服务部署到IIS7.5 我们把WCF寄宿在IIS之上,在IIS中宿主一个服务的主要优点是在发生客户端请求时宿主进程会被自动启动,并且你可以 ...
- WCF学习之旅—WCF服务的批量寄宿(十三)
上接 WCF学习之旅—WCF服务部署到IIS7.5(九) WCF学习之旅—WCF服务部署到应用程序(十) WCF学习之旅—WCF服务的Windows 服务程序寄宿(十一) WCF学习之旅—WCF ...
- WCF学习之旅—TcpTrace工具(二十六)
止文(WCF学习之旅—TcpTrace工具(二十五))介绍了关于TcpTrance的一种使用方式,接下来介绍第二种使用方式. 三.通过ListenUri实现基于tcpTracer的消息路由 对于路由的 ...
随机推荐
- 菜鸟学Struts2——零配置(Convention )
又是周末,继续Struts2的学习,之前学习了,Struts的原理,Actions以及Results,今天对对Struts的Convention Plugin进行学习,如下图: Struts Conv ...
- 简单有效的kmp算法
以前看过kmp算法,当时接触后总感觉好深奥啊,抱着数据结构的数啃了一中午,最终才大致看懂,后来提起kmp也只剩下“奥,它是做模式匹配的”这点干货.最近有空,翻出来算法导论看看,原来就是这么简单(先不说 ...
- PHP数据类型之间的强制转换
1.实型数据强制转换为整型数据 $float1=2.7; $int1=(int)$float1; echo var_dump($int1),"<br>"; 输出: in ...
- WPF 微信 MVVM 【续】修复部分用户无法获取列表
看过我WPF 微信 MVVM这篇文章的朋友,应该知道我里面提到了我有一个小号是无法获取列表的,始终也没找到原因. 前两天经过GitHub上h4dex大神的指导,知道了原因,是因为微信在登录以后,web ...
- MVC Core 网站开发(Ninesky) 1、创建项目
又要开一个新项目了!说来惭愧,以前的东西每次都没写完,不是不想写完,主要是我每次看到新技术出来我都想尝试一下,看到.Net Core 手又痒了,开始学MVC Core. MVC Core最吸引我的有三 ...
- Eclipse中启动tomcat报错java.lang.OutOfMemoryError: PermGen space的解决方法
有的项目引用了太多的jar包,或者反射生成了太多的类,异或有太多的常量池,就有可能会报java.lang.OutOfMemoryError: PermGen space的错误, 我们知道可以通过jvm ...
- virtualbox linux虚拟机相关
linux虚拟机设置为静态IP 在virtualbox中安装好linux虚拟机后,如果采用的是NAT方式的话,linux虚拟机默认采用dhcp方式自动上网,而且用的是NetworkManager服务而 ...
- 强强联合,Testin云测&云层天咨众测学院开课了!
Testin&云层天咨众测学院开课了! 共享经济时代,测试如何赶上大潮,利用碎片时间给女票或者自己赚点化妆品钱? 2016年12月13日,Testin联手云层天咨带领大家一起推开众测的大门 ...
- Flyweight(享元模式)
import java.util.Hashtable; /** * 享元模式 * @author TMAC-J * 享元模式一般和工厂模式一起使用,但此处为了更好说明,只用享元模式 * 定义:享元模式 ...
- es6小白学习笔记(一)
1.let和const命令 1.es6新增了let和const命令,与var用法类似,但它声明的变量只在let所在的代码块内有效(块级作用域,es5只有全局和函数作用域) { let a = 1; v ...