Определите, был ли заказан IQueryable <T>или нет - PullRequest
24 голосов
/ 14 декабря 2011

Есть ли способ узнать, был ли заказан IQueryable<T> (с использованием OrderBy или OrderbyDescending)?

Так что я знаю, стоит ли звонить OrderBy или ThenBy в коллекции.

IQueryable<Contact> contacts = Database.GetContacts();

Я пытался contacts is IOrderedQueryable<Contact>, но это всегда так.

Редактировать : я просто изменил свой пример, предыдущий не показывал мойточка.Предположим, что GetContacts использует Entity Framework и просто возвращает все записи таблицы.

Позже я применяю несколько функций к contacts, я не знаю, что делают эти функции.Они могут отсортировать или отфильтровать IQueryable<Contact>.

Когда я получу коллекцию обратно, мне нужно отсортировать ее еще раз.Для этого мне нужно знать, нужно ли мне звонить OrderBy или ThenBy.Поэтому я не переупорядочиваю всю коллекцию, если она уже отсортирована.

Ответы [ 7 ]

25 голосов
/ 06 июля 2015

Это возможно.Вот метод расширения:

public static bool IsOrdered<T>(this IQueryable<T> queryable)
{
    if (queryable == null)
    {
        throw new ArgumentNullException("queryable");
    }

    return queryable.Expression.Type == typeof(IOrderedQueryable<T>);
}
3 голосов
/ 06 ноября 2015

Да, вы можете проверить дерево IQueryable.Expression, чтобы узнать, вызывает ли оно какой-либо из методов OrderBy/ThenBy. Деревья выражений можно проверить, получив класс из ExpressionVisitor .

В System.Web есть внутренний OrderingMethodFinder, который вы можете адаптировать. Вот что я придумал:

// Adapted from internal System.Web.Util.OrderingMethodFinder http://referencesource.microsoft.com/#System.Web/Util/OrderingMethodFinder.cs
class OrderingMethodFinder : ExpressionVisitor
{
    bool _orderingMethodFound = false;

    protected override Expression VisitMethodCall(MethodCallExpression node)
    {
        var name = node.Method.Name;

        if (node.Method.DeclaringType == typeof(Queryable) && (
            name.StartsWith("OrderBy", StringComparison.Ordinal) ||
            name.StartsWith("ThenBy", StringComparison.Ordinal)))
        {
            _orderingMethodFound = true;
        }

        return base.VisitMethodCall(node);
    }

    public static bool OrderMethodExists(Expression expression)
    {
        var visitor = new OrderingMethodFinder();
        visitor.Visit(expression);
        return visitor._orderingMethodFound;
    }
}

Используйте это так:

bool isOrdered = OrderingMethodFinder.OrderMethodExists(myQuery.Expression);
1 голос
/ 21 марта 2013

Вы можете проверить ToString () вашего запроса, чтобы выяснить, используется ли Order By.

Когда происходит соединение, ToString IQueryable помещает паратезы в начало и конец внутреннего запроса.Поэтому, если вы найдете последние закрывающие паратезы, вы можете проверить, есть ли в вашем самом внешнем запросе предложение Order By.

    private bool isOrdered(IQueryable Data)
    {
        string query = Data.ToString();

        int pIndex = query.LastIndexOf(')');

        if (pIndex == -1)
            pIndex = 0;

        if (query.IndexOf("ORDER BY", pIndex) != -1)
        {
            return true;
        }

        return false;
    }

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

1 голос
/ 14 декабря 2011

На самом деле, вы можете.

Первая проблема, которую я обнаружил в вашем коде, заключается в том, что вы переводите коллекцию в IQueryable без какой-либо причины.

Следующий фрагмент:

var numbers = new[] {1, 5, 6, 87, 3};
Console.Write(numbers is IOrderedEnumerable<int>);
var ordered = numbers.OrderBy(c => c);
Console.Write(ordered is IOrderedEnumerable<int>);

Не нужно даже запускать: первая проверка выдает предупреждение о времени разработки, говорящее, что это выражение никогда не будет истинным.

В любом случае, если вы запустите его, онодаст вам False для первой проверки и True для второй проверки.

Вы можете сделать то же самое с IQueryable<T> и IOrderedQueryable<T>, если вы действительно используете этот тип, и не приводите коллекцию кэто.

1 голос
/ 14 декабря 2011

Краткий ответ - нет, класс Queryable не поддерживает флаг или сортировку коллекции, а также то, какой метод мог использоваться для такой сортировки.

http://msdn.microsoft.com/en-us/library/system.linq.queryable.aspx

1 голос
/ 14 декабря 2011

Вы никогда не узнаете, правильно ли упорядочены объекты, если вы сами не проверите порядок. Ваш пример легко увидеть, что они не упорядочены, потому что числа имеют естественный порядок, но IQueryable является универсальным, что означает, что он может обрабатывать различные типы объектов. Порядок, скажем, пользовательских объектов (FirstName, LastName, DateStart и LastPayDate) имеет произвольный порядок, и поэтому порядок, в котором они возвращаются, не обязательно является порядком, который вы ищете. (Что считается основным полем для сортировки? Это зависит от ваших потребностей.) Таким образом, в теории вопрос «Заказаны ли они» всегда может быть «Да!» Порядок, который вы ищете, может сильно отличаться от того, что возвращает система.

0 голосов
/ 30 мая 2019

Это работает для меня.

Когда список IQueryable сортируется (Упорядочивается), его тип изменяется на IOrderedQueryable с IQueryable.

if (iQueryableList.Expression.Type == typeof(IOrderedQueryable<T>))
{
    //IQueryable is sorted
}
else
{
    //IQueryable is sorted
}

Подробнее об этом вы можете прочитать в посте ниже.

подробнее

...