您的位置:首页 > 其它

WCF 快速入门

2011-08-10 10:54 260 查看
本文转自:/article/4967087.html

定义服务契约

  构建HelloWCF应用的第一步是创建服务契约。契约式是表示消息应用外形的主要方式。对于外形,是指服务暴露的操作,使用的消息schema和每个操作实现的消息交换模式(MEP)。总之,契约定义了我们消息应用生成和使用的东西。大多数契约是带有WCFAPI定义的属性标记的类型定义。

  在下面的例子里,服务契约是一个带有System.ServiceModel.ServiceContractAttribute和System.ServiceModel.OperationContractAttribute标记的接口,如下所示:

viewsourceprint?

[ServiceContract]
public
interface
IHelloWCF{
[OperationContract]
void
Say(Stringinput);
}
  在高层次上,我们的服务契约表示我们接收消息的应用包含一个名字为Say的操作,并且这个操作接收一个String类型的参数和void返回类型。发送消息的应用可以用它来构造和发送消息给接收程序。既然我们已经定义的服务契约,那就到了该定义接收程序侦听地址和如何与其它消息参与者交换消息的时候了。

定义地址和绑定

  定义侦听请求消息的地址需要使用System.Uri类型,定义如何与其它消息参与者交换消息需要我们使用System.ServiceModel.Channels.Binding类型。或者这些类型的继承类型。下面的代码说明了如何在我们的应用里使用Uri和Binding类型。

viewsourceprint?

static
void
Main(){
//definewheretolistenformessages定义侦听消息的地址
Uriaddress=
new
Uri(
"http://localhost:8000/IHelloWCF"
);
//definehowtoexchangemessages定义如何交换消息
BasicHttpBindingbinding=
new
BasicHttpBinding();
}
  注意局部变量address使用的是HTTP格式的统一资源标识符(URI)。选择这个地址强制要求我们使用HTTP传输。更高层次上,绑定是指定传输、消息编排和消息编码的主要方式。局部变量binding是BasicHttpBinding类型的实例。和你从名字看到的一样,BasicHttpBinding创建的是一个用于HTTP传输的消息架构。

创建一个终结点并启动侦听

  接下来我们要使用地址(address)、绑定(binding)和契约(contract)来构建一个终结点(endpoint)并在此终结点上侦听发送进来的消息。通常来说,一个WCF接受程序可以构建和使用多个终结点,并且每个终结点都需要一个地址、一个绑定和一个契约。System.ServiceModel.ServiceHost类型构建和托管终结点,并管理接受应用底层结构的其他部分,比如线程和对象的生命周期。下面代码块演示了如何实例化ServiceHost,如何添加终结点和如何开始侦听进入的消息:

viewsourceprint?

static
void
Main(){
//definewheretolistenformessages定义侦听消息的地址
Uriaddress=
new
Uri(
"http://localhost:4000/IHelloWCF"
);
//definehowtoexchangemessages定义如何交换消息
BasicHttpBindingbinding=
new
BasicHttpBinding();
//instantiateaServiceHost,passingthetypetoinstantiate实例化ServiceHost,传递服务类型
//whentheapplicationreceivesamessage
ServiceHostsvc=
new
ServiceHost(
typeof
(HelloWCF));
//addanendpoint,passingtheaddress,binding,andcontract增加终结点、绑定和契约
svc.AddServiceEndpoint(
typeof
(IHelloWCF),binding,address);
//beginlistening开始侦听
svc.Open();
//indicatethatthereceivingapplicationisreadyand指示应用准备接受消息
//keeptheapplicationfromexitingimmediately保持应用程序不会立即退出
Console.WriteLine(
"TheHelloWCFreceivingapplicationisready"
);
Console.ReadLine();
//closetheservicehost关闭宿主
svc.Close();
}
  上述代码里调用了svc.AddServiceEndpoint和svc.Open。AddServiceEndpoint实例方法设置ServiceHost对象的属性,这样它将使用地址、绑定和契约参数执行的行为来侦听消息。要着重指出的是AddServiceEndpoint方法没有开始循环侦听;它仅仅是简单地改变了ServiceHost对象的状态。ServiceHost实例的Open方法构建了消息基础结构,并开始循环侦听。Open方法会验证ServiceHost对象的状态,从它的状态里构建终结点,并且开始侦听。

映射接收的消息到HelloWCF的成员

  在目前状态,我们编译程序,当程序试图构建一个终结点的时候,会出现一个异常:InvalidOperationException。原因一目了然:在ServiceHost类型的构造函数里,我们传递了HelloWCF作为参数,因此,这就表示消息基础结构要分发消息给我们的HelloWCF对象。因此,必然存在消息到服务成员的映射关系。最简单的创建映射的方式就是使HelloWCF服务类实现服务契约IHelloWCF。

viewsourceprint?

using
System;
using
System.ServiceModel;
using
System.ServiceModel.Channels;
//implementtheIHelloWCFservicecontract
sealed
class
HelloWCF:IHelloWCF{
//indicatewhenaHelloWCFobjectiscreated
HelloWCF(){Console.WriteLine(
"HelloWCFobjectcreated"
);}
static
void
Main(){
//definewheretolistenformessages
Uriaddress=
new
Uri(
"http://localhost:4000/IHelloWCF"
);
//definehowtoexchangemessages
BasicHttpBindingbinding=
new
BasicHttpBinding();
//instantiateaServiceHost,passingthetypetoinstantiate
//whentheapplicationreceivesamessage
ServiceHostsvc=
new
ServiceHost(
typeof
(HelloWCF));
//addanendpoint,passingtheaddress,binding,andcontract
svc.AddServiceEndpoint(
typeof
(IHelloWCF),binding,address);
//beginlistening
svc.Open();
//indicatethatthereceivingapplicationisreadyand
//keeptheapplicationfromexitingimmediately
Console.WriteLine(
"TheHelloWCFreceivingapplicationisready"
);
//waitforincomingmessages
Console.ReadLine();
//closetheservicehost
svc.Close();
}
//receivedmessagesaredispatchedtothisinstance
//methodaspertheservicecontract
public
void
Say(Stringinput){
Console.WriteLine(
"Messagereceived,thebodycontains:{0}"
,input);
}
}
[ServiceContract]
public
interface
IHelloWCF{
[OperationContract]
void
Say(Stringinput);
}
  改变HelloWCF的类型定义会使得消息的基础结构分发接受到的消息到服务实例的Say操作上,因此会在控制台界面上输出一个简单的语句。

编译、运行和检验接受者

  我们现在准备使用下面的命令行编译并运行这个应用:

viewsourceprint?

C:\temp>csc/nologo/r:"c:\WINDOWS\Microsoft.Net\Framework\v3.0\WindowsCommunication
Foundation\System.ServiceModel.dll"HelloWCFApp.cs
C:\temp>HelloWCFApp.exe
TheHelloWCFreceivingapplication
is
ready
  此时,接受消息的应用在被动地等待请求消息的到来。我们是用netstat.exe可以检查一下应用是否确实在侦听,如下所示:

viewsourceprint?

c:\temp>netstat–a–b
TCPkermit:40000.0.0.0:0LISTENING1104
[HelloWCFApp.exe]
向接受者发送消息

  发送消息的基础结构也需要依靠地址、绑定和契约,这与接收消息的基础结构类似。

  与接受者不同,绝大多数发送者放弃直接使用Uri类型,而是使用System.ServiceModel.EndpointAddress类型去表示消息发送的目标。

  EndpointAddress类型是WCF对于WS-Addressing终结点参考的抽象。此外,发送者不使用ServiceHost类型,而是使用ChannelFactory<T>类型(T是服务契约类型)。ChannelFactory<T>类型构建发送消息的基础结构和ServiceHost构建接受消息的基础结构类似。下面的代码演示了如何使用EndpointAddress类型和ChannelFactory<T>构建发送基础结构。

viewsourceprint?

using
System;
using
System.ServiceModel;
using
System.ServiceModel.Channels;
//implementtheIHelloWCFservicecontract
sealed
class
HelloWCF:IHelloWCF{
//indicatewhenaHelloWCFobjectiscreated
HelloWCF(){Console.WriteLine(
"HelloWCFobjectcreated"
);}
static
void
Main(){
//definewheretolistenformessages
Uriaddress=
new
Uri(
"http://localhost:4000/IHelloWCF"
);
//definehowtoexchangemessages
BasicHttpBindingbinding=
new
BasicHttpBinding();
//instantiateaServiceHost,passingthetypetoinstantiate
//whentheapplicationreceivesamessage
ServiceHostsvc=
new
ServiceHost(
typeof
(HelloWCF));
//addanendpoint,passingtheaddress,binding,andcontract
svc.AddServiceEndpoint(
typeof
(IHelloWCF),binding,address);
//beginlistening
svc.Open();
//indicatethatthereceivingapplicationisreadyand
//keeptheapplicationfromexitingimmediately
Console.WriteLine(
"TheHelloWCFreceivingapplicationisready"
);
//beginthesendercode发送者代码开始
//createachannelFactory<T>withbindingandaddress
ChannelFactory<IHelloWCF>factory=
new
ChannelFactory<IHelloWCF>(binding,
new
EndpointAddress(address));
//usethefactorytocreateaproxy使用工厂创建代理
IHelloWCFproxy=factory.CreateChannel();
//usetheproxytosendamessagetothereceiver使用代理发送消息给接受者
proxy.Say(
"HithereWCF"
);
//endthesendercode发送者代码结束
Console.ReadLine();
//closetheservicehost
svc.Close();
}
//receivedmessagesaredispatchedtothisinstance
//methodaspertheservicecontract
public
void
Say(Stringinput){
Console.WriteLine(
"Messagereceived,thebodycontains:{0}"
,input);
}
}
[ServiceContract]
public
interface
IHelloWCF{
[OperationContract]
void
Say(Stringinput);
}
  更高层次上,ChannelFactory<T>对象是一个可以制造产生和发送消息给接受者(因此需要在构造函数里传递绑定和地址)的基础结构的类型。ChannelFactory<T>实例的CreateChannel方法实际创建的是发送基础结构,并且通过实现服务契约的一个对象返回这个基础结构的引用。

编译、运行和检验发送者

  既然我们已经完成了发送和接受基础结构代码,现在应该是编译和运行程序的时候了,如下所示:

viewsourceprint?

c:\temp>csc/nologo/r:"c:\WINDOWS\Microsoft.Net\Framework\v3.0\WindowsCommunication
Foundation\System.ServiceModel.dll"HelloWCFApp.cs
c:\temp>HelloWCFApp.exe
TheHelloWCFreceivingapplication
is
ready
HelloWCF
object
created
Messagereceived,thebodycontains:HelloWCF!
  如期望的一样,我们的程序执行步骤如下:

  1.为接受来自http://localhost:4000/IHelloWCF的消息构建基础结构。

  2.开始在http://localhost:4000/IHelloWCF上侦听消息。

  3.构建发送到http://localhost:4000/IHelloWCF消息的基础结构。

  4.生成和发送消息到http://localhost:4000/IHelloWCF。

  5.接受消息,实例化一个HelloWCF对象,分发消息到服务实例的Say方法上。

看一下消息

  现在代码写完了,貌似没看到我们HelloWCF例子里哪里使用到了消息。对于开发者,一个WCF应用看起来和感觉都很像面向对象或者面向组件的应用。在运行时,WCF应用要生成、发送和接受消息,同样也要处理消息。通过修改Say方法的实现我们能看到WCF接触结构的消息:

viewsourceprint?

public
void
Say(Stringinput){
Console.WriteLine(
"Messagereceived,thebodycontains:{0}"
,input);
//Showthecontentsofthereceivedmessage显示接受消息内容
Console.WriteLine(
OperationContext.Current.RequestContext.RequestMessage.ToString());
}
  修改Say方法后的输出如下:

viewsourceprint?

TheHelloWCFreceivingapplication
is
ready
HelloWCF
object
created
Messagereceived,thebodycontains:HelloWCF!
<s:Envelopexmlns:s=
"http://schemas.xmlsoap.org/soap/envelope/"
>
<s:Header>
<Tos:mustUnderstand=
"1"
xmlns=
"http://schemas.microsoft.com/ws/2005/05/adessing/none"
>
http:
//localhost:8000/IHelloWCF
</To>
<Actions:mustUnderstand=
"1"
xmlns=
"http://schemas.microsoft.com/ws/2005/05/addressing/none"
>
http:
//tempuri.org/IHelloWCF/Say
</Action>
</s:Header>
<s:Body>
<Sayxmlns=
"http://tempuri.org/"
>
<input>HelloWCF!</input>
</Say>
</s:Body>
</s:Envelope>
  注意到打印的SOAP消息,消息的Body部分包含我们传递给局部变量的channel上Say方法的字符串。宏观上讲,我们的应用程序使用这个字符来构建一个SOAP消息,然后发送这个SOAP消息到我们程序的接受部分。接受部分,换句话说,它要接受SOAP消息,创建一个HelloWCF实例,提取SOAPBody的内容,调用HelloWCF实例的Say方法,传递字符串参数。

暴露元数据

  我们的HelloWCF程序使用一个相当简单的方法就实现了接收者和发送者之间的兼容性。因为接收者和发送者驻留在同一个AppDomain里,并且接收者使用的对象对于发送者来说是可见的,我们在发送者代码里简单地重用了地址、绑定和契约。在发部分消息应用里,这个方法是可行的。绝大多数情况,我们希望发送者和接收者去驻留在不同的机器上的AppDomains里。在这些场景里,接收者显示指定消息需求,发送者遵守这些需求。

  WS-MetadataExchange规范规定了发送者和接收者在平台无关时如何交换这些数据信息。在更多的条款里,WS-MetadataExchange规范限定了方便终结点之间进行消息交换的schema和编排。在大部分现实世界的应用系统里,有一个暴露信息方式的需求,这个方式就是发送者询问接收者的终结点去提取元数据,并使用这些元数据构建能发送给接收终结点消息的基础结构。

  WCF缺省的情况下不会暴露元数据,这些原因都是因为对安全的考虑。元数据暴露的信息包含应用系统的安全需求。以保护秘密的名义,这个团队选择缺省情况下关闭这个特性。如果决定暴露系统的元数据,我们可以构建一个暴露元数据的终结点。构建元数据终结点的方式和其它终结点非常相似:使用地址、绑定和契约。但是目前为止你看到的终结点不太一样,就是服务契约已经定义到WCF的API里了。

  构建元数据终结点的第一步是修改ServiceHost到可以托管元数据的状态。我们可以通过System.ServiceModel.Description.ServiceMetadataBehavior对象增加到ServiceHost行为集合里。行为是WCF基础结构用来改变本地消息处理的特定信息。下面代码演示了如何增加ServiceMetadataBehavior对象到活动的ServiceHost对象:

viewsourceprint?

//instantiateaServiceHost,passingthetypetoinstantiate
//whentheapplicationreceivesa
messageServiceHostsvc=
new
ServiceHost(
typeof
(HelloWCF),address);
//BEGINNEWMETADATACODE
//createaServiceMetadataBehavior创建服务元数据行为
ServiceMetadataBehaviormetadata=
new
ServiceMetadataBehavior();
metadata.HttpGetEnabled=
true
;
//addittotheservicehostdescription
svc.Description.Behaviors.Add(metadata);
  下一步就是为元数据终结点定义Binding。元数据绑定的对象模型与其它绑定区别很大,我们通过调用工厂方法上的System.ServiceModel.Description.MetadataExchangeBindings的类型创建元数据绑定,如下所示:

viewsourceprint?

//instantiateaServiceHost,passingthetypetoinstantiate
//whentheapplicationreceivesamessage
ServiceHostsvc=
new
ServiceHost(
typeof
(HelloWCF));
//BEGINNEWMETADATACODE
//createaServiceMetadataBehavior创建服务元数据行为
ServiceMetadataBehaviormetadata=
new
ServiceMetadataBehavior();
//addittotheservicehostdescription
svc.Description.Behaviors.Add(metadata);
//createaTCPmetadatabinding创建TCP元数据绑定
BindingmexBinding=MetadataExchangeBindings.CreateMexTcpBinding();
由于ASMX(ASP.NETWebService)的影响,你也许会认为元数据只能通过HTTP传输呈现。事实上,元数据可以通过多种传输协议传递,并且WS-MetadataExchange说明了这个灵活性。在我们的例子里,我们调用CreateMexTcpBinding方法,它返回了一个继承自Binding类型的TCP传输绑定。因为我们使用的是TCP传输,所以我们必须确保元数据地址使用了TCP地址格式,如下所示:
//instantiateaServiceHost,passingthetypetoinstantiate
//whentheapplicationreceivesamessage
ServiceHostsvc=
new
ServiceHost(
typeof
(HelloWCF));
//BEGINNEWMETADATACODE
//createaServiceMetadataBehavior
ServiceMetadataBehaviormetadata=
new
ServiceMetadataBehavior();
//addittotheservicehostdescription
svc.Description.Behaviors.Add(metadata);
//createaTCPmetadatabinding
BindingmexBinding=MetadataExchangeBindings.CreateMexTcpBinding();
//createanaddresstolistenonWS-Metadataexchangetraffic
//创建元数据交换侦听地址
UrimexAddress=
new
Uri(
"net.tcp://localhost:5000/IHelloWCF/Mex"
);
  既然我们定义了元数据终结点需要的地址和绑定,我们要添加终结点到ServiceHost上,方式很像我们定义的第一个消息终结点。当添加元数据终结点时,我们要使用WCFAPI定义的名为System.ServiceModel.Description.IMetadataExchange的服务契约。下面代码演示了如何添加一个元数据终结点到ServiceHost上,使用适当的地址、绑定和契约。

viewsourceprint?

//instantiateaServiceHost,passingthetypetoinstantiate
//whentheapplicationreceivesamessage
ServiceHostsvc=
new
ServiceHost(
typeof
(HelloWCF));
//BEGINNEWMETADATACODE
//createaServiceMetadataBehavior
ServiceMetadataBehaviormetadata=
new
ServiceMetadataBehavior();
//addittotheservicehostdescription
svc.Description.Behaviors.Add(metadata);
//createaTCPmetadatabinding
BindingmexBinding=MetadataExchangeBindings.CreateMexTcpBinding();
//createanaddresstolistenonWS-Metadataexchangetraffic
UrimexAddress=
new
Uri(
"net.tcp://localhost:5000/IHelloWCF/Mex"
);
//addthemetadataendpoint添加元数据终结点
svc.AddServiceEndpoint(
typeof
(IMetadataExchange),
mexBinding,
mexAddress);
//ENDMETADATACODE
使用元数据

  Microsoft.NETFrameworkSDK安装了一个功能强大的工具名字是svcutil.exe,它的一个功能就是询问一个运行的消息应用并基于获得的信息生成代理。从内部来说,svcutil.exe使用的是WS-MetadataExchange协议,像与ASMX一起普及的WSDL里的“get”语义一样。因为我们的接收程序暴露了一个元数据终结点,我们可以把svcutil.exe指向这个运行的终结点,svcutil.exe会自动生成一个代理类型和与服务终结点兼容的配置信息,这些信息都是参考元数据生成。当使用这种方式的时候,svcutil.exe依照WS-MetadataExchange的方式发送消息给接收程序,并转化这些消息为.NETFramework的类型以方便发送消息程序的开发。

使用Svcutil.exe生成代理

  在你运行svcutil.exe以前,检查一下HelloWCFApp.exe正常运行并侦听请求消息。下一步就是打开一个新的WindowsSDK命令行窗口,输入下面的命令:

viewsourceprint?

C:\temp>svcutil/target:codenet.tcp:
//localhost:5000/IHelloWCF/Mex
  Svcutil.exe会创建2个文件:HelloWCFProxy.cs和output.config。如果你检查一下HelloWCFProxy.cs文件,你就会看到svcutil.exe产生了一个包含IHelloWCF、IHelloWCFChannel,和HelloWCFClient的代码文件。合起来看,这些类型定义就是帮助我们创建于接受程序兼容的发送代码。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: