2008-10-22 2 views
6

Я использую T4 для создания репозиториев для объектов LINQ to Entities.Как проверить наличие OrderBy в дереве выражений ObjectQuery <T>

Репозиторий содержит (среди прочего) метод списка, подходящий для подкачки. Документация для Supported and Unsupported Methods не упоминает об этом, но вы не можете «позвонить» Skip по неупорядоченному IQueryable. Это поднимет следующее исключение:

System.NotSupportedException: Метод «Пропустить» поддерживается только для отсортированного ввода в LINQ к Entities. Метод «OrderBy» должен вызываться перед методом «Пропустить» ..

Я решил это, позволяя определить значение по умолчанию для сортировки с помощью частичного метода. Но у меня проблемы с проверкой, действительно ли в дереве выражений есть OrderBy.

Я уменьшил эту проблему как меньше кода, насколько это возможно:

public partial class Repository 
{ 
    partial void ProvideDefaultSorting(ref IQueryable<Category> currentQuery); 

    public IQueryable<Category> List(int startIndex, int count) 
    { 
     IQueryable<Category> query = List(); 
     ProvideDefaultSorting(ref query); 
     if (!IsSorted(query)) 
     { 
      query = query.OrderBy(c => c.CategoryID); 
     } 
     return query.Skip(startIndex).Take(count); 
    } 
    public IQueryable<Category> List(string sortExpression, int startIndex, int count) 
    { 
      return List(sortExpression).Skip(startIndex).Take(count); 
    } 
    public IQueryable<Category> List(string sortExpression) 
    { 
     return AddSortingToTheExpressionTree(List(), sortExpression); 
    } 
    public IQueryable<Category> List() 
    { 
      NorthwindEntities ent = new NorthwindEntities(); 
      return ent.Categories; 
    } 

    private Boolean IsSorted(IQueryable<Category> query) 
    { 
     return query is IOrderedQueryable<Category>; 
    } 
} 

public partial class Repository 
{ 
    partial void ProvideDefaultSorting(ref IQueryable<Category> currentQuery) 
    { 
     currentQuery = currentQuery.Where(c => c.CategoryName.Contains(" ")); // no sorting.. 
    } 
} 

Это не мое реальное воплощение!

Но мой вопрос есть, как я мог реализовать метод IsSorted? Проблема в том, что запрос LINQ to Entities всегда имеет тип ObjectQuery, который реализует IOrderedQueryable.

Итак, как я должен убедиться, что в дереве выражений присутствует метод OrderBy? Единственный вариант для синтаксического анализа дерева?

Update
Я добавил два других перегрузки, чтобы сделать ясно, что речь идет не о том, как добавить сортировку поддержки в хранилище, но как проверить, если метод частичного ProvideDefaultSorting действительно добавляет OrderBy к выражению дереву ,

Проблема заключается в том, что первый частичный класс генерируется шаблоном, а реализация второй части частичного класса производится членом команды в другое время. Вы можете сравнить его с тем, как .NET Entity Framework генерирует EntityContext, он позволяет использовать точки расширения для других разработчиков. Поэтому я хочу попытаться сделать его надежным и не сбой, если ProvideDefaultSorting не выполнен правильно.

Так что, может быть, вопрос больше, как я могу подтвердить, что ProvideDefaultSorting действительно добавил сортировку в дерево выражений.

Update 2
Новый вопрос был дан ответ, и принял, я думаю, что я должен изменить название, чтобы соответствовать вопрос больше. Или я должен оставить текущий заголовок, потому что это приведет людей с той же проблемой к этому решению?

+0

Вы должны увидеть этот ответ http://stackoverflow.com/questions/36923850/how-to-know-if-orderby-was-applied-to-query – yosbel 2016-04-28 20:24:04

ответ

1

Вы можете обратиться в этом возвращаемом типе ProvideDefaultSorting. Этот код не строится:

public IOrderedQueryable<int> GetOrderedQueryable() 
    { 
     IQueryable<int> myInts = new List<int>() { 3, 4, 1, 2 }.AsQueryable<int>(); 
     return myInts.Where(i => i == 2); 
    } 

Этот код строит, но коварный, и кодер получает то, что заслуживает.

public IOrderedQueryable<int> GetOrderedQueryable() 
    { 
     IQueryable<int> myInts = new List<int>() { 3, 4, 1, 2 }.AsQueryable<int>(); 
     return myInts.Where(i => i == 2) as IOrderedQueryable<int>; 
    } 

Та же история с реф (это не строит):

public void GetOrderedQueryable(ref IOrderedQueryable<int> query) 
    { 
     query = query.Where(i => i == 2); 
    } 
1

Боюсь, это немного сложнее. Вы видите, что в определенных обстоятельствах Entity Framework будет иметь значение silently ignore an OrderBy. Так что недостаточно просто искать OrderBy в дереве выражений. OrderBy должен находиться в «правильном» месте, а определение «правильного» места - это деталь реализации Entity Framework.

Как вы уже догадались, я нахожусь на том же месте, что и вы; Я использую шаблон репозитория сущности и делаю Take/Skip на уровне презентации. Решение, которое я использовал, возможно, не идеальное, но достаточно хорошее для того, что я делаю, заключается в том, чтобы не делать никаких заказов до последнего момента, чтобы гарантировать, что OrderBy всегда является последним в дереве выражений. Таким образом, любое действие, которое будет выполнять Take/Skip (прямо или косвенно), сначала вставляет OrderBy. Код структурирован таким образом, что это может произойти только один раз.

+0

okay , это облом. Итак, если я правильно понимаю, вы перемещаете логику, если она отсортирована или нет на уровне презентации? Я не могу сказать, что это звучит как идеальное решение. – 2008-10-22 13:20:53

+0

Поскольку мы разрешаем пользователям динамически применять данные, сортировка на уровне презентации имеет для нас смысл. Но я не буду утверждать, что он подходит для всех приложений. Сделайте это там, где это имеет смысл для вас, но убедитесь, что это последнее, что вы делаете до Take/Skip, и это делается только один раз. – 2008-10-22 13:41:59

+0

Я понимаю, что вы разрешаете пользователям динамически использовать ваши данные (например, gridview). Один из моих перегруженных методов List имеет параметр String sortExpression, который анализируется и преобразуется в выражение LINQ. Возможно, проблема заключается в том, как обеспечить соблюдение сортировки по умолчанию. – 2008-10-22 14:01:19

2

Пейджинг зависит от порядка заказа. Почему бы не плотно связать операции? Вот один из способов сделать это:

объекты Поддержка

public interface IOrderByExpression<T> 
{ 
    ApplyOrdering(ref IQueryable<T> query); 
} 

public class OrderByExpression<T, U> : IOrderByExpression<T> 
{ 
    public IQueryable<T> ApplyOrderBy(ref IQueryable<T> query) 
    { 
    query = query.OrderBy(exp); 
    } 
    //TODO OrderByDescending, ThenBy, ThenByDescending methods. 

    private Expression<Func<T, U>> exp = null; 

    //TODO bool descending? 
    public OrderByExpression (Expression<Func<T, U>> myExpression) 
    { 
    exp = myExpression; 
    } 
} 

Рассмотренный метод:

public IQueryable<Category> List(int startIndex, int count, IOrderByExpression<Category> ordering) 
{ 
    NorthwindEntities ent = new NorthwindEntities(); 
    IQueryable<Category> query = ent.Categories; 
    if (ordering == null) 
    { 
     ordering = new OrderByExpression<Category, int>(c => c.CategoryID) 
    } 
    ordering.ApplyOrdering(ref query); 

    return query.Skip(startIndex).Take(count); 
} 

Через некоторое время, вызывая метод:

var query = List(20, 20, new OrderByExpression<Category, string>(c => c.CategoryName)); 
0
ProvideDefaultSorting(ref query); 
    if (!IsSorted(query)) 
    { 
      query = query.OrderBy(c => c.CategoryID); 
    } 

Изменить для:

//apply a default ordering 
    query = query.OrderBy(c => c.CategoryID); 
    //add to the ordering 
    ProvideDefaultSorting(ref query); 

Это не идеальное решение.

Он не решает проблему «фильтр в функции порядка», которую вы указали. Он решает «Я забыл реализовать заказ» или «Я не хочу заказывать».

Я проверил это решение в LinqToSql:

public void OrderManyTimes() 
    { 
     DataClasses1DataContext myDC = new DataClasses1DataContext(); 
     var query = myDC.Customers.OrderBy(c => c.Field3); 
     query = query.OrderBy(c => c.Field2); 
     query = query.OrderBy(c => c.Field1); 

     Console.WriteLine(myDC.GetCommand(query).CommandText); 

    } 

Формирует (обратите внимание на обратный порядок порядков):

SELECT Field1, Field2, Field3 
FROM [dbo].[Customers] AS [t0] 
ORDER BY [t0].[Field1], [t0].[Field2], [t0].[Field3] 
1

Благодаря David B я получил следующее решение. (Мне пришлось добавить обнаружение ситуации, когда частичный метод не был выполнен или просто вернул его параметр).

public partial class Repository 
{ 
    partial void ProvideDefaultSorting(ref IOrderedQueryable<Category> currentQuery); 

    public IQueryable<Category> List(int startIndex, int count) 
    { 
     NorthwindEntities ent = new NorthwindEntities(); 
     IOrderedQueryable<Category> query = ent.CategorySet; 
     var oldQuery = query; 
     ProvideDefaultSorting(ref query); 
     if (oldQuery.Equals(query)) // the partial method did nothing with the query, or just didn't exist 
     { 
      query = query.OrderBy(c => c.CategoryID); 
     } 
     return query.Skip(startIndex).Take(count); 
    } 
    // the rest..   
} 

public partial class Repository 
{ 
    partial void ProvideDefaultSorting(ref IOrderedQueryable<Category> currentQuery) 
    { 
     currentQuery = currentQuery.Where(c => c.CategoryName.Contains(" ")).OrderBy(c => c.CategoryName); // compile time forced sotring 
    } 
} 

Это обеспечивает во время компиляции, что если частичный метод реализован, он должен, по крайней мере, держать его IOrderdQueryable.

И когда неполный метод не реализован или просто возвращает его параметр, запрос не будет изменен и будет использовать резервный сортировку.

0

Я внедрил решение, которое сортирует любую коллекцию по его первичному ключу, поскольку порядок сортировки по умолчанию не указан. Возможно, это сработает для вас.

См. http://johnkaster.wordpress.com/2011/05/19/a-bug-fix-for-system-linq-dynamic-and-a-solution-for-the-entity-framework-4-skip-problem/ для обсуждения и кода общего назначения. (И случайное исправление ошибок для Dynamic LINQ.)

 Смежные вопросы

  • Нет связанных вопросов^_^