Using Typed DataSets from Web Services

The Hazards of Auto-generated Type Definitions

Brian Noyes

October 30, 2009

6 Min Read
ITPro Today logo

Ifyou've ever tried to return a typedDataSet from a Web method, you may havebeen confused by the result. You actually have a couple of choices of how tohandle this operation; I'll cover them both quickly, so you can choose the onethat'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 theData

Ofcourse, before you can return a typed DataSet, you must create it and fill itwith data. There are several ways to do this, but the simplest is to drag atable from Server Explorer onto the design surface of your Web service. Thiswill create a data adapter and connection; then you can right-click on the dataadapter and select GenerateDataset from thecontext menu. This allows you to create the typed DataSet that will wrap thetable you pulled out to the form.

Once thetyped DataSet is defined in your Web service, you can use it to load data, andreturn it from a method. You might declare something like Figure 1 in your Webservice.

[WebMethod]public CustomersDataSet GetCustomers(){  CustomersDataSet ds = new CustomersDataSet();  SqlHelper.FillDataSet(m_connString,CommandType.Text,    "SELECT * FROM Customers", ds,     new string[] {ds.Customers.TableName});   return ds;}

Figure1: Returning atyped DataSet from a Web method.

Themethod defined in Figure 1 will return a typed DataSet of typeCustomersDataSet. For simplicity, I use the Data Access Application BlockSqlHelper class to fill the DataSet based on a connection string and a dynamicSQL query. The FillDataSet method will open the connection, create a dataadapter under the covers, and call Fill on the adapter to populate the DataSetand set the resulting table name to the one specified in the last parameter. Atyped DataSet knows its own table names. So instead of hard coding, I just havethe Customers table from the typed DataSet tell the code its name.

When youdo this, the Web method will happily return the DataSet from the method, andwill embed enough information in the resulting WSDL (Web ServicesDescription Language) for aclient to figure out how to construct the typed DataSet type on the receivingend. Keep in mind that Web services make no assumptions about having access onboth the client and the server side to shared type definitions. So if youreturn a custom type such as a typed DataSet from a Web method, the Web servicemust provide type information to the client in order for it to work with thatcustom type. Web services do this through embedded schema information in theWSDL.

On theclient side in a .NET application, Visual Studio .NET is smart enough to parseout the type information in the WSDL and will create a new type definition thatmatches what the Web service is going to return as part of the service proxydefinitions. The resulting type definition will be part of the namespacecreated by adding the Web reference to the client, and will basically matchwhat was generated originally for the typed DataSet. This is one of thebenefits of having typed DataSets created from XSD (XML SchemaDefinition) files. The XSDrepresenting 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 thatschema. 

Usingthat generated type definition on the client side, the client can use code suchas that shown in Figure 2 to get back a typed DataSet. The code in Figure 2creates an instance of the Web service proxy, and then uses it to call theGetCustomers method from Figure 1. The type that is returned is a newly definedtype 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 =    serv.GetCustomers();  m_CustomersGrid.DataSource =  ds.Customers;}

Figure2: Returning atyped DataSet through a Web service.

 

Workingwith Shared DataSet Types

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


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

Youcould modify the generated proxy code, but requiring such maintenance is anobvious liability. There is a quick and easy way around this using the Mergemethod (I wrote about the Merge method of the DataSet in MergeDisparate Source Data). Basically, if you want to achieve the scenarioshown 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 aDataSet 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();  dsCust.Merge(serv.GetCustomers());  m_CustomersGrid.DataSource = dsCust.Customers;}

Figure4: Obtaining ashared typed DataSet instance of data returned from a Web method.

InFigure 4, the DataSet returned from the GetCustomers method is passed directlyinto the Merge method of the shared typed DataSet (defined in theDataServiceTypes class library that is referenced by the client and the Webservice projects). The result is that the data is transferred into the specifictype you want. You cannot simply cast the results, because they are twodistinct types. This would also work if the Web service simply returned aDataSet with a matching schema to the typed DataSet.

TheMerge method should always work, if both the client and server are referencingthe same shared assembly containing the typed DataSet definition. This isbecause the structure of the typed DataSet generated in the proxy, and of theone in the shared assembly, will be based on the same original (shared) typeddefinition. Using this little trick, you can work with the type definition youexpected from the shared library, instead of the new type constructed from theproxy. This can be especially important if you're then going to pass areference of that typed DataSet off to other code that doesn't know anythingabout the proxy-generated class.

Thedownload code for this article contains three projects: a Web service, aWinForms client, and a class library assembly containing the DataSet typedefinition. The client loads both the proxy-generated DataSet type and theshared assembly DataSet type. It then displays them in a grid to show bothapproaches.

Thefiles accompanying this article are available for download.

 

BrianNoyes is asoftware architect with IDesign, Inc. (http://www.idesign.net),a .NET-focused architecture and design consulting firm. Brian specializes indesigning and building data-driven distributed Windows and Web applications.Brian writes for a variety of publications and is working on a book forAddison-Wesley on building Windows Forms Data Applications with .NET 2.0.Contact him at mailto:[email protected].

Sign up for the ITPro Today newsletter
Stay on top of the IT universe with commentary, news analysis, how-to's, and tips delivered to your inbox daily.

You May Also Like