1. 概述
很多正在开发或者打算开发XML Web Services的程序员都问过这样的一个问题:"我的WebService返回的结果是一个DataSet类型的对象,但如果我的客户端不是用.NET写的(因而没有内建的DataSet类型),那该如何调用这个WebService并访问DataSet中的数据呢?"。
对于这个问题,首先应该说的是:1)在多种语言共存的编程环境下,是不适合使用类似DataSet这种只属于特定语言的数据类型的。不管是在XMLWebServices还是CORBA的环境中,都应该尽量使用简单数据类型以及简单数据类型的数组。2)应当很谨慎的决定是否需要通过WebService来返回大量数据。由于网络传输的开销既包括HTTP连接建立的时间,也包括传送数据的时间,因此需要在减少访问服务器次数和减少网络传输量之间寻找一个合适的平衡。如非必须,则不适合通过WebService传送含有几十条或者几百条数据的数据表。
然后,就问题本身而言,.NET WebServices返回的DataSet类型是可以直接被其他非.NET的客户端解析的,因为即便是DataSet类型的返回值,也会被表达成XML格式再进行传输。下面的例子就是一个返回类型为DataSet的WebMethod,及其被调用后返回的XML格式数据:
表1. 返回类型为DataSet的Web Method
1 [WebMethod]
2 public DataSet GetPersonData()
3 {
4 DataTable table=new DataTable("Person");
5 table.Columns.Add("Name");
6 table.Columns.Add("Gender");
7 table.Rows.Add(new string[2]{"Alice","Female"});
8 table.Rows.Add(new string[2]{"Bob","Male"});
9 table.Rows.Add(new string[2]{"Chris","Male"});
10
11 DataSet dataset=new DataSet("PersonTable");
12 dataset.Tables.Add(table);
13 return dataset;
14 }
表2. 被格式化成XML的DataSet
1 <?xml version="1.0"encoding="utf-8"?>
2 <DataSetxmlns="http://tempuri.org/">
3 <xs:schema id="PersonTable" xmlns=""xmlns:xs="http://www.w3.org/2001/XMLSchema"
4 xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
5 <xs:element name="PersonTable"msdata:IsDataSet="true" msdata:Locale="zh-CN">
6 <xs:complexType>
7 <xs:choicemaxOccurs="unbounded">
8 <xs:element name="Person">
9 <xs:complexType>
10 <xs:sequence>
11 <xs:element name="Name" type="xs:string"minOccurs="0" />
12 <xs:element name="Gender" type="xs:string"minOccurs="0" />
13 </xs:sequence>
14 </xs:complexType>
15 </xs:element>
16 </xs:choice>
17 </xs:complexType>
18 </xs:element>
19 </xs:schema>
20 <diffgr:diffgramxmlns:msdata="urn:schemas-microsoft-com:xml-msdata"
21 xmlns:diffgr="urn:schemas-microsoft-com:xml-diffgram-v1">
22 <PersonTable xmlns="">
23 <Person diffgr:id="Person1" msdata:rowOrder="0"diffgr:hasChanges="inserted">
24 <Name>Alice</Name>
25 <Gender>Female</Gender>
26 </Person>
27 <Person diffgr:id="Person2" msdata:rowOrder="1"diffgr:hasChanges="inserted">
28 <Name>Bob</Name>
29 <Gender>Male</Gender>
30 </Person>
31 <Person diffgr:id="Person3" msdata:rowOrder="2"diffgr:hasChanges="inserted">
32 <Name>Chris</Name>
33 <Gender>Male</Gender>
34 </Person>
35 </PersonTable>
36 </diffgr:diffgram>
37 </DataSet>
从上面的例子可以看出,直接使用DataSet作为返回类型,其结果是相当复杂的,其中不但包含了DataSet中的数据,还包括了数据更改的信息,以及DataSet的Schema。虽然有些工具能够生成一个类似DataSet的客户端类型,但无论是直接解析复杂的XML还是使用类似DataSet的类,都不够直接不够清晰。
解决这个问题的方案有两种:
1)用简单数据类型构造自定义类型,用每一个自定义类型对象封装数据集中的一行,将自定义类型对象的数组(Array)返回客户端;由于是用简单数据类型定义,客户端能够完全不变的还原出自定义类型的定义;
2)用DataSet.WriteXML()方法将数据集中的数据提取成XML格式,并以字符串的形式返回给客户端,再由客户端解析XML字符串,还原出数据。由于使用WriteXML()的时候能够过滤掉冗余信息,返回的内容和图表2中的内容相比大大简化了。
2. 创建.NET Web Services,返回数据集合
借助于Visual Studio.NET,只需编写Web Method本身的代码,即可非常快速的创建可以实用的WebServices:
表3. 用.NET实现的XML Web Services
1 [WebMethod]
2 public Person[] GetPersons()
3 {
4 Person Alice=newPerson("Alice","Female");
5 Person Bob=newPerson("Bob","Male");
6 Person Chris=newPerson("Chris","Female");
7 Person Dennis=newPerson("Dennis","Male");
8
9 return newPerson[]{Alice,Bob,Chris,Dennis};
10 }
11
12 [WebMethod]
13 public string GetPersonTable()
14 {
15 DataTabletable=new DataTable("Person");
16 table.Columns.Add("Name");
17 table.Columns.Add("Gender");
18 table.Rows.Add(new string[2]{"Alice","Female"});
19 table.Rows.Add(new string[2]{"Bob","Male"});
20 table.Rows.Add(new string[2]{"Chris","Female"});
21 table.Rows.Add(new string[2]{"Dennis","Male"});
22 table.Rows.Add(newstring[2]{"Eric","Male"});
23
24 DataSetdataset=new DataSet("PersonTable");
25 dataset.Tables.Add(table);
26
27 System.Text.StringBuilderstrbuilder=new System.Text.StringBuilder();
28 StringWriter writer=new StringWriter(strbuilder);
29 dataset.WriteXml(writer,System.Data.XmlWriteMode.IgnoreSchema);
30
31 returnstrbuilder.ToString();
32 }
在上面的代码中,函数GetPersons()和GetPersonTable()分别对应于"1.概述"中所提到的两种解决方案。其中,Person类型就是用于封装数据集中一行数据的自定义的数据类型:
表4. 自定义类型Person
1 [Serializable]
2 public class Person
3 {
4 public Person()
5 {
6 }
7
8 public Person(stringname,string gender)
9 {
10 this.Name=name;
11 this.Gender=gender;
12 }
13
14 public string Name="";
15 public stringGender="";
16 }