2010-08-05 109 views
1

这是迄今为止我最棘手的问题,我希望以前有人偶然发现了这个问题,并找到了一个优雅的答案。基本上,我有几个linq扩展方法(它们恰好在亚音速中,但适用于任何linq导数),它们完美地工作(扩展名为.WhereIn()和.WhereNotIn())。这些方法用于将linq转换为in()的sql等价物。现在下面的代码工作完全供给已知类型的参数(即,阵列或参数数组)时:现在对于复杂的部分LINQ扩展方法帮助寻求

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

然而,:

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

用法。我希望能够将一个IQueryable对象传递给上述的超载版本,该版本接受第二个linq对象作为参数,以实现select * from table1 where table1.id in(select id from table2)的等效。这里是实际编译正常,但缺少具有所有重要的逻辑方法签名:

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

是我吠叫了一个不存在的(表达)树在这里:-) - 或这是相当可行的。

所有最好的 - 这里的希望。

jim

回答

4

为什么不只是使用连接?

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

我可能失去了一些东西更大的......或者它可能是翻译上查询到的扩展方法是你在找什么:)

+0

jon - 我不能使用连接语法,因为代码是'提交'来在目前的表达式树和泛型中操作。然而,你的第三种选择可能会取得成果。我会在上面提到的上下文中仔细检查。所以是的,翻译成扩展方法的目标是 – 2010-08-05 09:00:19

+0

@jim:“join”语法就等同于调用'Join'方法。我碰巧发现查询表达式中的连接比点符号更容易表达,但您可以将其表示为table1.Join(table2,x => x.Id,y => y.Id,(x,y)=> x)'没有任何问题。 – 2010-08-05 09:08:17

+0

jon - 一个'最终游戏'会在上面使用你的想法,我能够这样做:var newJoin = query.Join(entity2,selector,selector2);但显然,这是不合法的语法等 – 2010-08-05 09:21:28

-1

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

我将在稍后的某个时刻删除实际的完整工作代码,一旦我将它与所有其他选项集成在一起。

+0

已经4年了,你有任何代码可以分享吗?如果它不完整,也许这里的其他人可以帮助对它进行批评,直到它有效。 – jpierson 2014-02-07 16:19:37

+0

嗨jp - 时间过得真快。唉,我从来没有得到这个工作的解决方案,并在扩展方法的道路上蹒跚前行,以使任何不足之处变得更好。如果它诞生了,那将是一段奇妙的代码!哦,好吧..如果有什么东西出现了,我会按照承诺的更新在这里。 – 2014-02-07 17:43:41