Artech

Develop every application as an art using the most suitable technologies!

常用链接

统计

积分与排名

网上邻居

我的博文系列

最新评论

[原创]我的WCF之旅(5):面向服务架构(SOA)和面向对象编程(OOP)的结合——如何实现Service Contract的重载(Overloading)

对于.NET重载(Overloading)——定义不同参数列表的同名方法(顺便提一下,我们但可以在参数列表上重载方法,我们甚至可以在返回类型层面来重载我们需要的方法——页就是说,我们可以定义两个具有相同参数列表但不同返回值类型的两个同名的方法。不过这种广义的Overloading不被我们主流的.NET 语言所支持的——C#, VB.NET, 但是对于IL来说,这这种基于返回值类型的Overloading是支持的)。相信大家听得耳朵都要起老茧了。我想大家也清楚在编写传统的XML Web Service的时候,Overloading是不被支持的。

原因很简单,当我们用某种支持.NET的高级语言写成的程序被相应的编译器编译成Assembly的过程中,不单单是我们的Source Code会被变成IL Code,在Assembly中还会生成相应的原数据Metadata——这些Metadata 可以被看看是一张张的Table。这些Table存储了定义了主要3个方面的信息——构成这个Assembly文件的信息;在Assembly中定义的Type及其相关成员的信息;本引用的Assembly 及Type的信息。这些完备的Metadata成就了Assembly的自描述性(Self-Describing),也只是有了这些Metadata,使.NET可以很容易地根据方法参数的列表甚至是返回值得类型来判断调用的究竟了那个方法。

而对于XML Web Service,它的标准实际上是基于XML的,近一步说,一个XML Web Service是通过一个一段XML来描述的,而这个描述XML Web Service的XML,我们称之为WSDL(Web Service Description Language)。在WSDL中,Web Service的一个方法(Method)对应的是一个操作(Operation),Web Service 所有的Operation定义在WSDL中的portType Section。我们可以参照下面一段XML,它是从一个完整的WSDL中截取下来的。我们可以看到,portType包含了Web Service定义的所有Operation,每个Operation由一个operation XML Element表示。看过我前面Blog的读者应该知道,从消息交换(Message Exchange)的层面上讲,一个Operation实际上体现的是一种消息交换的模式(Message Exchange Pattern——MEP)。所以我们完全可以通过一定消息交换的输入消息(Input Message)和输出(Output Message )定义一个Operation。而WSDL也是这样做的。(这里顺便提一下,Output Message部仅仅对应一个方法的Return Value,还包括表明ref 和out的Parameter)。除了定义进行消息交互的Message的格式(一般通过XSD)之外,每个Operation还应该具有一个能够为一标识该Operation的ID,这个ID通过name XML Attribute来定义。通常的情况下,Operation的Name使用Web Service的方法名——这就是在传统XML Web Service不可以使用Overloading的原因。

<wsdl:portType name="ICalculator">
  
<wsdl:operation name="AddWithTwoOperands">
    
<wsdl:input wsaw:Action="http://tempuri.org/ICalculator/AddWithTwoOperands" message="tns:ICalculator_AddWithTwoOperands_InputMessage" />
    
<wsdl:output wsaw:Action="http://tempuri.org/ICalculator/AddWithTwoOperandsResponse" message="tns:ICalculator_AddWithTwoOperands_OutputMessage" />
  
</wsdl:operation>
  
<wsdl:operation name="AddWithThreeOperands">
    
<wsdl:input wsaw:Action="http://tempuri.org/ICalculator/AddWithThreeOperands" message="tns:ICalculator_AddWithThreeOperands_InputMessage" />
    
<wsdl:output wsaw:Action="http://tempuri.org/ICalculator/AddWithThreeOperandsResponse" message="tns:ICalculator_AddWithThreeOperands_OutputMessage" />
  
</wsdl:operation>
</wsdl:portType>

和XML Web Service,WCF也面临一样的问题——我觉得我们可以把WCF看成.NET平台下新一代的Web Service。虽然现有XML Web Service现在具有广泛的使用——尤其在构建跨平台性的分布是应用和进行系统集成上面,但是从Microsoft已经明确提出WSE 3.0将是最后一个Version的WSE,所以,现有的Web Service将会全面的过渡到WCF。WCF到底是什么东西,我在前面的文章中不断地提出这个问题,在这里我们从 另外一个方面来看待WCF。我们知道W3C定义了一系列关于WS的规范Specification,成为WS-* Specification。这一系列的Specification定义了建立在XML和SOAP标准之上的基于如何将一个可互操作系统(Interoperable System)的各个方面的标准,比如WS-Messaging,WS-Security,WS-Transaction等等。而WCF则可以看成是这一整套Specification的实现。但是这种实现最终还是落实到我们.NET编程上。我们可以把WS-Specification和我们的基于.NET语言的编程看成是两种截然不同的编程模型(Programming Model)。WCF的功能则是把这两种不同的编程模型统一起来,实现他们之间的一个Mapping——可以把WCF看成一个Adapter。

回到我们的Overloading上面来,Overloading是.NET Framework原生支持的。通过Overloading,我们可以使用同名的方法来定义不同的操作,从而使我们的Code显得更加优雅(Elegant)。要是Overloading在WCF中可以使用,WCF必须提供这样的一个Mapping——是被重载的具有相同方法的的方法Mapping到不同的Operation上。而提供着一个功能的就是ServiceContract。下面我们来结合一个Sample来看如何在WCF中使用Overloading。

沿用我们的Calculator的应用,现在我们做一个加法器,它具有两个Operation——两书相加和三数相加。这两个方法都用一个名称Add。

1.下面是Solution的结构。不像前面的结构,这这里我们没有把Service Contract单独提取出来,供Client和Service供用。因为我们现在模拟的是,Service完全由一个外部的第三方提供,Service 已经确定,不能根据Client的具体要求来修改Service。Source Code从这里下载。


2.Service端的Code:

Service Contract: Artech.OverloadableContract.Service ICalculator.cs.

using System;
using System.Collections.Generic;
using System.Text;
using System.ServiceModel;

namespace Artech.OverloadableContract.Service
{
    [ServiceContract]
   
public interface ICalculator
    
{
        [OperationContract(Name 
= "AddWithTwoOperands")]
       
double Add(double x, double y);

       [OperationContract(Name 
= "AddWithThreeOperands")]
       
double Add(double x, double y, double z);
    }

}

这个Service Contract定义了Overloading的两个Add方法,为了把这两个方法映射到两个不同的Operation,我们通过System.ServiceModel.OperationAttribute 的Name属性为Operation指定一个Name——AddWithTwoOperands 和AddWithThreeOperands。

下面是Service的Code,简单地实现了Service Conract,无须赘言。

using System;
using System.Collections.Generic;
using System.Text;

namespace Artech.OverloadableContract.Service
{
    
public class CalculatorService:ICalculator
    
{
        
ICalculator Members
    }

}

3.Hosting Service

App.config

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
    
<system.serviceModel>
      
        
<behaviors>
            
<serviceBehaviors>
                
<behavior name="calculatorServiceBehavior">
                    
<serviceMetadata httpGetEnabled="true" />
                
</behavior>
            
</serviceBehaviors>
        
</behaviors>
      
        
<services>
            
<service behaviorConfiguration="calculatorServiceBehavior" name="Artech.OverloadableContract.Service.CalculatorService">
                
<endpoint binding="basicHttpBinding" contract="Artech.OverloadableContract.Service.ICalculator" />
                
<host>
                    
<baseAddresses>
                        
<add baseAddress="http://localhost:1234/calcuator" />
                    
</baseAddresses>
                
</host>
            
</service>
        
</services>
    
</system.serviceModel>
</configuration>

Program.cs

using System;
using System.Collections.Generic;
using System.Text;
using System.ServiceModel;
using Artech.OverloadableContract.Service;

namespace Artech.OverloadableContract.Hosting
{
    
class Program
    
{
        
static void Main(string[] args)
        
{
            
using (ServiceHost host = new ServiceHost(typeof(CalculatorService)))
            
{
                host.Open();
                Console.WriteLine(
"Calculator service has begun to listen ");
                Console.Read();
            }

        }

    }

}

相关的已经在前面的文章中说过,代码很简单,没有什么好说的。

现在我们来启动这个Host,在IE中通过键入这个地址http://localhost:1234/calcuator?wsdl看看生成的WSDL是什么样子。

通过截图我们可以看到,在WSDL的portType Section,两个Operation的Name已经成功地变成了我们在OperationContract Attrbute中指定的那样。


4.接下来我们为Client端添加一个Server Reference。就像在使用XML Web Service中添加Web Reference一样,添加Server Reference会为Client添加相应的客户端代码——倒入的Service Contract,继承自ClientBase<T>的Proxy Class, 和相应的Confugration。下面我们来分析这些通过添加Service Reference而生成的Code。

Imported Service Contract:

[System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel""3.0.0.0")]
    [System.ServiceModel.ServiceContractAttribute(ConfigurationName
="Artech.OverloadableContract.Client.CalculatorService.ICalculator")]
    
public interface ICalculator
    
{
        
        [System.ServiceModel.OperationContractAttribute(Action
="http://tempuri.org/ICalculator/AddWithTwoOperands", ReplyAction="http://tempuri.org/ICalculator/AddWithTwoOperandsResponse")]
        
double AddWithTwoOperands(double x, double y);
        
        [System.ServiceModel.OperationContractAttribute(Action
="http://tempuri.org/ICalculator/AddWithThreeOperands", ReplyAction="http://tempuri.org/ICalculator/AddWithThreeOperandsResponse")]
        
double AddWithThreeOperands(double x, double y, double z);
    }

    
    [System.CodeDom.Compiler.GeneratedCodeAttribute(
"System.ServiceModel""3.0.0.0")]
    
public interface ICalculatorChannel : Artech.OverloadableContract.Client.CalculatorService.ICalculator, System.ServiceModel.IClientChannel
    
{
}

我们可以看到这个Service Contract已经不是Service端的Contract了,Overloading方法已经被换成了与Oper阿tion Name相匹配的方法了。我们再看看Proxy Class:

[System.Diagnostics.DebuggerStepThroughAttribute()]
    [System.CodeDom.Compiler.GeneratedCodeAttribute(
"System.ServiceModel""3.0.0.0")]
    
public partial class CalculatorClient : System.ServiceModel.ClientBase<Artech.OverloadableContract.Client.CalculatorService.ICalculator>, Artech.OverloadableContract.Client.CalculatorService.ICalculator
    
{
        
        
public CalculatorClient()
        
{
        }

        
        
public CalculatorClient(string endpointConfigurationName) : 
                
base(endpointConfigurationName)
        
{
        }

        
        
public CalculatorClient(string endpointConfigurationName, string remoteAddress) : 
                
base(endpointConfigurationName, remoteAddress)
        
{
        }

        
        
public CalculatorClient(string endpointConfigurationName, System.ServiceModel.EndpointAddress remoteAddress) : 
                
base(endpointConfigurationName, remoteAddress)
        
{
        }

        
        
public CalculatorClient(System.ServiceModel.Channels.Binding binding, System.ServiceModel.EndpointAddress remoteAddress) : 
                
base(binding, remoteAddress)
        
{
        }

        
        
public double AddWithTwoOperands(double x, double y)
        
{
            
return base.Channel.AddWithTwoOperands(x, y);
        }

        
        
public double AddWithThreeOperands(double x, double y, double z)
        
{
            
return base.Channel.AddWithThreeOperands(x, y, z);
        }

}

实现了我们倒入的Service Contract并提供了相应的Constract,相关的也在前面的Blog提及,这里不用再多说什么了。现在我们毫无疑问,可以直接调用非重载的方法AddWithTwoOperands和AddWithThreeOperands来调用Calculator Service。但是我们需要的不是这样,我们需要的Overloading,在Service 我们实现以Overlaoding的方式提供Service,在Client端我们也希望以相同的方式来调用这个Service。下面我们来看怎么做:

在Client端,重写Service Contract,当然是一Overloading的方式,同时像在Service端一样,通过OperatonContract的Name属性为Operation 制定一个和Service完全匹配的Operation Name。

using System;
using System.Collections.Generic;
using System.Text;
using System.ServiceModel;

namespace Artech.OverloadableContract.Client
{
    [ServiceContract(Name 
= "ICalculator")]
   
public interface IMyCalculator
    
{
        [OperationContract(Name 
= "AddWithTwoOperands")]
       
double Add(double x, double y);

       [OperationContract(Name 
= "AddWithThreeOperands")]
       
double Add(double x, double y, double z);
    }

}

重写Proxy Class

using System;
using System.Collections.Generic;
using System.Text;
using System.ServiceModel;

namespace Artech.OverloadableContract.Client
{
    
class MyCalculatorClient:ClientBase<IMyCalculator>,IMyCalculator
    
{
        
IMyCalculator Members
    }

}

现在我们有两个Proxy Class,我们同时使用,看看他们会不会返回一样的结果:

using System;
using System.Collections.Generic;
using System.Text;
using Artech.OverloadableContract.Client.CalculatorService;

namespace Artech.OverloadableContract.Client
{
    
class Program
    
{
        
static void Main(string[] args)
        
{
            Console.WriteLine(
"Begin to invocate generated proxy");
            InvocateGeneratedProxy();
            Console.WriteLine(
"\nBegin to invocate revised proxy");
            InvocateGeneratedProxy();
Console.Read();
        }


        
static void InvocateGeneratedProxy()
        
{
            
using (CalculatorClient calculator = new CalculatorClient())
            

                Console.WriteLine(
"x + y = {2} where x = {0}and y = {1} ",1,2,calculator.AddWithTwoOperands(1,2));
                Console.WriteLine(
"x + y + z = {3} where x = {0}and y = {1} and z = {2}"123,calculator.AddWithThreeOperands(12,3));
            }

        }


        
static void InvocateRevisedProxy()
        
{
            
using (MyCalculatorClient calculator = new MyCalculatorClient())
            
{
                Console.WriteLine(
"x + y = {2} where x = {0}and y = {1} "12, calculator.Add(12));
                Console.WriteLine(
"x + y + z = {3} where x = {0}and y = {1} and z = {2}"123, calculator.Add(123));
            }

        }

    }

}

同时在加入下面简单的Configuration:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
    
<system.serviceModel>
        
<client>
            
<endpoint address="http://localhost:1234/calcuator" binding="basicHttpBinding"                 contract="Artech.OverloadableContract.Client.IMyCalculator" />
                  
</client>
    
</system.serviceModel>
</configuration>

 

运行Client,下面是Screen Shot,可见两个Proxy是等效的。



WCF相关内容:
[原创]我的WCF之旅(1):创建一个简单的WCF程序
[原创]我的WCF之旅(2):Endpoint Overview
[原创]我的WCF之旅(3):在WCF中实现双向通信(Bi-directional Communication)
[原创]我的WCF之旅(4):WCF中的序列化(Serialization)- Part I
[原创]我的WCF之旅(4):WCF中的序列化(Serialization)- Part II
[原创]我的WCF之旅(5):Service Contract中的重载(Overloading)
[原创]我的WCF之旅(6):在Winform Application中调用Duplex Service出现TimeoutException的原因和解决方案
[原创]我的WCF之旅(7):面向服务架构(SOA)和面向对象编程(OOP)的结合——如何实现Service Contract的继承
[原创]我的WCF之旅(8):WCF中的Session和Instancing Management
[原创]我的WCF之旅(9):如何在WCF中使用tcpTrace来进行Soap Trace
[原创]我的WCF之旅(10): 如何在WCF进行Exception Handling
[原创]我的WCF之旅(11):再谈WCF的双向通讯-基于Http的双向通讯 V.S. 基于TCP的双向通讯

[原创]我的WCF之旅(12):使用MSMQ进行Reliable Messaging
[原创]我的WCF之旅(13):创建基于MSMQ的Responsive Service

Tag标签: WCF,SOA,OOP,Overload

posted on 2007-03-19 02:25 Artech 阅读(8040) 评论(22)  编辑 收藏 网摘 所属分类: J. WCF

评论

#1楼 2007-03-19 10:14 idior      

文章还没有细看,提一下看法:
SOA强调的是Message Oriented。所以我们应该牢记是消息的交互,而不是远程方法调用。那么方法中的参数最好能封装成一个消息,这样消息的schema还可以不断的演化而不影响之前的应用。如果在方法中直接使用一个个的参数,则不利于方法的进化。
以Add为例:
version1.0 add two parameters
Add(AddParameter p)
{
//...
}

version2.0 add three parameters
Add(AddParameter p)
{
//...
}
这样Contract(在Schemea中应用一些技巧)并没有什么变化我们就可以支持使用三个参数,甚至更多。也就是说仅仅是服务端的变化,而客户端并无需做什么修改。

ASMX,WCF中支持overload,但是在正式的应用场合我们应该避免使用这一技术。除非你的应用是XML-RPC方式。
  回复  引用  查看    

#2楼[楼主] 2007-03-19 11:28 Artech      

@idior
谢谢idior的评论,这样的评论是我希望看到的,顺便提一下,我把你的Blog连接放在我的首页,但愿你不会介意。
不错SOA的一个主要的特征就是Message Oriented。但是我所理解面向Message主要是对于Application的交互层面来讲的。
由于Application的业务逻辑最终还是落实到用.NET语言写的Code。对于Developer来说,他们熟悉的是OO,他们最擅长的也是OO,他们最能理解的依然是OO。因为我们的C# ,VB.NET是基于OO而设计的。我们也不应该为了为了Message Oriented,而放弃OO那么好的编程模式,比如重载,继承,多态。
WCF实际上可以看成是在现有的.NET编程模型方面作了一个框架,它的目的是保持现有的编程模型不变的情况下,在上面做的一层封装,在他之上实现Message Oriented,实现松耦合。而我们不应该把Message Oriented贯穿到我们整个编成层次的始终。这样对于Developer来说是不公平的,也不应该,因为我觉得他们是可以不用意识到WCF的存在的。
如果单单想做到兼容,我们完全可以把Operation的输入和输出定义成一个个的Soap Message,WCF也支持这么做,但我觉得好的设计不应该如此。
  回复  引用  查看    

#3楼[楼主] 2007-03-19 15:07 Artech      

下一篇,我将介绍Service Contract中的继承。   回复  引用  查看    

#4楼 2007-03-19 19:08 idior      

@Artech
我觉得你对SOA的认识存在误区。SOA是用于解决什么问题的?我们为什么需要SOA?

其实在SOA中会大量的摒弃OO的一些特性,比如使用粗粒度的接口(这也就意味着多态的减少),使用无状态服务(对象竟然无状态,封装何在?)。
而产生这一现象的原因何在?——分布式。

不是说SOA不需要OO,其关键在于SOA中服务与服务之间的交互不OO,服务的接口也就是Contract不OO,但是服务的实现完全可以是由OO来做的。

你现在对Web Services的看法更接近于XML-RPC,而不是Message-Exchange。

你可以看一下我的这两篇文章,欢迎更多的讨论 :)
http://www.cnblogs.com/idior/archive/2004/12/13/76600.html">http://www.cnblogs.com/idior/archive/2004/12/13/76600.html
http://www.cnblogs.com/idior/articles/606938.html">http://www.cnblogs.com/idior/articles/606938.html

BTW 我也加了你的链接,希望多交流。
  回复  引用  查看    

#5楼[楼主] 2007-03-19 20:32 Artech      

@idior
我完全同意你说的,我想你对我的意思存在一定的误解。
无论是建立粗粒度的接口也好,还是使用状态的Service也好,都是为了一个目的,实现构成分布式系统的各个子系统的之间的松耦合,说白了就是使他们使他们之间共享的Contract更够实现最大程度的稳定。而Overloading是不会影响这个稳定性的,因为我们作的Overloading是所在的位置是在Contract之下。

可能对于WCF中的Contract,可能很多人理解为这个Interface,我的理解是,这个单纯的经过重载的Interface,并不是Contract,而真正的的Contract是这个Interface运用了ServiceContract Attribute之后它所具有的结构。而这个结构是和Overloading无关的。

SOA 确实在为我们搭建分布式应用提供了很好的解决方案,但是OO确是对现实的逼真的体现。我的意思是,既然WCF能在Applition之间能够为我们实现了基于SOA的功能,那么在单个的Application中间 ——无论是Service的提供者,还是Service的使用者,为什么不用基于OO这种在语义上更加具有可读性的编程方式呢。这和RPC和Message Exchange 没有关系。

BTW,读了你的文章,让我对Web Service有了较深的了解,我对Web Service了解得不是很深入。
  回复  引用  查看    

#6楼[楼主] 2007-03-19 20:51 Artech      

@idior
可能是我的Sample不是太好,现在只是两个数相加和三个数相加的情形,他也许很容易把操作数封装在一个特殊定义个类中。

但是对于很多重载来说,这种方式是行不通的,比如我进行两个数相加,两个向量相加,或者是两条狗,两只猫相加(呵呵)。在这种情况下,Overloading是很容易让人理解的解决方式。
  回复  引用  查看    

#7楼 2007-09-26 17:03 初学者_123[未注册用户]

程序有问题,调用得都是同一个函数
Console.WriteLine("Begin to invocate generated proxy");
InvocateGeneratedProxy();
Console.WriteLine("\nBegin to invocate revised proxy");
InvocateGeneratedProxy();
改为InvocateRevisedProxy(),这个函数出错!
  回复  引用    

#8楼 2007-09-26 18:10 初学者_123[未注册用户]

搞定了,要改几个地方
"public interface IMyCalculator"
这要改得和service contract一样才行.
"class MyCalculatorClient:ClientBase<IMyCalculator>,IMyCalculator"
这里<>中得interface也必须是service contract一样得名字.
并且配置也要改
"contract="Artech.OverloadableContract.Client.MyCalculatorClient" />"

改为Artech.OverloadableContract.Client.ICalculator
  回复  引用    

#9楼[楼主] 2007-09-26 23:57 Artech      

@初学者_123
很抱歉,当初由于粗心,把代码Copy错了。
其实我们的思路是这样的:Server通过ServierContractAttribute和ServiceOperationAttibute以Overload的方式定义了一个从本质上讲无法Overload的Contract。我们在Client段也需要通过ServierContractAttribute和ServiceOperationAttibute使我们以Overload方式定义的Interface和该Contract的对应:Contract name 和Operation Name。
你提供了一种方法,另一种方法就是使用ServierContractAttribute和ServiceOperationAttibute的参数:Name和NameSpace。
  回复  引用  查看    

#10楼 2008-06-08 21:39 cc@      

请教下博主在。net中做p2p用什么比较好呢?   回复  引用  查看    

#11楼 2008-06-12 17:16 cc@      

请教博主,如果我一个契约中的多个服务要回调不同的契约,【ServiceContract】怎么设置?   回复  引用  查看    

#12楼 2008-12-04 16:51 江湖飘      

为了重载好像没必要弄得那么麻烦吧,只要像以前一样,定义一个contract接口,然后再客户端继承实现就可以了阿!   回复  引用  查看    

#13楼 2009-02-06 19:25 yza0088[未注册用户]

楼主自己读读本篇的第一、二段话,感觉比MSDN翻译的还差啊 呵呵
不知所云
  回复  引用    

#14楼 2009-03-20 18:07 camel0564[未注册用户]

Console.WriteLine("Begin to invocate generated proxy");
InvocateGeneratedProxy();
Console.WriteLine("\nBegin to invocate revised proxy");
InvocateGeneratedProxy();

should be:
Console.WriteLine("Begin to invocate generated proxy");
InvocateGeneratedProxy();
Console.WriteLine("\nBegin to invocate revised proxy");
InvocateRevisedProxy();
  回复  引用    

#15楼[楼主] 2009-03-23 08:00 Artech      

--引用--------------------------------------------------
camel0564: Console.WriteLine(&quot;Begin to invocate generated proxy&quot;);
InvocateGeneratedProxy();
Console.WriteLine(&quot;\nBegin to invocate revised proxy&quot;);
InvocateGeneratedProxy();

should be:
Console.WriteLine(&quot;Begin to invocate generated proxy&quot;);
InvocateGeneratedProxy();
Console.WriteLine(&quot;\nBegin to invocate revised proxy&quot;);
InvocateRevisedProxy();

--------------------------------------------------------
哦,是的写错了!
  回复  引用  查看    

#16楼 2009-05-23 10:31 陈希章      

我觉得要修改客户端代理文件的做法有些牵强。同时,感觉这样做的意义不大。

web service里面可以通过messageName来实现这个要求,其实也用得极少
  回复  引用  查看    

#17楼[楼主] 2009-05-23 17:32 Artech      

--引用--------------------------------------------------
陈希章: 我觉得要修改客户端代理文件的做法有些牵强。同时,感觉这样做的意义不大。

web service里面可以通过messageName来实现这个要求,其实也用得极少
--------------------------------------------------------
如果完全按照基于添加服务引用,或者其他的通过元数据生成代码的方式进行,这绝对不是一种好的方式。

这只是为重载方法和WCF服务契约之间提供了一种适配的可能的。
  回复  引用  查看    




发表评论

昵称: [登录] [注册]

主页:

邮箱:(仅博主可见)

评论内容:

  登录  注册

[使用Ctrl+Enter键快速提交评论]

0 679315




相关文章:

相关链接: