Web applications are, for the most part,
just data-driven applications. For this reason, the ability to bind
HTML elements such as drop-down lists or tables to structured data is a
key feature for any development platform. Data binding is the process
that retrieves data from a fixed source and dynamically associates this
data to properties on server controls. Valid target controls are those
that have been specifically designed to support data binding—that is,
data-bound controls. Data-bound controls are not another family of
controls; they’re simply server controls that feature a few well-known
data-related properties and feed them using a well-known set of
collection objects.
1. Feasible Data Sources
Many
.NET classes can be used as data sources—and not just those that have
to do with database content. In ASP.NET, any object that exposes the IEnumerableIEnumerable interface defines the minimal API to enumerate the contents of the data source: interface is a valid bindable data source. The
public interface IEnumerable
{
IEnumerator GetEnumerator();
}
Many bindable objects, though, actually implement more advanced versions of IEnumerable, such as ICollection and IList. In particular, you can bind a Web control to the following classes:
ADO.NET container classes such as DataSet, DataTable, and DataView
Data readers
Custom collections, dictionaries, and arrays
To be honest, I should note that the DataSet and DataTable classes don’t actually implement IEnumerable
or any other interfaces that inherit from it. However, both classes do
store collections of data internally. These collections are accessed
using the methods of an intermediate interface—IListSource—which performs the trick of making DataSet and DataTable classes look like they implement a collection.
ADO.NET Classes
ADO.NET provides a bunch of data container classes that can be filled
with any sort of data, including results of a database query. These
classes represent excellent resources for filling data-bound controls
such as lists and grids. If having memory-based classes such as the DataSet
in the list is no surprise, it’s good to find data readers there too.
An open data reader can be passed to the data-binding engine of a
control. The control will then walk its way through the reader and
populate the user interface while keeping the connection to the
database busy.
Note
Data
binding works differently for Web pages and Microsoft Windows desktop
applications. Aside from the internal implementations, both Web and
Windows forms can share the same data source objects with the exception
of the data reader.
|
The DataSet
class can contain more than one table; however, only one table at a
time can be associated with standard ASP.NET data-bound controls. If
you bind the control to a DataSet, you then need to set an additional property to select a particular table within the DataSet.
Be aware that this limitation is not attributable to ASP.NET as a
platform; it is a result of the implementation of the various
data-bound controls. In fact, you could write a custom control that
accepts a DataSet as its sole data-binding parameter.
DataSet and DataTable act as data sources through the IListSource interface; DataView and data readers, on the other hand, implement IEnumerable directly.
Collection-Based Classes
At
the highest level of abstraction, a collection serves as a container
for instances of other classes. All collection classes implement the ICollection interface, which in turn implements the IEnumerable interface. As a result, all collection classes provide a basic set of functionalities. All collection classes have a Count property to return the number of cached items; they have a CopyTo method to copy their items, in their entirety or in part, to an external array; they have a GetEnumerator method that instantiates an enumerator object to loop through the child items. GetEnumerator is the method behind the curtain whenever you call the foreach statement in C# and the For...Each statement in Microsoft Visual Basic.
IList and IDictionary are two interfaces that extend ICollection, giving a more precise characterization to the resultant collection class. ICollection provides only basic and minimal functionality for a collection. For example, ICollection does not have any methods to add or remove items. Add and remove functions are exactly what the IList interface provides. In the IList interface, the Add and Insert methods place new items at the bottom of the collection or at the specified index. The RemoveRemoveAt methods remove items, while Clear
empties the collection. Finally, Contains verifies whether an item with a given value belongs to the collection, and IndexOf returns the index of the specified item. Commonly used container classes that implement both ICollection and IList are Array, ArrayList, StringCollection, and generics. and
The IDictionary interface defines the API that represents a collection of key/value pairs. The interface exposes methods similar to IList, but with different signatures. Dictionary classes also feature two extra properties, Keys and Values. They return collections of keys and values, respectively, found in the dictionary. Typical dictionary classes are ListDictionary, Hashtable, and SortedList.
You’ll
likely use custom collection classes in ASP.NET data-binding scenarios
more often than you’ll use predefined collection classes. The simplest
way to code a custom collection in .NET 1.x is to derive a new class
from CollectionBase and override at least the method Add and the Item property, as shown in the following code snippet:
public class OrderCollection : CollectionBase
{
public OrderCollection()
{
}
// Add method
public void Add(OrderInfo o)
{
InnerList.Add(o);
}
// Indexer property
public OrderInfo this[int index]
{
get { return (OrderInfo) InnerList[index]; }
set { InnerList[index] = value; }
}
}
public class OrderInfo
{
private int _id;
public int ID
{
get { return _id; }
set { _id = value; }
}
private DateTime _date;
public DateTime Date
{
get { return _date; }
set { _date = value; }
}
...
}
It is important that the element class—OrderInfo, in the preceding code—implements data members as properties, instead of fields.
public class OrderInfo
{
public int ID;
public DateTime Date;
}
Data members coded as fields
are certainly faster to write, but they are not discovered at run time
unless the class provides a custom type descriptor (read, it implements
the ICustomTypeDescriptor interface) that exposes fields as properties.
In
ASP.NET 2.0 and newer versions, the advent of generics greatly
simplifies the development of custom collections. In some cases, the
code to write reduces to the following:
using System.Collections.ObjectModel;
public class OrderCollection : Collection<OrderInfo>
{
// Optional:: Add methods overrides
}