WCF中的异常处理

在软件开发过程中,不可能没有异常的出现,所以在开发过程中,对不可预知的异常进行解决时,异常处理显得尤为重要。对于一般的.NET系统来说,我们简单地借助try/catch可以很容易地实现这一功能。但是对于 一个分布式的环境来说,异常处理就没有那么简单了。按照面向服务的原则,我们把一些可复用的业务逻辑以服务的形式实现,各个服务处于一个自治的环境中,一个服务需要和另一个服务进行交互,只需要获得该服务的描述(Description)就可以了(比如 WSDL,Schema和Strategy)。借助标准的、平台无关的通信构架,各个服务之间通过标准的Soap Message进行交互。Service Description、Standard Communication Infrastructure、Soap Message based Communication促使各服务以松耦合的方式结合在一起。但是由于各个服务是自治的,如果一个服务调用另一个服务,在服务提供方抛出的Exception必须被封装在Soap Message中,方能被处于另一方的服务的使用者获得、从而进行合理的处理。下面我们结合一个简单的示例来简单地介绍一下我们可以通过哪些方式在WCF服务应用程序中进行异常处理。

一、传统的异常处理

我们还是使用上面文章中使用过的书籍管理示例。如下图。


1. SCF.Contracts

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]
void DoWork(); [OperationContract]
string GetBook(string Id); [OperationContract]
string AddBook(string book); [OperationContract]
string EditBook(string book); [OperationContract]
string Search(string Category, string searchString); } }

2.  SCF.WcfService

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 void DoWork()
{ } 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;
throw ex;
}
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>();
//return XMLHelper.ToXML<List<Books>>(list);
}
else
{
list = books.Where(x => x.Category == Category).ToList<Books>();
// return XMLHelper.ToXML<IQueryable<Books>>(books.Where(x => x.Category == Category));
}
return XMLHelper.ToXML<List<Books>>(list);
} } }

注:在编辑书籍信息时,会抛出一个DbUpdateException信息。我在数据库中设了一个唯一索引,不能插入重复值

3.  Service Hosting

配置文件信息如下:

<?xml version="1.0" encoding="utf-8"?>
<configuration>
<configSections>
<!-- For more information on Entity Framework configuration, visit http://go.microsoft.com/fwlink/?LinkID=237468 -->
<section name="entityFramework" type="System.Data.Entity.Internal.ConfigFile.EntityFrameworkSection, EntityFramework, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" requirePermission="false" /> </configSections>
<entityFramework>
<defaultConnectionFactory type="System.Data.Entity.Infrastructure.SqlConnectionFactory, EntityFramework" />
<providers>
<provider invariantName="System.Data.SqlClient" type="System.Data.Entity.SqlServer.SqlProviderServices, EntityFramework.SqlServer" />
</providers>
</entityFramework> <system.serviceModel> <behaviors>
<serviceBehaviors>
<behavior name="metadataBehavior">
<serviceMetadata httpGetEnabled="true" httpGetUrl="http://127.0.0.1:8888/BookService/metadata" />
<serviceDebug includeExceptionDetailInFaults="True" />
</behavior>
</serviceBehaviors> </behaviors> <services>
<service behaviorConfiguration="metadataBehavior" name="SCF.WcfService.BookService">
<endpoint address="http://127.0.0.1:8888/BookService" binding="wsHttpBinding"
contract="SCF.Contracts.IBookService" />
</service>
</services>
</system.serviceModel> <startup> <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5.2" />
</startup> <connectionStrings>
<add name="TestEntities" connectionString="Data Source=.\SQLEXPRESS;Initial Catalog=Test;Integrated Security=SSPI"
providerName="System.Data.SqlClient" />
<add name="Entities" connectionString="metadata=res://*/BookModel.csdl|res://*/BookModel.ssdl|
res://*/BookModel.msl;provider=System.Data.SqlClient;provider connection string=&quot;data source=.\SQLEXPRESS;initial catalog
=Test;integrated security=SSPI;MultipleActiveResultSets=True;App=EntityFramework&quot;" providerName="System.Data.EntityClient" />
</connectionStrings>
</configuration>

Host程序代码如下:

using SCF.WcfService;
using SCF.Contracts;
using System;
using System.Collections.Generic;
using System.Linq;
using System.ServiceModel;
using System.ServiceModel.Description;
using System.Text;
using System.Threading.Tasks; namespace Hosting
{
class Program
{ static void Main(string[] args)
{
try
{
using (ServiceHost host = new ServiceHost(typeof(BookService)))
{
host.Opened += delegate
{
Console.WriteLine("BookService,使用配置文件,按任意键终止服务!");
};
host.Open();
Console.ForegroundColor = ConsoleColor.Yellow;
foreach (ServiceEndpoint se in host.Description.Endpoints)
{
Console.WriteLine("[终结点]: {0}\r\n\t[A-地址]: {1} \r\n\t [B-绑定]: {2} \r\n\t [C-协定]: {3}",
se.Name, se.Address, se.Binding.Name, se.Contract.Name); } Console.Read();
}
}
catch (Exception ex)
{ Console.WriteLine(ex.Message);
} //Console.Read();
}
}
}

4.  客户端代码如下

配置文件信息如下:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5.2" />
</startup> <system.serviceModel>
<bindings>
<wsHttpBinding>
<binding name="WSHttpBinding_IBookService" />
<binding name="WSHttpBinding_IBookService1" /> </wsHttpBinding>
<customBinding>
<binding name="listenUriBinding">
<textMessageEncoding />
<httpTransport />
</binding>
</customBinding>
</bindings>
<client>
<endpoint address="http://127.0.0.1:8888/BookService" binding="wsHttpBinding"
bindingConfiguration="WSHttpBinding_IBookService" contract="SCF.Contracts.IBookService"
name="WSHttpBinding_IBookService">
<identity>
<userPrincipalName value="DEVELOPER\Administrator" /> </identity>
</endpoint>
<endpoint address="http://127.0.0.1:8888/BookService" binding="wsHttpBinding"
bindingConfiguration="WSHttpBinding_IBookService1" contract="BookServiceRef.IBookService"
name="WSHttpBinding_IBookService1">
<identity>
<userPrincipalName value="DEVELOPER\Administrator" />
</identity>
</endpoint>
</client>
</system.serviceModel>
</configuration>

客户端程序代码如下:

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 btnGetBook_Click(object sender, EventArgs e)
{
Books book = new Books();
BookServiceRef.BookServiceClient bookSvrClient = new BookServiceRef.BookServiceClient();
if (gridBooks.SelectedRows.Count > )
{
book = gridBooks.SelectedRows[].DataBoundItem as Books; textBoxMsg.Text = bookSvrClient.GetBook(book.BookID.ToString());
ShowBook();
}
else
{
textBoxMsg.Text = "没有选中相应的记录!";
}
} /// <summary>
/// ChannelFactory方式,直接在代码中写配置信息
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void buttonChannelFactory_Click(object sender, EventArgs e)
{ using (ChannelFactory<IBookService> channelFactory = new ChannelFactory<IBookService>
(new WSHttpBinding(), "http://127.0.0.1:8888/BookService"))
{
IBookService proxy = channelFactory.CreateChannel();
using (proxy as IDisposable)
{
textBoxMsg.Text = proxy.GetBook("");
ShowBook();
}
} } /// <summary>
/// ChannelFactory配置方式,在配置文件中写配置信息
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void buttonChannelConfig_Click(object sender, EventArgs e)
{
using (ChannelFactory<IBookService> channelFactory = new ChannelFactory<IBookService>("WSHttpBinding_IBookService"))
{
IBookService proxy = channelFactory.CreateChannel();
using (proxy as IDisposable)
{
textBoxMsg.Text = proxy.GetBook("");
ShowBook();
}
}
} 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 btnSearchCategory_Click(object sender, EventArgs e)
{
BookServiceRef.BookServiceClient bookSvrClient = new BookServiceRef.BookServiceClient();
textBoxMsg.Text = bookSvrClient.Search(txtCategory.Text, 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 (Exception ex) {
throw ex;
} } 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;
} } }

把Service调用放在一个try/catch 程序代码段中,看看Service端抛出的DbUpdateException能否被Catch。

现在我们运行这个程序,看看客户端报错信息如下:

我们发现客户端无法捕捉服务端真正抛出的出错信息,而是一个比较通用的FaultException。错误信息也是很通用的一种,无法有效提供详细的错误信息,以供我们来解决问题。

WCF学习之旅—WCF中传统的异常处理(十六)的更多相关文章

  1. WCF学习之旅—基于Fault Contract 的异常处理(十八)

       WCF学习之旅—WCF中传统的异常处理(十六) WCF学习之旅—基于ServiceDebug的异常处理(十七) 三.基于Fault Contract 的异常处理 第二个示例是通过定制Servic ...

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

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

  3. WCF学习之旅—WCF服务部署到应用程序(十)

    上接  WCF学习之旅—WCF寄宿前的准备(八) WCF学习之旅—WCF服务部署到IIS7.5(九) 五.控制台应用程序宿主 (1) 在解决方案下新建控制台输出项目 ConsoleHosting.如下 ...

  4. WCF学习之旅—WCF服务的Windows 服务程序寄宿(十一)

    上接    WCF学习之旅—WCF服务部署到IIS7.5(九) WCF学习之旅—WCF服务部署到应用程序(十) 七 WCF服务的Windows 服务程序寄宿 这种方式的服务寄宿,和IIS一样有一个一样 ...

  5. WCF学习之旅—WCF服务的WAS寄宿(十二)

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

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

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

  7. WCF学习之旅—WCF第二个示例(五)

    二.WCF服务端应用程序 第一步,创建WCF服务应用程序项目 打开Visual Studio 2015,在菜单上点击文件—>新建—>项目—>WCF服务应用程序.在弹出界面的“名称”对 ...

  8. WCF学习之旅—WCF第二个示例(七)

    三.创建客户端应用程序 若要创建客户端应用程序,你将另外添加一个项目,添加对该项目的服务引用,配置数据源,并创建一个用户界面以显示服务中的数据. 在第一个步骤中,你将 Windows 窗体项目添加到解 ...

  9. WCF学习之旅—WCF第二个示例(六)

    第五步,创建数据服务 在“解决方案资源管理器”中,使用鼠标左键选中“SCF.WcfService”项目,然后在菜单栏上,依次选择“项目”.“添加新项”. 在“添加新项”对话框中,选择“Web”节点,然 ...

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

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

随机推荐

  1. 深入理解C#

    简单认识.NET框架    (1)首先我们得知道 .NET框架具有两个主要组件:公共语言进行时CLR(Common Language Runtime)和框架类库FCL(Framework Class ...

  2. HashMap与TreeMap源码分析

    1. 引言     在红黑树--算法导论(15)中学习了红黑树的原理.本来打算自己来试着实现一下,然而在看了JDK(1.8.0)TreeMap的源码后恍然发现原来它就是利用红黑树实现的(很惭愧学了Ja ...

  3. Golang 编写的图片压缩程序,质量、尺寸压缩,批量、单张压缩

    目录: 前序 效果图 简介 全部代码 前序: 接触 golang 不久,一直是边学边做,边总结,深深感到这门语言的魅力,等下要跟大家分享是最近项目 服务端 用到的图片压缩程序,我单独分离了出来,做成了 ...

  4. 缓存、队列(Memcached、redis、RabbitMQ)

    本章内容: Memcached 简介.安装.使用 Python 操作 Memcached 天生支持集群 redis 简介.安装.使用.实例 Python 操作 Redis String.Hash.Li ...

  5. 页面布局class常见命名规范

    头:header 内容:content/container 尾:footer 导航:nav 侧栏:sidebar 栏目:column 页面外围控制整体布局宽度:wrapper 左右中:left rig ...

  6. iOS之延时执行(睡眠)的几种方法

    1. 最直接的方法: [self performSelector:@selector(deleyMethod) withObject:nil afterDelay:1.0]; 此方式要求必须在主线程中 ...

  7. git 命令总结

    1 删除分支 git push origin :branch name(Task_******) //删除远程分支 git branch -D branch name(Task_******)     ...

  8. C#高级知识点&(ABP框架理论学习高级篇)——白金版

    前言摘要 很早以前就有要写ABP高级系列教程的计划了,但是迟迟到现在这个高级理论系列才和大家见面.其实这篇博客很早就着手写了,只是楼主一直写写停停.看看下图,就知道这篇博客的生产日期了,谁知它的出厂日 ...

  9. 《徐徐道来话Java》(2):泛型和数组,以及Java是如何实现泛型的

    数组和泛型容器有什么区别 要区分数组和泛型容器的功能,这里先要理解三个概念:协变性(covariance).逆变性(contravariance)和无关性(invariant). 若类A是类B的子类, ...

  10. 【腾讯Bugly干货分享】微信iOS SQLite源码优化实践

    本文来自于腾讯bugly开发者社区,非经作者同意,请勿转载,原文地址:http://dev.qq.com/topic/57b58022433221be01499480 作者:张三华 前言 随着微信iO ...