Почему свойства навигации в linq для объектов внутри подзапроса не реализуют IQueryable?

#c# #.net #.net-3.5 #expression

#c# #.net #.net-3.5 #выражение

Вопрос:

Недавно я заметил кое-что странное: свойства навигации в объекте не реализуют IQueryable, хотя, насколько я знаю, извлечение одного объекта и последующее объединение операторов в цепочку в свойстве навигации может привести к множеству (по 1 на элемент) обращений к базе данных из сгенерированного кода, который, как я видел, имеет один большой запрос с результатами подзапросов (когда это возможно) в одном большом запросе.

Однако мне интересно, как это работает, потому что перегрузки, доступные в подзапросе, такие же, как те, которые были бы вызваны в свойстве навигации вне подзапроса. Вопрос 1: я предполагаю, что, даже если он включен в IEnuerable, в то время как сам вызов any является частью выражения, передаваемого в where, он затем встраивается в единое дерево выражений, сгенерированное в where и проанализированное там, а не как отдельное выражение (поскольку оно само по себе не является выражением), это правильно?

Вопрос 2: Если # 1 верно, как это работает, если вы создаете код извне из выражения, который может быть создан только в делегате во время выполнения (например, как бы это сработало, если бы я передал PredicateExprEntityTwo.Compile() в q5, не скомпилировался бы и не сработал во время выполнения, потому что компилятор не знает об использовании функции в выражении во время компиляции?

Вопрос 3: Предполагая, что у меня правильные # 1 и # 2, в чем преимущество этого дизайна по сравнению с использованием выражения там? Недостаток, с которым я столкнулся, заключается в том, что для бизнес-проверки я думал о наличии набора предикатов с включенной в них бизнес-логикой для фильтрации объектов одного и того же типа во многих местах программы, однако я могу захотеть использовать их и в подзапросах, и, предполагая, что # 2 правильный, может быть, нецелесообразно повторно использовать один и тот же?

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

 public class Class1
{
    void Test()
    {
        Func<Entity1, bool> PredicateFuncEntityOne = i => i.Id == 2;
        Expression<Func<Entity1, bool>> PredicateExprEntityOne = i => i.Id == 2;
        Func<Entity2, bool> PredicateFuncEntityTwo = i => i.Id == 2;
        Expression<Func<Entity2, bool>> PredicateExprEntityTwo = i => i.Id == 2;
        using (var Context = new TestModelContainer())
        {
            // Works as this expects an expression
            var q1 = Context.Entity1Set.Where(PredicateExprEntityOne);
            // Works but would call the IEnumerable version
            var q2 = Context.Entity1Set.Where(PredicateFuncEntityOne);
            // This compiles, any on item.Entity2 expects a func on IEnumerable, not IQueryable overload
            var q3 = Context.Entity1Set.Where(item => item.Entity2.Any(PredicateFuncEntityTwo));
            // This fails for the reason mentioned above
            var q4 = Context.Entity1Set.Where(item => item.Entity2.Any(PredicateExprEntityTwo));
            // Does this work and if so how is it possible?
            var q5 = Context.Entity1Set.Where(item => item.Entity2.Any(PredicateExprEntityTwo.Compile()));
        }
    }
}
  

Ответ №1:

Q1: Я тоже думаю, что это правильно.

Q2: Да, если вы передадите функцию или любой делегат в выражение, то компиляция запроса завершится неудачей.

Я не уверен насчет Q3.

Но некоторое время назад у меня была похожая проблема во время игры в EF. И я обнаружил, что приведение этих реляционных свойств к IQueryable и вызов выражений методов подавляющим образом сработали. Но это может быть случайностью.

Комментарии:

1. Здравствуйте, хотя это утешает, на самом деле это не отвечает на мои вопросы, я действительно ищу ответы на уровне спецификации по этому вопросу, поскольку я собираюсь основывать на этом некоторые сильные решения по разработке программы, и хотя я мог бы протестировать это, я бы предпочел четкое объяснение того, как и почему (особенно потому, что то, что я предлагаю, противоречит интуиции по сравнению с маркировкой их как IQueryable, и мне действительно интересно, не упускаю ли я что-то).