Attention: We are retiring the ASP.NET Community Blogs. Learn more >

A primitive, generic DataLayer using Generics

I've only just starting dabbling with Generics and although I can see how cool they are I can also see that it will take some time to get used to them. To me it seems that although I know what they are I'm not sure that I always use it when I should - or how I should for that matter!

Today I started tinkering with some Whidbey code and decided to try and write a DataAccessLayer which used Generics. It started off super-well. I created a small abstract base class named BaseClass and then inherited from it to create SpecializedClass. Normally I would now go about the arduous task of creating a wrapper collection class to return strongly typed instances but with Generics that's not necessary. In my DataLayer class I can return collections of SpecializedClass's like so:

public static ICollection<SpecializedClass> GetSpecializedClassByCategory(int categoryId) 
{...}

 

So, as you can see I've removed the need to create (possibly) dozens of strongly typed collection classes - NEAT!

After that I thought that I'd see if I could take it a bit further. I decided that I'd create a Data Helper class which could take a DataTable and return strongly typed collections; that way I could expand the above code to something like this:

public static ICollection<SpecializedClass> GetSpecializedClassByCategory(int categoryId) 
{
    // Fill a DataTable
    adapter.Fill( dt ) ;
    // Then return it as a strongly typed class...
    return ConvertTableToCollection<SpecializedClass>( dt );
}

 

The possibilites here looked good... if only I can get that ConvertTableToCollection method to work then I can use it to create all of my strongly typed collections! Now this is about the time that it started to get tricky for me. On paper this is what I had:

private static Collection<T> ConvertTableToCollection<T>(DataTable dt)
    where T : BaseClass
{
    Collection<T> items = new Collection<T>();
    foreach(DataRow row in dt.Rows)
    {
        // how can I use generics to enforce that T can be
        // seeded with a DataRow?  Something like...
            // items.Add( T.Create( row ) ) ;
        // or...
            // items.Add( new T( row ) ) ;
    }
    return items;
}

 

As you can see I really wasn't sure exactly how I could create the actual instances. What would have been ideal is if Generics allowed me to create a constructor constraint which took typed args such as this:

private static Collection<T> ConvertTableToCollection<T>(DataTable dt)
    where T : Item, new( DataRow )

 

Then I could simply change the above method to look like this:

private static Collection<T> ConvertTableToCollection<T>(DataTable dt)
    where T : BaseClass, new(DataRow)
{
    Collection<T> items = new Collection<T>();
    foreach(DataRow row in dt.Rows)
    {
        items.Add( new T( row ) ) ;
    }
    return items;
}

 

Unfortunately you can't do that. What I ended up doing was to create an interface named IApplicationObject with a Create(DataRow r) member on it and then add the interface constraint to the Generic method. The only problem here is that, because my interface returns BaseClass items I need to cast the return of IApplicationObject.Create to a T after the call. Here's the final code snippet:

private static Collection<T> ConvertTableToCollection<T>(DataTable dt)
    where T : Item, IApplicationObject, new()
{
    Collection<T> items = new Collection<T>() ;
    foreach(DataRow row in dt.Rows)
    {
        T item = new T() ;
        items.Add((T)item.Create(row)) ;
    }
    return items;
}

That code is so neat because now *all* of my DataLayer calls which return collections look the same internally......

public static ICollection<A> GetAs(...) 
public static ICollection<B> GetBs(...) 
public static ICollection<C> GetCs(...) 
public static ICollection<D> GetDs(...) 

 

... you just fill a data table and pass it to a 10 line Generic method!


Recommended Reading:
An Introduction to C# Generics

3 Comments

  • A proposal: Instead of using ICollection you can for example use the List&lt;T&gt; class. With a List you will have binary search and Find method etc that aren't implemented into the Collection.

  • BinarySearch only works on sorted lists, so you wouldnt' have that.



    Also, Darren, in many cases you can have a generic interface to prevent those casts. Something like IApplicationObject&lt;T&gt; would be the constraint instead. Not at a Whidbey box, so i can't test, but I know there are some constructs dealing with type parameters in constraints that are declared illegal.



    T item = new T() ;

    items.Add((T)item.Create(row)) ;



    The above could also be made easier, even without this addition by:



    items.Add((new T()).Fill(row));



    In your code you are creating an object, then using an interface method on that object to create a second object. Just as easy to have a void return method called Fill, that simply fills the object you just created. If you do this you don't even need the generic interface idea.

  • Thanks Justin... the code that I showed was actually &quot;psuedo-code&quot; so, yes, I would actually change it to:



    items.Add((new T()).Fill(row));



    As Tor your suggestion about doing something with IApplicationObject&lt;T&gt;, I'll have to take a peek at that.



    Cheers,

    - Darren

Comments have been disabled for this content.