Вопрос

Есть ли способ использовать CompiledQuery.Метод Compile для компиляции выражения, связанного с IQueryable?В настоящее время у меня есть IQueryable с очень большим деревом выражений за ним.IQueryable был создан с использованием нескольких методов, каждый из которых предоставляет компоненты.Например, два метода могут возвращать IQueryables, которые затем объединяются в третий.По этой причине я не могу явно определить все выражение в вызове метода compile().

Я надеялся передать выражение в метод компиляции как someIQueryable.Выражение, однако это выражение не в форме, требуемой методом компиляции.Если я попытаюсь обойти это, поместив запрос непосредственно в метод компиляции, например:

    var foo = CompiledQuery.Compile<DataContext, IQueryable<User>>(dc => dc.getUsers());
    var bar = foo(this);

когда я создаю форму вызова внутри datacontext, я получаю сообщение об ошибке, в котором говорится, что "getUsers не отображается как хранимая процедура или определяемая пользователем функция".Опять же, я не могу просто скопировать содержимое метода getUsers туда, где я выполняю вызов compile, поскольку он, в свою очередь, использует другие методы.

Есть ли какой-нибудь способ передать выражение для 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[Модель.Сущности.Пользователь]}

У меня нет возможности просмотреть результаты в foo, так как вместо предложения развернуть результаты и запустить запрос я вижу только сообщение "Операция может дестабилизировать среду выполнения".

Мне просто нужно найти способ, чтобы строка sql генерировалась только один раз и использовалась в качестве параметризованной команды при последующих запросах, я могу сделать это вручную, используя метод getCommand в контексте данных, но тогда мне придется явно задать все параметры и самому выполнить сопоставление объектов, что составляет несколько сотен строк кода, учитывая сложность этого конкретного запроса.

Обновленный

Джон Раск предоставил самую полезную информацию, поэтому я присудил ему победу в этом деле.Однако потребовалась некоторая дополнительная настройка, и по пути я столкнулся с парой других проблем, поэтому я решил "Расширить" ответ.Во-первых, ошибка "Операция может дестабилизировать время выполнения" была вызвана не компиляцией выражения, а фактически некоторым приведением глубоко в дереве выражений.В некоторых местах мне нужно было позвонить в .Cast<T>() метод формального приведения элементов, даже если они были правильного типа.Не вдаваясь в излишние подробности, это в основном требовалось, когда несколько выражений были объединены в одно дерево и каждая ветвь могла возвращать другой тип, каждый из которых был подтипом общего класса.

Решив проблему с дестабилизацией, я вернулся к проблеме с компиляцией.Решение Джона по расширению было почти готово.Он искал выражения вызова метода в дереве и пытался преобразовать их в базовое выражение, которое обычно возвращал бы метод.Мои ссылки на выражения были предоставлены не вызовами методов, а вместо этого свойствами.Поэтому мне нужно было изменить выражение visitor, которое выполняет расширение, чтобы включить эти типы:

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);
}

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

Это было полезно?

Решение

Что-то подобное работает, по крайней мере, в моих тестах:

Expression<Func<DataContext, IQueryable<User>> queryableExpression = GetUsers();
var expressionWithSomeAddedStuff = (DataContext dc) => from u in queryableExpression.Invoke(dc) where ....;
var expressionThatCanBeCompiled = expressionWithSomeAddedStuff.Expand();
var foo = CompiledQuery.Compile<DataContext, IQueryable<User>>(expressionThatCanBeCompiled);

Это выглядит немного многословно, и, вероятно, есть улучшения, которые вы можете внести.

Ключевым моментом является то, что он использует методы Invoke и Expand из LinqKit.По сути, они позволяют вам создать запрос с помощью композиции, а затем скомпилировать готовый результат.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top