Philip Hendry's Blog

September 18, 2009

Generic Sorting Routine for ASP.NET GridView

Filed under: ASP.NET, Dev Problem, LINQ — philiphendry @ 7:41 pm

I noticed a lot of code in the current project I’m working on which looked something like this :

switch (sortExpression)
{
    case "Name":
        items.OrderBy(i => i.Name);
        break;
    case "Date":
        items.OrderBy(i => i.Date);
        break;
    case "Cost":
        items.OrderBy(i => i.Cost);
        break;
}

This was then repeated all over again for descending orders!! I’ve now replaced it with something like this :

private void Populate(string sortExpression)
{
   var items = GetData();
   gvList.DataSource = CreateValueList(items).OrderByExpression(new OrderByExpression(sortExpression));
   gvList.DataBind();  
}

The sort expression is handled by the GridView code such that AllowSorting is turned on and the OnSorting event is wired up to a method :

<asp:GridView ID="gvList" runat="server" AllowSorting="True" OnSorting="gvList_Sorting" DataKeyNames="ID">
   <Columns>
       <asp:TemplateField HeaderText="Name" SortExpression="Name">
           <ItemTemplate>
               <asp:Label Text='<%# DataBinder.Eval(Container.DataItem, "Name") %>' runat="server" ID="lblName" />
           </ItemTemplate>
       </asp:TemplateField>
   </Columns>
</asp:GridView>

The code-behind deals with the sorting events and calls populate :

protected void gvList_Sorting(object sender, GridViewSortEventArgs e)
{
   Populate(e.SortExpression + " " + GetSortDirection(e.SortExpression));
}

private string GetSortDirection(string column)
{

   // By default, set the sort direction to ascending.
   string sortDirection = "ASC";

   // Retrieve the last column that was sorted.
   string sortExpression = ViewState["SortExpression"] as string;

   if (sortExpression != null)
   {
       // Check if the same column is being sorted.
       // Otherwise, the default value can be returned.
       if (sortExpression == column)
       {
           string lastDirection = ViewState["SortDirection"] as string;
           if ((lastDirection != null) && (lastDirection == "ASC"))
           {
               sortDirection = "DESC";
           }
       }
   }

   // Save new values in ViewState.
   ViewState["SortDirection"] = sortDirection;
   ViewState["SortExpression"] = column;

   return sortDirection;
}

What this all boils down to is the OrderByExpression() extension method called in the Populate() method above:

   public static IEnumerable<TSource> OrderByExpression<TSource>(this IEnumerable<TSource> data, OrderByExpression expression)
   {
       string sortOrderMethod = (expression.SortOrder == SortOrderEnum.Ascending) ? "OrderBy" : "OrderByDescending";

       // data.OrderBy(o => o.propertyname);   
       var dataAsQueryable = data.AsQueryable<TSource>();
       ParameterExpression lambdaParameter = Expression.Parameter(typeof(TSource), "o");
       MemberExpression member = Expression.PropertyOrField(lambdaParameter, expression.SortProperties[0]);
       LambdaExpression lambda = Expression.Lambda(member, lambdaParameter);
       Type[] argumentTypes = { dataAsQueryable.ElementType, lambda.Body.Type };
       MethodCallExpression orderBy = Expression.Call(typeof(Queryable), sortOrderMethod, argumentTypes, dataAsQueryable.Expression, lambda);
       return dataAsQueryable.Provider.CreateQuery<TSource>(orderBy);
   }

I have to say thanks to Joseph Albahari and Ben Albahari of LinqPad fame since I found this solution to my problems (after getting within so close through my own efforts whilst our internet connection was down in the office!!) in the samples that came with LinqPad.

The code above creates a dynamic Linq expression then executes it through the queryables provider – although this only occurs once the whole linq expression is enumerated.

The last few modifications I need to make require the order by to cope with multiple properties (The OrderByExpression.SortProperties class already supports multiple properties but I’m only taking the first at the moment) and I need to ensure that the query always deferred since composable against a database – this is important since I’ve yet to consider paging in the GridView and I want to make sure that specifiying .Skip(n).Take(m) can be applied after the ordering but the Linq expression be composed as SQL and executed against the database in one go otherwise I’ll be returning all the rows to the application layer before extracting just the required rows for the current page.

March 19, 2008

ADO.NET Entity Framework Overview and First Look

Filed under: Dev Tools, LINQ — philiphendry @ 7:00 pm

I haven’t really taken a serious look at the Entity Framework yet so here are just a few notes I’ve jotted down to get myself started – nothing ground-breaking I’m afraid! I think my goal here will be to establish terms and a baseline view of what’s provided.

Tools

The Entity Data Model

The ADO.NET Entity Framework is an implementation of Microsoft’s Entity Data Model which supports mapping to relational schemas.

Entities

An instance of an EntityType has properties which define the entity – for example, a Person entity will have name, birthdate, height etc.) The entity must also specify Key properties which define its uniqueness. Inheritance can be employed to build on an extend previous entities.

EntitySet

Entities can exist within an EntitySet with one or more of them defined inside an EntityContainer.

Relationships

Relationships are defined by an AssociationType which defines the entities in the association, the Roles of each and their Cardinality (one-to-one, one-to-many or many-to-many.) Multiple relationships  are contained in RelationshipSets.

The ADO.NET Entity Framework

The ADO.NET Entity Framework allows you to define and program against an Entity Data Model and comprises an EntityClient that exposes a storage schema and a set of ObjectServices that exposes the Entity Data Model as strongly typed Business Objects.

EntityClient Data Provider

An EntityClient Data Provider accepts EntitySQL and adapts it to the underlying storage and therefore provides a common command syntax across many different stores of data

Conceptual Schema

The C-Space describes the schema in terms of the Entity Data Model and is typically loaded from an XML file or stream following the Conceptual Schema Definition Language (CSDL.)

Storage Schema

The S-Space defines the schema of the relational store – following the Storage Schema Definition Language (SSDL.)

Entity Mapping

The C-S Map describes the mapping between the conceptual and storage schemas following the Mapping Schema Language (MSL.)

Object Services

ObjectServices map business objects to the Entity Data Model defined by the CSDL and support querying through EntitySQL and LINQ.

Theme: Shocking Blue Green. Blog at WordPress.com.

Follow

Get every new post delivered to your Inbox.