Using Typed DataSets from Web Services

The Hazards of Auto-generated Type Definitions

If you've ever tried to return a typedDataSet from a Web method, you may have been confused by the result. You actually have a couple of choices of how to handle this operation; I'll cover them both quickly, so you can choose the one that's appropriate for your situation. For more information on typed DataSets, keep your eyes peeled for my article in the May issue of asp.netPRO magazine.


Get the Data

Of course, before you can return a typed DataSet, you must create it and fill it with data. There are several ways to do this, but the simplest is to drag a table from Server Explorer onto the design surface of your Web service. This will create a data adapter and connection; then you can right-click on the data adapter and select Generate Dataset from the context menu. This allows you to create the typed DataSet that will wrap the table you pulled out to the form.

Once the typed DataSet is defined in your Web service, you can use it to load data, and return it from a method. You might declare something like Figure 1 in your Web service.


public CustomersDataSet GetCustomers()


  CustomersDataSet ds = new CustomersDataSet();


    "SELECT * FROM Customers", ds,

     new string[] {ds.Customers.TableName});

   return ds;


Figure 1: Returning a typed DataSet from a Web method.

The method defined in Figure 1 will return a typed DataSet of type CustomersDataSet. For simplicity, I use the Data Access Application Block SqlHelper class to fill the DataSet based on a connection string and a dynamic SQL query. The FillDataSet method will open the connection, create a data adapter under the covers, and call Fill on the adapter to populate the DataSet and set the resulting table name to the one specified in the last parameter. A typed DataSet knows its own table names. So instead of hard coding, I just have the Customers table from the typed DataSet tell the code its name.

When you do this, the Web method will happily return the DataSet from the method, and will embed enough information in the resulting WSDL (Web Services Description Language) for a client to figure out how to construct the typed DataSet type on the receiving end. Keep in mind that Web services make no assumptions about having access on both the client and the server side to shared type definitions. So if you return a custom type such as a typed DataSet from a Web method, the Web service must provide type information to the client in order for it to work with that custom type. Web services do this through embedded schema information in the WSDL.

On the client side in a .NET application, Visual Studio .NET is smart enough to parse out the type information in the WSDL and will create a new type definition that matches what the Web service is going to return as part of the service proxy definitions. The resulting type definition will be part of the namespace created by adding the Web reference to the client, and will basically match what was generated originally for the typed DataSet. This is one of the benefits of having typed DataSets created from XSD (XML Schema Definition) files. The XSD representing that DataSet can be embedded in the WSDL for a Web service. Then, a receiving .NET client can re-create the typed DataSet exactly, based on that schema. 

Using that generated type definition on the client side, the client can use code such as that shown in Figure 2 to get back a typed DataSet. The code in Figure 2 creates an instance of the Web service proxy, and then uses it to call the GetCustomers method from Figure 1. The type that is returned is a newly defined type in the client that resides in the namespace generated for the proxy.

private void m_LoadButton_Click(

   object sender, System.EventArgs e)


  CustomersDataService.CustomersDataService serv =

     new CustomersDataService.CustomersDataService();

  CustomersDataService.CustomersDataSet ds =


  m_CustomersGrid.DataSource =  ds.Customers;


Figure 2: Returning a typed DataSet through a Web service.


Working with Shared DataSet Types

This works fine if the client is happy working with the auto-generated type definition for the typed DataSet. However, this means that the client will be using a different concrete type for the received data than what the Web server constructed and sent. But what if you want to share the type definition for the DataSet between the server and client, and make sure they are both using the same code definition, referenced from the same assembly. You have a problem in such a case, because you can't stop the proxy from auto-generating the type definition for the return type of the Web method. .NET will see it as a different type from the one declared in the shared assembly. To visualize the situation I am describing, take a look at Figure 3.

Figure 3: A Web service client and a Web service may want to share a type definition for a typed DataSet.

You could modify the generated proxy code, but requiring such maintenance is an obvious liability. There is a quick and easy way around this using the Merge method (I wrote about the Merge method of the DataSet in Merge Disparate Source Data). Basically, if you want to achieve the scenario shown in Figure 3, you'll need to merge the data from the returned DataSet, which will be of the type automatically created in the proxy namespace, into a DataSet of the shared type. The code to do this is shown in Figure 4.

private void m_LoadSharedButton_Click(

   object sender, System.EventArgs e)


  CustomersDataService.CustomersDataService serv =

     new CustomersDataService.CustomersDataService();

  DataServiceTypes.CustomersDataSet dsCust =

     new DataServiceTypes.CustomersDataSet();


  m_CustomersGrid.DataSource = dsCust.Customers;


Figure 4: Obtaining a shared typed DataSet instance of data returned from a Web method.

In Figure 4, the DataSet returned from the GetCustomers method is passed directly into the Merge method of the shared typed DataSet (defined in the DataServiceTypes class library that is referenced by the client and the Web service projects). The result is that the data is transferred into the specific type you want. You cannot simply cast the results, because they are two distinct types. This would also work if the Web service simply returned a DataSet with a matching schema to the typed DataSet.

The Merge method should always work, if both the client and server are referencing the same shared assembly containing the typed DataSet definition. This is because the structure of the typed DataSet generated in the proxy, and of the one in the shared assembly, will be based on the same original (shared) typed definition. Using this little trick, you can work with the type definition you expected from the shared library, instead of the new type constructed from the proxy. This can be especially important if you're then going to pass a reference of that typed DataSet off to other code that doesn't know anything about the proxy-generated class.

The download code for this article contains three projects: a Web service, a WinForms client, and a class library assembly containing the DataSet type definition. The client loads both the proxy-generated DataSet type and the shared assembly DataSet type. It then displays them in a grid to show both approaches.

The files accompanying this article are available for download.


Brian Noyes is a software architect with IDesign, Inc. (, a .NET-focused architecture and design consulting firm. Brian specializes in designing and building data-driven distributed Windows and Web applications. Brian writes for a variety of publications and is working on a book for Addison-Wesley on building Windows Forms Data Applications with .NET 2.0. Contact him at mailto:[email protected].

Hide comments


  • Allowed HTML tags: <em> <strong> <blockquote> <br> <p>

Plain text

  • No HTML tags allowed.
  • Web page addresses and e-mail addresses turn into links automatically.
  • Lines and paragraphs break automatically.