4

All,

I have a grid view that has the following columns. The paging work great, but not sorting. Everytime I click on the Category column to sort by category I would get this error:

Instance property 'Category.CategoryName' is not defined for type 'ESA.Data.Models.Entity.Project'

This error statement is not true because the gridview was able to display the column correctly.

Here is the select method

    public IQueryable<Project> getProjects()
    {
        ApplicationServices objServices = new ApplicationServices();
        IQueryable<Project> lstProject;
        lstProject = objServices.getProjects();
        return lstProject;
    }

Any suggestion?

    <asp:GridView ID="grdProject" runat="server" ShowHeader="true" 
        AutoGenerateColumns="false" CellPadding="2" CellSpacing="2" 
        ItemType="ESA.Data.Models.Entity.Project"
        SelectMethod="getProjects"
        DataKeyNames="ProjectID" 
        AllowSorting="true"
        AllowPaging="true"
        PageSize="5">
        <Columns>
            <asp:BoundField DataField="ProjectID" HeaderText="ID " ItemStyle-Width="10" />
            <asp:BoundField DataField="Category.CategoryName" HeaderText="Category" SortExpression="Category.CategoryName" />
            <asp:BoundField DataField="ProjectName" HeaderText="Project Name" ItemStyle-Width="300"  />
            <asp:BoundField DataField="Status.StatusName" HeaderText="Status" SortExpression="Status.StatusName"  />
            <asp:BoundField DataField="AddedByUser.UserName" HeaderText="Added By" ItemStyle-Width="120"  />
            <asp:BoundField DataField="AddedDate" HeaderText="Added Date" ItemStyle-Width="90" DataFormatString="{0:d}"  />
        </Columns>
    </asp:GridView>
JYL
  • 8,228
  • 5
  • 39
  • 63
dnguyen77
  • 73
  • 1
  • 5
  • can you paste the code you send the data from the controller. – İsmet Alkan May 14 '13 at 18:31
  • i suggest taking a look here, you might need to add an `asp:LinqDataSource` tag: http://forums.asp.net/t/1213261.aspx/1 – İsmet Alkan May 14 '13 at 18:33
  • Ismet this is not ASP.NET MVC. It is ASP.Net Webform 4.5, and I am trying to use the new Model Binding feature that ASP.NET 4.5 has to offer. – dnguyen77 May 14 '13 at 18:48
  • ok i see, have you checked the link? – İsmet Alkan May 14 '13 at 18:55
  • Yes on that link the user seem to have a diffrent issue. Where the column doesn't show the data. My issue is that the gridview will render the correct data, but not able to sort by that same column. – dnguyen77 May 14 '13 at 19:01

3 Answers3

4

I was having a similar issue with a Listview control. I solved it like this.

firstly I'm using the code from this post by Marc Gravell Dynamic LINQ OrderBy on IEnumerable<T>

in my Listview's 'OnSorting' event I added the following code.

protected void lv_Sorting(object sender, ListViewSortEventArgs e)
{
    e.Cancel = true;
    ViewState["OrderBy"] = e.SortExpression;
    lvList.DataBind();
}

I added a fairly standard way to capture the sortdirection list this

public SortDirection sortDirection
{
    get
    {
        if (ViewState["sortdirection"] == null)
        {
            ViewState["sortdirection"] = SortDirection.Ascending;
            return SortDirection.Ascending;
        }
        else if ((SortDirection)ViewState["sortdirection"] == SortDirection.Ascending)
        {
            ViewState["sortdirection"] = SortDirection.Descending;
            return SortDirection.Descending;
        }
        else
        {
            ViewState["sortdirection"] = SortDirection.Ascending;
            return SortDirection.Ascending;
        }
    }
    set
    {
        ViewState["sortdirection"] = value;
    }
}

In my Listview the Selectmethod looks like this (using the extension method from Marc)

public IQueryable<SomeObject> GetObjects([ViewState("OrderBy")]String OrderBy = null)
{
    var list = GETSOMEOBJECTS();
    if (OrderBy != null)
    {
        switch (sortDirection)
        {
            case SortDirection.Ascending:
                list = list.OrderByDescending(OrderBy);
                break;
            case SortDirection.Descending:
                list = list.OrderBy(OrderBy);
                break;
            default:
                list = list.OrderByDescending(OrderBy);
                break;
        }
    }
    return list;
}

I Haven't tried it with a GridView but I'm fairly certain it would work just the same.

EDIT Here is an example of the linq extension class that should work

public static class LinqExtensions
{
    public static IOrderedQueryable<T> OrderBy<T>(this IQueryable<T> source, string property)
    {
        return ApplyOrder<T>(source, property, "OrderBy");
    }
    public static IOrderedQueryable<T> OrderByDescending<T>(this IQueryable<T> source, string property)
    {
        return ApplyOrder<T>(source, property, "OrderByDescending");
    }
    public static IOrderedQueryable<T> ThenBy<T>(this IOrderedQueryable<T> source, string property)
    {
        return ApplyOrder<T>(source, property, "ThenBy");
    }
    public static IOrderedQueryable<T> ThenByDescending<T>(this IOrderedQueryable<T> source, string property)
    {
        return ApplyOrder<T>(source, property, "ThenByDescending");
    }
    static IOrderedQueryable<T> ApplyOrder<T>(IQueryable<T> source, string property, string methodName)
    {
        string[] props = property.Split('.');
        Type type = typeof(T);
        ParameterExpression arg = Expression.Parameter(type, "x");
        Expression expr = arg;
        foreach (string prop in props)
        {
            // use reflection (not ComponentModel) to mirror LINQ
            PropertyInfo pi = type.GetProperty(prop);
            expr = Expression.Property(expr, pi);
            type = pi.PropertyType;
        }
        Type delegateType = typeof(Func<,>).MakeGenericType(typeof(T), type);
        LambdaExpression lambda = Expression.Lambda(delegateType, expr, arg);

        object result = typeof(Queryable).GetMethods().Single(
                method => method.Name == methodName
                        && method.IsGenericMethodDefinition
                        && method.GetGenericArguments().Length == 2
                        && method.GetParameters().Length == 2)
                .MakeGenericMethod(typeof(T), type)
                .Invoke(null, new object[] { source, lambda });
        return (IOrderedQueryable<T>)result;
    } 
}

Simply add a using 'whatevernamespaceyouused' to the page and you should be good to go.

Community
  • 1
  • 1
Drauka
  • 1,217
  • 2
  • 10
  • 21
  • thank you so much for your help. I was searching googling several days for the solution, and finally you helpded me solved it. – dnguyen77 May 15 '13 at 20:55
  • By setting `e.Cancel = true;` you disable the GridView sort logic, so you don't have the specific styles for the "sorted" state, such as `SortedAscendingCellStyle` or `SortedAscendingHeaderStyle`. Moreover, the "sort direction" logic is not exactly the same as the gridView. Despite this, this solution works. – JYL Aug 03 '14 at 09:21
  • I've added [an answer](http://stackoverflow.com/a/25170208/218873) which is compatible with gridView logic and styles. – JYL Aug 06 '14 at 21:09
2

Add a string parameter named sortByExpression to your getProjects() method.

If the gridView find this parameter in your method signature, it won't try to "auto order" your result and it will let you do the job.

To do the job yourself, you can use DynamicLinq (you can add the nuget package, or use the LinqExtensions class posted by Drauka).

So your method will look like this :

// using System.Linq.Dynamic;

public IQueryable<Project> getProjects(string sortByExpression)
{
    ApplicationServices objServices = new ApplicationServices();
    IQueryable<Project> lstProject = objServices.getProjects();
    if (!String.IsNullOrEmpty(sortByExpression))
        lstProject = lstProject.OrderBy(sortByExpression);
    return lstProject;
}

This way you don't bypass the gridView sort styles.

Source : decompiled framework code, specially System.Web.UI.WebControls.ModelDataSourceView.IsAutoSortingRequired(...)

Community
  • 1
  • 1
JYL
  • 8,228
  • 5
  • 39
  • 63
  • @Drauka Yes, I use this for my own website (Asp.net 4.5, EF5). This solution use the same "OrderBy" method as yours. – JYL Aug 08 '14 at 14:16
0

Drauka your solution works for me, but I would get an error on this line of code eventhough I already reference System.Linq.Dynamic

The two line that give me syntax error are

lstProject = lstProject.OrderByDescending(OrderBy);

and the error messge is

The type arguments for method 'System.Linq.Enumerable.OrderByDescending(System.Collections.Generic.IEnumerable, System.Func)' cannot be inferred from the usage. Try specifying the type arguments explicitly.

    public IQueryable<Project> getProjects([ViewState("OrderBy")]String OrderBy = null)
    {
        ApplicationServices objServices = new ApplicationServices();
        var lstProject = objServices.getProjects();
        if (OrderBy != null)
        {
            switch (sortDirection)
            {
                case SortDirection.Ascending:
                    lstProject = lstProject.OrderByDescending(OrderBy);
                    break;
                case SortDirection.Descending:
                    lstProject = lstProject.OrderBy(OrderBy);
                    break;
                default:
                    lstProject = lstProject.OrderByDescending(OrderBy);
                    break;
            }
        }
        return lstProject;

    }
dnguyen77
  • 73
  • 1
  • 5