代码之家  ›  专栏  ›  技术社区  ›  James Bloomer

使用Linq到实体的“Contains()”解决方法?

  •  87
  • James Bloomer  · 技术社区  · 16 年前

    我试图使用Silverlight ADO创建一个查询,该查询在where子句中使用id列表。Net Data Services客户端api(以及Linq To Entities)。有人知道解决不支持“包含”的方法吗?

    我想做这样的事情:

    List<long?> txnIds = new List<long?>();
    // Fill list 
    
    var q = from t in svc.OpenTransaction
            where txnIds.Contains(t.OpenTransactionId)
            select t;
    

    试过这个:

    var q = from t in svc.OpenTransaction
    where txnIds.Any<long>(tt => tt == t.OpenTransactionId)
    select t;
    

    但得到“不支持方法'Any'”。

    10 回复  |  直到 14 年前
        1
  •  96
  •   Shimmy Weitzhandler 500 - Internal Server Error    13 年前

    更新: EF 4支持 Contains 直接(结账 Any ),所以你不需要任何变通方法。

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

    用法:

    public static void Main()
    {
      using (MyObjectContext context = new MyObjectContext())
      {
        //Using method 1 - collection provided as collection
        var contacts1 =
          context.Contacts.WhereIn(c => c.Name, GetContactNames());
    
        //Using method 2 - collection provided statically
        var contacts2 = context.Contacts.WhereIn(c => c.Name,
          "Contact1",
          "Contact2",
          "Contact3",
          "Contact4"
          );
      }
    }
    
        2
  •  18
  •   Felipe Miosso    8 年前

    你可以随时编写一些e-sql(注意关键字“it”):

    return CurrentDataSource.Product.Where("it.ID IN {4,5,6}"); 
    

    以下是我用来从集合YMMV生成一些e-sql的代码:

    string[] ids = orders.Select(x=>x.ProductID.ToString()).ToArray();
    return CurrentDataSource.Products.Where("it.ID IN {" + string.Join(",", ids) + "}");
    
        3
  •  13
  •   James Bloomer    16 年前

    来自 MSDN :

    static Expression<Func<TElement, bool>> BuildContainsExpression<TElement, TValue>(
        Expression<Func<TElement, TValue>> valueSelector, IEnumerable<TValue> values)
    {
        if (null == valueSelector) { throw new ArgumentNullException("valueSelector"); }
        if (null == values) { throw new ArgumentNullException("values"); }
        ParameterExpression p = valueSelector.Parameters.Single();
    
        // p => valueSelector(p) == values[0] || valueSelector(p) == ...
        if (!values.Any())
        {
            return e => false;
        }
    
        var equals = values.Select(
                 value => (Expression)Expression.Equal(valueSelector.Body, Expression.Constant(value, typeof(TValue))));
    
        var body = equals.Aggregate<Expression>((accumulate, equal) => Expression.Or(accumulate, equal));
    
        return Expression.Lambda<Func<TElement, bool>>(body, p);
    } 
    

    查询变为:

    var query2 = context.Entities.Where(BuildContainsExpression<Entity, int>(e => e.ID, ids));
    
        4
  •  2
  •   AndreasN    16 年前

    我不确定Silverligth,但在linq对象中,我总是使用any()进行这些查询。

    var q = from t in svc.OpenTranaction
            where txnIds.Any(t.OpenTransactionId)
            select t;
    
        5
  •  1
  •   James Bloomer    15 年前

    为了完成记录,这是我最终使用的代码(为清楚起见省略了错误检查)。..

    // How the function is called
    var q = (from t in svc.OpenTransaction.Expand("Currency,LineItem")
             select t)
             .Where(BuildContainsExpression<OpenTransaction, long>(tt => tt.OpenTransactionId, txnIds));
    
    
    
     // The function to build the contains expression
       static System.Linq.Expressions.Expression<Func<TElement, bool>> BuildContainsExpression<TElement, TValue>(
                    System.Linq.Expressions.Expression<Func<TElement, TValue>> valueSelector, 
                    IEnumerable<TValue> values)
            {
                if (null == valueSelector) { throw new ArgumentNullException("valueSelector"); }
                if (null == values) { throw new ArgumentNullException("values"); }
                System.Linq.Expressions.ParameterExpression p = valueSelector.Parameters.Single();
    
                // p => valueSelector(p) == values[0] || valueSelector(p) == ...
                if (!values.Any())
                {
                    return e => false;
                }
    
                var equals = values.Select(value => (System.Linq.Expressions.Expression)System.Linq.Expressions.Expression.Equal(valueSelector.Body, System.Linq.Expressions.Expression.Constant(value, typeof(TValue))));
                var body = equals.Aggregate<System.Linq.Expressions.Expression>((accumulate, equal) => System.Linq.Expressions.Expression.Or(accumulate, equal));
                return System.Linq.Expressions.Expression.Lambda<Func<TElement, bool>>(body, p);
            }
    
        6
  •  0
  •   Phani Raj    16 年前

    下面是一个示例,我演示了如何使用DataServiceContext编写基于集合的查询: http://blogs.msdn.com/phaniraj/archive/2008/07/17/set-based-operations-in-ado-net-data-services.aspx

        7
  •  0
  •   javierlinked    16 年前

    非常感谢。WhereIn扩展方法对我来说已经足够了。我分析了它,并向数据库生成了与e-SQL相同的SQL命令。

    public Estado[] GetSomeOtherMore(int[] values)
    {
        var result = _context.Estados.WhereIn(args => args.Id, values) ;
        return result.ToArray();
    }
    

    生成此内容:

    SELECT 
    [Extent1].[intIdFRLEstado] AS [intIdFRLEstado], 
    [Extent1].[varDescripcion] AS [varDescripcion]
    FROM [dbo].[PVN_FRLEstados] AS [Extent1]
    WHERE (2 = [Extent1].[intIdFRLEstado]) OR (4 = [Extent1].[intIdFRLEstado]) OR (8 = [Extent1].[intIdFRLEstado])
    
        8
  •  0
  •   Gabriel Chung    16 年前

    我认为LINQ中的加入可以是一个漫游。

    不过,我还没有测试代码。希望它能有所帮助。干杯。 :-)

    List<long?> txnIds = new List<long?>();
    // Fill list 
    
    var q = from t in svc.OpenTransaction
            join tID in txtIds on t equals tID
            select t;
    

    加入LINQ:

    http://weblogs.asp.net/salimfayad/archive/2008/07/09/linq-to-entities-join-queries.aspx

        9
  •  0
  •   Shannon    15 年前

    对不起,新用户,我会对实际答案发表评论,但似乎我还不能这样做?

    无论如何,关于BuildContainsExpress()的示例代码的答案,请注意,如果你在数据库实体(即非内存对象)上使用该方法,并且你正在使用IQuery,那么它实际上必须转到数据库,因为它基本上做了很多SQL“或”条件来检查“where in”子句(用SQL Profiler运行它以查看)。

    这可能意味着,如果你用多个BuildContainsExpress()来精炼一个IQuery,它不会像你期望的那样将其转换为一个在最后运行的SQL语句。

    我们的解决方法是使用多个LINQ连接将其保持为一个SQL调用。

        10
  •  0
  •   smg    10 年前

    除了选定的答案。

    更换 Expression.Or 具有 Expression.OrElse 与Nhibernate一起使用并修复 Unable to cast object of type 'NHibernate.Hql.Ast.HqlBitwiseOr' to type 'NHibernate.Hql.Ast.HqlBooleanExpression' 例外情况。