示例下载(VS2005 下编写) 三、WCF的技术要素
- using System.ServiceModel
- [ServiceContract]
- public class Service1
- {
- public string SayHello(string name)
- {
- return "Hello: " + name;
- }
- }
using System.ServiceModel [ServiceContract] public class Service1 { public string SayHello(string name) { return "Hello: " + name; } }System.ServiceModel是微软为WCF提供的一个新的类库,以用于面向服务的程序设计。在开发WCF应用程序时,需要先添加对System.ServiceModel的引用。WCF中的大部分类和接口也都是在命名空间System.ServiceModel下。 我们为Service1类标记了[ServiceContract],这就使得该类成为了一个WCF Service,而其中的方法SayHello()则因为标记了[OperationContract],而成为该Service的一个Operation。 不过WCF推荐的做法是将接口定义为一个Service,这使得WCF Service具有更好的灵活性,毕竟对于一个接口而言,可以在同时有多个类实现该接口,这也就意味着可以有多个Service Contract的实现。那么上面的例子就可以修改为:
- [ServiceContract()]
- public interface IService1
- {
- [OperationContract]
- string SayHello(string name);
- }
[ServiceContract()] public interface IService1 { [OperationContract] string SayHello(string name); }而类Service1则实现该IService1接口:
- public class Service1 : IService1
- {
- public string SayHello(string name)
- {
- return "Hello: " + name;
- }
- }
public class Service1 : IService1 { public string SayHello(string name) { return "Hello: " + name; } }注意在实现了IService1接口的类Service1中,不再需要在类和方法中标注ServiceContractAttribute和OperationContractAttribute了。 前面我已经提过,一个WCF Service必须有host作为它运行的环境。这个host可以是ASP.Net,可以是Windows Service,也可以是一个普通的应用程序,例如控制台程序。下面就是一个Host的实现:
- using System.ServiceModel
- class HostApp
- {
- static void Main(string[] args)
- {
- MyServiceHost.StartService();
- Console.WriteLine("服务已经启动...");
- Console.Read();
- MyServiceHost.StopService();
- }
- }
- internal class MyServiceHost
- {
- internal static ServiceHost myServiceHost = null;
- internal static void StartService()
- {
- //Consider putting the baseAddress in the configuration system
- //and getting it here with AppSettings
- Uri baseAddress = new Uri("http:localhost:8080/service1");
- //Instantiate new ServiceHost
- myServiceHost = new ServiceHost(typeof(WCFServiceLibrary2.Service1), baseAddress);
- //Open myServiceHost
- myServiceHost.Open();
- }
- internal static void StopService()
- {
- //Call StopService from your shutdown logic (i.e. dispose method)
- if (myServiceHost.State != CommunicationState.Closed)
- myServiceHost.Close();
- }
- }
using System.ServiceModel class HostApp { static void Main(string[] args) { MyServiceHost.StartService(); Console.WriteLine("服务已经启动..."); Console.Read(); MyServiceHost.StopService(); } } internal class MyServiceHost { internal static ServiceHost myServiceHost = null; internal static void StartService() { //Consider putting the baseAddress in the configuration system //and getting it here with AppSettings Uri baseAddress = new Uri("http:localhost:8080/service1"); //Instantiate new ServiceHost myServiceHost = new ServiceHost(typeof(WCFServiceLibrary2.Service1), baseAddress); //Open myServiceHost myServiceHost.Open(); } internal static void StopService() { //Call StopService from your shutdown logic (i.e. dispose method) if (myServiceHost.State != CommunicationState.Closed) myServiceHost.Close(); } }在这个HostApp中,我们为Server1创建了一个ServiceHost对象。通过它就可以创建WCF运行时(Runtime),WCF Runtime是一组负责接收和发送消息的对象。ServiceHost可以创建SerivceDescription对象,利用SerivceDescription,SercieHost为每一个ServiceEndpoint创建一个EndpointListener。ServiceHost的组成如下图:
- [System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "3.0.0.0")]
- [System.ServiceModel.ServiceContractAttribute(ConfigurationName="IService1")]
- public interface IService1
- {
- [System.ServiceModel.OperationContractAttribute(Action="http:tempuri.org/IService1/SayHello", ReplyAction="http:tempuri.org/IService1/SayHelloResponse")]
- string SayHello(string name);
- }
- [System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "3.0.0.0")]
- public interface IService1Channel : IService1, System.ServiceModel.IClientChannel
- {
- }
- [System.Diagnostics.DebuggerStepThroughAttribute()]
- [System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "3.0.0.0")]
- public partial class Service1Client : System.ServiceModel.ClientBase<IService1>, IService1
- {
- public Service1Client()
- {
- }
- public Service1Client(string endpointConfigurationName) :
- base(endpointConfigurationName)
- {
- }
- public Service1Client(string endpointConfigurationName, string remoteAddress) :
- base(endpointConfigurationName, remoteAddress)
- {
- }
- public Service1Client(string endpointConfigurationName, System.ServiceModel.EndpointAddress remoteAddress) :
- base(endpointConfigurationName, remoteAddress)
- {
- }
- public Service1Client(System.ServiceModel.Channels.Binding binding, System.ServiceModel.EndpointAddress remoteAddress) :
- base(binding, remoteAddress)
- {
- }
- public string SayHello(string name)
- {
- return base.Channel.SayHello(name);
- }
- }
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "3.0.0.0")] [System.ServiceModel.ServiceContractAttribute(ConfigurationName="IService1")] public interface IService1 { [System.ServiceModel.OperationContractAttribute(Action="http:tempuri.org/IService1/SayHello", ReplyAction="http:tempuri.org/IService1/SayHelloResponse")] string SayHello(string name); } [System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "3.0.0.0")] public interface IService1Channel : IService1, System.ServiceModel.IClientChannel { } [System.Diagnostics.DebuggerStepThroughAttribute()] [System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "3.0.0.0")] public partial class Service1Client : System.ServiceModel.ClientBase<IService1>, IService1 { public Service1Client() { } public Service1Client(string endpointConfigurationName) : base(endpointConfigurationName) { } public Service1Client(string endpointConfigurationName, string remoteAddress) : base(endpointConfigurationName, remoteAddress) { } public Service1Client(string endpointConfigurationName, System.ServiceModel.EndpointAddress remoteAddress) : base(endpointConfigurationName, remoteAddress) { } public Service1Client(System.ServiceModel.Channels.Binding binding, System.ServiceModel.EndpointAddress remoteAddress) : base(binding, remoteAddress) { } public string SayHello(string name) { return base.Channel.SayHello(name); } }至于后者,则是WCF Service的配置信息,主要包含的是Endpoint中Address,Binding以及Contract的配置(在后续文章我会详细介绍)。 现在客户端就可以直接使用Service1Client对象,来完成与服务端的通信了:
- class ClientApp
- {
- static void Main(string[] args)
- {
- Service1Client client = new Service1Client();
- // 使用 "client" 变量在服务上调用操作。
- Console.WriteLine("请输入你的名字: ");
- string name = Console.ReadLine();
- Console.WriteLine(client.SayHello(name));
- // 始终关闭客户端。
- client.Close();
- Console.ReadLine();
- }
- }
class ClientApp { static void Main(string[] args) { Service1Client client = new Service1Client(); // 使用 "client" 变量在服务上调用操作。 Console.WriteLine("请输入你的名字: "); string name = Console.ReadLine(); Console.WriteLine(client.SayHello(name)); // 始终关闭客户端。 client.Close(); Console.ReadLine(); } }除了可以使用SvcUtil工具产生客户端代码,同样我们也可以利用代码的方式来完成客户端。客户端在发送消息给服务端时,其通信的基础是Service的Endpoint,WCF提供了System.ServiceModel.Description.ServiceEndpoint类,通过创建它来实现两端的通信。在前面,我还提到“对于客户端而言,WCF管理的是发送消息时需要使用到的通道Channel”,为此,WCF提供了ChannelFactory(其命名空间为System.ServiceModel.Channel),专门用于创建客户端运行时(runtime)。ChannelFactory与ServiceHost相对应,它可以创建ChannelDescription对象。与服务端ServiceHost不同的是,客户端并不需要侦听器,因为客户端往往是建立连接的“发起方”,并不需要侦听进来的连接。因此客户端的Channel Stack会由ChannelDescription创建。 ChannelFactory和ServiceHost都具有Channel Stack,而服务端与客户端的通信又是通过channel来完成,这就意味着,利用ChannelFactory,客户端可以发送消息到服务端。而客户端本身并不存在Service对象,因此该Service的Proxy,是可以通过Channel来得到的。所以客户端的代码可以修改如下:
- using System.ServiceModel;
- using System.ServiceModel.Description;
- using System.ServiceModel.Channel
- class ClientApp
- {
- static void Main(string[] args)
- {
- ServiceEndpoint httpEndpoint = new ServiceEndpoint(ContractDescription.GetContract(typeof(IService1)), new WSHttpBinding(), new EndpointAddress("http:localhost:8080/service1"));
- using (ChannelFactory<IService1> factory = new ChannelFactory<IService1>(httpEndpoint))
- {
- //创建IHello服务的代理对象;
- IService1 service = factory.CreateChannel();
- Console.WriteLine("请输入你的名字: ");
- string name = Console.ReadLine();
- Console.WriteLine(service.SayHello(name));
- }
- Console.ReadKey();
- }
- }
using System.ServiceModel; using System.ServiceModel.Description; using System.ServiceModel.Channel class ClientApp { static void Main(string[] args) { ServiceEndpoint httpEndpoint = new ServiceEndpoint(ContractDescription.GetContract(typeof(IService1)), new WSHttpBinding(), new EndpointAddress("http:localhost:8080/service1")); using (ChannelFactory<IService1> factory = new ChannelFactory<IService1>(httpEndpoint)) { //创建IHello服务的代理对象; IService1 service = factory.CreateChannel(); Console.WriteLine("请输入你的名字: "); string name = Console.ReadLine(); Console.WriteLine(service.SayHello(name)); } Console.ReadKey(); } }对于上面的代码,我们有两点需要注意: 1、采用这种方式,前提条件是客户端能够访问IHello接口。这也印证了之前我所叙述的最好使用interface来定义Service的好处。此外,为了保证部署的方便,有关Service的interface最好单独编译为一个程序集,便于更好的部署到客户端。 2、客户端必须知道服务端binding的方式以及address。 对于服务端而言,我们也可以直接在浏览器中打开该Service,在地址栏中输入 http://localhost:8080/service1,如下图:
点击链接:http://localhost:8080/service1?wsdl,我们可以直接看到Service1的WSDL。注意到在这里我并没有使用IIS,实际上WCF内建了对httpsys的集成,允许任何应用程序自动成为HTTP listener。 示例下载 参考: 1、David Chappell,Introducing Windows Communication Foundation 2、Aaron Skonnard,Learn The ABCs Of Programming Windows Communication Foundation 3、Microsoft Corporation,Windows Communication Foundation Architecture Overview
[From:http://www.xwy2.com/article.asp?id=21]