Brendan Tompkins

Sponsors

The Lounge

News

Advertisement

Images in this post missing? We recently lost them in a site migration. We're working to restore these as you read this. Should you need an image in an emergency, please contact us at imagehelp@codebetter.com
An ObjectDataSource/GridView Adapter for Business Entity Collections

Using ASP.NET 2.0’s new GridView control with the new ObjectDataSource allows you to properly layer your applications.  You can create strongly-typed collections of your business objects, and quickly bind them to the new Grid.  The hype sounds good.  Real good, in fact:

From Move Over DataGrid, There's a New Grid in Town!

The GridView control is the successor to the DataGrid and extends it in a number of ways. First, it fully supports data source components and can automatically handle data operations, such as paging, sorting, and editing, provided its bound data source object supports these capabilities.

The key phrase here is “provided its bound data source object supports these capabilities” When you bind your GridView to an ObjectDataSource wired to a DataSet, DataView or DataTable, you do indeed get automatic sorting, and paging, and you can also filter your data through the ObjectDataSource’s FilterExpression property.  All this adds up to a really useful control, one that you can base your entire web applications around. 

Business Entities and SOA.

Okay, great. Well, say you’ve been using Business Entities and Strongly-Typed collections in lieu of DataSets to build your application framework and business layers.  If you’ve been developing this way, you’re probably a convert.  Having a nice, clean framework of entities can be a great foundation for application development.  You also may be using a service oriented pattern to return these entity objects, instead of filling your objects with an instance method of the business entity itself. 

Both of these patterns are good to follow, but neither work out very well using the ObjectDataSource.  If you’ve done this and you try wiring up these collections to an ObjectDataSource and GridView, you’ll find that a Strongly-Typed Collection doesn’t work with the filtering (setting the ObjectDataSource’s FilterExpression property) and automatic sorting capabilities of the GridView.  The GridView’s current implementation requires that you use a DataTable, DataSet or DataView, unless you want to handle these events yourself and do your sorting on your own.  It will automatically do paging, but other than that you’re on your own.

It also doesn’t like the service oriented/entity pattern much.  It actually works best when you have smart business objects that are DataTables, DataSets or DataViews and also implement methods which populate the entities.  In Fact, the ObjectDataSource’s control designer won’t complete until you specify which method on your business object is the “Fill” method. This is too bad, because often what we need is to be able to use our entity objects, our service tier methods, and a nice GridView that does the sorting/paging/filtering stuff easily. 

Since this doesn’t exist currently, the next best thing is to create a DataTable, fill it with your entity collection’s data, and hand this off to the GridView for presentation.  It’s a little strange having to go back to a DataTable after having built a nice clean framework of entities.  Whenever I find myself having to revert back to using DataSets or DataTables, just for the sake of using one control or another, I feel Michael Corleone in Godfather III -  “Just when I thought that I was out they pull me back in.” 

This is where I really can jibe with Jeffrey’s RAD Kills message.  We have this great RAD grid, but to use it you have to revert to a sometimes poor design choice (DataSets). 

The EntityDataTable – Best of Both Worlds?

To solve this problem of allowing my business entity collections to be quickly bind-able to a GridView I whipped together a quick abstract adapter class which I can sub-class that is actually a DataTable. This way, when I bind it to a GridView, it will support all this auto-sorting/filtering stuff.   This class has two methods, GetDataTable which uses reflection and returns a DataTable containing a column for each public property in my entity class.  It also contains an abstract method Fill which can be overridden to connect to your service tier, get your collection, and call the GetDataTable method with your collection and your collection’s contained type.

using System;

using System.ComponentModel;

using System.Data;

using System.Reflection;

 

public abstract class EntityDataTable : DataTable

{

 

    [DataObjectMethod(DataObjectMethodType.Select, true)]

    public abstract DataTable Fill(object parameter);

 

    /// <summary>

    /// Gets the data table.

    /// </summary>

    /// <param name="list">The list.</param>

    /// <returns></returns>

    protected DataTable GetDataTable(System.Collections.IList list, Type typ)

    {

        DataTable dt = new DataTable();

 

        PropertyInfo[] pi = typ.GetProperties();

        foreach (PropertyInfo p in pi)

        {

            dt.Columns.Add(new DataColumn(p.Name, p.PropertyType));

        }

 

        foreach (object obj in list)

        {

            object[] row = new object[pi.Length];

            int i = 0;

 

            foreach (PropertyInfo p in pi)

            {

                row[i++] = p.GetValue(obj, null);

            }

 

            dt.Rows.Add(row);

        }

 

        return dt;

    }

}

EntityDataTable in Action

Here’s a quick example of a derived class, that adpts my SomethingCollection to easily work with the GridView and ObjectDataSource.

    public class BindableSomethingCollection : EntityDataTable

    {

        public override DataTable Fill(object parameter)

        {

            SomeService somesrv = new SomeService();

            return GetDataTable(somesrv.GetSomeCollection(parameter as SomeType), typeof(SomeContainedType));

        }       

    }

And here’s a quick ASCX control snippet that shows it all tied together:

<asp:GridView ID ="GridView1" runat="server" AutoGenerateColumns="True" EmptyDataText="No Records" ShowFooter ="True" AllowPaging ="True"  AllowSorting="True" DataSourceID="ObjectDataSource1" ></asp:GridView>

<asp:ObjectDataSource ID="ObjectDataSource1" runat="server" SelectMethod="Fill" TypeName="BindableSomethingCollection">
  <SelectParameters>
    <asp:SessionParameter DefaultValue="null" Name="parameter" SessionField="PARAMETER" Type="Object" />
  </SelectParameters>
</
asp:ObjectDataSource>

All that's left for me to do is pop my parameter in the session, and call GridView1.DataBind().. Voila!  My grid is bound to my entity collection, sorting and paging work like a charm, and I can filter my data buy calling by setting the Filter parameter of the ObjectDataSource.

[tags: ASP.NET, .NET, C#]


Posted Thu, May 11 2006 4:31 PM by Brendan Tompkins

[Advertisement]

Comments

Scott Hanselman wrote re: An ObjectDataSource/GridView Adapter for Business Entity Collections
on Thu, May 11 2006 4:48 PM
Interesting stuff, but don't you think that call to GetProperties is going to cost you? It's rather expensive.
scottgu wrote re: An ObjectDataSource/GridView Adapter for Business Entity Collections
on Thu, May 11 2006 5:14 PM
Note that one other thing you can do is implement Comparer Utilities for your custom business objects.  This allows you to use the built-in sorting support within the ObjectDataSource with any collection of a custom business class type.

Hope this helps,

Scott
Brendan Tompkins wrote re: An ObjectDataSource/GridView Adapter for Business Entity Collections
on Thu, May 11 2006 5:14 PM
Hi Scott,

Yah. I knew that would be one of the first comments.  :)

For me the call to GetProperties is small potatoes. It only happens once, and if you're doing intelligent caching on the server, it's really not much of an issue at all.  

That said, I usually favor ease of use over performance in my code, so take that with a grain of salt.  I can tell you that I'm using this with good results, and no noticable slowness with a 7K+ row set of data.
Brendan Tompkins wrote re: An ObjectDataSource/GridView Adapter for Business Entity Collections
on Thu, May 11 2006 5:19 PM
Hi the other Scott... That works too? Aside from that being extra work, I didn't go that route b/c the Grid's error messages says doesn't say anything about IComparer... It actually specifically says "use DataTable DataGrid, or DataView" or somehing of the sort. I was going to use Reflector to see if this was the case, and you could use the comparer interface, but never got around to it.
Sahil Malik wrote re: An ObjectDataSource/GridView Adapter for Business Entity Collections
on Thu, May 11 2006 5:35 PM
Re: Performance -

Reflection at that tier shouldn't be a problem. You probably are not going to databind 7K rows in one shot anyway in most real applications. So IMO, as long as you do this only for the presentation tier - you can get away with it.

Objects unfortunately are almost never a rXc linear list. They almost always need to be semi-structured. So you can't have a business object framework that is based off-of EntityDataTable, because you will almost always have to convert your semi-structured objects to linear rowsXcolumns in the somesrv.GetSomeCollection - in your case IList.

So I've gotta ask - if you're doing that work anyway ~ to create an IList, why not just stick with a DataTable as god (msft) intended it to be?

So in short - what architectural value does this code buy me?

SM
Brendan Tompkins wrote re: An ObjectDataSource/GridView Adapter for Business Entity Collections
on Thu, May 11 2006 5:50 PM
"Objects unfortunately are almost never a rXc linear list."

Yes, you're absolutely right, none of my framework is linear. But I've found that if I need to bind a list to a grid for presentation purposes, it's usually a useful get  property to pull up from a nested type into the object I'm working with.  If it's useful for one display, it's probably going to be useful enough to justify a simple getter.

Why not use DataTable for my business entities?  Well, for one thing, I find it much cleaner to use object, and use List<> collections of them in my framework.  I can serialize them easily to human readable form, expose them at the ends of web services for other people to easily consume, add only the methods that I want people to call, easily subclass them, etc etc.  
Sahil Malik wrote re: An ObjectDataSource/GridView Adapter for Business Entity Collections
on Thu, May 11 2006 5:53 PM
Fair enough - yes it hit me after I hit the send button that "you have Objects all over the place" - so you don't have a DataTable where you are trying to bind - and there are good reasons for that.
Ben Reichelt wrote re: An ObjectDataSource/GridView Adapter for Business Entity Collections
on Thu, May 11 2006 8:19 PM
Rocky Lhotka ended up writing his own CslaDataSource object for asp.net with his latest version of CSLA to make business objects that are based on CSLA play nicely with asp.net databinding.

http://www.lhotka.net/WeBlog/CommentView,guid,57dfa1ef-c33e-4a2a-a762-269f4e5235ef.aspx
Eric Wise wrote re: An ObjectDataSource/GridView Adapter for Business Entity Collections
on Thu, May 11 2006 10:14 PM
This is the reason why I break my business layer into "managers" that return strongly typed objects and "queries" that return datasets.  99.9% of the time I want paging/sorting/etc I'm on the presentation layer and I'm showing the results of a query, not really performing business logic, just binding some data to a grid for selection, etc.

When I work with the individual records I call my strongly typed classes, but for the mass display I call the query classes and get datasets.
bdiaz wrote re: An ObjectDataSource/GridView Adapter for Business Entity Collections
on Fri, May 12 2006 3:14 AM
I started working with the .NetTiers CodeSmith templates (http://www.nettiers.com) on a project that I started earlier this year and also got frustrated with the limitations of the ObjectDataSource control. I ended up writing my own as well, called the EntityDataSource, and submitted it to the .NetTiers project. After seeing it in action and reading the overwhelming responses from the users through their forum, the .NetTiers contributors asked me to become part of the team. I have written several examples in the Documentation forum, which you are welcome to review and comment on. (http://community.codesmithtools.com/forums/33/ShowForum.aspx)
Joshua Flanagan wrote re: An ObjectDataSource/GridView Adapter for Business Entity Collections
on Fri, May 12 2006 9:54 AM
One other possible gotcha - I don't think the order of properties returned by GetProperties can be guaranteed to be consistent, so the order of your columns may change across runs. I've seen this mentioned numerous times on the web - for example:
http://dotnetjunkies.com/WebLog/johnwood/archive/2005/06/28/128723.aspx

I suppose if you explicitly set the columns in the GridView (instead of using AutoGenerate), its a non-issue.
Liam Ponder wrote re: An ObjectDataSource/GridView Adapter for Business Entity Collections
on Fri, May 12 2006 9:45 PM
I agree with ewise.  The best way I have found over the years is have my business objects handle the CRUD on a individual basis. Everything else like reporting, grids, etc use either a datatable or a dataset.  Less code, more familar to jr developers trying to make a change rather having tons of comparer routines for every class.

Christopher Steen wrote Link Listing - May 13, 2006
on Sat, May 13 2006 10:37 PM
Add item to SharePoint list but returning to another
location [Via: Serge van den Oever [Macaw]
]...
bdiaz wrote re: An ObjectDataSource/GridView Adapter for Business Entity Collections
on Sun, May 14 2006 1:45 PM
I started my most recent project at the begining of the year and it required that I use ASP.NET 2.0, CodeSmith and NetTiers.  I also struggled with the ObjectDataSource and all of the work-arounds required to get it to play well with the NetTiers generated DAL.  I ended up writing my own EntityDataSource control as well as some entity relationship manager controls.  After I posted it on the forums and they saw the overwhelming response from the user base, the NetTiers contributors asked me to become part of the team!

You can check out my posts documenting features of the EntityDataSource along with several code samples:
http://community.codesmithtools.com/forums/33/ShowForum.aspx
Bil Simser wrote re: An ObjectDataSource/GridView Adapter for Business Entity Collections
on Mon, Jul 24 2006 11:05 AM
Nice post and something I'm struggling with right now. I too have objects all over the place and prefer to have strongly typed collections for my business collections.

In the past, I would just return that collection to the UI layer and have the UI bind the collection or walk through it and convert it into something UI-like (usually a datagrid). This would happen in the page code, getting a strongtly typed collection and either binding it or walking through the objects to create UI objects on the fly. It wasn't expensive (unless there were thousands on a web page, but I wouldn't do that in general) so it worked well.

What I did for using the object data source was to create an intermediate set of classes in the UI layer that knew about UI elements (data tables, etc.). They exist only in some UI classes in the App_Code (not sure if they'll selectable if they're in the code behind of the pages) and simply call my facade for my business classes and do the conversion to whatever works best for the object data source. Then I point the ods to these and can bind directly to them. I think this gives me a good separation from my business layer and doesn't sacrifice the work I've done in the backend.
Dave Dolan wrote re: An ObjectDataSource/GridView Adapter for Business Entity Collections
on Fri, Sep 8 2006 1:53 PM

I have a solution.  Instead of using the Reflection ad hoc, use the Introspector from my open source project RefGen (http://sourceforge.net/projects/refgen). This is exactly the sort of thing that it's written to handle.  It caches the reflection data by generating DynamicMethod calls to the property Getters (and setters) as well as object instantiators so that you can do it at about oh... 2000% as fast as reflection.   It works only in FullTrust scenarios at the moment, but I'm updating it to fall back to cached MethodInfo's if you only have minimal or medium trust.

dotnetCarpenter wrote re: An ObjectDataSource/GridView Adapter for Business Entity Collections
on Thu, Oct 26 2006 11:40 PM

What do you use the parameter argument for?

Craig Wilson wrote re: An ObjectDataSource/GridView Adapter for Business Entity Collections
on Sat, Nov 4 2006 12:58 PM

I saw your example and ran with it, modifying to allow Nullable types. In addition, I added the ability to add in "expression" like columns against the underlying collection to allow display of complex child properties.  You can read the article and see the code at http://www.codeproject.com/useritems/DataTableAdapter.asp.

Thanks for the idea :)

SomeGuy wrote re: An ObjectDataSource/GridView Adapter for Business Entity Collections
on Sun, May 20 2007 11:36 PM

Hey, wait a second. You shouldn't have web service calls in a business entity, right?

Prabaht wrote re: An ObjectDataSource/GridView Adapter for Business Entity Collections
on Sat, Jun 2 2007 2:16 PM

How to set the FormField Property of select method when working with Business Object and ObjectDataSource with gridview Control.

David Rodecker wrote re: An ObjectDataSource/GridView Adapter for Business Entity Collections
on Sat, Feb 2 2008 1:45 PM

Ironic that this is so complicated.  I would think that a DataTable would be naturally convert  to a ObjectDataSource.  For years we've used a custom DBCoder application which establishes the framework for all our application-to-db interaction.  The DBCoder creates structured Static Functions for all SPs; one of the SP outputs is a DataTable.  Until now, we weren't able to map this DataTable to an ObjectDataSource.  

Brendan, Thanks for this addition to our code!

Mike Quinn wrote re: An ObjectDataSource/GridView Adapter for Business Entity Collections
on Mon, Mar 3 2008 12:03 PM

Hi there, I've been searching for the same type of thing.  I ended up stumbling upon a solution that, sorta, even makes some sense.

I have the following:

One FormView to display the equivalent of the paper form that is editable online.

An object that contains some basic data and two nested objects within it.  One nested object for address info and one for line item detail.  It "looks" like this:

public class BasicObject

{

 public List<AddressInformation> Addresses;

 public List<LineItemInformation> LineItems;

}

I was able to have a ObjectDatasource for the FormView control that ties to the "BasicObject" and a GridView control within it to bind to the LineItems.

The interesting bits are here:

The formview binding

<asp:FormView ID="FormView1" runat="server" DataSourceID="MyDataSource">

The gridview binding:

<asp:GridView Width="600px" ID="GridView1" runat="server" AutoGenerateColumns="False" DataSource='<%# Eval("LineItems") %>'>

<columns>

 <asp:BoundField DataField="MyLineItemField" HeaderText="Line Item Field" ReadOnly="true"

Thanx,

-q

An Ordinary Developer’s Rants » Blog Archive » MS Demos and the Reality Gap wrote An Ordinary Developer&#8217;s Rants &raquo; Blog Archive &raquo; MS Demos and the Reality Gap
on Wed, May 21 2008 5:21 AM

Pingback from  An Ordinary Developer&#8217;s Rants  &raquo; Blog Archive   &raquo; MS Demos and the Reality Gap

Gypwvogu wrote re: An ObjectDataSource/GridView Adapter for Business Entity Collections
on Tue, Jul 14 2009 11:10 AM

GQZajk

Add a Comment

(required)  
(optional)
(required)  
Remember Me?