Есть ли способ использовать метод CompiledQuery.Compile для компиляции выражения, связанного с IQueryable? В настоящее время у меня есть IQueryable с очень большим деревом выражений за ним. IQueryable был создан с использованием нескольких методов, каждый из которых предоставляет компоненты. Например, два метода могут возвращать IQueryables, которые затем объединяются в третий. По этой причине я не могу явно определить все выражение в вызове метода compile ().
Я надеялся передать выражение в метод компиляции как someIQueryable.Expression, однако это выражение не в форме, требуемой методом компиляции. Если я попытаюсь обойти это, поместив запрос непосредственно в метод компиляции, например:
var foo = CompiledQuery.Compile<DataContext, IQueryable<User>>(dc => dc.getUsers());
var bar = foo(this);
когда я делаю форму вызова в текстовом тексте, я получаю сообщение об ошибке, в котором говорится, что «getUsers не отображается как хранимая процедура или пользовательская функция». Опять же, я не могу просто скопировать содержимое метода getUsers туда, куда я делаю вызов компиляции, поскольку он, в свою очередь, использует другие методы.
Есть ли способ передать Expression для IQueryable, возвращенного из getUsers, в метод Compile?
Обновлено
Я попытался навязать свою волю системе, используя следующий код:
var phony = Expression.Lambda<Func<DataContext, IQueryable<User>>>(
getUsers().Expression, Expression.Parameter(typeof(DataContext), "dc"));
Func<DataContext, IQueryable<User>> wishful = CompiledQuery.Compile<DataContext, IQueryable<User>>(phony);
var foo = wishful(this);
foo заканчивается:
{System.Data.Linq.SqlClient.SqlProvider + OneTimeEnumerable`1 [Model.Entities.User]}
У меня нет возможности просмотреть результаты в foo, так как вместо предложения развернуть результаты и выполнить запрос я вижу только сообщение «Операция может дестабилизировать среду выполнения».
Мне просто нужно найти способ, чтобы строка sql генерировалась только один раз и использовалась в качестве параметризованной команды при последующих запросах. Я могу сделать это вручную, используя метод GetCommand для контекста данных, но затем я должен явно установить все параметры и сделать отображение объекта самостоятельно, что составляет несколько сотен строк кода, учитывая сложность этого конкретного запроса.
Обновлено
Джон Раск предоставил самую полезную информацию, поэтому я наградил его победой. Тем не менее, потребовалась некоторая дополнительная настройка, и я столкнулся с парой других проблем, поэтому я решил «развернуть» в ответе. Во-первых, ошибка «Операция могла дестабилизировать среду выполнения» произошла не из-за компиляции выражения, а из-за некоторого приведения глубоко в дерево выражений. В некоторых местах мне нужно было вызывать метод .Cast<T>()
для формального приведения предметов, даже если они были правильного типа. Не вдаваясь в подробности, это в основном требовалось, когда несколько выражений были объединены в одно дерево, и каждая ветвь могла возвращать свой тип, каждый из которых был подтипом общего класса.
После решения проблемы дестабилизации я вернулся к проблеме компиляции. Расширенное решение Джона было почти там. Он искал выражения вызова метода в дереве и пытался разрешить их в базовом выражении, которое метод обычно возвращает. Мои ссылки на выражения были предоставлены не вызовами методов, а свойствами. Поэтому мне нужно было изменить посетитель выражения, который выполняет расширение, чтобы включить следующие типы:
protected override Expression VisitMemberAccess(MemberExpression m) {
if(m.Method.DeclaringType == typeof(ExpressionExtensions)) {
return new ExpressionExpander().Visit((Expression)(((System.Reflection.PropertyInfo)m.Member).GetValue(null, null)));
}
return base.VisitMemberAccess(m);
}
Этот метод может быть не уместным во всех случаях, но он должен помочь любому, кто окажется в том же затруднительном положении.