Someone asked me about using a typed DataSet on both ends of the wire using a web service. I honestly don't work with typed DataSets, but this question was to accomodate the client's existing architecture. We both tried it, and couldn't get it to work. This shouldn't be that hard,so I asked quite a few people... nada. Most admitted they just frankly don't use typed DataSets.So, I settledin behind the keyboard to fight through it. The answer turned out to be simple, but non obvious.
Suppose you have a typed DataSet and you want to use that with a Web Service. If you plan on exposing that web service to any clients not running .NET (such as VB6 using the Soap Toolkit), then you should not return a typed DataSet from a web service method. For more background on the interoperability problems, read Aaron Skonnard's "Web Services and DataSets" article to see the effect on the WSDL when returning a DataSet. Aaron's article shows that one workaround is to use an XmlDataDocument and return that to the client as an XmlNode type. I quote that article often, but hadn't actually tried to implement the solution.
I created a C# Class Library project file and left the name as ClassLibrary1. I added an XML Schema to the project, and dragged some tables from Northwind to the design surface. Then I tweaked the schema for a little while (I am a control freak about the XML Schema layout, no big deal). The reason for the library is so that I can create a typed DataSet that is used by both the WinForms client and the web service.
In the same .sln, I added a C# Web Service project and named it WebService1 (clever, huh?). I set a project reference to ClassLibrary1 above so that I could use the typed DataSet. Here is the HelloWorld code, uncommented and modified:
[WebMethod]
public XmlNode HelloWorld()
{
System.Data.SqlClient.SqlConnection cn = new System.Data.SqlClient.SqlConnection("Data Source=L00261;User ID=sa;Password=asdfasdf;Initial Catalog=Northwind;");
cn.Open();
System.Data.SqlClient.SqlDataAdapter adap = new System.Data.SqlClient.SqlDataAdapter("SELECT * FROM CUSTOMERS",cn);
ClassLibrary1.Document s = new ClassLibrary1.Document();
adap.Fill(s,"Customers");
adap.Fill(s,"Orders");
adap.Fill(s,"OrderDetails");
adap.Dispose();
cn.Close();
cn.Dispose();
System.Xml.XmlDataDocument doc = new XmlDataDocument(s);
return doc;
}
Using this web service method, the contents of the SOAP body will be the same as if you used:
s.WriteXml(Response.OutputStream, XmlWriteMode.IgnoreSchema);
Once you have the web service running and you can invoke it from the ?wsdl test page (for example, my new service was http://localhost/webservice1/service1.asmx?wsdl), you can create the client.
Create a WinForms client and set a project reference to ClassLibrary1. Then create a Web Reference to your service, using the WSDL endpoint specified above. Drop a DataGrid and a Button onto Form1. Double-click the button and add the following code:
localhost.Service1 s = new localhost.Service1();
System.Xml.XmlNode n = s.HelloWorld();
ClassLibrary1.Document x = new ClassLibrary1.Document();
x.ReadXml( new System.Xml.XmlNodeReader(n),XmlReadMode.IgnoreSchema );
this.dataGrid1.DataSource = x;
The trick to using the typed DataSet on both ends of the wire is that you have to specify XmlReadMode.IgnoreSchema, or you will not see any data show up in your DataGrid control.