LINQ to Entities不识别方法'Boolean Contains[Int32]'

4

我有以下扩展方法,用于在LINQ-To-Entities中进行Contains操作:

  public static class Extensions
  {    
        public static IQueryable<TEntity> WhereIn<TEntity, TValue>
        (
            this ObjectQuery<TEntity> query,
            Expression<Func<TEntity, TValue>> selector,
            IEnumerable<TValue> collection
        )
        {
            if (selector == null) throw new ArgumentNullException("selector");
            if (collection == null) throw new ArgumentNullException("collection");
            if (!collection.Any())
                return query.Where(t => false);

            ParameterExpression p = selector.Parameters.Single();

            IEnumerable<Expression> equals = collection.Select(value =>
               (Expression)Expression.Equal(selector.Body,
                    Expression.Constant(value, typeof(TValue))));

            Expression body = equals.Aggregate((accumulate, equal) =>
                Expression.Or(accumulate, equal));

            return query.Where(Expression.Lambda<Func<TEntity, bool>>(body, p));
        }

        //Optional - to allow static collection:
        public static IQueryable<TEntity> WhereIn<TEntity, TValue>
          (
            this ObjectQuery<TEntity> query,
            Expression<Func<TEntity, TValue>> selector,
            params TValue[] collection
          )
        {
            return WhereIn(query, selector, (IEnumerable<TValue>)collection);
        }
  }

当我调用扩展方法来检查一个id列表是否在特定的表中时,它可以正常工作并返回id列表,就像这样:

List<int> Ids = _context.Persons
                        .WhereIn(x => x.PersonId, PersonIds)
                        .Select(x => x.HeaderId).ToList();

当我执行下面这条语句时,它报错说LINQ-To-Entities不识别Contains(int32),但是我认为我已经不再使用实体了,而是一个整数集合。
predicate = predicate.And(x=> Ids.Contains(x.HeaderId));

如果我有一个逗号分隔的字符串,例如“1,2,3”,那么以下内容可行:
predicate = predicate.And(x=>x.Ids.Contains(x.HeaderId));

我正在尝试将返回的List创建为逗号分隔的字符串列表,问题在于现在当我执行 predicate = predicate.And(x=>sb.Contains(x.HeaderId.ToString()); 时,它会抱怨不喜欢ToString()。

我还尝试过:

predicate = predicate.And(x=>Extensions.WhereIn(Ids, x.id));, but it can't resolve WhereIn.  It says I must add `<>`, but I am not sure what to add here and how implement it.

@SamBauwens - http://www.albahari.com/nutshell/predicatebuilder.aspx - Xaisoft
为什么要展示WhereIn扩展方法?如果Ids确实是一个List<int>,我们不介意你如何获取它。问题似乎来自于PredicateBuilder(猜测您使用了这个)。 - Raphaël Althaus
1
@Xaisoft:你已经回答了自己的问题。.NET3.5中的EF不支持IEnumerable<T>.Contains。就是这样。 - Slauma
x.PersonId和x.HeaderId的类型是什么? - Azhar Khorasany
它们是整数,我不能在它们上使用ToString。 - Xaisoft
显示剩余6条评论
3个回答

0

你的WhereIn没有问题,而且你是正确的:当你使用Ids时,你不再针对实体,而是针对一组整数。

问题在于当你在谓词上使用.And时:LINQ-To-Entities试图将括号内的所有内容转换为实体方法,但是没有相应的Contains方法。

解决方案:

不要使用

predicate = predicate.And(x=> Ids.Contains(x.HeaderId));

使用

predicate = predicate.And(Contains<XClassName, int>(x.HeaderId));

其中,包含被定义如下:

private static Expression<Func<TElement, bool>> Contains<TElement, TValue>(Expression<Func<TElement, TValue>> valueSelector, List<TValue> values)
    {
        if (null == valueSelector) { throw new ArgumentNullException("valueSelector"); }
        if (null == values) { throw new ArgumentNullException("values"); }

        if (!values.Any())
            return e => false;

        var equals = values.Select(value => (Expression)Expression.Equal(valueSelector.Body, Expression.Constant(value, typeof(TValue))));

        return Expression.Lambda<Func<TElement, bool>>(@equals.Aggregate(Expression.Or), valueSelector.Parameters.Single());
    }

并且XClassName是您的x类的名称


-1

你不能这样使用数组,你需要预先访问这个lambda表达式以将其扩展为基元。或者你可以更改底层提供程序,使其知道如何生成IN语句,因为默认情况下它不会。

没有找到一个人实际实现它的帖子,我会在完成后更新。

基本上当你使用你的扩展方法时,就像

x=>arr.Contains(x)

如果您尝试对实体集等执行此类lambda,它将抛出异常,指出参数只能是原始类型。

原因是底层提供程序不知道如何将数组的.Contains方法作为函数参数转换为SQL查询。为了解决这个问题,您有两个选择:

  • 教它如何使用T[]作为参数,并使用此参数的Contains

  • 更新您的扩展方法,以生成新的lamda,该lamda将使用“允许”的构建块,即使用诸如int、string、guid等原始类型的表达式。

请查看本文。

http://msdn.microsoft.com/en-us/library/bb882521(v=vs.90).aspx


你能举个例子详细解释一下吗?我还有点困惑。 - Xaisoft

-2

替换你的:

List<int> Ids = _context.Persons
                    .WhereIn(x => x.PersonId, PersonIds)
                    .Select(x => x.HeaderId).ToList();

使用

var Ids = _context.Persons
                    .WhereIn(x => x.PersonId, PersonIds)
                    .Select(x => x.HeaderId).ToList();

然后尝试。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接