Exec Summary:
Expression.Compile
создает метод CLR, тогда как CompiledQuery.Compile
создает делегат, который является заполнителем для SQL.
Одна из причин, по которой вы этого не сделалиполучить правильный ответ до сих пор, что некоторые вещи в вашем примере кода неверны.А без базы данных или обобщенного примера кто-то другой может сыграть с шансами, которые еще больше уменьшатся (я знаю, это трудно представить, но обычно это того стоит).
На фактах:
Expression<Func<TaskFile, double>> TimeSpent = (t =>
t.TimeEntries.Sum(te => (te.DateEnded - te.DateStarted).TotalHours));
Затем мы можем использовать вышеупомянутое в запросе LINQ, как показано ниже:
TaskFiles.Select(t => new {
t.TaskId,
TimeSpent = TimeSpent(t),
})
(Примечание: возможно, вы использовали тип Func<>
для TimeSpent. Это даетта же ситуация, что и в вашем сценарии, была описана в параграфе ниже. Обязательно прочитайте и поймите это).
Нет, это не скомпилируется.Выражения не могут быть вызваны (TimeSpent
является выражением).Сначала их нужно скомпилировать в делегат.Когда вы вызываете Expression.Compile()
, то происходит то, что дерево выражений компилируется в IL, который внедряется в DynamicMethod
, для которого вы получаете делегат.
Следующее будет работать:
var q = TaskFiles.Select(t => new {
t.TaskId,
TimeSpent = TimeSpent.Compile().DynamicInvoke()
});
Это приводит к ожидаемому выводу, за исключением того, что для подключенного выражения генерируется запрос на строку.Это видно в LINQPad.Не хорошо.
Почему это происходит?Что ж, Linq To Sql нужно будет извлечь все TaskFiles
, обезвоживать TaskFile
экземпляров и затем запустить селектор против него в памяти .Вы получаете запрос для каждого TaskFile, вероятно, потому что он содержит одно или несколько отображений 1: m.
Хотя LTS позволяет проецировать в память выборки, он не делает это для Wheres (требуется цитирование, это лучшемои знания).Когда вы думаете об этом, это имеет смысл: скорее всего, вы перенесете намного больше данных, отфильтровав целую базу данных 1040 * в памяти, а затем преобразовав ее часть в памяти.(Хотя, как вы видите, это создает проблемы с производительностью запросов, что следует учитывать при использовании ORM).
CompiledQuery.Compile()
делает что-то другое.Он компилирует запрос в SQL, и возвращаемый им делегат является только заполнителем, который Linq для SQL будет использовать для внутреннего использования.Вы не можете «вызвать» этот метод в CLR, его можно использовать только как узел в другом дереве выражений.
Так почему же тогда LTS генерирует эффективный запрос с выражением CompiledQuery.Compile
'd?Потому что он знает, что делает этот узел выражения, потому что он знает SQL, стоящий за ним.В случае Expression.Compile
это просто InvokeExpression
, который вызывает DynamicMethod
, как я объяснил ранее.
Почему для этого требуется параметр DataContext?Да, это удобнее для создания полных запросов, но это также потому, что компилятору дерева выражений необходимо знать сопоставление, используемое для генерации SQL.Без этого параметра было бы трудно найти это отображение, поэтому это очень разумное требование.