c# - property - the linq expression node type invoke is not supported in linq to entities




Как проверить наличие OrderBy в дереве выражений ObjectQuery<T> (4)

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

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

Я использую T4 для создания репозиториев для объектов LINQ to Entities.

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

System.NotSupportedException: метод «Пропустить» поддерживается только для отсортированного ввода в LINQ to 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 присутствует в дереве выражений? Единственный вариант для синтаксического анализа дерева?

Обновить
Я добавил две другие перегрузки, чтобы уточнить, что речь идет не о том, как добавить поддержку сортировки в репозиторий, но как проверить, ProvideDefaultSorting частичный метод OrderBy добавил OrderBy в дерево выражений.

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

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

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


Вы можете указать это в возвращаемом типе 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>;
    }

Такая же история с ref (это не получается):

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

    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]

Благодаря 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.

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





code-generation