2008-12-17 58 views
84

我想創建一個使用Silverlight ADO.Net數據服務客戶端API(因此Linq To Entities)在where子句中使用id列表的查詢。有誰知道解決方法包含不被支持?'包含()'解決方法使用Linq到實體?

我想要做這樣的事情:

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; 

卻得到了 「方法 '任意' 不支持」。

+34

注意:實體框架4(在.NET 4中)有一個「Contains」方法,以防有人碰巧讀這些不知道它的東西。我知道OP使用EF1(.NET 3.5)。 – DarrellNorton 2010-12-20 19:46:59

+7

@達雷爾我只是浪費了半個小時,因爲我跳過了你的評論。我希望我可以讓你的評論在屏幕上閃爍和選擇。 – 2011-01-15 00:09:20

回答

94

更新: EF≥4直接支持Contains(Checkout 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

我不確定Silverligth,但是在linq對象中,我總是使用any()來處理這些查詢。

var q = from t in svc.OpenTranaction 
     where txnIds.Any(t.OpenTransactionId) 
     select t; 
+5

任何不接受序列類型的對象 - 它沒有參數(在這種情況下,它只是「這是否爲空」)或它需要一個謂詞。 – 2008-12-17 11:44:24

+0

我非常高興找到這個答案:)+1 感謝AndreasN – SDReyes 2010-02-23 19:59:37

13

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

如果你想要做一個「不包含」,才使在BuildContainsExpression方法進行以下修改: - Expression.Equal成爲表達。 NotEqual - Expression.Or變成Expression.And – Merritt 2009-06-25 15:11:42

18

你可以回到韓d編碼一些E-SQL(注意關鍵字 「它」):

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

這裏是我用來生成從一個集合了一些E-SQL代碼,情況因人而異:

string[] ids = orders.Select(x=>x.ProductID.ToString()).ToArray(); 
return CurrentDataSource.Products.Where("it.ID IN {" + string.Join(",", ids) + "}"); 
0

非常感謝。 Where擴展方法對我來說是足夠的。我對它進行了剖析並生成了與e-sql相同的SQL命令到DataBase。

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]) 
1

完成備案的,這裏是我最後使用的(檢查省略了清晰的錯誤)的代碼...

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

對不起新用戶,我會評論實際答案,但似乎我不能這樣做呢?

無論如何,關於BuildContainsExpression()的示例代碼的答案,請注意,如果您在數據庫實體(即不是內存對象)上使用該方法並且您使用IQueryable,那它實際上必須去因爲它基本上會執行很多SQL「或」條件來檢查「where in」子句(使用SQL Profiler運行它以查看)。

這可能意味着,如果您正在使用多個BuildContainsExpression()來精煉一個IQueryable,它將不會將其轉換爲一個如您期望的那樣在最終運行的SQL語句。

對我們來說,解決方法是使用多個LINQ連接來將其保持爲一個SQL調用。

0

除了選定的答案。

Expression.Or替換爲Expression.OrElse與Nhibernate一起使用並修復Unable to cast object of type 'NHibernate.Hql.Ast.HqlBitwiseOr' to type 'NHibernate.Hql.Ast.HqlBooleanExpression'異常。