Click here to Skip to main content
11,641,971 members (59,488 online)
Click here to Skip to main content

DataTableAdapter

, 4 Nov 2006 CPOL 46.5K 296 38
Rate this:
Please Sign up or sign in to vote.
How to flatten collections for an ObjectDataSource using the Adapter Pattern.

Introduction

This is a class and example of how to flatten a domain model into a DataTable to ease the use of an ObjectDataSource.

Background

I have been using OR/M modeling for a while now in WinForms applications. Recently, I had the need for an ASP.NET application, and quickly got very frustrated with the ObjectDataSource. I don't want to get into a debate between DataSets and Collections. Suffice it to say, I use strongly typed collections.

Problem

When using a non-flat (hierarchical) domain model, it is impossible to bind to a complex property. For instance, in the Domain Model below, I will need to show the Instructor's FullName in the class list and subsequently be able to edit it. However, while I can bind to the Instructor, it will show whatever is in the Person's toString() method. This is fine for display, but what about editing? I could implement IConvertible<T>, but I don't need this functionality outside of my presentation layer. Therefore, let's use the Adaptor design pattern to adapt a collection of objects (or just a single object) into a DataTable.

This approach also yields a number of other advantages. The ObjectDataSource doesn't sort or page well without a lot of plumbing. When bound to a DataTable, it handles this natively so no extra code needs to be written.

DataStore.png

Using the code

Since Course doesn't have the display properties "InstructorId", "InstructorFullName", or "AvailableSlots", we can essentially mix them in to the data type as if they were a part of the class. Dynamic languages like Ruby and Python allow this on the object level, negating the need to do this in the conversion. C#3 (and VB9) have something called extension methods, but so far no mention has been made of extension properties (which we need for data binding).

[DataObjectMethod(DataObjectMethodType.Select)]
public static DataTable FindAll()
{
    DataTableAdapter<Course> dta = new DataTableAdapter<Course>();
    dta.AddColumn("InstructorId", typeof(string), 
        delegate(Course c) { return c.Instructor.Id; });
    dta.AddColumn("InstructorFullName", typeof(string), 
        delegate(Course c) { return c.Instructor.FullName; });
    dta.AddColumn("AvailableSlots", typeof(string), 
        delegate(Course c) { return c.MaxStudents - c.Students.Count; });
    return dta.GetDataTable(CourseService.FindAll());
}

Potential Issues

Obviously, reflection is being used extensively here. This may cause some performance issues depending on how large an object base is being used.

Source

The full source for the DataTableAdapter is in the App_Code folder under DataTableAdapter.cs. It is well commented and fairly self-explanatory. I would like to point out the part for which I had to search. It has to do with detecting a nullable type and then getting its underlying type. The reason this is necessary is because a DataTable will not accept a nullable type in a column definition. It uses DBNull for this value. Anyways, here is an excerpt...

//Get all the pi's once.
PropertyInfo[] pi = typeof(T).GetProperties();

Type piType;
//Add all the properties as columns
for (int i = 0; i < pi.Length; i++)
{
    piType = pi[i].PropertyType;

    //Data tables don't accept nullables in the column type.  Therefore,
    //when we encounter a nullable, we need to get it's underlying type.
    if (piType.IsGenericType && 
           piType.GetGenericTypeDefinition() == typeof(Nullable<>))
        piType = Nullable.GetUnderlyingType(piType);

    dt.Columns.Add(new DataColumn(pi[i].Name, piType));
}

References

The idea for this came from a Google search that yielded this result at Brendan Tompkin's blog.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Craig G. Wilson
Web Developer
United States United States
I work at a financial services firm using mostly .NET. I enjoy my job immensely and love teaching, training, and mentoring younger developers.

You may also be interested in...

Comments and Discussions

 
NewsSource code available online Pin
Gilad Khen30-May-09 16:38
memberGilad Khen30-May-09 16:38 
Questiondreamviewer question Pin
SSN4-Jun-08 5:14
memberSSN4-Jun-08 5:14 
GeneralJust what I've been looking for Pin
waltersm23-Oct-07 22:53
memberwaltersm23-Oct-07 22:53 
GeneralRe: Just what I've been looking for Pin
Jafin26-Dec-07 14:05
memberJafin26-Dec-07 14:05 
GeneralGood Job Pin
Marco225020-Sep-07 18:20
memberMarco225020-Sep-07 18:20 
GeneralCollections Pin
LurkingVariable21-Jan-07 10:43
memberLurkingVariable21-Jan-07 10:43 
GeneralRe: Collections Pin
Craig G. Wilson21-Jan-07 15:50
memberCraig G. Wilson21-Jan-07 15:50 
GeneralRe: Collections Pin
LurkingVariable21-Jan-07 15:59
memberLurkingVariable21-Jan-07 15:59 
GeneralData Base Connectivity Pin
Fazal_qureshi17-Dec-06 22:39
memberFazal_qureshi17-Dec-06 22:39 
GeneralRe: Data Base Connectivity Pin
Craig G. Wilson18-Dec-06 2:45
memberCraig G. Wilson18-Dec-06 2:45 
QuestionI receive the error: Parameter count mismatch Pin
klhkdsjhfksjhdfkjshf15-Nov-06 11:53
memberklhkdsjhfksjhdfkjshf15-Nov-06 11:53 
This would be really useful if i could get it to work. I keep getting the error:

Exception Details: System.Reflection.TargetParameterCountException: Parameter count mismatch.

Source Error:


Line 100: for (int i = 0; i < pi.Length; i++)
Line 101: {
Line 102: row[i] = pi[i].GetValue(item, null);
Line 103: }


on line 101.

This is part of my code that calls the DataTableAdapter class (using nHibernate)

hql.Append("select cw.Id, cw.customer, cw.currentward, cw.ward, cw.fromdate, cw.todate from CustomersWard cw");
hql.Append(" where cw.customer.Id = "+customerid);

IList usersWards = Db.Session.Find(hql.ToString());

if (usersWards.Count > 0)
{
EntityDataTable ed = new EntityDataTable();
DataTable dt = ed.GetDataTable(usersWards);

//return GetDataTable(usersWards, typeof(IList));
}

return null;

I have no idea why i might be receiving the error.
AnswerRe: I receive the error: Parameter count mismatch Pin
Craig G. Wilson15-Nov-06 12:01
memberCraig G. Wilson15-Nov-06 12:01 
GeneralRe: I receive the error: Parameter count mismatch Pin
Wilhelm Berg7-Feb-07 10:07
memberWilhelm Berg7-Feb-07 10:07 
QuestionSource Pin
LoneRanger7-Nov-06 8:32
memberLoneRanger7-Nov-06 8:32 
AnswerRe: Source Pin
Craig G. Wilson7-Nov-06 8:35
memberCraig G. Wilson7-Nov-06 8:35 
AnswerRe: Source Pin
LoneRanger8-Nov-06 6:38
memberLoneRanger8-Nov-06 6:38 
GeneralRe: Source Pin
Craig G. Wilson8-Nov-06 6:56
memberCraig G. Wilson8-Nov-06 6:56 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Terms of Use | Mobile
Web01 | 2.8.150731.1 | Last Updated 4 Nov 2006
Article Copyright 2006 by Craig G. Wilson
Everything else Copyright © CodeProject, 1999-2015
Layout: fixed | fluid