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中:

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Runtime.Serialization;
  5. using System.Text;
  6. using System.Threading.Tasks;
  7.  
  8. namespace SCF.Contracts
  9. {
  10.  
  11. [DataContract]
  12. public class SQLError
  13. {
  14.  
  15. private string _operation;
  16. private string _errorMessage;
  17.  
  18. public SQLError(string operation, string errorMessage)
  19. {
  20.  
  21. this._operation = operation;
  22. this._errorMessage = errorMessage;
  23. }
  24.  
  25. [DataMember]
  26. public string Operation
  27. {
  28.  
  29. get { return _operation; }
  30. set { _operation = value; }
  31.  
  32. }
  33.  
  34. [DataMember]
  35. public string ErrorMessage
  36. {
  37. get { return _errorMessage; }
  38. set { _errorMessage = value; }
  39.  
  40. }
  41. }
  42.  
  43. }

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

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

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

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Runtime.Serialization;
  5. using System.ServiceModel;
  6. using System.Text;
  7.  
  8. namespace SCF.Contracts
  9. {
  10.  
  11. // 注意: 使用“重构”菜单上的“重命名”命令,可以同时更改代码和配置文件中的接口名“IBookService”。
  12.  
  13. [ServiceContract]
  14. public interface IBookService
  15. {
  16.  
  17. [OperationContract]
  18. string GetBook(string Id);
  19.  
  20. [OperationContract]
  21. string AddBook(string book);
  22.  
  23. [OperationContract]
  24. [FaultContract(typeof(SQLError))]
  25. string EditBook(string book);
  26.  
  27. [OperationContract]
  28. string Search(string Category, string searchString);
  29. }
  30. }

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

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

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Runtime.Serialization;
  5. using System.ServiceModel;
  6. using System.Text;
  7. using System.Data.Entity;
  8. using SCF.Contracts;
  9. using SCF.Model;
  10. using SCF.Common;
  11.  
  12. namespace SCF.WcfService
  13. {
  14.  
  15. // 注意: 使用“重构”菜单上的“重命名”命令,可以同时更改代码、svc 和配置文件中的类名“BookService”。
  16.  
  17. // 注意: 为了启动 WCF 测试客户端以测试此服务,请在解决方案资源管理器中选择 BookService.svc 或 BookService.svc.cs,然后开始调试。
  18. public class BookService : IBookService
  19. {
  20.  
  21. Entities db = new Entities();
  22. public string AddBook(string mbook)
  23. {
  24. try
  25. {
  26.  
  27. Books book = XMLHelper.DeSerializer<Books>(mbook);
  28. db.Books.Add(book);
  29. db.SaveChanges();
  30.  
  31. }
  32. catch (Exception ex)
  33. {
  34. return ex.Message;
  35. }
  36.  
  37. return "true";
  38. }
  39.  
  40. public string EditBook(string mbook)
  41. {
  42.  
  43. try
  44. {
  45. Books book = XMLHelper.DeSerializer<Books>(mbook);
  46. db.Entry(book).State = EntityState.Added;
  47. db.SaveChanges();
  48. }
  49.  
  50. catch (Exception ex)
  51. {
  52. //return ex.Message;
  53. SQLError error = new SQLError("更新数据库操作", ex.Message);
  54. string reason = string.Empty;
  55. if (ex.InnerException != null)
  56. {
  57. reason = string.Format("{0}。{1}"ex.Message, ex.InnerException.Message);
  58. }
  59. else reason = ex.Message;
  60. throw new FaultException<SQLError>(error, new FaultReason(reason), new FaultCode("Edit"));
  61. }
  62. return "true";
  63. }
  64.  
  65. public string GetBook(string Id)
  66. {
  67.  
  68. int bookId = Convert.ToInt32(Id);
  69. Books book= db.Books.Find(bookId);
  70. string xml=XMLHelper.ToXML<Books>(book);
  71. return xml;
  72. //throw new NotImplementedException();
  73. }
  74.  
  75. public string Search(string Category, string searchString)
  76. {
  77.  
  78. var cateLst = new List<string>();
  79.  
  80. var cateQry = from d in db.Books
  81. orderby d.Category
  82. select d.Category;
  83. cateLst.AddRange(cateQry.Distinct());
  84.  
  85. var books = from m in db.Books
  86. select m;
  87.  
  88. if (!String.IsNullOrEmpty(searchString))
  89. {
  90.  
  91. books = books.Where(s => s.Name.Contains(searchString));
  92. }
  93.  
  94. List<Books> list = null;
  95. if (string.IsNullOrEmpty(Category))
  96. {
  97.  
  98. list = books.ToList<Books>();
  99.  
  100. }
  101. else
  102. {
  103. list = books.Where(x => x.Category == Category).ToList<Books>();
  104.  
  105. }
  106. return XMLHelper.ToXML<List<Books>>(list);
  107. }
  108.  
  109. }
  110.  
  111. }

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


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

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

  1. using SCF.Contracts;
  2. using System;
  3. using System.Collections.Generic;
  4. using System.ComponentModel;
  5. using System.Data;
  6. using System.Drawing;
  7. using System.Linq;
  8. using System.ServiceModel;
  9. using System.Text;
  10. using System.Threading.Tasks;
  11. using System.Windows.Forms;
  12. using SCF.Model;
  13. using SCF.Common;
  14.  
  15. namespace WinClient
  16. {
  17. public partial class FrmBook : Form
  18. {
  19. public FrmBook()
  20. {
  21. InitializeComponent();
  22. }
  23.  
  24. private void ShowBook()
  25. {
  26. Books book = XMLHelper.DeSerializer<Books>(textBoxMsg.Text);
  27. txtBookId.Text = book.BookID.ToString();
  28. txtAuthorID.Text = book.AuthorID.ToString();
  29. textBoxName.Text = book.Name;
  30. textBoxCategory.Text = book.Category.ToString();
  31. textBoxPrice.Text = book.Price.ToString();
  32. textBoxRating.Text = book.Rating.ToString();
  33. textBoxNumberofcopies.Text = book.Numberofcopies.ToString();
  34. dateTimePickerPublishDate.Text = book.PublishDate.ToString();
  35. }
  36.  
  37. private void btnSearch_Click(object sender, EventArgs e)
  38. {
  39.  
  40. BookServiceRef.BookServiceClient bookSvrClient = new BookServiceRef.BookServiceClient();
  41. textBoxMsg.Text = bookSvrClient.Search(string.Empty, string.Empty);
  42. List<Books> books = XMLHelper.DeSerializer<List<Books>>(textBoxMsg.Text);
  43. gridBooks.DataSource = books;
  44. }
  45.  
  46. private void buttonSave_Click(object sender, EventArgs e)
  47. {
  48. try
  49. {
  50. using (ChannelFactory<IBookService> channelFactory = new ChannelFactory<IBookService>("WSHttpBinding_IBookService"))
  51. {
  52. IBookService proxy = channelFactory.CreateChannel();
  53. using (proxy as IDisposable)
  54. {
  55. if (string.IsNullOrEmpty(txtBookId.Text))
  56. {
  57. textBoxMsg.Text = proxy.AddBook(GetBookInfo());
  58. }
  59. else
  60. textBoxMsg.Text = proxy.EditBook(GetBookInfo());
  61. }
  62. }
  63. }
  64. catch (FaultException<SQLError> ex)
  65. {
  66. SQLError error = ex.Detail;
  67. 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);
  68. }
  69.  
  70. catch (Exception ex)
  71. {
  72. if (ex.InnerException != null)
  73. {
  74. textBoxMsg.Text = ex.Message + ex.InnerException.Message;
  75. }
  76. else
  77. textBoxMsg.Text = ex.Message;
  78. }
  79. }
  80. public String GetBookInfo()
  81. {
  82. Books book = new Books();
  83. book.AuthorID = NumberHelper.ToInt(txtAuthorID.Text);
  84. book.BookID = NumberHelper.ToInt(txtBookId.Text);
  85. book.Category = textBoxCategory.Text;
  86. book.Name = textBoxName.Text;
  87. book.Numberofcopies = NumberHelper.ToInt(textBoxNumberofcopies.Text);
  88. book.Price = NumberHelper.ToDecimal(textBoxPrice.Text);
  89. book.PublishDate = dateTimePickerPublishDate.Value;
  90. book.Rating = textBoxRating.Text;
  91. textBoxMsg.Text = XMLHelper.ToXML<Books>(book);
  92. return textBoxMsg.Text;
  93. }
  94. }
  95. }

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

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. 开源:Taurus.MVC 框架

    为什么要创造Taurus.MVC: 记得被上一家公司忽悠去负责公司电商平台的时候,情况是这样的: 项目原版是外包给第三方的,使用:WebForm+NHibernate,代码不堪入目,Bug无限,经常点 ...

  2. hadoop 2.7.3本地环境运行官方wordcount-基于HDFS

    接上篇<hadoop 2.7.3本地环境运行官方wordcount>.继续在本地模式下测试,本次使用hdfs. 2 本地模式使用fs计数wodcount 上面是直接使用的是linux的文件 ...

  3. ASP.NET Web API 跨域访问(CORS)

    一.客户端用JSONP请求数据 如果你想用JSONP来获得跨域的数据,WebAPI本身是不支持javascript的callback的,它返回的JSON是这样的: {"YourSignatu ...

  4. SQL Server-聚焦NOT IN VS NOT EXISTS VS LEFT JOIN...IS NULL性能分析(十八)

    前言 本节我们来综合比较NOT IN VS NOT EXISTS VS LEFT JOIN...IS NULL的性能,简短的内容,深入的理解,Always to review the basics. ...

  5. [C#] C# 知识回顾 - 学会处理异常

    学会处理异常 你可以使用 try 块来对你觉得可能会出现异常的代码进行分区. 其中,与之关联的 catch 块可用于处理任何异常情况. 一个包含代码的 finally 块,无论 try 块中是否在运行 ...

  6. Photoshop将普通照片快速制作二次元漫画风格效果

    今天为大家分享Photoshop将普通照片快速制作二次元漫画风格效果,教程很不错,对于喜欢漫画的朋友可以参考本文,希望能对大家有所帮助! 一提到日本动画电影,大家第一印象肯定是宫崎骏,但是日本除了宫崎 ...

  7. UWP开发之Mvvmlight实践六:MissingMetadataException解决办法(.Net Native下Default.rd.xml配置问题)

    最近完成一款UWP应用,在手机端测试发布版(Release)的时候应用莫名奇妙的强行关闭,而同样的应用包在PC端一点问题都没有,而且Debug版在两个平台都没有问题,唯独手机的Release版有问题. ...

  8. P2V之后的磁盘扩容新思路

    背景: 原先的物理机环境多是若干块物理磁盘经过RAID卡进行了RAID5之后的虚拟磁盘组,这样我们在操作系统内看到的也就是一块完整的磁盘.我们会在上面进行分区,然后格式化后以便使用. Figure 1 ...

  9. Android 几种消息推送方案总结

    转载请注明出处:http://www.cnblogs.com/Joanna-Yan/p/6241354.html 首先看一张国内Top500 Android应用中它们用到的第三方推送以及所占数量: 现 ...

  10. 个人网站对xss跨站脚本攻击(重点是富文本编辑器情况)和sql注入攻击的防范

    昨天本博客受到了xss跨站脚本注入攻击,3分钟攻陷--其实攻击者进攻的手法很简单,没啥技术含量.只能感叹自己之前竟然完全没防范. 这是数据库里留下的一些记录.最后那人弄了一个无限循环弹出框的脚本,估计 ...