Linq к SQL DynamicInvoke (Система. Объект [])', не имеет никакого поддерживаемого перевода в SQL

У меня есть класс, Пользователи.

У пользователей есть свойство UserId.

У меня есть метод, который выглядит примерно так:

static IQueryable<User> FilterById(this IQueryable<User> p, Func<int, bool> sel)
{
   return p.Where(m => sel(m.UserId));
}

Неизбежно, когда я вызываю функцию:

var users = Users.FilterById(m => m > 10);

Я получаю следующее исключение:

Метод 'Система. Возразите DynamicInvoke (Система. Объект [])', не имеет никакого поддерживаемого перевода в SQL.

Там какое-либо решение к этой проблеме? Как далеко вниз кроличья нора Выражения. KillMeAndMyFamily () мне, возможно, придется пойти?

Для разъяснения, почему я делаю это: я использую шаблоны T4 для автоматической генерации простого репозитория и системы каналов. В каналах, вместо записи:

new UserPipe().Where(m => m.UserId > 10 && m.UserName.Contains("oo") && m.LastName == "Wee");

Я хотел бы генерировать что-то как:

new UserPipe()
  .UserId(m => m > 10)
  .UserName(m => m.Contains("oo"))
  .LastName("Wee");
8
задан Evan Nagle 28 April 2010 в 13:39
поделиться

3 ответа

Возьмем для примера UserId . Вы хотите написать:

new UserPipe().UserId(uid => uid > 10);

и хотите, чтобы это было таким же, как:

new UserPipe().Where(user => user.UserID > 10);

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


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

public static IQueryable<User> UserId(
    IQueryable<User> source, Expression<Func<int, bool>> predicate)

Затем напишите метод, который преобразует первое дерево выражения во вторую версию. Давайте посмотрим на два дерева выражений:

Вход:

        Lambda
         uid
          |
       BinaryOp
          >
    /            \
Parameter     Constant
   uid           10

Выход:

         Lambda
          user
            |
        BinaryOp
            >
     /            \
 Property         Constant
  UserID             10
     | 
Parameter
   user

Как видите, все, что вам нужно сделать, это взять тело лямбды и рекурсивно заменить все вхождения параметра uid со свойством UserId в параметре user и создайте новое лямбда-выражение с преобразованным телом и параметром user .

Для замены можно использовать ExpressionVisitor .

15
ответ дан 5 December 2019 в 10:39
поделиться

Благодаря dtb, вот что я придумал:

public class ExpressionMemberMerger : ExpressionVisitor
{
    MemberExpression mem;
    ParameterExpression paramToReplace;

    public Expression Visit<TMember, TParamType>(
        Expression<Func<TParamType, bool>> exp,
        Expression<Func<TMember, TParamType>> mem)
    {
        //get member expression
        this.mem = (MemberExpression)mem.Body;

        //get parameter in exp to replace
        paramToReplace = exp.Parameters[0];

        //replace TParamType with TMember.Param
        var newExpressionBody = Visit(exp.Body);

        //create lambda
        return Expression.Lambda(newExpressionBody, mem.Parameters[0]);
    }

    protected override Expression VisitParameter(ParameterExpression p)
    {
        if (p == paramToReplace) return mem;
        else return base.VisitParameter(p);
    }
}

Теперь я может преобразовать предикат, как мне кажется, с помощью кода, подобного приведенному ниже. Я провел небольшое тестирование этого кода; похоже, он работает, но мне было бы интересно услышать какие-либо комментарии / замечания:

static IQueryable<User> FilterById(this IQueryable<User> p, Expression<Func<int, bool>> sel)
{
   var merger = new ExpressionMemberMerger();

   Expression<Func<User, int>> mem = m => m.UserId;

   var expression = (Expression<Func<User, bool>>)merger.Visit(sel, mem);

   return p.Where(expression);
}
1
ответ дан 5 December 2019 в 10:39
поделиться

На первый взгляд кажется, что вы создаете некоторые выражения, которые Linq не знает, как перевести в T-SQL.

Возможно, я неправильно понимаю, что вы пытаетесь сделать, но если вы хотите создать цепные выражения, понятные Linq To Sql, я настоятельно рекомендую посмотреть на расширения PredicateBuilder здесь . Даже если это не совсем то, что вы хотите, понимание того, как это работает, может дать вам некоторое представление о том, чего вам нужно достичь под прикрытием, чтобы то, что вы делаете, работало.

0
ответ дан 5 December 2019 в 10:39
поделиться
Другие вопросы по тегам:

Похожие вопросы: