EntityDataSource: To wrap or not to wrap

Note:  Somehow the <TEntity> generic argument had disappeared from the extension method definition. I am fixing it today after several months. Sorry for the inconvenience!

Fresh from the forums today: A customer asks how to get the real entity object in the RowDataBound event of a GridView.

We made some complex design decisions during the development of the EntityDataSource, and I guess that will give us plenty of material for blogging :)

With the EntityDataSource, we faced the interesting problem of building a bridge between the existing ASP.NET databinding infrastructure and the world of EDM. One of the techniques we use to solve the problem is wrapping entities in smart databinding objects. As Colin explains it in his answer posted to the forums:

Why are you seeing a wrapper instead of the entity? Some of the unique features of the Entity Data Model prevent us from directly binding the entity. For instance, when inserting a Product I also need to insert a relationship to a Category. The wrapper adds the relationship to the entity, basically as a foreign key value.

The wrapper object implements the ICustomTypeDescriptor interface, which makes it work well with databinding, but when you try to get your original entity object, for instance form the arguments of the RowDataBound, you will get a wrapper object instead of the entity object you are expecting.

I don't want to make things much more complicated than they need to be, but I think the generic solution may be useful for some customers.

First of all, here are the rules for wrapping:

  • The wrapping mechanism only takes place if you initialize your EntityDataSource using EntitySetName.

  • When you instead set CommandText to a query that returns entities (i.e. "SELECT VALUE c FROM Northwind.Customers AS c", then you get normal entities.

  • When you instead set CommandText to a query that returns a projection of properties (i.e. "SELECT c.CustomerID, c.CustomerName FROM Northwind.Customers AS c"), then you get a DbDataRecord.

  • Finally, if you set the Select property to do a projection (i.e. "it.CustomerID, it.CustomerName", you get DbDataRecord regardless of how you start your query.

If you use the RowDataBound event very often in your code, then, I would suggest having around some code similar to this (thanks David for coming up with this code first):

static class EntityDataSourceExtensions

{

    public static TEntity GetItemObject<TEntity>(object dataItem)

        where TEntity : class

    {

        var entity = dataItem as TEntity;

        if (entity != null)

        {

            return entity;

        }

        var td = dataItem as ICustomTypeDescriptor;

        if (td != null)

        {

            return (TEntity)td.GetPropertyOwner(null);

        }

        return null;

    }

}

And this is the usage:

protected void GridView_RowDataBound(object sender, GridViewRowEventArgs e)

{

   var entity = EntityDataSourceExtensions.GetItemObject<Product>(e.Row.DataItem);

   //...

}

 

I hope this will help some.

Comments

  • Anonymous
    May 19, 2008
    Lots to read and download from the Data Programmability Team &#160; SP1 Beta Download, what&#39;s new

  • Anonymous
    May 24, 2008
    I explained a few day ago the rules of wrapping in this blog post . But why do we wrap after all? Julie

  • Anonymous
    January 22, 2009
    Why not expose WrappedEntity of EntityDataSourceWrapper as public? Thanks

  • Anonymous
    January 24, 2009
    The comment has been removed

  • Anonymous
    February 01, 2009
    This post is about a small issue that I have seen in the forums and that arises often in cases in which

  • Anonymous
    October 02, 2009
    Thank you!  This helped a lot!

  • Anonymous
    May 12, 2010
    The comment has been removed