WCF体验之旅(1):创建一个简单的WCF程序(1)
3、创建Service
前面我们已经创建了我的Artech.WCFService.Contract。其实我们从Contract这个单词上讲,它就是一种契约,一种承诺。他表明在上面签了字你就的履行Contract上义务。Service就是这样一个需要履行Contract义务的人。在这个例子中,Contract以Interface的方式定义的一些Operation。作为Service,在Contract上签字的方式就是实现这样的一个Interface。下面的Service得到code,很简单。
|
4、Hosting Service
就像Remoting一样,我们继承自System.MarshalByRefObject的对象必须Host到某一个运行的进程中,他才开始监听来自Client端的请求,当Client才能通过Proxy远程的调用,Remoting Infrastructure监听到来自Client端的请求,他会激活相应的remote Object(我们只考虑Server Activate Object——SAO)。实际上对于WCF Service也需要一个Host环境才有其发挥作用的舞台。就像Remoting一样,你可以使用任何一种Managed Application——Console Application、WinForm. Application、ASP.NET Application——作为它的Host环境。你甚至可以用它Host到Windows Service中和IIS中(后面我将会讲到如何做)。
我们知道WCF中,Client端和Service端是通过Endpoint来通信的,Endpoint有包含3个部分,经典地称为ABC。
A代表Address,它包含一个URI,它指明Service存在于网络的某个地方,也就是说它为Client断指明在什么地方去找到这个Service。很多人认识Address仅仅只是一个具有Identity的URI,实际上不然, Address不止于此,它还包含一些Header,这些信息在某些情况下对于如何寻址有很大的意义(比如在client的最终Service之间还有一些Intermediary节点的情况下)。在.NET中,Address用System.ServiceModel.EndpointAddress来表示。
B代表Binding,Binding封装了所有Client和Service段消息交换的通信细节。比如他定义了通信应该采用的Transport-比如我们是因该采用Http,TCP,Named Pipe或者是MSMQ;通信的内容应该采取怎样的编码——比如是Text/XML,Binary还是MTOM。以上这些都得一个Binding所必须定义的内容,此外,Binding还可以定义一些其他的有关通信的内容,比如Security,Reliable Messaging,Session,Transaction等等。正因为Binding对于通信的重要性,只有Service端的Binding和Client的Binding相互匹配的时候,他们之间在可以相互通信。如何使Client Binding匹配Service Binding呢?最简单也是最直接的方法就是使用相同的Binding。WCF为我们定义了一系列的System Defined Binding,这些Binding在Transport,Interoperability,Security,Session Support,以及Transaction Support方面各有侧重。基本上WCF为我们定义的这些Binding已经够我们使用的了,如果,实在满足不了要求,你还可以建立自己的Custom Binding。
C代表Contract这在上面已经提及,这里不再累赘。
Host的本质就是把一个Service置于一个运行中的进程中,并以Endpoint的形式暴露出来,并开始监听来自Client端的请求。这里值得注意的是,同一个Service可以注册若干不同的Endpoint,这样不同的Client就可以以不同的方式来访问同一个Service.比如,同一个Intranet的Client可以以TCP的方式访问Service,另一个存在已Internet中的Client则只能以Http的方式访问。
using System; namespace Artech.WCFService.Hosting static void HostingServiceViaCode() Service and base Address the sevice host can be closed properly. new ServiceHost(typeof(CalculatorService), baseUri)) //Create a Service Endpoint by specify the Address (it is absolute or relative path based on the base Address, the empty string indicates the Address equals base Address), //Binding(the basicHttpBinding created) and Contrace (it is now the type info of the contract interface) calculatorServiceHost.AddServiceEndpoint(typeof(ICalculator), Binding, string.Empty); //Such a segment of code snip shows how to make the metadata exposed to the outer world by setting the Service metadata behavior //Find the Service metadata behavior. if exists, otherwize return null. ServiceMetadataBehavior. behavior. = calculatorServiceHost.Description.Behaviors.Find //If the Service metadata behavior. has not to added to the Service. we will create a new one and evaluate the HttpGetEnabled%26amp;HttpGetUrl to make outer world can retrieve to metadata. if (behavior. == null) {otherwize it will never take effect. //if the metadata behavior. exists in the behavior. collection, we just need to evaluate the HttpGetEnabled%26amp;HttpGetUrl else //Add the opened event handler to make a friendly message displayed after opening the Service host indicating the Service begin to listen to request from Clients. calculatorServiceHost.Opened += delegate { Console.WriteLine("Calculator Service begin to listen via the Address:{0}",calculatorServiceHost.BaseAddresses[0].ToString()); }; //Open the Service host make it begin to listen to the Clients. |
我们现在可以单独运行Hosting Projet,以下是运行后的截图。
当Hosting启动之后,由于我们为它开启了Http Enabled,Hosting为专门创建一个Metadata Endpoint,通过访问这个Endpoint,我们可获取Service相关的Metadata。像ServiceModel Metadata Utility Tool (Svcutil.exe) 这得工具就是通过获取Metadata来帮我们生成相应的客户端代码和配置文件。当Hosting被启动之后,我们可以在IE中输入Metadata Endpoint的Address来测试Service的可访问性。如下图。
![]() |
| 图3 |
以上我们完全以代码的方式Host一个已经创建的Service。在这个例子中,Endpoint的所有信息都在代码中指定。实际上真正的项目开发之中,我们基本上不采用这样的方法。这是因为,Service的Addresss以及Binding 信息在开发阶段和部署阶段往往是不一样的。所以我们通常的做法是把这些信息放在Config文件中,这样我们可以根据实际的需求改变存储于Config文件中的信息,比如我们把Service移植上另外的位置,我们只要改变Endpoint的Address配置信息就可以了; 再比如,我们把原来存在Intranet的Service放到Internet上,原来可能基于TCP的Binding必须改成基于Http的Binding,在这种情况下,我们依然可修改配置文件就可以了,这样的改动通常是很小的,并且修改了配置文件之后我们不需要对现有的代码进行重编译和重部署,它们可以其实生效。下面我们来看看如何创建基于Configuration的Hosting。
首先,在Artech.WCFService.Hosting中创建App.config,并编写如下结构的配置信息。
![]() |
| 图4 |
http://localhost:8888/GeneralCalculator"/%26gt;
然后我们相应地改变我们的Hosting代码,现在由于我们的Endpoint的信息都放在我们的配置文件中,所以我们可以较大的简化我们的Hosting 代码。
|
当calculatorServiceHost.Open();被调用之后,系统会查看calculatorServiceHost对应的Service的类型,结果发现它的类型是Artech.WCFService.Service. CalculatorService(在ServiceHost被创建时最为第一个传入参数);然后会在config文件中Services部分中找name属性和这个Service type相匹配,找到之后,把它host进来,然后添加它在配置文件中定义的所有的Endpoint,并设置在配置文件中定义的所有Binding 以及 Behavior。
现在我们运行Hosting,将会得到同上面一样的结果。同样,在IE中输入Metadata Endpoint的Address,也会看到上解图一样的显示。
5、创建Client
到现在为止,Service端的工作已经完成,当你启动Hosting的时候,一个可用的Service就已经存在了。现在所做的事情是如何创建我们的客户段程序去使用这个Service。几乎所有的WCF的书,其中包括MSDN都是叫你如何使用Service Utility这样的一个工具来帮你生成客户端代码和配置信息。为了让我们能够清晰的Client的整体内容, 我们现在选择手工的方式来编写这样的部分代码。
几乎所有分布式的调用都有这样的一个概念,调用的具体实现被封装在Server端,Client不可能也不需要了解这个具体实现,它所关心的就是我如何去调用,也就是说Cient需要的不是Service的实现,而是一个interface。WCF也是一样,Client不需要了解Service的具体实现,它只需要获得Service 的Contract,已经如何与Service通信就足够了。说到Contract和通信,我们很自然地会想到Endpoint,不错,Endpoint恰恰给我们提供这两个方面的内容。所以到现在我们可以这样说,这样在Client建立和Serivce端相匹配的Endpoint,Client就可以调用它所希望的Service。前面提到Endpoint包括三个部分, Address, Binding,Contract那我们现在来看看Client如何获得这3要素的信息。
在System。Service。Model 命名空间里,定义了一个类abstract class ClientBase
1、Conract:我们看到了ClientBase是一个Generic的类,我们在创建一个继承自这个类的时候必须给它指定特定的TChannel.我们可以把Contract对应的类型作为Client的generic类型。
2、Binding和Address:和Service端的Endpoint一样,我们可以把相关的信息放在我们的Client端代码里面,也可以放在Client的Config里面。那个这些数据如何应用要我们创建的派生自ClientBase的类的对象上呢。其实很简单,ClientBase给我们定义了若干重载的构造函数,我们只要定义我们相应的构造函数应简单地调用基类的构造函数。下面列出了ClientBase定义的全部的构造函数。
protected ClientBase():这个构造函数没有任何的参数,它用于Endpoint的信息全部存放于Config
protected ClientBase(InstanceContext callbackInstance):指定一个Callback instance用于Service回调Client代码,这用Deplex Communication。
protected ClientBase(string EndpointConfigurationName):指定一个ID,它标识configuration 文件中定义的某一个Endpoint。这个方法在使用不同的Endpoint调用同一个Service的情况下用到的比较多。
|
上面的例子中我们仅仅定义了一个无参的构造函数,因为我们会把所有的Endpoint信息放在Config文件里面:
|
然后我们再Client Project中的Program class里面通过这样的方式调用CalculatorService;
|




