WPF(Windows Presentation Foundation), WCF(Windows Communication Foundation), WF(Windows Workflow Foundation) 是.NET 3.0 中新加入的类库. 这里会介绍一下WCF 和WF 的大概应用和架构, 关于WPF, 由于丸子也还没能完全得到那个点, 所以可能就 不介绍了, 还需要在多看看砖头.
进入正题, 今天讲的是WCF. 这个东西是用来创建分布式系统的.
首先介绍一下SOA(Service-Oriented Architecture) 的概念. SOA 实际上是一个基于service 的分布式架构. 每个service 对外提供一定的功能接口. 当我们要做一个大的软件的时候, 只要把这些service 提供的接口组合一下就可以了. 那这些service 之间, 或者我们client 程序之间怎么交互呢? 这里就是SOA 实现的问题了. 比如比较流行的有Web Service, Windows 下比较老的DCOM 和MSMQ. 当然还有WCF. Web Service, WCF 代表的是一种SOA的技术或者说实现, 而它们的通信协议(protocol) 是另外一个概念. 比如Web Service 用的可以是SOAP 或是RESTful 协议. WCF 的好处就是, 把以前的一些技术都整合到了一起, 包括DCOM, MSMQ, .NET remoting, Web Service 等.
要建立一个WCF 的应用程序, 基本的组件有三个: a) service assembly, b) service host, c) client. client 端和server 端通信是通过WCF 中的所谓ABC: Address, Binding, Contract. Address 表示的是service 的地址, Binding 表示的是service 的协议, 编码等, Contract 表示的是WCF 对外暴露出的方法.
service assembly 的实现, 主要就是在Contract 这块. 我们用接口(interface) 来定义contract, 然后再用一个具体的类来实现它. 比如:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
// interface [ServiceContract] public interface IService { [OperationContract] int Add(int a, int b); } // implementation public class MyService: IService { public MyService() { /* ... */ } int Add(int a, int b); { return a + b; } } |
然后把以上代码编译成一个dll, 就算完成了.
service host 的稍微复杂一些. 主要用到了System.ServiceModel.ServiceHost 这个WCF 中的类来实现application 级的service host. 大概的代码可能是这样:
1 2 3 4 5 6 7 8 9 10 11 12 |
class MyHost { static void Main(string[] args) { using (ServiceHost serviceHost = new ServiceHost(typeof(MyService))) { serviceHost.Open(); Console.WriteLine("The service is ready."); Console.ReadLine(); } } } |
在代码中, 我们在ServiceHost 类的constructor 中传入了MyService 类型作为所要host 的service. 注意, 要调用这个constructor 的话, MyService 类必须有一个不带参数的constructor, 不然运行时会抛错的那. 当然也可以用一个service 的实例来初始化ServiceHost, 但是比较麻烦, 丸子也没研究过.
以上的代码显然是缺少Address 和Binding 这两快内容的, 我们要在app.config 的配置文件中指定. 配置文件大概是这样的:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
<?xml version="1.0" encoding="utf-8" ?> <configuration> <system.serviceModel> <services> <service name="MyServiceLib.MyService" behaviorConfiguration="MyServiceMexBehavior"> <endpoint address="" binding="basicHttpBinding" contract="MyServiceLib.IService" /> <endpoint address="" binding="basicHttpBinding" contract="MyServiceLib.IService2" /> <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" /> <host> <baseAddresses> <add baseAddress ="http://localhost:8080/MyService"/> </baseAddresses> </host> </service> </services> <behaviors> <serviceBehaviors> <behavior name="MyServiceMexBehavior"> <serviceMetadata httpGetEnabled="true" /> </behavior> </serviceBehaviors> </behaviors> </system.serviceModel> </configuration> |
好处一串… 先来看<service>这个tag, 它对应的就是contract 的实现类, 用name 属性来指定. 然后是<endpoint>这个tag, 它对应的是单个的contract 的接口. 在运行时, ServiceHost 会进行这样的检查: 根据代码中的MyService 类找到app.config 中对应的<service>的tag, 这个tag 中的name 属性默认就是类名, 但是也可以指定为一个friend name, 不过具体做法未知=v=. 然后分别比对实现类和config 文件中的接口定义是否一致, 如果不一致的话会抛运行时错误. 注意: 我们在所有的<endpoint>中的address 属性都为空, 其实是利用了下面的<baseAddresses>中的baseAddress 属性, 这是一个基地址.
另外还有一个MEX(Metadata Exchange) 的概念, 在config 文件中, 对应一个<endpoint>和<behavior>的tag. 简单来说, 这个东西就是用来生产client 端的代理(proxy) 代码的. 它提供了对于metadata 请求的响应. 当运行这个service host 的时候, 我们可以用browser 来访问mex 这个<endpoint>中的address, 会看到关于这个service 的metadata 信息, 以及如何生产client 端代码的提示. 一旦我们生产了client 端的代码, 如果不希望这些metadata 信息再被发现(discover) 的话, 可以删掉这两个tag.
注意, 如果是vista或者win7 的话, service host 程序必须以管理员权限运行.
最后就是client 端的写法了. client 端怎样通过WCF 的框架来调用远程的service 呢? 我们需要一个proxy 的类, 这个类通过在service host 得到的metadata 信息, 来封装对于service 的远程调用. vs2008 提供了wizard 来实现这个繁冗的过程, 请自由的在项目节点上右键点击add service reference 菜单项. 在出现的对话框中, 会看到service 的列表, 而每个service 包含多个service contract, 而每个service contract 又包含多个operation contract. 我们要生成的proxy 类的代码是service 级别的, 对于每个service, 所有的代码都生成在一个namespace 中. 一旦生产了代码, 我们的client 端代码将是非常简单的:
1 2 3 4 5 6 7 8 9 10 |
class MyClient { static void Main(string[] args) { using (MyServiceClient client = new MyServiceClient()) { Console.Write("{0} + {1} = {2}", 1, 1, client.Add(1,1)); } } } |
WCF 还提供了异步调用和自定义数据类型的支持, 这些就请自行查阅MSDN 吧.