WCF4.0 进阶系列--第一章 WCF简介
第一章 WCF简介
1. 什么是WCF
如果你阅读本书的目的是为了解如何使用WCF构建分布式应用程序和服务,那么什么是WCF,为什么使用WCF?该如何使用WCF? 回答这些问题,让我们来回顾一下历史。
(1)早期个人电脑时代的应用程序
个人电脑时代,业务解决方案由一套应用程序组合而成。一般会包含文字处理程序,电子表格处理程序,和数据库套件。有经验用户能够把业务数据存贮在数据库中,然后使用电子表格分析数据,并创建基于数据的报表与文档,或利用文字处理软件写分析报告。这些程序往往安装在同一台电脑上,并且其数据和文件格式只为其所有。这就是典型的桌面应用程序平台,其特点是仅供单个用户使用,并且只在很小范围内处理多任务。
随着个人电脑的价格逐渐便宜并且广泛地作为商业工具,应用程序面对的挑战是如何面对多个用户之间数据的共享。它并不是一个全新的挑战,因为多用户数据库已经开始面世一段时间,但是多用户数据库是运行在大型计算机而非个人电脑上。很快,基于网络的解决方案和网络操作系统出现在个人电脑平台上,使得同一个机构内部部门间的电脑均连接到网络中,并且在网络里共享其资源。而且,数据库厂商也很快开发出独立于大型机的的数据库管理软件,它可以运行在网络中的个人电脑上,其结果,人们开始利用这些软件或方案便利地共享业务数据。
(2) 进程间的通讯技术
网络平台仅仅是整个故事的一部分。 虽然基于网络的方案允许个人电脑之间相互通讯、共享打印机和磁盘;但是程序还需要和运行在其他个人电脑上的程序之间发送数据、接收数据以及互操作。许多常见的进程间的通迅机制开始逐步应用,比如named pipes和sockets。 这些技术在当时使用率十分低下,因为要掌握它们,你必须很好的理解网络。如今这种境况仍旧一样。比如,使用socket创建收发送数据的应用程序的职位仍旧是一个富有挑战的工作岗位。表面上,进程相当简单,但是并行访问是一件相当复杂的事情。随着计算机和网络的不断进化,进程间通讯的种类和能力也相应变化。比如:微软开发了COM技术使Windows平台上应用程序和组件之间可以相互通讯。开发人员能够使用COM创建可复用的软件组件,连接多个组件构建程序,或利用Windows服务。微软自己也使用COM技术使自己的程序作为服务的组件用以构建各种方案。
微软设计COM的最初想法是解决同一计算机上应用程序和组件之间通讯。后来,微软推出了DCOM,也称作分布式COM,它使应用程序可以访问网络中其他计算机上的组件。再之后,DCOM发展为COM+。COM+包含很多特性,比如与微软分布式服务器集成,使程序能将一系列的操作放在一个组中,并使该组操作以事务的方式与组件相互通讯。COM+还具有自动管理资源(比如你使用一个组件连接到数据库,你能确保当程序使用完组件后,组件会自动关闭该数据连接)和异步操作能力。COM+后来被.NET Framework替代。.NET Framework不仅扩展了COM+, 而且提供很多新功能(Remoting:使客户端应用程序像访问本地对象一样的方式访问位于远程服务器上的对象)。微软将此项技术命名为企业服务。
(3) Web与Web服务
COM,DCOM,COM+,企业服务,.NET Framework Remoting在局域网上运行的非常不错,并且它们都能运行在Windows操作系统之上。
然而,当微软推出COM和DCOM的时候,万维网出现了。万维网是基于互联网,其已经经过数十年的发展。万维网提供了一个架构,开发人员使用此架构,利用位于世界各地的组件和其他元素构建应用程序,而且这些应用程序能运行在不同计算机体系下。第一代web应用程序非常简单,仅仅由一些静态网页组成;用户可以使用本地计算机上的网络浏览器下载并查看这些页面。第二代web应用程序提供给开发者一些可编程的元素(组件或者插件),用户从web站点下载然后在本地计算机的浏览器上计算出结果。第三代则推出了web服务。 Web服务是一个应用程序或者组件,其在服务器端运行。Web服务能接收来自客户端程序的请求,在服务器端运行,然后将结果返回给发出请求的客户端。Web服务还可以访问另外一个位于互联网上Web服务。这就是全球的、分布式的程序。
你可以通过visual studio和.NET Framework创建基于Windows的web服务。当然你也可以使用其他技术创建运行在非Windows平台的web服务。尽管web服务不限定某种语言或者平台。创建一个分布式的、全球性的web服务,开发人员必须遵循一些原则,其包括数据的格式,发送和接收消息的协议,安全处理方式。这些特性都是与平台无关的。
(4) 使用XML作为通用的数据格式
不同的计算机能使用其内部的表现形式存贮相同的值。比如:最高字节位在前的32为处理器与最低字节位置在前的32为处理器,它们使用不同的数据格式,但是可以存贮相同的值。因此,为了保证同一个程序在不同的计算机上的共享数据,开发人员必须统一采用一种与计算机体系无关的数据存贮格式。简而言之,xml是目前大家认可的数据存贮格式。 Xml是基于文本的、易读的、简单标记描述各种类型的数据的可扩展标记语言。
<Person><Forname>John</Forename><Surname>Sharp</surname></Person>
不需要费力,你就能猜测到此数据秒速的实际意义。一个需要发送信息的程序可以利用这个格式将Person的信息发送给另外一个程序;接收方可以接收Person信息,并将其转化为有意义的数据。当然上面的数据格式也可以改成成如下:
<Person Forname="John" Surname="Sharp" />
这里有其他很多种可能的格式变化。应用程序如何知道其格式能被其他的程序正确地识别?答案在于两个程序都统一采用同一种数据结构。这个数据结构就是xml schema。Xml schema是用来描述xml文档的结构,发送方利用其来描述发出信息的结构;接收方利用其将接收到的信息转化为有意义的数据。
(5)接收与发送Web服务请求
通过使用xml和xml schema格式化数据,web服务和客户端之间可以相互收发数据,前提是客户端与web服务端必须采用统一的发送和接收请求的协议。此外,客户端能识别自己发送的消息和从web服务端返回的消息。
简而言之,web服务和客户端程序通过使用SOAP协议进行通讯。SOAP协议定义了如下几个方面:
- SOAP消息格式
- 输入如何编码
- 如何发送消息
- 如何处理返回的消息
Web服务能发布WSDL文档,其是一个xml文档,描述该web服务可接收的消息以及如何响应客户端传递过来的消息。客户端程序能使用这些信息来决定如何与该服务进行通讯。
(6)JavaScript对象和RIA
XML/SOAP模型用以定义和传播可交换消息的格式,这非常容易理解。但是在某些情况下,此模型显得比较复杂。因为此模型会产生大量的冗余,特别是在传送和接收数据比较小的情况下。对于当今流行RIA就是一个典型的例子。
RIA是一种近似于传统桌面应用软件系统功能,但实际上是通过web浏览器与远程服务器进行交互的网络应用系统。为了提供桌面交互功能,RIA使用JavaScript和AJAX。许多厂商提供相应的架构,比如微软的Silverlight,开发人员使用Silverlight可以快速且便利地实现RIA。
尽管XML是AJAX技术的一部分,轻量级的原生的JavaScript格式被用以表示对象并且用户和网络间的传输。这种格式就是JSON。虽然它的名字与JavaScript有关,但实际上JSON是语言无关的一种格式;大部分程序员一眼就看明白它。JSON通过名称/值来描述数据。比如:{"forename":"John","surname":"Sharp","Age":46} .
你可以通过代码的方式直接读写JSON数据。但是大多数开发人员喜欢使用JSON转换器自动地将其他格式的数据转化成JSON,尤其当源数据结构非常复杂的时候。现代大多数语言都提供JSON转化器来创建分布式应用程序,比如WCF。
JSON与XML最大的不同在于XML是一个完整的标记语言,而JSON不是。这使得XML在程序判读上需要更多的气力。主要原因在于XML的设计理念与JSON不同。XML利用标记元素的特性提供绝佳的扩展性;而JSON的重点在于数据的交换。
(7)全局环境下的安全和隐私的处理
安全用以识别用户和服务,然后授权其访问相应的资源。在分布式环境中,安全性有非常重要的影响。在与网络环境隔离环境、或桌面应用程序环境中,可以通过物理的安全方式来保护非法用户通过键盘输入或者查看显示屏的内容;而在网络环境中,物理安全保护措施是不充分的。在网络环境下,你需要确保合法用户能够访问共享资源、数据和运行在网络环境中的组件。系统提供商比如微软推出包含许多安全特性的操作系统。一般情况下,这些安全特性包括维护一个用户列表,凭据【用以建立自己的标识,比如密码】。这些安全特性在一个组织内部工作非常好。但是,当你在互联网公布服务时,很显然记录所有用户和计算机的身份和凭证信息是一件不可行的事情。
经过大量关于安全性方面的调查和研究,提出了许多解决方案。为了保证服务端和客户端通讯的安全,两者需要采用统一的安全机制,在该机制下,双方可以识别和验证对方。OASIS是提出许多实现安全标准的联合组织,比如用户名/密码,X509证书,和Kerberos口令。如果一个服务提供保密信息的访问,需要考虑使用上述安全标准中的一种。
隐私与安全紧密相关,并且同等重要,特别是你的服务提供基于互联网的访问。你不希望非授权用户破译和读取客户端程序和服务之间的消息。为了达到这个目的,服务和客户端必须隐藏两者之间的对话。一般来说,通过加密需要交换的消息的来保证隐私性。有多种加密方式可供选择,最广泛的加密方式是通过公钥与私钥来实现对消息的加密。
在服务和客户端之间实现加密和安全并不是一件容易的任务。幸运的是在.NET Framework下,微软引入了WSE,其作为 visual studio的一个插件,使得程序员能够非常便利的创建安全的服务,并且简化了服务和客户端的配置。
(8)SOA架构与WCF
软件开发人员很快意识到web服务的原则应采用一种更通用的方式—"软件作为服务",并且开始倾向与实现面向服务的架构。SOA背后的驱动力是实现竞争力和效益性—业务解决方案应该能快速适应业务环境的变化。SOA架构的主要原则是:在任何地方重用现存的软件,并且将这些软件对外公开为服务。
服务提供了一系列实现预定义的操作,以实现公司的业务逻辑。 开发人员通过调用新的或现存的软件实现这些操作;或者通过组合现存的软件满足企业的业务需求。服务创建者对外隐藏了实现的细节,对内创建者可以显示地调用其他应用程序或服务来实现该其想要的功能。实现一个服务包含下列关键因素:
- 基于预定的标准提供可重用、可扩展的接口,最大化互操作
- 提供可扩展的主机环境,在此环境下,服务能快速响应用户请求,甚至面对大量用户请求时也可以快速响应。
WCF提供一个模型,使用该模型,你可以实现符合大部分大众认可的标准,包括SOAP,XML和JSON。此外,WCF还支持其他Windows平台上特有的技术比如MSMQ,并对这些标准提供了一个统一的编程模型。这样,你可以创建尽可能独立的五福,而无须掌握底层服务和客户端之间连接机制。使用WCF,你可以兼容早期的相关技术,比如WCF客户端可以访问你使用WSE创建的web服务。
Windows为SOA提供了一个可扩展的,安全的,健壮的平台。Windows 2008 Server对上述特性做了进一步地优化。你可以构建可扩展的Windows Server簇,充分利用现有的便宜的硬件。另外一方面,如果你不想自己折腾硬件,你还可以在Windows云平台上实现基于SOA的服务。
Windows Azure是一个云计算平台,通过微软数据中心提供按需计算和存储设备。你可以在本地构建和测试服务,完后上传该服务到数据中心上一台或多台虚拟机,这些虚拟机运行在由微软员工管理和维护的计算机上。你可以设定参数,当访问需求增加时扩展你的服务到多个计算机上;反之当访问需求减少是,是使用的计算机数量减少。Windows Azure提供高可靠连接架构,其允许客户端应用程序定位并调用你的服务。此连接利用了WCF相应的特性。
2. 构建一个WCF服务
Visual Studio 2010为创建WCF服务和客户端应用程序提供了完美的开发环境。其提供了创建WCF服务的项目模板。你可以使用该模板创建一个简单的WCF服务,该服务可以对外提供查询和维护数据库的方法。本书中使用的数据库是SQL Server 2008自带的AdventureWorks OLTP数据库。
AdventureWorks公司生产自行车和自行车配件。其数据库包含了产品信息、销售信息、客户信息和员工数据。在本章的练习中,你将创建一个包含下列操作的WCF服务:
- 列出所有销售的产品
- 获取产品的详细信息
- 查询某个产品的存库
- 修改某个产品的库存
这些练习所需的数据存储在AdventureWorks数据库的Product和ProductInventory表中。图表1-1列出了这两张表及其它们之间一对多的关系:即一条产品记录对应多条存货记录。这是因为一个产品可以存贮在一个或者多个仓库中。他们之间通过ProductID来建立一对多的关系。
为了简化读取数据库的代码,你将使用ADO.NET实体框架。它是.NET Framework 4.0的一部分并与Visual Studio 2010一起发布。实体框架的目的是将数据库的数据表转化为一系列的对象,以供开发人员在程序和服务中使用。使用实体框架,指定数据库中的一个表,创建一个实体模型,并生成一个对象模型;利用该对象模型,你可以对该表进行查询,插入,更新和删除操作。使用实体框架的最大优点是你不用关心具体的数据库管理系统、你可以读取数据而不需要掌握数据库管理系统是如何工作的(你甚至都不必了解SQL)。
(1)为WCF服务创建实体模型
启动visual studio 2010并创建一个C#类库的新项目,然后删除class1.cs
添加一个新的ADO.NET实体数据模型到你的项目中
在添加模型向导中,在选择模型内容页面,点击"从数据库生成模板",然后点击下一步
选择你的数据库连接,如果为空,请创建一个新的连接.这一步,很简单,略过
在选择数据库对象页,选择product和productionInventory表,并且选中"生成数据的对象使用单数或者复数名称"
点击完成,你将建立一个数据库实体模型。
(2)创建名为ProductsService的WCF 服务
Visual Studio提供了多个WCF Service模板. 此时你使用的模板创建的是一个基于Web的WCF Service。在随后的章节中,你将会了解到如何实现其他方式的实现WCF服务,比如在一个类库中,在单独的程序中,在WF中。
WCF Service模板将会生成一个默认的服务(在本示例中,分别为Service.svc, IService.cs和Service.cs)。同时还会在web.config中添加system.serviceModel节点及子节点配置)。在Service.cs文件的头部,我们发现引用了using System.Collections.Generic,using System.Linq和using System.Text命名空间;同时还引用了using System.ServiceModel和using System.ServiceModel.Web命名空间。
System.ServiceModel命名空间包含了供WCF用来定义服务和服务所对应的操作。随着本书的深入,你将会了解到该命名空间下许多其他的类和类型。WCF使用using System.Runtime.Serialization命名空间来将对象序列化为各种供网络的传输的数据流;反之,还用来将流反序列化成对象。关于序列化和反序列化,将在后续的章节中介绍。System.ServiceModel.Web命名空间包含了用来创建REST Web服务的所需的类型。关于REST Web服务,将在15章介绍。
Service.cs定义了一个简单的WCF服务。该服务提供了GetData和GetDateUsingDataContract方法。目前,我们不需要关注这两个方法的细节,我们需要注意的是该实例是告述我们如何实现服务所包含的操作。 在一个WCF服务中,一个操作就是一个方法,它接收0个或者多个参数,并返回值。当一个客户端应用程序向该服务发送消息,WCF运行时将把该消息传递到对应的方法,并且将消息中的数据传递给该方法。类似地,当方法返回值时,返回值将被包装到一个消息中,然后回传给客户端程序。
Service.cs实现了IService.cs接口。IService接口简单的定义了GetData和GetDateUsingDataContract方法,它们在Service.cs中具体实现。这个接口就是服务协定,它通过ServiceContract和OperationContract属性来实现。IService.cs同时还包含一个名为CompositeType的类,其使用了DataContract和DataMember属性;CompositeType就是数据协定。
WCF服务架构采取了"协定优先"的开发方式。 当执行该开发方式,你定义接口(协定),服务实现该接口,然后构建一个符合这些接口(协定)的服务。 这不是一项新的技术,COM开发者在数十年前就已经熟悉该策略。"协定优先"开发方式让开发人员把注意力放在服务的设计上。如果有必要,服务的设计能够快速的审核,以确保其不会开发人员不用太关注其实现的细节。请注意,在大多数情况下,客户端程序不要采用WCF实现,即使该程序运行在Windows平台上。
在解决方案中添加一个名为ProductsService的WCF Service类型的网站。
定义协定。 重命名IService.cs和Service.cs。将他们分别命名为IProductsService.cs和ProductsService。然后添加如下代码:
[ServiceContract]
public interface IProductsServcie
{
[OperationContract]
List<string> ListProducts();
[OperationContract]
ProductData GetProduct(string productNumber);
[OperationContract]
int CurrentStockLevel(string productNumber);
[OperationContract]
bool ChangStockLevel(string productNumber, short newStockLevel, string shelf, int bin);
}
实现服务
View Code
public class ProductsService : IProductsServcie
{
public List<string> ListProducts()
{
List<string> productsList = new List<string>();
try
{
using (AdventureWorksEntities database = new AdventureWorksEntities())
{
var products = from product in database.Products
select product.ProductNumber;
productsList = products.ToList();
}
}
catch(Exception ex)
{
throw new SystemException(ex.Message);
}
return productsList;
}
public ProductData GetProduct(string productNumber)
{
ProductData productData = null;
try
{
using (AdventureWorksEntities database = new AdventureWorksEntities())
{
Product matchingProduct = database.Products.First(
p => String.Compare(p.ProductNumber, productNumber) == 0
);
productData = new ProductData() {
Name = matchingProduct.Name,
ProductNumber = matchingProduct.ProductNumber,
Color = matchingProduct.Color,
ListPrice = matchingProduct.ListPrice
};
}
}
catch
{ }
return productData;
}
public int CurrentStockLevel(string productNumber)
{
int stockLevel = 0;
try
{
using (AdventureWorksEntities database = new AdventureWorksEntities())
{
stockLevel = (from pi in database.ProductInventories
join p in database.Products
on pi.ProductID equals p.ProductID
where String.Compare(p.ProductNumber, productNumber) == 0
select (int)pi.Quantity).Sum();
}
}
catch
{ }
return stockLevel;
}
public bool ChangStockLevel(string productNumber, short newStockLevel, string shelf, int bin)
{
try
{
using (AdventureWorksEntities database = new AdventureWorksEntities())
{
int productID = (from p in database.Products
where String.Compare(p.ProductNumber, productNumber) == 0
select p.ProductID).First();
ProductInventory productInventory = database.ProductInventories.First(
pi => String.Compare(pi.Shelf, shelf) == 0
&& pi.Bin == bin
&& pi.ProductID == productID
);
productInventory.Quantity += newStockLevel;
database.SaveChanges();
}
}
catch
{
return false;
}
return true;
}
}
配置和测试服务.
你需要为WCF service提供一个主机环境,并且设置好相应的配置文件。其配置文件告诉host哪一个class包含了WCF服务以及如何侦听来自客户端的请求。WCF提供了一个默认的配置文件,其内容覆盖了大部分上述配置信息。在后续章节中,你将会了解到更多相关内容。
设置Service.svc
<%@ ServiceHost Language="C#" Debug="true" Service="Products.ProductsService" CodeBehind="~/App_Code/ProductsService.cs" %> |
设置web.config(创建Service时,已经自动添加了下列内容)。关于<system.serviceModel>配置信息,后续章节中将做详细介绍。
View Code
<system.serviceModel>
<behaviors>
<serviceBehaviors>
<behavior>
<serviceMetadata httpGetEnabled="true"/>
<serviceDebug includeExceptionDetailInFaults="false"/>
</behavior>
</serviceBehaviors>
</behaviors>
<serviceHostingEnvironment multipleSiteBindingsEnabled="true"/>
</system.serviceModel>
因为使用了ADO.NET实体模型,所以需要将ProdcutsEntityModel中的数据库连接字符串copy到web.config中
View Code
<connectionStrings>
<add name="AdventureWorksEntities" connectionString="metadata=res://*/ProductsModel.csdl|res://*/ProductsModel.ssdl|res://*/ProductsModel.msl;provider=System.Data.SqlClient;provider connection string="data source=localhost;initial catalog=AdventureWorks;integrated security=False; user id=ap_wcf; password=ap_wcf; multipleactiveresultsets=True;App=EntityFramework"" providerName="System.Data.EntityClient" />
</connectionStrings>
Ok,保存web.config.右键点击service.svc,选择浏览,确认你的服务正常工作。(如果你想部署一个WCF Service且不创建*.svc文件,请参考dudu文章)
(4)创建WCF服务客户端
创建一个控制端应用程序
引用WCF服务:在bin上右键添加服务引用或者通过visual studio菜单"项目-添加 服务引用"
服务引用文件夹将会自动出现该项目中。如果你展开该文件夹,你将会看到ProductsService的实体。服务引用包含一个代理类。你可以看到代理类的代码,如果你选择了现实所有项目文件选项。
同时,添加服务引用时,将会自动为项目添加app.config。它包含了调用WCF服务所需的一些信息。
<system.serviceModel>
<bindings>
<basicHttpBinding>
<binding name="BasicHttpBinding_IProductsServcie" closeTimeout="00:01:00"
openTimeout="00:01:00" receiveTimeout="00:10:00" sendTimeout="00:01:00"
allowCookies="false" bypassProxyOnLocal="false" hostNameComparisonMode="StrongWildcard"
maxBufferSize="65536" maxBufferPoolSize="524288" maxReceivedMessageSize="65536"
messageEncoding="Text" textEncoding="utf-8" transferMode="Buffered"
useDefaultWebProxy="true">
<readerQuotas maxDepth="32" maxStringContentLength="8192" maxArrayLength="16384"
maxBytesPerRead="4096" maxNameTableCharCount="16384" />
<security mode="None">
<transport clientCredentialType="None" proxyCredentialType="None"
realm="" />
<message clientCredentialType="UserName" algorithmSuite="Default" />
</security>
</binding>
</basicHttpBinding>
</bindings>
<client>
<endpoint address="http://localhost:2003/ProductsService/Service.svc"
binding="basicHttpBinding" bindingConfiguration="BasicHttpBinding_IProductsServcie"
contract="ProductsService.IProductsServcie" name="BasicHttpBinding_IProductsServcie" />
</client>
</system.serviceModel>
WCF客户端程序与服务端不一样,没有使用默认的配置文件。因为客户端的配置文件比服务器端的设置要多一些。
<client>片段用来设置客户端如何连接到服务端。<endpoint>元素提供了供客户端调用的服务端的细节信息。一个端点包含三个必须属性:地址,绑定和服务协定。 其他可选项中,端的的名称值得注意,因为当你的客户端程序连接到多个服务时,每个服务在客户端的配置文件中,都可以有自己的名称以区别其他的服务。
地址是服务端WCF服务的URL地址。
绑定元素用来设置访问Web服务的传输机制,采用的协议和其他项目。一个服务可以使用WCF内建的一种绑定类型。在本例中,你可以不指定任何绑定,因为WCF服务会采取默认的绑定—basicHttpBinding。该绑定是基于HTTP协议,并与之前版本的非WCF 服务兼容。你可以修改一个绑定的属性以提供额外的信息,比如延迟,消息加密方式和安全性。 本例中,当你引用web服务时,自动添加了一个名为BasicHttpBinding_IProductsServcie的绑定设置。该设置会根据客户端端点的不同而变化。也就是说bindingConfiguration的具体内容与端点的细节密切相关。一定要根据端点来设置相应的内容,否则客户端和服务器端不能够正常地通讯。
协定元素指向服务的协定。在本地例中,该属性的值为ProductsService.IProductService.
调用ListProducts的方法
static void Main(string[] args)
{
ProductsServcieClient proxy = new ProductsServcieClient();
Console.WriteLine("Test 1: List all products");
string[] procutNumbers = proxy.ListProducts();
foreach (string productNumber in procutNumbers)
{
Console.WriteLine("Number: {0}", productNumber);
}
Console.ReadLine();
}
类似地,调用其他方法。
最后,生成解决方案,并运行客户端序。并确认测试的结果与你预计的结果一致。
3. WCF和SOA基本原则
在本章一开始提到,WCF是实现SOP的完美平台。
从前面的例子中,使用WCF,你可以创建服务,并集成到企业的项目中去。你可以不用编写新的代码,就将现有的应用程序集成到WCF服务中去。在WCF出现之前,实现这个功能是相当困难的。WCF能够非常便利地把程序和组件"粘合"在一起。此外,WCF可以使用标准的协议,数据格式,通讯机制,还可以与通过其他技术创建的服务进行互操作。
SOA是网络上一系列的资源,它们作为相互独立的服务而存在,彼此之间可以相互访问且不需要知道服务实现的细节。你可以将这些服务组合在一起形成一个SOA,用以创建企业应用。这里,我并不会涉及SOA的整个理论,但是SOA的主要好处在于可以创建与平台和位置无关的复杂应用。这意味着,你可以迅速的升级一个服务或者迁移一个服务到另外一个站点。只要服务对外暴露与此前服务同样的接口,这样你就可以继续使用该服务而不需要修改任何代码。但是,SOA并不是一个可以直接解决分布式应用架构的魔杖。为了成功的设计和实现SOA,你需要注意"面向服务的四个基本原则":
明确边界。服务和应用程序通过相互发送消息进行通讯。你不应该假设一个服务如何处理一个请求,或者一个应用程序如何处理响应。准遵循此原则,能够移除服务和应用程序之间的依赖关系。此外,发送和接收消息都是有代价的。在你设计服务端的服务是应该考虑到这点,而且尽量减少客户端与服务端的之间的通讯。
服务是自治的。如果你创建一个基于服务的应用程序,你不一定能控制你所使用的每个服务,即使该服务寄宿于你的组织之内。Web服务的位置可能发生改变,或者一个服务由于维护或者其他原因临时离线。你应该设计一个松耦合的架构,这样你的程序是可以自动容错,即使有一个服务不可用而你的客户端程序依然可以运行。
服务共享数据结构和协定,而非类和类型。 服务对外发布操作以及收发数据结构的信息。客户端使用这些信息与服务端进行通讯。你应该使用协定和数据结构来定义你对外的服务。这将会减小客户端程序对某一个版本服务的依赖。服务可能会随着时间而演变,一个新版本的服务可能会替代之前版本的服务。如果一个服务更新,那么该服务应该对现有的客户端保持兼容性,该兼容性包括先前实现的协定和构成消息的数据结构。如果你修改一个服务,使其提供其他的功能,你可以添加协定和数据结构以扩展你原有的服务,且保留之前的协定和数据结构。这样,旧的客户端不需要做任何改变仍可以访问更新后的服务。
兼容性是基于策略的。数据结构和协定定义了服务的"形状",而非客户端访问服务端所需要的非功能性需求。比如,一个服务可能有安全性方面的需求,其规定客户端必须某种方式连接到服务端,并且收发的消息必须以某种方式进行加密。这就是一个策略。策略方面的需求不能通过协定来指定,而且不应该要求在客户端或者服务器端进行额外的编码。而且这些策略还可能随时变化,因此它应该从服务的实现和客户端解耦出来。你服务的具体实现应该和策略是相互独立的,而且你应该强制客户端遵守服务器端策略的要求。此外,所有的服务和客户端应该在策略方面达成统一。
4. 总结
本章为你介绍了WCF。你应该已经熟悉WCF的目的,如何通过"协定优先"的方式创建一个简单的Web服务;在IIS上部署一个WCF服务S,并且创建一个客户端应用程序访问WCF服务。最后,你还了解到SOA的基本原则。而且理解了使用WCF你可以快速地创建一个基于SOA的服务。
本章源文件下载