ASA与Web Service
最近一段时间以内,大家都在大搞.Net 2.0,苦于家里的机器拖这个东西实在有点累,所以只是天天上博客园看看大家写的文章使自己不至于太落后于形势。瞧瞧将近年关岁尾,已经有半年多没有发表过什么技术文章了,不能如此不务正业吧。下面是我前段时间了解Sybase公司的SQL Anywhere Studio时的一点心得。
Web Service这个东西大家都比较的熟悉,在VS.Net里创建一个Web Service以及使用一个Web Service都是很方便的,更重要的是Web Service体现的软件设计思想。现在一种系统、一种软件跟Web Service沾一下边,好像是比较前卫的,SQL-Server 2005不也是提供了Web Service功能吗。
前不久看SQL Anywhere Studio的Help时发现原来数据库带Web Service这样的一种功能在此之前Sybase的Adaptive Server Anywhere(ASA)上也有的,当时在非MS的“领地”上看到了Web Service这样熟悉的字眼就特意的关注了一下(虽然Web Service不属MS专有的,但不可否认MS对于Web Service的“普及”功不可没)。都说Web Service是异构系统之间协作的利器,但我让它协作起来还是费了不少周折。
欲作其事,需拥其器,首先要到Sybase网站上下载SQL Anywhere Studio
如何在ASA中创建Web Service这里就不细讲了,只是要明确的一点是,在ASA中Create Service时候Type选SOAP的话,创建出来的东西相当于我们在.Net里的一个WebMethod。只有Type是DISH时,才相当于创建了一个.Net里的.asmx文件,也就是说只有那种DISH类型的Service才向外提供WSDL文件的。
照着手册上的步骤创建了一个名为“sample/hello”的SOAP类型的Service,一个名为“sample/dish_service”的DISH类型的Service,功能很简单,就是提供一个string参数,Web Service向这个string Hello一下。
接下来写个简单的.Net Console程序来访问一下上面创建的Web Service看看,如果Web Service是我们用ASP.Net创建的,那写这种Hello式的访问程序目前来讲也有点太玷污硬盘了。可这个Web Service不是ASP.Net创建的话,情况就大大的不同了。
第一步先要把本机的80端口留出来,80端口现在要给ASA用了。虽然ASA是可以在其他端口上监听并处理HTTP的,但我发现它发布出来的WSDL文件居然其Web Service地址是不带端口号的,比如我的ASA在8080上启动监听,但其Web Service的WSDL的“service”部分居然是这样的(“web”是数据库名)
<service name="sample/dish_service">
<port name="sample/dish_serviceSoap" binding="s3:sample/dish_serviceSoapBinding">
<soap:address location="http://localhost/web/sample/dish_service" />
</port>
</service>
用它公布出来的这个WSDL是访问不了Web Service(非80端口时)的。(刚开始还以为是生成代理类的wsdl.exe有问题,后来才发现是Web Service的WSDL文件的问题)。
用这个例子第二个要注意的问题是无法用VS.Net的“添加Web引用”来生成代理类。它的service name里有“/”的缘故,添加Web引用的时候VS.Net会告诉你未能找到某子虚乌有的文件夹路径的一部分。用命令行吧,不要被GUI给惯坏了。
代理类生成后,所有的一切都进入了我们所熟悉的轨道。下面要开始hello了。
ASA Create Service时有个FORMAT 选项,用于指定向外提供数据的格式,它的缺省格式居然是DENT,它返回一个.Net的DataSet(看来Sybase与MS的关系还是不错)。第二种是CONCRETE格式,它返回一个ASADataset的结构,这个结构的定义在WSDL里会公布出来。第三种是XML格式,返回的是一个字符串,手册上说是一个SOAP响应的字符串。
前二种格式虽然是某种数据结构,但都是有迹可寻的,都比较的简单。送个“lichdr”进去,回来一个东西,我们到里面可以找“Hello lichdr”出来。第三种格式表面上看也没什么,回来一个字符串这样的方式是很受欢迎的,自己解析一下XML也能读出“Hello lichdr”吧,没想到就在这个第三种看来很简单的方案上搁了浅,说什么XML文档中有错误。结果一个hello化了三天时间,也还没hello出来。
化了点功夫,了解了一些.Net里Web Service调用的简单机理,跟踪SOAP消息。发现XML格式的Service与CONCRETE格式的Service返回的SOAP消息居然是一模一样的。
它们的Body部分如下:
<tns:sample_helloResponse>
<tns:sample_helloResult xsi:type="tns:ASADataset">
<tns:rowset>
<tns:row>
<tns:result_out>hello lichdr
</tns:result_out>
</tns:row>
</tns:rowset>
</tns:sample_helloResult>
<tns:sqlcode>0</tns:sqlcode>
</tns:sample_helloResponse>
很显然这里是会出问题的。对于FORMAT为XML的Service来说其公布出来的WSDL里关于返回消息的定义是这样的:
<s:element name="helloResponse">
<s:complexType>
<s:sequence>
<s:element minOccurs="1" maxOccurs="1" name="helloResult" type="s:string" />
<s:element name="sqlcode" type="s:int" />
</s:sequence>
</s:complexType>
</s:element>
根据这个定义,wsdl.exe生成的代理类不会有什么大的问题:Invoke后返回一个object数组,第一个元素是string,第二个是int。
当.Net程序运行,开始创建代理类的时候,系统背地里会根据当前这个代理类的定义,通过反射等手段,创建出一些XmlSerializer,这些XmlSerializer就是.Net的类型系统与SOAP的桥梁。比如对于上面那个hello方法,会产生出一个名为returnSerializer的XmlSerializer用于处理返回值,显而易见这个returnSerializer的任务就是把返回的SOAP消息里Response部分反序列成相应的object数组,当然对于一个Method来讲,还会有处理输入参数以及处理SoapHeader用的等XmlSerializer。
至此问题已经比较的明了了,问题出在ASA的Web Service(FORMAT为XML时)其公布出来的WSDL与实际的响应SOAP消息不太匹配引起,.Net程序接收到那个消息准备反序列的时候,发现Response部分的XML不是预期的XML,所以它报XML文档中有错误,它报错误的位置刚好就是helloResult的内容开始的地方,刚碰上那个“<”就抛出异常了。预期的XML应该是helloResult那个xsi:Type不要(字符串类型可以不用声明Type),然后把里面的内容编码成字符串,在Web Service调用中不知如何修改它的SOAP消息,象我们一般Debug的时候一样可以修改某些变量的值让程序继续往下跑,所以到现在那个Web Service也没有向我hello过。
在ASA中,既然可以创建Web Service让别人访问,当然就可以访问别人提供的Web Service,这个功能是双向的,否则缺只脚太难看了。
用ASP.Net写了个简单的Web Service,在ASA里是能调用到它,没有出现什么大的问题,这个跟.Net关系不大,所以就不细说了。只是ASA调用Web Service不像我们写程序一样这么的顺溜,数据库的本职工作到底不是做这个的,只能用OpenXml来分析接收进来的SOAP消息来达到目的。
上面写了这么多,好像尽是些关于ASA的Web Service功能不足的地方,不过Web Service也不是ASA主打的功能,而且Web Service这个东西还在突飞猛进中,用不了大惊小怪。Sybase的产品还是挺不错的,我很喜欢它的数据同步功能。