Welcome Guest!
Create Account | Login
Locator+ Code:

Search:
FTPOnline Channels Conferences Resources Hot Topics Partner Sites Magazines About FTP RSS 2.0 Feed

Free Trial Issue of Visual Studio Magazine

email article
printer friendly
get the code
more resources

Use DataSets as Business Objects
Take advantage of OOP and runtime metadata-based techniques to enable DataSets to perform validations.
by Enrico Sabbadin

Posted January 12, 2004

Technology Toolbox: C#, XML

In a service-oriented application based on the application/domain architectural model, lightweight business objects are responsible for encapsulating their data to trigger proper field-level validation and validation of the whole set of data the business object manages (see the sidebar, "Understand Design Patterns"). Unfortunately, .NET's primary candidate for holding your business data—the ADO.NET DataSet—doesn't provide a natural way to encapsulate and protect the data it contains. The DataSet triggers events when data changes, but it delegates validation responsibilities to outer classes, thereby breaking the principles of encapsulation and self-containment. "Naked" DataSets might be acceptable for some simple applications, but larger applications require a better container.

The mainstream approach to this issue is to define your own business objects. This typically implies the definition of a layer super-type and a custom framework that provides low-level services, especially for Object-Relational (O-R) mapping. This solution has several benefits, but it has drawbacks too. First, it requires you to renounce the many tools and wizards in VS.NET that work on the DataSet data type—a high price to pay in terms of productivity. Second, you must develop your own O-R mapping routines instead of using the .NET Framework's out-of-the-box mapping tool—the DataAdapter class.

ADVERTISEMENT

This article explores the easiest way to extend the DataSet class to make it protect its own data: augmenting the DataSet type with standard object-oriented programming and some runtime metadata-based techniques. This enables you to use both standard custom-validation code and a declarative approach to hook validation modules automatically into the DataSet object. Both types of code trigger automatically when the data is modified or saved.

You might wonder if extending typed DataSets is a viable solution for data encapsulation. Unfortunately, it isn't. The whole typed DataSet is regenerated after each schema change, so writing validation code within the typed field-setter methods isn't a realistic option. Also, even if you manage to protect typed setter methods, setter methods inherited from the DataRow base type aren't overridable, so you could bypass validation checks by setting values through indexers, as you do with untyped DataSets.

The approach I'll describe is based on the implementation of a DataSet-derived class that receives notifications of data changes from the DataSet it inherits from. The relevant notifications are DataTable's ColumnChanging event for single-field validation and DataTable's RowChanging event for row-wide validation. (The RowChanging event fires when row changes are committed with the EndEdit method.)

Create a New Class
You start by creating a new class called SmartDS, which inherits from System.Data.DataSet. This class hooks into the ColumnChanging and RowChanging events of all DataTables defined in DataSet. You hook into the DataTableCollection's CollectionChanged event in the SmartDS constructor (instead of iterating at some point through the collection of tables and hoping no tables will be added later):

public class SmartDS1 : DataSet {
        public SmartDS1() {
this.Tables.CollectionChanged +=         new 
        CollectionChangeEventHandler(
        this.SchemaChangedHook);
}

private void SchemaChangedHook(
        object sender,  CollectionChangeEventArgs e) {
        ...
}

The preceding code guarantees a notification will occur when a table is added to DataSet, no matter how it's added. You hook the SchemaChangedHook method into the DataTable events you're interested in (and unhook it when tables are removed):

private void SchemaChangedHook(object 
        sender, CollectionChangeEventArgs e) 
{
if (e.Action == 
        CollectionChangeAction.Add ) {
if (e.Element is DataTable ) {

//hook into the ColumnChanging Event
((DataTable)e.Element).ColumnChanging 
        +=new DataColumnChangeEventHandler(
        DS_ColumnChanging); 
//hook into the RowChangin Event
((DataTable)e.Element).RowChanging +=new 
        DataRowChangeEventHandler(
        DS_RowChanging);

        }
}
else if (e.Action == 
        CollectionChangeAction.Remove ) {
//unsubscribe from row and column 
//changing events 
        }
}



Back to top














Java Pro | Visual Studio Magazine | Windows Server System Magazine
.NET Magazine | Enterprise Architect | XML & Web Services Magazine
VSLive! | Thunder Lizard Events | Discussions | Newsletters | FTP Home