C#, Linq to Sql: почему я не могу использовать выражение для фильтрации SubEntities?

Я сделал себе класс ExpressionBuilder, который помогает мне составлять выражения, которые можно использовать в качестве предиката при выполнении запросов Linq to Sql. Это сработало отлично. Однако я только что обнаружил, что выражения можно использовать только для фильтрации таблиц, а не наборов сущностей? С какой стати это так?

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

Expression<Func<Company, bool>> cp = x => x.Name.StartsWith("Micro");
Expression<Func<Employee, bool>> ep = x => x.Name.StartsWith("John");

Затем я ожидаю, что смогу сделать следующее, однако это работает только частично:

var companies = dataContext.Companies
    .Where(cp)                                // Goes fine
    .Select(x => new 
        {
            x.Name,
            SumOfSalaries = x.Employees
                .Where(ep)                    // Causes compile-time error
                .Sum(y => y.Salary),
        }
    .ToList();

Кроме того, если я делаю ep.Compile(), он компилируется, но затем я получаю сообщение об ошибке при выполнении запроса.

Почему это так? Я что-то упускаю? Я не нахожу это логичным. Могу ли я это как-то исправить? Или у вас есть хороший обходной путь?

Я знаю, что в этом случае я мог бы просто использовать Where(x => x.Name.StartsWith("John")) вместо этого, но проблема в том, что выражения, которые мне нужны, не так уж тривиальны. Это более длинные строки из AndAlsos и OrElses.


person Svish    schedule 30.09.2009    source источник
comment
Вы пытались изменить Expression‹Func‹Employee, bool›› ep = x =› x.Name.StartsWith(John); не использовать x? Я заметил, что при использовании аргументов с простыми именами я не могу связать одно и то же имя с разными типами.   -  person JustLoren    schedule 30.09.2009
comment
не использовать х? Как бы Вы это сделали? и как это изменит? Я все равно не могу этого увидеть...   -  person Svish    schedule 30.09.2009
comment
Я опубликовал ответ на вопрос Ecyrb, который может решить эту проблему: > stackoverflow.com/questions/1424251/   -  person Merlyn Morgan-Graham    schedule 18.10.2011


Ответы (3)


Если вы собираетесь передать лямбда-выражение поставщику LINQ to SQL, не создавайте его как Expression<T> — пусть поставщик сделает это за вас.

person Andrew Hare    schedule 30.09.2009
comment
Но я не передаю это как лямбда-выражение, однако это то, что он хочет. В чем проблема, так как у меня есть предикат не как лямбда-выражение, а как Expression‹Func‹T, bool››. - person Svish; 30.09.2009

Для меня работает следующее - обратите внимание, что оба скомпилированы:

var companies = dataContext.Companies.Where(cp.Compile())
                .Select(x => new
                                 {
                                     x.Name,
                                     SumOfSalaries = x.Employees
                                        .Where( ep.Compile() )
                                        .Sum(y => y.Salary),
                                 }

                 ).ToList();

Парсер выражений, похоже, теряет информацию о типе где-то после первого предложения where, когда вы вставляете второе. Честно говоря, я пока не знаю, почему.

Изменить: Чтобы было ясно, я действительно понимаю, что EntitySet не поддерживает передачу выражения в предложение where. Чего я не совсем понимаю, так это почему происходит сбой, когда вы добавляете Where(ep.Compile()).
Моя теория заключается в том, что при компиляции первого where (Where(cp.Compile()) Linq2Sql прекращает синтаксический анализ выражение - что он не может проанализировать ep.Compile() относительно набора сущностей и не может решить разбить запрос на два, пока вы не скомпилируете первое предложение where.

person Philip Rieck    schedule 30.09.2009
comment
Уверены, что это не приводит к тому, что весь запрос выполняется локально? Или это все еще делается на сервере SQL? - person Svish; 30.09.2009
comment
Это, безусловно, заставляет весь запрос выполняться локально. - person Philip Rieck; 30.09.2009
comment
Тогда это было бы плохо :p - person Svish; 01.10.2009

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

Таким образом, с учетом заработной платы сотрудников мы можем написать:

    //Expression<Func<Employee, bool>> ep = x => x.Name.StartsWith("John");
    //Expression<Func<Company, bool>> cp = x => x.Name.StartsWith("Micro");

    Expression<Func<Employee, bool>> ep = x => x.Name.StartsWith("John");
    Expression<Func<Employee, bool>> cp = x => x.Company.Name.StartsWith("Micro");

    var salaryByCompany = dataContext.Employees
        .Where(ep)
        .Where(cp)
        .GroupBy(employee => employee.Company.Name)
        .Select(companyEmployees => new
                                        {
                                            Name = companyEmployees.Key,
                                            SumOfSalaries = companyEmployees.Sum(employee => employee.Salary)
                                        });

    var companies = salaryByCompany.ToList();
person Thomas Eyde    schedule 12.10.2009