wcf系列之服务契约ServiceContract 之操作重载
在C#中存在方法重载,我们可以定义相同方法名但是参数类型或者个数不同,从而实现方法的重载功能。在wcf中,如果能够实现方法重载,那么我们就可以传递不同类型的数据,让服务返回不同的结果。这真是一个不错的主意,但是wcf能够实现方法重载吗?
我们先简短的思考一下:wcf服务和客户端通过soap消息(也就是xml数据)进行交互,soap消息会包含参数类型以及返回值类型,还有方法名,客户端或服务会解析soap消息,转换成本地对象,从技术功能上说可以实现方法的重载,但是考虑到soap消息的传输安全性问题,soap消息可能会被更改,所以从这方面来说方法的重载就不太可能实现了,因为服务的调用要的就是稳定、可靠。
当然这些都是我们自己的推测,wcf服务究竟能不能实现方法的重载还是我们要自己测试以后才可以知道。
1 using System;
2 using System.Collections.Generic;
3 using System.Linq;
4 using System.Text;
5 using System.ServiceModel;
6
7 namespace Chinaer.WcfDemo.Contracts
8 {
9 [ServiceContract]
10 public interface ICalculator
11 {
12 [OperationContract]
13 int Add(int args1, int args2);
14
15 [OperationContract]
16 double Add(double args1, double args2);
17 }
18 }
我们创建了一个具有相同方法名,但是参数类型不同的方法重载,具体的服务托管与寄宿我们有很多资料讲述,这里不再多说,下面我们运行托管程序来看看究竟会发生什么?
出现了一个异常信息,提示我们同一协定中不允许出现两个相同的操作。然后提示我们可以通过OperationContractAttribute的Name属性更改方法在客户端显示的名称。好,我们按照它说的做,通过Name更改操作的名称。
1 using System;
2 using System.Collections.Generic;
3 using System.Linq;
4 using System.Text;
5 using System.ServiceModel;
6
7 namespace Chinaer.WcfDemo.Contracts
8 {
9 [ServiceContract]
10 public interface ICalculator
11 {
12 [OperationContract(Name = "AddInt")]
13 int Add(int args1, int args2);
14
15 [OperationContract(Name = "AddDouble")]
16 double Add(double args1, double args2);
17 }
18 }
我们更改了OperationContractAttribute的Name属性的值,让她们可以在客户端显示不同的名称。
1 public int AddInt(int args1, int args2) {
2 return base.Channel.AddInt(args1, args2);
3 }
4
5 public double AddDouble(double args1, double args2) {
6 return base.Channel.AddDouble(args1, args2);
7 }
我们可以看到在客户端,调用服务的操作名称已经更改了。
再次运行托管程序,就可以正常的启动服务。这就告诉我们在wcf中不存在方法重载这一说,当然这有个前提是在wcf服务中。
回归主题,今天的主题是wcf的操作重载,也就是说我们要实现wcf的方法重载,既然wcf本身不提供,我们自己就要想办法来通过改动来实现它。
wcf的操作重载分为服务重载和客户端重载两种方式。
刚才说到的就是服务端的重载,通过OperationContract的Name属性重命名方法在客户端显示的名称。现在我们来看客户端重载,首先我们不通过添加服务引用的方式,我们通过svcutil.exe实用工具生成客户端的代理类。
中间的地址是服务元数据的地址,/out:proxy.cs 表示最后输出的文件,/noconfig 表示不声称配置文件,我们一般不会让svcutil来帮助我们生成配置文件,因为它会像添加了服务引用一样,把我们的配置文件搞的很乱,并且会为我们添加很多的默认值,这可能不是我们想要的结果。
生成的代理类文件为:
1 //------------------------------------------------------------------------------
2 // <auto-generated>
3 // 此代码由工具生成。
4 // 运行时版本:4.0.30319.1
5 //
6 // 对此文件的更改可能会导致不正确的行为,并且如果
7 // 重新生成代码,这些更改将会丢失。
8 // </auto-generated>
9 //------------------------------------------------------------------------------
10
11
12
13 [System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "4.0.0.0")]
14 [System.ServiceModel.ServiceContractAttribute(ConfigurationName="ICalculator")]
15 public interface ICalculator
16 {
17
18 [System.ServiceModel.OperationContractAttribute(Action="http://tempuri.org/ICalculator/AddInt", ReplyAction="http://tempuri.org/ICalculator/AddIntResponse")]
19 int AddInt(int args1, int args2);
20
21 [System.ServiceModel.OperationContractAttribute(Action="http://tempuri.org/ICalculator/AddDouble", ReplyAction="http://tempuri.org/ICalculator/AddDoubleResponse")]
22 double AddDouble(double args1, double args2);
23 }
24
25 [System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "4.0.0.0")]
26 public interface ICalculatorChannel : ICalculator, System.ServiceModel.IClientChannel
27 {
28 }
29
30 [System.Diagnostics.DebuggerStepThroughAttribute()]
31 [System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "4.0.0.0")]
32 public partial class CalculatorClient : System.ServiceModel.ClientBase<ICalculator>, ICalculator
33 {
34
35 public CalculatorClient()
36 {
37 }
38
39 public CalculatorClient(string endpointConfigurationName) :
40 base(endpointConfigurationName)
41 {
42 }
43
44 public CalculatorClient(string endpointConfigurationName, string remoteAddress) :
45 base(endpointConfigurationName, remoteAddress)
46 {
47 }
48
49 public CalculatorClient(string endpointConfigurationName, System.ServiceModel.EndpointAddress remoteAddress) :
50 base(endpointConfigurationName, remoteAddress)
51 {
52 }
53
54 public CalculatorClient(System.ServiceModel.Channels.Binding binding, System.ServiceModel.EndpointAddress remoteAddress) :
55 base(binding, remoteAddress)
56 {
57 }
58
59 public int AddInt(int args1, int args2)
60 {
61 return base.Channel.AddInt(args1, args2);
62 }
63
64 public double AddDouble(double args1, double args2)
65 {
66 return base.Channel.AddDouble(args1, args2);
67 }
68 }
请注意代理类中的我加红色的那部分方法名,很熟悉吧,不错,那就是我们通过Name属性更改后的方法名,在客户端生成的代理类中,也是和服务端方法保持了一致。那么我们如何让客户端代理实现方法重载呢?其实我们有了本地的代理类,要实现方法重载可以说是非常之简单,我们可以更改方法名,让它们相同就可以实现。
1 //------------------------------------------------------------------------------
2 // <auto-generated>
3 // 此代码由工具生成。
4 // 运行时版本:4.0.30319.1
5 //
6 // 对此文件的更改可能会导致不正确的行为,并且如果
7 // 重新生成代码,这些更改将会丢失。
8 // </auto-generated>
9 //------------------------------------------------------------------------------
10
11 namespace WcfDemo.ConsoleClient
12 {
13
14 [System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "4.0.0.0")]
15 [System.ServiceModel.ServiceContractAttribute(ConfigurationName = "ICalculator")]
16 public interface ICalculator
17 {
18
19 [System.ServiceModel.OperationContractAttribute(Action = "http://tempuri.org/ICalculator/AddInt", Name="AddInt" ,ReplyAction = "http://tempuri.org/ICalculator/AddIntResponse")]
20 int Add(int args1, int args2);
21
22 [System.ServiceModel.OperationContractAttribute(Action = "http://tempuri.org/ICalculator/AddDouble",Name="AddDouble" ,ReplyAction = "http://tempuri.org/ICalculator/AddDoubleResponse")]
23 double Add(double args1, double args2);
24 }
25
26 [System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "4.0.0.0")]
27 public interface ICalculatorChannel : ICalculator, System.ServiceModel.IClientChannel
28 {
29 }
30
31 [System.Diagnostics.DebuggerStepThroughAttribute()]
32 [System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "4.0.0.0")]
33 public partial class CalculatorClient : System.ServiceModel.ClientBase<ICalculator>, ICalculator
34 {
35
36 public CalculatorClient()
37 {
38 }
39
40 public CalculatorClient(string endpointConfigurationName) :
41 base(endpointConfigurationName)
42 {
43 }
44
45 public CalculatorClient(string endpointConfigurationName, string remoteAddress) :
46 base(endpointConfigurationName, remoteAddress)
47 {
48 }
49
50 public CalculatorClient(string endpointConfigurationName, System.ServiceModel.EndpointAddress remoteAddress) :
51 base(endpointConfigurationName, remoteAddress)
52 {
53 }
54
55 public CalculatorClient(System.ServiceModel.Channels.Binding binding, System.ServiceModel.EndpointAddress remoteAddress) :
56 base(binding, remoteAddress)
57 {
58 }
59
60
61
62 public int Add(int args1, int args2)
63 {
64 return base.Channel.Add(args1, args2);
65 }
66
67 public double Add(double args1, double args2)
68 {
69 return base.Channel.Add(args1, args2);
70 }
71 }
72 }
其实客户端的重载我们也只是修改了方法的名称,但是我们仍然添加了Name属性的值用来区分不同方法的调用,这也是服务端的重载功能的体现。
客户端重载调用成功。
课后总结:
- 服务端重载的实际意义不大,因为不能在客户端调用的时候真正实现重载。
- 客户端的重载还有点实际作用,最起码方便客户端的方法调用功能,但是要实现客户端的重载相比直接添加服务引用,我们的工作量还是显著的增加,并且客户端重载还是记得要添加客户端接口OperationContractAttribute的Name属性的值。
- 客户端重载还有另外一个好处,其实这个应该算不上重载的好处,就是我们直接生成的代理类,可以对代理做更多的控制操作,例如添加默认值等。
wcf的操作重载属于服务契约的范畴,虽然说意义不大,但是我们可以对比.Net中的方法重载来体会其中的不同点以及原因。其实原因应该是wsdl不支持操作重载的缘故。