Tuesday, March 23, 2010

Serializing, Consuming, and Binding Generics in .NET 2.0

Introduction:

Serializing, Consuming, and Binding Generics in .NET 2.0
Generics are a powerful new feature in .NET 2.0, but it's not so obvious how to use them as return values from Web services, or how to bind them to controls. Find out how to serialize, data bind, and consume generic collections in ASP.NET 2.0


Generics, a new feature introduced with .NET 2.0, provides type safety at compile time. Generics let you create data structures without committing to a specific data types in your code at design time. At compile time, the compiler ensures that the types used with the data structure are consistent with type safety. In other words, generics provide type safety, but without any loss of performance or code bloat. While they are similar to templates in C++ in this regard, their implementation is very different.

This article discusses the application of generics in .NET development and different ways in which you can leverage generic types and collections within a .NET application.

What are Generics?
Generics are code templates generalized to use the same code repeatedly with different data types without needing to rewrite any of the internal code, and thus increasing the reusability of your software components. Within the data structure, member or argument usage adapts to different data types. Generics let you avoid those all-too-common, messy, and resource-intensive conversions from reference types to native types, creating routines that are much more type-safe.


You define a generic using a slightly different notation than you're used to with non-generic types. For example, here's the basic code for a generic class named Compare that can compare two items of the same type and return the larger or smaller value, depending on which method is invoked:

public class Compare
{
public ItemType ReturnLarger
(ItemType data, ItemType data2)
{
//logic...
}

public ItemType ReturnSmaller
(ItemType data, ItemType data2)
{
//logic...
}
}


You've probably written similar code, but you've also probably written it many times, once for each type you want to compare. In contrast, you can use this generic class with any data type, ranging from basic data types such as integers to complex classes and structures. When you use the generic, you identify what data type you're planning to use with it. For example, to compare Integers with the Compare generic class, you would write code similar to the following:

Compare compare = new
Compare;
int result = compare.ReturnLarger(3,5);



To serialize and deserialize a generic class, you just need to follow two steps.

* Create an instance of the XmlSerializer class and pass the type of the generic type to be serialized as an argument
* Invoke the Serialize() or Deserialize() method of the XmlSerializer class, passing in the object to be serialized or deserialized


using System;
using System.Collections;
using System.Xml.Serialization;

[XmlRoot("KeyValuePair")]
public class KeyValuePair
{
private KeyType _key;
private ValueType _value;
public KeyValuePair()
{}

public ValueType Value
{
get{return _value; }
set{_value = value;}
}

public KeyType Key
{
get{return _key; }
set{_key = value; }
}
}

The preceding code declares a class named KeyValuePair that accepts two runtime types; one for the key element and another one for the value element. As part of the class declaration, there is also an XmlRoot attribute that ensures the root element of the serialized XML document is named "KeyValuePair." The code defines two public properties named Value and Key that simply set or get values from the private variables _value and _key respectively. Keeping the KeyValuePair class definition in mind, Listing 1 discusses the code required to serialize or deserialize an instance of the KeyValuePair class using XML serialization.

Listing 1 contains two methods named Serialize() and Deserialize(). The Serialize() method starts by declaring a KeyValuePair object with the type parameters set to int and string respectively.

KeyValuePair keyVal = new
KeyValuePair();

Then the code invokes the Key and Value properties of the KeyValuePair object to set the values appropriately.

keyVal.Key = 1;
keyVal.Value = "Production";

After doing that, you supply the KeyValuePair object as a parameter to the constructor of the XmlSerializer object, indicating the typed parameters.

XmlSerializer serializer = new
XmlSerializer(typeof
(KeyValuePair));

That's all there is to serializing a generic type. The rest of the code in Listing 1 is similar to the code examples shown earlier. The Deserialize() method works along the same lines, passing in typed parameters to the constructor of the XmlSerializer and then finally invoking the XmlSerializer.Deserialize() method to deserialize the XML data into an instance of the KeyValuePair object. If you browse to the page using the browser, you will see buttons for invoking the Serialize() and Deserialize() methods. Clicking the "Serialize" button causes the server to create an XML file named KeyValuePair.xml file created in the C:\Data directory containing the XML for the serialized KeyValuePair object. Here's how that file looks.



Production
1




Implementing Data-binding with Generic Collections
In this section, you will see how to return generic collections from a middle tier object and bind them directly to the ObjectDataSource control. The ObjectDataSource control is then bound to a GridView control so that you can display the values returned through the generic collection in the GridView.


First, create a class named Category that acts as the placeholder for capturing attributes related to a Category. The code for the Category class is as follows:

using System;
public class Category
{
private string _categoryID;
private string _name;

public string CategoryID
{
get{return _categoryID;}
set{_categoryID = value;}
}

public string Name
{
get{return _name; }
set{_name = value; }
}
}

As you can see, the implementation of the Category class is very simple and straightforward. It just has two properties, named CategoryID and Name. Next, here's a data access layer class named CategoryDB that leverages the Category class.

using System;
using System.Data;
using System.Data.SqlClient;
using System.Web.Configuration;
using System.Collections.Generic;

public class CategoryDB
{
public CategoryDB()
{}

public IList
GetCategories()
{
IList categoryColl = new
List();
string connString =
WebConfigurationManager.
ConnectionStrings
["adventureWorks"].
ConnectionString;
using (SqlConnection sqlConn = new
SqlConnection(connString))
{
sqlConn.Open();
SqlCommand sqlCmd =
sqlConn.CreateCommand();
sqlCmd.CommandText = "Select "
"ProductCategoryID, Name from "
+"Production.ProductCategory";
//Create the SqlDataReader object
using (SqlDataReader reader =
sqlCmd.ExecuteReader())
{
while (reader.Read())
{
Category cate = new
Category();
cate.CategoryID =
reader["ProductCategoryID"].
ToString();
cate.Name =
reader["Name"].ToString();
categoryColl.Add(cate);
}
}
}
return categoryColl;
}
}


The CategoryDB class code begins by declaring an object of type IList generic collection and assigning that to the generic collection of type List. Then it opens the connection to the database, executes the query and retrieves the results in a SqlDataReader object. It then loops through the returned rows retrieving values and assigning them to a new Category object, one for each row, adding each Category object to the previously declared generic collection. After adding all the rows to the collection, it returns the collection object to the caller.

For the code to work, you need to define the connection string in the web.config as shown below:





At this point, the CategoryDB object is ready for consumption; the next step is to create a page that leverages the CategoryDB.GetCategories() method. The following code defines an ASP.NET page that data-binds the output of the GetCategories() method to a GridView control.

<%@ Page Language="C#" %>


Implementing Data Binding with <br /> Generics<br />




Figure 1. ASP.NET Page Implementing Object Binding: The ObjectDataSource Control lets you perform data binding with the "categories" data returned from the CategoryDB class.










Note that the page contains an ObjectDataSource control named categoriesSource with its TypeName and SelectMethod attributes set to CategoryDB and GetCategories respectively. The page binds a GridView named gridCategories to the ObjectDataSource through its DataSourceID attribute. Loading the page in a browser results in the output shown in


Returning a Generic Collection from a Web Service
So far, you have seen how to perform data binding with a generic collection returned by the data access layer object. This section discusses how to return generic collections from a Web service, which has the advantage of letting client applications bind directly to the output returned by the Web service method.

The following code shows how to return a generic collection from a Web service. It leverages the CategoryDB class you've already seen to retrieve data from the AdventureWorks database.

<%@ WebService Language="C#" Class="CategoryService" %>
using System;
using System.Web.Services;
using System.Collections.Generic;

[WebService(Namespace =
"http://tempuri.org/")]
[WebServiceBinding(ConformsTo =
WsiProfiles.BasicProfile1_1)]
public class CategoryService :
System.Web.Services.WebService {
[WebMethod]
public List GetCategories()
{
CategoryDB category = new
CategoryDB();
return (List)
category.GetCategories();
}
}



Figure 2. CategoryService Output: The figure shows the XML for the generic collection object returned by the CategoryService Web service. Note that the Web service returns the collection as an array of Category objects.
In the preceding code, the GetCategories() method simply invokes the GetCategories() method of the CategoryDB class and returns the result to the Web service consumer.

Now, if you browse to the Web service using the browser and follow the on-screen directions to invoke the GetCategories() method, you'll see the output shown in Figure 2.

As you can see from the above figure, the .NET framework serializes the generic collection as an array of Category objects when invoked from the browser. You can then easily bind this array of Category objects to data bound controls in the client application.

Keep It Short
As you can see, the ability to leverage generics as the middle tier return type opens up a whole avenue of new and interesting opportunities to work with data in the user interface layer. Using the techniques shown here, you can now easily serialize, deserialize, and databind generic types and collections. In addition, you can also return generic types from your Web service that can then be seamlessly consumed by client applications.

link
http://www.devx.com/dotnet/Article/30158/0/page/1

No comments:

Post a Comment