企业部署SOA重在通过一定的投入进行组织的深刻变革以获得最大的利益。而并不关心是否用WCF来开发。真正需要注重它的则是技术人员。但肯定的是脱离了企业应用的技术实现是没有任何意义的。这里我们完成一个简单的案例,在这个系统中,使用WCF来发布一个服务,用来完成摄氏温度的转换。为了实现这个架构,我们首先需要创建一个新的Soluting。
这里我们使用VS2008,要创建WCF项目有多种方法,例如,可以在“新建项目”中选择“WCF服务库”。
点击“确定”,IDE将为你完成一个基础的WCF架构模板,其架构如下:
其中IService1为一个接口层,它定义服务端和客户端共同继承的接口,并通过Attribute将其转换为契约(契约的概念后面会详细讲到)。Service1为服务层,通过继承接口层的契约向外界提供服务。这样创建WCF项目非常简便,但有一问题,它创建的架构是在同一个项目中使用Class文件进行分层。这不适合企业级应用的需要。因此,我们选择自己创建WCF架构。
1)新建一个“类库”解决方案,方案的名称改为MyWCF,项目名称为MyWCF.Contract,类名称改为IContract。
2)首先为改项目添加System.ServiceModel的引用。
3)在IContract.cs文件中,首先引用System.ServiceModel的命名空间。将主class改为interface。代码入下:
using System;using System.Collections.Generic;using System.Linq;using System.Runtime.Serialization;using System.ServiceModel;using System.Text;
namespace MyWCF.Contract{ [ServiceContract] public interface IContract { [OperationContract] float GetFahrenheit(float ceisius); }}
其中ServiceContract Attribute为接口服务契约,表示此接口能被作为WCF的契约,OperationContract Attribute为方法契约。使用Attribute申明的方式,使得契约的设计更为方便,结构也更加清晰。
这里我们还需要对契约的概念做一个简单说明,以利于大家理解前面讲的概念。在SOA架构中,契约是一个非常重要的概念。SOA的核心是服务,既然是服务就要牵涉到两方,服务方和被服务方。就好比电信局向大家提供电话通讯服务,为了保障服务的顺利和安全,电信局(服务方)需要大家签订一个服务条款,约定电信局提供服务的形式,内容等信息,也约定了被服务方的权利和义务。签订了此协议后,服务双方都需要遵守这份合同条款。这个服务条款,就可以被理解成契约。被服务方要想接受服务方提供的服务,就必须和服务方遵守相同的契约。当然,WCF的契约远不只这么简单,在后面我们还会进行详细的介绍。
4)理解了契约的概念,我们再回头看刚才的程序。在上面的程序中,契约被表现为了一个接口和相关的方法,并使用不同的Attribute将其指定为契约。有了这个契约后,我们就可以在其基础上来创建服务了。因此接下来需要一个服务层,我们在解决方案中再添加一个类库项目,取名为MyWCF.Service。并在项目中添加System.ServiceModel的引用(具体添加方法见上面的操作)和对MyWCF.Contract项目的引用。
5)有了对契约的引用后,服务就可以继承契约的接口了,代码如下:
using System;
using System.Collections.Generic;using System.Linq;using System.Runtime.Serialization;using System.ServiceModel;using System.Text;using MyWCF.Contract;namespace MyWCF.Service
{ public class TemperatureService : IContract { public float GetFahrenheit(float ceisius) { ceisius = ceisius * 1.8F; return ceisius + 32; } }}6)契约已签署,服务已设计完成,接下来需要一个发布服务的平台,这里我们建立一个ConsoleApplication作为服务的载体,在WCF中,称为对服务的“寄存”。
这里我们顺便要提到一些新的概念,以便你对寄存过程的理解。前面我们讲到过,寄存实际上是用来发布服务。有了服务后,用户就可以通过这个服务进行通讯了。但你要和服务端通讯,就必须有一个通讯的渠道,好比你与电信签署了通讯协议,但最后通讯过程完全还是由电信局来控制。而这个通讯过程对于用户来说则是完全封闭的,用户也不需要去关系通讯的细节,反正只要拿起电话,拨通号码,能和对方通话就行了。但对于开发人员来说,就必须考虑到如何通讯,于是WCF为你提供了这样一个对象Endpoint,它负责服务端和客户端通讯的细节。
既然涉及到通讯的细节,就需要考虑到几个东西,首先是地址Address,就好比我们的拨号号码。在Endpoint中,地址用uri对象表示。比如Uri uri = new Uri(); 表示地址在本机的8080端口上的MyWCF项目中。
其次是Binding,这个概念在WCF中十分重要。因为除地址外,通讯还需要重要考虑的就是通讯所使用的协议、编码方式,已经安全性等内容。对于其它开发架构来说,要设置好这些内容是一件非常麻烦的事情,要考虑的东西很多。但对于WCF来说就再简单不过了,这些所有的东西只需要用一个已设置好的对象来指明就行了,因为WCF已为我们提供了一整套用于应付各种通讯的对象,这个在后面我们还会单独介绍。这里我们用BasicHttpBinding对象,这个对象表示使用HTTP协议,没有任何安全设置,消息都以明文传送,对客户端也不进行验证。
最后一个是契约Contract,前面我们已经提到过,它用来描述服务提供的各种操作。看到这里,细心的朋友已经可以看出一个小技巧。就是Endpoint中的三要素取其每项的第一个字母,合起来就是ABC,呵呵,不知是凑巧还是故意这样设置,不过也倒方便我们记忆了。
设置好Endpoint,实际上已能够进行通讯了,但要发布服务,还差一个东西,就是行为Behavior。行为的作用是把服务通过WSDL对外暴露对服务的Metadata描述。这句话很绕口,还是举个例子,我们在打10086充电话费时,首先会听到一个语音提示,说听到提示音后请按键选择服务项目,1为普通话,2为英语。按1进入后又会说为手机充值请按1,查询本手机花费请按2...... OK,这下你清楚了吧。行为好比提供一个服务清单,供你选择,如果没有它,即使服务发布出来了,你也无法知道该选择什么。当然,行为还有一些其它功能,这里我们不详述,还是留到后面去深入理解。
7)好了,理解了这些,我们又可以继续项目的开发了,我们把上面刚添加的控制台项目的名称定为MyWCF.Hosting,在该项目中,需引用前面的两个项目,其代码如下:
using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.ServiceModel;using MyWCF.Service;using MyWCF.Contract;using System.ServiceModel.Description;
namespace MyWCF.Hosting{ class Program { static void Main(string[] args) { CreateHosting(); }
static void CreateHosting() { Uri uri = new Uri(""); using(ServiceHost host = new ServiceHost(typeof(TemperatureService),uri)) { host.AddServiceEndpoint(typeof(IContract), new BasicHttpBinding(),string.Empty); ServiceMetadataBehavior behavior = host.Description.Behaviors.Find<ServiceMetadataBehavior>();
if (behavior == null) { behavior = new ServiceMetadataBehavior(); behavior.HttpGetEnabled = true; behavior.HttpGetUrl = uri; host.Description.Behaviors.Add(behavior); }
else { behavior.HttpGetEnabled = true; behavior.HttpGetUrl = uri; }
host.Opened += new EventHandler(host_Opened); host.Open(); Console.ReadLine(); }
}
static void host_Opened(object sender, EventArgs e) { Console.WriteLine("服务已启动,按回车键停止!"); }
}}
8)全部代码写完后,一个WCF的服务端实际上已经被建立起来了,这时候已经可以发布项目了。我们把MyWCF.Hosting项目定为启动项目,编译运行。
这表面服务已经在运行过程中了。我们可以测试这个服务,还记得前面设置的Uri吗,现在就可以通过这个Uri来访问服务页面。打开你的浏览器,将Uri的地址输入到里面并回车。
可以看到,我们在MyWCF.Service中定义的服务此时已被显示出来。并且可以通过svcutil.exe工具来创建客户端所需的信息。
对于创建WCF服务,我们需要重点理解架构为何如此安排,再来看看VS 2008中项目的架构:
这个架构中,首先是把服务与契约分离。因为契约是服务端和客户端都需要遵循的,分离出来有助于降低耦合度。而服务端与提供服务的寄存方也单独分离开,这样,寄存方就不局限于单一的某种平台,可以是ConsoleApplication,也可以是WinForm,或是WebForm,当服务发生变化时,寄存方也不会受到耦合的影响。另外,在WCF的架构中,要求服务是自治的,每一个服务之间不应受到牵连,也是采用这种架构的主要原因。
配置Endpoint和Behavior是一件较为繁琐的工作,特别是当服务器的Uri发生变化时(在网络环境下,这会较为非常频繁),难免要涉及到代码的修改。而WCF正是为了解决这些配置问题而生,利用.NET自身的优势,微软将这件工作变得非常简单。所有的工作都可以在配置文件中完成,这时候,我们只需要在MyWCF.Hosting项目中添加一个App.config文件,代码如下:
<?xml version="1.0" encoding="utf-8" ?>
<configuration> <system.serviceModel> <services> <service name="MyWCF.Service.TemperatureService" behaviorConfiguration="behavior"> <host> <baseAddresses> <add baseAddress="> </baseAddresses> </host> <endpoint address="" binding="basicHttpBinding" contract="MyWCF.Contract.IContract"></endpoint> </service> </services> <behaviors> <serviceBehaviors> <behavior name="behavior"> <serviceMetadata httpGetEnabled="true" httpGetUrl=""/> </behavior> </serviceBehaviors> </behaviors> </system.serviceModel></configuration>可以看到,在配置文件中,Uri、Endpoint和Behavior都被配置好了,这时候MyWCF.Hosting的代码变得异常简单:
using System;
using System.Collections.Generic;using System.Linq;using System.Text;using System.ServiceModel;using MyWCF.Service;using MyWCF.Contract;using System.ServiceModel.Description;namespace MyWCF.Hosting
{ class Program { static void Main(string[] args) { CreateHosting(); }static void CreateHosting()
{using (ServiceHost host = new ServiceHost(typeof(TemperatureService)))
{ host.Opened += new EventHandler(host_Opened); host.Open(); Console.ReadLine(); } }static void host_Opened(object sender, EventArgs e)
{ Console.WriteLine("服务已启动,按回车键停止!"); }}
}
配置好后,当服务启动时,自动到config文件中读取相应的配置。程序的结构被进一步简化,服务被彻底的从应用层分离开,完全具备了自治的特性。当你再看到这里时,是不是觉得整体系如此简单,清晰,这就是WCF来的深度体验。