[zhuan]服务契约与操作重载

本节我们继续学习WCF分布式开发步步为赢的第(5)节:服务契约与操作重载。这里我们首先讲解OOP面向对象的编程中方法重载,重载的意义,WCF服务编程开发如何实现操作重载,随后是代码分析部分,给出了服务端服务契约定义和实现操作重载的注意的问题和实现过程,然后详细介绍了客户端实现操作重载的方式。最后是本文的总结部分。本节的结构是:【1】重载概念【2】操作重载【3】代码实现分析【4】运行结果【5】总结

【1】重载概念:

【1.1】什么是重载(OverLoad):

所谓重载是指同一个方法名可以对应着多个方法的实现。这些方法的名字相同,但是方法的参数的类型不同。这就是方法重载的概念。函数方法类和对象的应用尤其重要。

 方法重载要求编译器能够唯一地确定调用一个方法时应执行哪个方法代码,即采用哪个方法实现。确定方法实现时,要求从方法参数的个数和类型上来区分。这就是说,进行方法重载时,要求同名方法在参数个数上不同,或者参数类型上不同。否则,将无法实现重载。ITPUB个人空间/N.A?'l%cl Vff
关于重载一定要注意:重载方法的参数类型和参数个数一定要不同(即:要么参数的类型不同,要么参数的个数不同,要么参数的类型和个数都不同),否则,编译器就不知道该调用那个方法了。

方法重载的好处就是相同的方法,带来不同的结果和实现,这里我们可以根据传递参数的不同来决定调用飞方法。这是编译时多态的一种实现机制。

【1.2】C#类方法重载示例:

我们这里给出一个简单的c#语言实现的方法重载的列子,这里对于SayHelloOverLoading方法,同一个类里给出的三个方法的参数个数不同。内部实现也不同。具体代码如下:

//3.面向对象里的类,如何实现操作重载,和WCF服务类里的操作重载做对比ITPUB个人空间x\%lDe7n q |5v
public class ClassOverLoadingITPUB个人空间#Z,}'~ |oUX
{ITPUB个人空间2J.bt8A[O%E
public ClassOverLoading()
&^9sQVZW4s.t5B,{3inL14461632 { ITPUB个人空间Q+|A0? N)Nom^
ITPUB个人空间e~ x3B Hy8L?%a0xd
}ITPUB个人空间l D lE/r?
//掩饰方法重载,分别实现三个方法,C#等面向对象的语言提供了方法重载机制的支持。
'w#B7T8u;eb#Ua14461632
public string SayHelloOverLoading()ITPUB个人空间$xDU8~~1Xg
{ ITPUB个人空间 Pt_i|-iyv
//编写代码ITPUB个人空间0X1M&R m*C@I%R
return "Hello,This an C# class overloading demo";ITPUB个人空间YY-?3Q_2o E
}ITPUB个人空间c n\+h*C)^6}P
//类里的方法重载不需要别名
#y.y(?\k3t\14461632
public string SayHelloOverLoading(string name)ITPUB个人空间 V7|l2S5W!j S8N6ZQ]
{ITPUB个人空间B3k5E0TW [2b,[!HO
//编写代码ITPUB个人空间e7rEq,x
return "Hello:" + name + "This an C# class overloading demo";ITPUB个人空间1hz;Yx.dUqW
}ITPUB个人空间m2g@zka

)O5z c0f9goE"B2L[7|14461632
public string SayHelloOverLoading(string firstName, string lastName)
1DP [ON+IS14461632
RXA R!z;`l"z2Os14461632 {
,F {5GApj14461632
//编写代码
k{D6Iq0Y@9x14461632
return "Hello:" + firstName + lastName + "This an C# class overloading demo";
tav'j.IE'xF a_14461632 }
2LDQ w X#| @H14461632ITPUB个人空间a^N~.}\XfY
}

【2】操作重载:

【2.1】操作重载:

WCF服务支持核心的Web 服务协议,同样其元数据交换也是基于XML语言描述,客户端通过WSDL文件来了解服务方法相关的信息,包括参数的个数、类型、返回值、调用顺序等重要信息。由于WSDL不支持方法的重载,因此我们的WCF服务操作重载就无法通过WSDL暴露给客户端。如果我们在服务契约里定义了方法的重载,编译可以正常通过,但是启动服务宿主就会抛出System.InvalidOperationException异常,如下图:

因此我们不能在WCF服务类了定义和实现方法重载,否则无法暴露为服务操作。

【2.2】解决办法:

WCF给我们提供了一个解决办法,让我们可以在WCF服务类里使用服务操作的重载。WCF定义了一个机制OperationContract,使用OperationContract特性的Name属性,为操作指定别名:

[AttributeUsage(AttributeTargets.Method)]ITPUB个人空间(q4p a+v#jF U(_
public sealed class OperationContractAttribute : AttributeITPUB个人空间g%B;r[y|1c
{ITPUB个人空间8TN7blQY
public string NameITPUB个人空间j2ZBp*{&{ MgN2rp
{
get;set;}
$VX#IW#| e14461632
(}CSI*C1Ge2ZAT X14461632
//更多成员
'{ lB5lz5Q!Q c9nS14461632
ITPUB个人空间i XB i}
}
CUQBL8_jM!A7u)C14461632
B1oam0~8DDy)~14461632

【3】代码实现分析:

下面我们来给出一个具体的WCF服务实现操作重载,包括服务定义、宿主配置、客户端引用和测试的完整过程。

【3.1】服务契约:

定义了服务契约IWCFOverLoadingService,分别给出SayHelloOverLoading操作契约的3种不同定义和WCFService服务类里的实现。具体代码如下:

//1.服务契约,操作契约重载
']?9? e B?X14461632
[ServiceContract(Namespace = "http://www.cnblogs.com/frank_xl/")]
5P!Tpf(d7|n*YFe14461632
public interface IWCFOverLoadingService
*}d+c"@| D4Ek14461632 {ITPUB个人空间MqTE"i%^-mlLi c
//操作契约
*|2eIwl$A14461632
[OperationContract(Name = "SayHelloOverLoading1")]
em~T0s&o,| O&y14461632
string SayHelloOverLoading();
ta,Jd7Y+^14461632
//操作契约ITPUB个人空间 W8S:O}}%^^k
[OperationContract(Name = "SayHelloOverLoading2")]ITPUB个人空间7jgxx b
string SayHelloOverLoading(string name);ITPUB个人空间,C F6oNJ9Bm.W2V
//操作契约
)f'`'}Cj+wt@C9C14461632
[OperationContract(Name = "SayHelloOverLoading3")]ITPUB个人空间Y4xdnG!Zjp
string SayHelloOverLoading(string firstName, string lastName);ITPUB个人空间%r%L(I5AE Fn
ITPUB个人空间|2Z&V%N3SL]'x
}ITPUB个人空间V.\J8}B_V
//2.服务类,集成接口。实现契约ITPUB个人空间v9tR0D)j W
public class WCFService : IWCFOverLoadingService
$g QE'X:@|iW:k14461632 {
w^n^:b/W14461632
//实现接口定义的方法ITPUB个人空间A DF1nwe1m
public string SayHelloOverLoading()
!tH)D5Q:Va7v14461632 {
{4I grBd0L/?,i14461632 Console.WriteLine(
"Hello! ,This an overloading demo for WCF Service ");ITPUB个人空间#A}1\P~M+r}[
return "Hello! This an overloading demo for WCF Service ";
T"X'T|X^G6g;Z JD14461632 }
uPa#x lD%fl Dd14461632
//实现接口定义的方法ITPUB个人空间 b2C5U @x)GK
public string SayHelloOverLoading(string name)
7FE^-WM14461632 {ITPUB个人空间N$ve!|v'E Q(n8AG
Console.WriteLine(
"Hello! {0},This an overloading demo WCF Service ", name);
VWS g j2Lb7l0F14461632
return "Hello! " + name + ", This an overloading demo for WCF Service ";
W#O0Z%agVBK14461632 }ITPUB个人空间$J }!dI5\+h*K NNu
//实现接口定义的方法
jBO_;W'I!bDm0^14461632
public string SayHelloOverLoading(string firstName, string lastName)ITPUB个人空间a7\ o)^+FH'Vh
{ITPUB个人空间Ff}G:K^6evpSs
Console.WriteLine(
"Hello! {0} {1},This an overloading demo WCF Service", firstName, lastName);
yy,|/q3[Nk14461632
return "Hello! " + firstName + " " + lastName + ", This an overloading demo for WCF Service "; ;ITPUB个人空间;Ch4ek8C
}ITPUB个人空间QFr-lR'vP;Uv;c
}

【3.2】托管宿主:

自定义托管宿主使用配置文件来定义服务的终结点和元数据交换节点,服务的交换行为等其他属性也在配置文件里给出,我们配置了三种不同的数据服务通信方式,分别是http、tcp、IPC.具体配置信息如下:

<services>
*q o,M9E P14461632
<service behaviorConfiguration="WCFService.WCFServiceBehavior" name="WCFService.WCFService">
%`8GK&C A&P l-V14461632
<endpointITPUB个人空间(n#C)pC8aSz b$e
address
="http://localhost:9001/WCFService"ITPUB个人空间C`3hD&f#E6z
binding
="wsHttpBinding"
wUAnjBI&r(Vo14461632 contract
="WCFService.IWCFOverLoadingService">
K1E5L eN!@3e%JE0Q14461632
</endpoint>
7@$b`fp"l!Ar5L%OZ~#[14461632
<endpointITPUB个人空间,RCYr6M!y3RY
address
="net.tcp://localhost:9002/WCFService"
6AMQ'u0L3L-ov8EGA)u14461632 binding
="netTcpBinding"ITPUB个人空间)wV Ux2g ts7q
contract
="WCFService.IWCFOverLoadingService">ITPUB个人空间X!B?m3Y}$y}'w
</endpoint>ITPUB个人空间-{N"_d']6rGU/y
<endpointITPUB个人空间uoauw#H9{T1}
address
="net.pipe://localhost/WCFService"ITPUB个人空间3ANu%FLHM
binding
="netNamedPipeBinding"ITPUB个人空间6@Z.w w:jt6f
contract
="WCFService.IWCFOverLoadingService">
^eo#fKV3?3f14461632
</endpoint>ITPUB个人空间f/L n/a3R.^v E
<endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />ITPUB个人空间[%gx6ud g
<endpoint address="mex" binding="mexTcpBinding" contract="IMetadataExchange" />
]K"Xx"k3neU14461632
<endpoint address="mex" binding="mexNamedPipeBinding" contract="IMetadataExchange" />ITPUB个人空间)j5X _u:o{&x
<host>ITPUB个人空间U%X3MJ _2J'Bb
<baseAddresses>
5c?6e#O l~14461632
<add baseAddress="http://localhost:9001/"/>ITPUB个人空间5Ab S!s8\!o4U[
<add baseAddress="net.tcp://localhost:9002/"/>ITPUB个人空间~v;P%K-}} ^
<add baseAddress="net.pipe://localhost/"/>
4X4hEppa^UcV~14461632
</baseAddresses>
0g {NA|5M[fL7[|14461632
</host>ITPUB个人空间L {G4\1c
</service>
I m*W1f$ZxhgD ~[14461632
</services>ITPUB个人空间q7],B!H}'dpx7j
<behaviors>
.UG6t#r}14461632
<serviceBehaviors>ITPUB个人空间Y#Xg8~.{^?
<behavior name="WCFService.WCFServiceBehavior">
$A*aP,t&ud G5c14461632
<serviceMetadata httpGetEnabled="true" />ITPUB个人空间9l~:E,tF@Da
<serviceDebug includeExceptionDetailInFaults="false" />ITPUB个人空间^nQu-yv[
</behavior>ITPUB个人空间gx%s'Z?T$wf R
</serviceBehaviors>ITPUB个人空间It+q(D]O6S
</behaviors>

【3.3】客户端服务引用:

我们来分别添加对服务端的引用,首先启动托管宿主程序。然后使用Visual Studio2008工具直接添加服务引用,你也可以使用svcUtil.exe工具,如图所示:

客户端输入服务的基地址,查找服务,成功够我们可以看到服务契约的信息,这里显示的3个操作名称已经不同,实际上这里给出的是三个不同名称的服务方法。输入命名空间,确定即可完成。

【3.4】代理代码:

客户端反序列化生成的服务契约等信息,我们查看操作契约对应的客户端方法名称以及改变,这样一来,客户端就没有实现对应的方法重载,也就不能使用重载带来的优势,也即是编译时多态的特性。手动更改客户端服务代理类和服务契约代码,使之支持操作方法重载,代码如下:

public interface IWCFOverLoadingService {ITPUB个人空间 h8WjzS

J1qX'P#T(X14461632 [System.ServiceModel.OperationContractAttribute(Name
= "SayHelloOverLoading1" ,Action="http://www.cnblogs.com/frank_xl/IWCFOverLoadingService/SayHelloOverLoading1", ReplyAction="http://www.cnblogs.com/frank_xl/IWCFOverLoadingService/SayHelloOverLoading1Respon" +ITPUB个人空间&F-^(k#T4d)o-}J
"se")]
`.F(P/t(|rav14461632
string SayHelloOverLoading();ITPUB个人空间JG2koj
ITPUB个人空间ob vDx]
[System.ServiceModel.OperationContractAttribute(Name
= "SayHelloOverLoading2", Action = "http://www.cnblogs.com/frank_xl/IWCFOverLoadingService/SayHelloOverLoading2", ReplyAction = "http://www.cnblogs.com/frank_xl/IWCFOverLoadingService/SayHelloOverLoading2Respon" +ITPUB个人空间g,@R*b#QqdD#Q0Igu*_
"se")]ITPUB个人空间X L^+b lS m*H+X
string SayHelloOverLoading(string name);
f-{eN"z3ue J14461632ITPUB个人空间L]f gk};cV
[System.ServiceModel.OperationContractAttribute(Name
= "SayHelloOverLoading3", Action = "http://www.cnblogs.com/frank_xl/IWCFOverLoadingService/SayHelloOverLoading3", ReplyAction = "http://www.cnblogs.com/frank_xl/IWCFOverLoadingService/SayHelloOverLoading3Respon" +ITPUB个人空间(Gd3fah
"se")]ITPUB个人空间2w [as R'R0T
string SayHelloOverLoading(string firstName, string lastName);ITPUB个人空间3q"V7tk0KFeW
}
6H4]gmS*Ge4l14461632

这样我们客户端方法也支持操作方法的重载特性。

【3.5】客户端测试代码:

为了测试操作契约,我们在客户端应用里添加了部分的测试代码,这里为了测试服务端定义的不同的操作。我们分组按照协议给出了测试的代码:

//实例化客户端服务代理TcpITPUB个人空间0xqB1O)yG:N N^
ServiceOverLoadingTcp.WCFOverLoadingServiceClient wcfServiceProxyTcp =ITPUB个人空间-p k3{lfo n1u)}
new ServiceOverLoadingTcp.WCFOverLoadingServiceClient("WSHttpBinding_IWCFOverLoadingService1");
.\t[-]5@b14461632 Console.WriteLine(
"Test call service using TCP--------------------.");ITPUB个人空间y.a0r]Z;{mL"~
//通过代理调用SayHelloOverLoading服务,分别传递不同的参数,进行测试ITPUB个人空间9i"@-u `q
Console.WriteLine(wcfServiceProxyTcp.SayHelloOverLoading());
FQ,VKU%r%~.c14461632 Console.WriteLine(wcfServiceProxyTcp.SayHelloOverLoading(
"Frank Xu Lei"));
F3JeLLfG14461632 Console.WriteLine(wcfServiceProxyTcp.SayHelloOverLoading(
"Lei", "Xu"));ITPUB个人空间CK.E^RV'e

{%`(|qUb4Z,f14461632
//实例化客户端服务代理HttpITPUB个人空间:}Y ~.H5?+{3}6DC0q K{
ServiceOverLoadingHttp.WCFOverLoadingServiceClient wcfServiceProxyHttp =
3Dl @;A:U!kZ/z:D14461632
new ServiceOverLoadingHttp.WCFOverLoadingServiceClient("NetTcpBinding_IWCFOverLoadingService");
&OIT v:IT14461632 Console.WriteLine(
"Test call service using Http-------------------");
#]z3@W+C-G-[14461632
//通过代理调用SayHelloOverLoading服务,分别传递不同的参数,进行测试ITPUB个人空间Fan/ofFY
Console.WriteLine(wcfServiceProxyHttp.SayHelloOverLoading());ITPUB个人空间|QaV5M
Console.WriteLine(wcfServiceProxyHttp.SayHelloOverLoading(
"Frank Xu Lei"));ITPUB个人空间O-X/{'xB1Ij&M
Console.WriteLine(wcfServiceProxyHttp.SayHelloOverLoading(
"Lei", "Xu"));ITPUB个人空间)Sb8b8Xi)I Bh
ITPUB个人空间'RkX^*VZnSOu
//DebugingITPUB个人空间.K"V|s!Qr#N{
Console.WriteLine("Press any key to continue");ITPUB个人空间*u0H8l#o4C
Console.Read();

【4】运行结果:

这里分别调用三种服务操作,进行测试。运行的结果如图所示:

【5】总结:

以上就是本节对WCF服务操作重载的介绍,包括一般重载的基本定义和c#语言中简单的方法重载的实现。然后介绍了WCF操作重载的实现机制、局限性和解决办法,服务契约默认不支持操作方法重载,我们可以利用WCF已有的机制给出方法的别名来解决这个问题。然后给出了包括客户端等完整的测试解决方案,客户端反序列话生成服务类默认不支持服务操作方法重载的,生成的也是服务操作的别名方法。我们在客户端要想使服务代理类支持重载,以利用重载的优势,就需要重新修改客户端服务代理代码。 另外给出本节的实例代码供大家参考:

/Files/frank_xl/WCFServiceOverLoadFrankXuLei.rar

参考资料:

1.《函数重载》:http://baike.baidu.com/view/534068.htm

原文地址:https://www.cnblogs.com/itelite/p/2014833.html