Сортировка привязки модели ASP.Net 4.5 по свойству навигации

Все,

У меня есть вид сетки, который имеет следующие столбцы. Пейджинг отлично работает, но не сортировка. Каждый раз, когда я нажимаю на столбец Категория для сортировки по категории, я получаю эту ошибку:

Свойство экземпляра "Category.CategoryName" не определено для типа "ESA.Data.Models.Entity.Project".

Это утверждение об ошибке неверно, потому что grid view смог правильно отобразить столбец.

Вот метод выбора

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

Любое предложение?

    <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>

3 ответа

Решение

У меня была похожая проблема с элементом управления Listview. Я решил это так.

во-первых, я использую код из этого поста Марка Грэвелла, динамический LINQ OrderBy на IEnumerable;

в событие OnSorting моего Listview я добавил следующий код.

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

Я добавил довольно стандартный способ захвата списка сортировки

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;
    }
}

В моем ListView метод выбора выглядит следующим образом (с использованием метода расширения от 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;
}

Я не пробовал это с GridView, но я уверен, что он будет работать точно так же.

РЕДАКТИРОВАТЬ Вот пример класса расширения linq, который должен работать

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;
    } 
}

Просто добавьте на страницу "whatevernamespaceyouused", и все будет хорошо.

Добавьте строковый параметр с именем sortByExpression на ваш getProjects() метод.

Если gridView найдет этот параметр в сигнатуре вашего метода, он не будет пытаться автоматически упорядочить ваш результат и позволит вам выполнить эту работу.

Чтобы сделать работу самостоятельно, вы можете использовать DynamicLinq (вы можете добавить пакет nuget или использовать LinqExtensions класс размещен Драукой).

Итак, ваш метод будет выглядеть так:

// 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;
}

Таким образом, вы не обойдете стили сортировки gridView.

Источник: декомпилированный фреймворк, специально System.Web.UI.WebControls.ModelDataSourceView.IsAutoSortingRequired(...)

Drauka, ваше решение работает для меня, но я получаю сообщение об ошибке в этой строке кода, хотя я уже ссылаюсь на System.Linq.Dynamic

Две строки, которые дают мне синтаксическую ошибку:

lstProject = lstProject.OrderByDescending(OrderBy);

и сообщение об ошибке

Аргументы типа для метода 'System.Linq.Enumerable.OrderByDescending(System.Collections.Generic.IEnumerable, System.Func)' не могут быть выведены из использования. Попробуйте указать аргументы типа явно.

    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;

    }
Другие вопросы по тегам