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.
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. 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. Figure
2: Returning a
typed DataSet through a Web service. 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. 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. 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. (http://www.idesign.net),
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].Get the
Data
[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;
}
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;
}
Working
with Shared DataSet Types
Figure 3: A Web service client and a Web
service may want to share a type definition for a typed DataSet. 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;
}
Using Typed DataSets from Web Services
The Hazards of Auto-generated Type Definitions
0 comments
Hide comments