我们只要给作为互联网服务的Add()、Subtract()、Divide()和Multiply()这四种方法添加[WebMethod]属性即可,剩下的工作就全由.NET来替我们完成。注意,使WebService作为基本类是可选的,从WebService中进行继承生成类将使用户可以更方便地使用公共的ASP.NET对象,但我们在这里不会用到这些对象。尽管WebService属性是可选的,但我建议用户使用它。
WebService属性可以让我们指定一个包含服务的Web名字空间,用户可以用它来作为一个普通的.NET名字空间来减少名字冲突。如果不指定名字空间,VS.NET将缺少地使用http://tempuri.org/作为名字空间。一个发布的服务通常使用一个URL作为其名字空间,一般情况下就是服务商的公司名字。WebService属性还使我们能够提供一个有关服务的文字描述,这些文字描述将出现在互联网服务用户使用的自动生成的网页中。
编写客户端代码不需要什么特别的技巧。在VS.NET中的用户工程中选择Add Web Reference,并把向导指向包含有互联网服务ASPX文件的站点,这将使VS.NET生成一个被称作SimpleCalculator的包装类,客户端程序会用到这一包装类(见表2)。SimpleCalculator类拥有互联网服务开发商用[WebMethod]标识为公共方法的方法。包装类(有时也被称作“互联网服务代理”类)完全封装了与远程对象之间复杂的交互活动,它也是唯一的与服务的位置相关的实体,它的基本类SoapHttpClientProtocol有一个被称为Url的的属性,Url属性指向这一对象的位置。
表2:
public class SimpleCalculator : SoapHttpClientProtocol { public SimpleCalculator() { Url ="http://www.CalculationServices.com /SimpleCalculator.asmx"; }
[SoapDocumentMethod("http://CalculationServices.com/Add")] public int Add(int num1,int num2) { object[] results = Invoke("Add", new object[]{num1,num2}); return (int)(results[0]); } // 其他方法的包装 } |
专门为表1中的互联网服务生成的SimpleCalculator互联网服务包装类完全封装了与互联网服务之间的交互活动,使得客户端不会受到这些细节问题的影响,它在其公共的Url属性中包含有服务的位置。
客户端的代码在使用SimpleCalculator对象时,把它看作与本地对象是一样的:
SimpleCalculator calculator; calculator = new SimpleCalculator(); int result = calculator.Add(2,3); Debug.Assert(result == 5); |
很显然,在VS.NET中调用Web方法是非常简单的事。
我们发现了一个新问题:客户端软件不再直接针对提供服务的对象(在这里是SimpleCalculator)进行编程,而是针对这一服务的抽象进行编程。我们希望SimpleCalculator互联网服务通过服务抽象━━接口,具有多态性。
例如,假设客户端应用希望从SimpleCalculator转向另一种被称作ScientificCalculator的计算器服务,ScientificCalculator支持与SimpleCalculator相同的接口,但它的速度更快、价格更低,而且计算更准确。因此,我们希望定义一个通用的计算器接口━━ICalculatorlw接口,并将它作为互联网服务:
[WebInterface] interface ICalculator { int Add(int num1,int num2); int Subtract(int num1,int num2); int Divide(int num1,int num2); int Multiply(int num1,int num2); } |
假定我们可以完成这一工作(很快我会向你说明如何作的。),我们可以只针对接口定义而不是它的一个特定的具体实现来进行编程。(见表3)
表3
ICalculator calculator = (ICalculator) new ScientificCalculator();
file://或
ICalculator calculator = (ICalculator) new SimpleCalculator();
// 这部分的客户端应用代码对于任何服务提供商都具有多态性 int result = calculator.Add(2,3);
Debug.Assert(result == 5); |
我们希望计算器互联网服务通过定义服务抽象━━接口实现多态性,可以在不修改或尽量少修改客户端代码的情况下改变服务提供商。
在服务提供商之间进行转换时客户端应用代码中唯一需要改变的是决定使用哪个接口实现的部分。我们可以根据不同的集合而不仅是主要客户的逻辑来作出决策,只在二者之间传递接口。我们可以从基于接口的互联网服务中得到的另一个好处是:客户可以发布接口的定义,使不同的服务开发商更方便地实现客户的要求。
现在我们就可以开始使用不支持基于接口的互联网服务的VS.NET来编写服务器和客户机端的代码了。
Web接口的定义和实现
为了实现一个基于接口的互联网服务,首先需要解决互联网服务接口的定义这个问题。为了简单起见,我们在这里假定服务提供商同时负责接口的定义和实现。(客户端应用软件和第三方都可以知道接口定义,任何人都可以实现它,但这还需要其他一些工作。)
创见一个被称作CalculationServices的新的互联网项目。右击该项目,并选择Add Web Service,在Add New Item对话框,输入ICalculator作为接口名字,并点击Open。
|
图:开发基于接口的.NET互联网服务1 接口的定义:要定义一个互联网服务的接口,需要添加一个互联网服务条目,并给它命名。点击OK按钮,使VS.NET创建一个互联网服务,并在向导生成的代码中删除所有具体实现互联网服务的代码。
|
VS.NET将创建一个被称作ICalculator的互联网服务。打开ICalculator.asmx.cs文件,并把ICalculator的类型定义由class改为interface,从System.Web.Services.WebService中删除派生类。这样,该接口就仅仅剩下了定义,而没有了实现代码━━删除了构建器、InitializeComponent()和Dispose()方法。最后,删除有注释的HelloWorld()方法。
然后,添加接口的方法━━Add()、Subtract()、Divide()和Multiply()。尽管从理论上可以将[WebMethod]属性用在每个接口方法上,把接口当作是一个互联网服务定义,但由于[WebService]的原因,实际上你无需这么作。这一属性只能用在类中,而且它是密封的,不能对它作任何改变,因此不能给接口指定一个名字空间和描述。为了解决这一难题,我们必须给接口提供一个缓冲━━一个象纯接口定义的抽象类。在ICalculator.asmx.cs文件中添加ICalculatorShim纯抽象类定义:
[WebService( Name = "ICalculator",Namespace= "http://CalculationServices.com", Description = "This Web Service is only the definition of the interface. You cannot invoke method calls on it.")] abstract class ICalculatorShim : ICalculator { abstract public int Add(int num1,int num2);
abstract public int Subtract(int num1,int num2);
abstract public int Divide(int num1,int num2);
abstract public int Multiply(int num1,int num2); } |
需要注意的是,由于ICalculatorShim是一个类,我们可以使用[WebService]属性来提供一个名字空间和描述。此外,可以将[WebService]属性的Name属性设置为ICalculator,以使服务的描述成为ICalculator而不是ICalculatorShim。
我们感兴趣的只是知道该服务的标志,因此无需知道具体的实现代码。由于我们使用了抽象的类和方法,VS.NET也不会生成ICalculatorShim互联网服务的实现代码,而只生成一个服务说明。
为了确认所有这一切都已经正确地完成,把ICalculator.asmx文件设置为起始页,并运行该工程,自动生成的浏览器页面将显示ICalculator的接口定义。如果试图调用任何一个方法,都会得到一个错误提示,因为我们并没有完成实现该服务所需要的代码。
然后,在互联网服务类上实现ICalculator接口。这与在.NET中实现其他类完全一样,互联网类应该由接口继承而得到,并实现它的方法。在本例中,需要完成二个类的实现:SimpleCalculator和ScientificCalculator互联网服务类。(见表4)
[WebService(amespace="http://CalculationServices.com", Description = "The SimpleCalculator web service implements ICalculator. It provides the four basic arithmetic operations for integers.")] public class SimpleCalculator : ICalculator { public SimpleCalculator() {} [WebMethod] public int Add(int num1,int num2) { return num1 + num2; } file://ICalculator的其他方法 } [WebService(Namespace="http://CalculationServices.com", Description = "The ScientificCalculator web service implements ICalculator. It provides the four basic arithmetic operations for integers.")] public class ScientificCalculator : ICalculator { public ScientificCalculator () {}
[WebMethod] public int Add(int num1,int num2) { return num1 + num2; } file://ICalculator的其他方法 } |
实现ICalculator与实现其他的.NET接口非常相似,被当作互联网服务的该类继承自接口,并实现接口的方法。注意:必须在接口方法的实现中提供[WebMethod]属性。
再次使用Add Web Service菜单项添加二种互联网服务,添加ICalculator接口的一个继承类(可以删除WebService基础类中的继承类,它不适合基于接口的互联网服务。),添加Add()、Subtract()、Divide()和Multiply()接口类方法的具体实现。必须提供希望将之作为互联网服务的接口方法的[WebMethod]属性,如果类的方法没有[WebMethod]属性,.NET就不会将之作为互联网服务的一部分。
编写客户端代码