代码之家  ›  专栏  ›  技术社区  ›  jim tollan

找不到Linq扩展方法帮助

  •  1
  • jim tollan  · 技术社区  · 14 年前

    这是迄今为止我最难回答的问题,我希望有人以前偶然发现了这个问题,并找到了一个优雅的答案。基本上,我有一些Linq扩展方法(恰好是亚音速的,但适用于任何Linq导数)工作得很好(对.where()和.wherenotin()的扩展)。这些方法的作用是将linq转换为in()的SQL等价物。现在,当提供已知类型参数(即数组或参数数组)时,下面的代码工作得很好:

    public static IQueryable<T> WhereIn<T, TValue>(
        this IQueryable<T> query, 
        Expression<Func<T, TValue>> selector, 
        params TValue[] collection) where T : class
    {
        if (selector == null) throw new ArgumentNullException("selector");
        if (collection == null) throw new ArgumentNullException("collection");
        ParameterExpression p = selector.Parameters.Single();
    
        if (!collection.Any()) return query;
    
        IEnumerable<Expression> equals = collection.Select(value =>
           (Expression)Expression.Equal(selector.Body,
                Expression.Constant(value, typeof(TValue))));
    
        Expression body = equals.Aggregate(Expression.Or);
    
        return query.Where(Expression.Lambda<Func<T, bool>>(body, p));
    }
    

    用途:

    var args = new [] { 1, 2, 3 };
    var bookings = _repository.Find(r => r.id > 0).WhereIn(x => x.BookingTypeID, args);
    // OR we could just as easily plug args in as 1,2,3 as it's defined as params
    var bookings2 = _repository.Find(r => r.id > 0).WhereIn(x => x.BookingTypeID, 1,2,3,90);
    

    然而,现在对于复杂的部分来说。我希望能够将IQueryable对象传递到上面的重载版本中,该版本接受第二个Linq对象作为参数,以便实现 select * from table1 where table1.id in(select id from table2) . 下面是方法签名,它实际编译为OK,但缺少所有重要的逻辑:

    public static IQueryable<T> WhereIn<T, TValue, T2, TValue2>(
        this IQueryable<T> query, 
        Expression<Func<T, TValue>> selector, 
        T2 entity2, 
        Expression<Func<T2, TValue2>> selector2) where T : class
    {
        if (selector == null) throw new ArgumentNullException("selector");
        if (selector2 == null) throw new ArgumentNullException("selector2");
        ParameterExpression p = selector.Parameters.Single();
        ParameterExpression p2 = selector2.Parameters.Single();
    
        /* this is the missing section */
    
        /* i'd like to see the final select generated as 
         *
         * select * from T where T.selector in(select T2.selector2 from T2)
         */
    
    
        return  null; 
        // this is just to allow it to compile - proper return value pending
    }
    

    用途:

    var bookings = _repository.Find(r => r.BookingID>0)
                    .WhereIn(x => x.BookingTypeID, new BookingType(), y => y.BookingTypeID);
    

    我是在这堆不存在的(表达式)树上乱叫…—还是这棵树真的可以。

    一切都很好——希望如此。

    吉姆

    2 回复  |  直到 14 年前
        1
  •  4
  •   Jon Skeet    14 年前

    为什么你不直接使用一个连接?

    var query = from x in table1
                join y in table2 on x.Id equals y.Id
                select x;
    

    或者如果有多个 y 每个值 x :

    var query = from x in table1
                join z in table2.Select(y => y.Id).Distinct() on x.Id equals z
                select x;
    

    我希望这样的查询在SQL数据库中得到很好的优化。

    或者如果你真的想用 Where :

    var query = table1.Where(x => table2.Select(y => y.Id).Contains(x.Id));
    

    我可能错过了更大的东西…或者可以将上述查询转换为扩展方法,这就是您要查找的内容:)

        2
  •  -1
  •   jim tollan    14 年前

    我最终选择了一种扩展方法来实现这一点,但仍然没有100%成功。

    稍后,我将把实际的完整工作代码放到这里,一旦我将它与所有其他选项集成在一起。

    推荐文章