Выражение вызова в LINQ to Entities Выберите с помощью LINQkit

Вот что я хочу сделать:

class MyDbContext : DbContext 
{
    private static Expression<Func<MyClass, int>> myExpression1 = x => /* something complicated ... */;
    private static Expression<Func<Item, int>> myExpression2 = x => /* something else complicated ... */;

    public object GetAllData()
    {
        return (
            from o in MyClassDbSet.AsExpandable() 
            select new 
            {
                data1 = myExpression1.Invoke(o),                      // problem 1
                data2 = o.Items.Select(myExpression2.Compile())       // problem 2
            }
        );
    }
}

ОБНОВЛЕНИЕ:

myExpression должен оставаться отделенным от моего запроса, потому что я хочу повторно использовать его в нескольких запросах LINQ.

ОБНОВЛЕНИЕ 2:

Разделил myExpression на myExpression1 и myExpression2, чтобы прояснить тот факт, что я хочу повторно использовать их по отдельности.

ОБНОВЛЕНИЕ 3:

В пример добавлен LINQkit.

Проблема 1 выдает: невозможно привести объект типа «System.Linq.Expressions.FieldExpression» к типу «System.Linq.Expressions.LambdaExpression».

Проблема 2 выдает: Внутренняя ошибка поставщика данных .NET Framework 1025.


person billy    schedule 16.07.2013    source источник
comment
Взгляните на LINQKit.   -  person svick    schedule 17.07.2013
comment
Спасибо, попробую!   -  person billy    schedule 17.07.2013
comment
@svick, я обновил вопрос, чтобы отразить проблемы, с которыми я столкнулся при использовании LINQkit...   -  person billy    schedule 17.07.2013


Ответы (2)


В первой проблеме при использовании LinqKit вам нужно присвоить выражение локальной переменной до того, как .Invoke() его вызовет. Более полное объяснение можно найти в этом вопросе.

Вторая проблема заключается в том, что метод select принимает объект типа:

Expression<Func<TSource, TResult>>

Это означает, что вы должны предоставить лямбда-выражение, принимающее объект TSource в качестве параметра и возвращающее объект TResult.

Ваш объект TSource — это Item, то есть таблица, из которой вы делаете запрос. Ваш TResult - это int в вашем примере, это то, что вы определили в выражении.

Таким образом, вы должны вызвать .Invoke() для второго выражения, передающего объект Item в качестве параметра, так же, как вы передавали объект MyClassDbSet "o". На самом деле, в обоих операторах select есть только синтаксическая разница, они, по сути, делают одно и то же.

И вы не должны вызывать .Compile() для выражений, это создает:

Func<TSource, TResult>

Который является делегатом скомпилированной версии дерева выражений и не может быть преобразован в выражение SQL. Дополнительную информацию можно найти здесь.

Он должен работать со следующими изменениями:

class MyDbContext : DbContext 
{
    private static Expression<Func<MyClass, int>> myExpression1 = x => /* something complicated ... */;
    private static Expression<Func<Item, int>> myExpression2 = x => /* something else complicated ... */;

    public object GetAllData()
    {
        Expression<Func<MyClass, int>> myLocalExpression1 = myExpression1;
        Expression<Func<MyClass, int>> myLocalExpression2 = myExpression2;

        return (
            from o in MyClassDbSet.AsExpandable() 
            select new 
            {
                data1 = myLocalExpression1.Invoke(o),
                data2 = o.Items.Select(item => myLocalExpression1.Invoke(item)) 
            }
        );
    }
}
person Lucas Corsaletti    schedule 22.01.2014

Вы можете попробовать использовать только делегат, а не выражение:

  private static Func<MyClass, int> myExpression = x => /* something complicated ... */;

Проблема с этим подходом заключается в том, что весь MyClass будет извлечен из базы данных, а не только поля, необходимые для вычисления выражения.

person fcuesta    schedule 16.07.2013