2017-02-03 128 views
2

我有一個MongoDB數據庫,每個數據庫都存儲一個特定類型的對象。我想實現一個通用的選擇功能,以在下面的定義上的特定集合操作取決於類型,如:MongoDB與C#:查找具有自定義謂詞的元素

object[] Select<T>(Func<T, bool> condition) 

例如,如果對象類型之一是Person類,我將實現以下:

object[] Select<T>(Func<T, bool> condition) 
{ 
    if (typeof(T) == typeof(Person)) 
    { 
     Func<Person, bool> f = (Person p) => 
     { 
      return true; 
     }; 
     return this.collectionPersons.AsQueryable().Where(p=>f(p)).ToArray(); 
    } 
    else // ... 
} 

此代碼編譯,但是當我嘗試運行它,我得到與

Additional information: Unsupported filter:   
Invoke(value(System.Func`2[Person,System.Boolean]), {document}). 

一個System.ArgumentException細讀之後API文檔我有這樣的印象:通常不可能使用抽象類型的lambda表達式(如上例),但僅支持 FilterDefinitionBuilder支持的那些類型,例如Gt(),Eq()等 我很好奇,如果我理解這正確或存在一種可能性來查詢具有抽象謂詞的集合(我對MongoDB C#驅動程序相當陌生)。

回答

1

當然你可以使用Lambda表達式作爲參數。驅動程序會將您的表達式轉化爲簡單的過濾器,但不可能翻譯每個表達式,但如果可能的話,它將起作用。 我真的不明白你在你的樣品在做什麼,你不使用你的病情

你的功能應該不是Func鍵,但表達式作爲參數:

public static T[] Select<T>(IMongoCollection<T> collection, 
          Expression<Func<T, bool>> condition) 
{ 
    if (typeof(T) == typeof(Person)) 
    { 
     return collection.AsQueryable().Where(condition).ToArray(); 
    } 
    return null; 
} 

而且你可以用它打電話表情:

var res = Select(this.collectionPersons, x => x.FirstName == "a3"); 
如果要查詢剛纔的所有項目,因爲它看起來像

,你可以做到這一點爲:

return this.collectionPersons.Find(p=>true).ToArray(); 

而且就不得不提到的所有命名空間中usings:

using System; 
using System.Collections.Generic; 
using System.Linq.Expressions; 
using System.Linq; 
using MongoDB.Driver; 
using MongoDB.Driver.Linq; 
+0

感謝Maksim,我試圖完全按照您的建議來實現Expression,而不是Predicate。以下錯誤是我得到的: MongoDB.Driver.Linq.IMongoQueryable '不包含'Where'和最佳擴展方法重載的定義'System.Linq.Queryable.Where (System.Linq.IQueryable ,System.Linq.Expressions.Expression >)'有一些無效的參數。 我自然不會以這種方式查詢所有項目,'返回true;'只是最簡單的情況,反正無效。 –

+0

我已經嘗試過現在,用這種方法我已經在回答(我已經更新了一個litle位),我可以查詢我的收藏。 –

+0

@AlexKonnen也許你錯過了一些使用聲明? –

0

我的錯誤是我怎麼叫謂語。 下面是它工作的例子:

public object[] SelectByPredicate<T>(Func<T, bool> predicate) 
{ 
    if (typeof(T) == typeof(Person)) 
    { 
     Func<Person, bool> f = (Person p) => 
     { 
      T t = (T)Convert.ChangeType(p, typeof(T)); 
      return predicate(t); 
     }; 

     return this.SelectPersonsByPredicate(f); 
    } 
    else if (typeof(T) == typeof(Pet)) 
    { 
     Func<Pet, bool> f = (Pet p) => 
     { 
      T t = (T)Convert.ChangeType(p, typeof(T)); 
      return predicate(t); 
     }; 

     return this.SelectPetsByPredicate(f); 
    } 
    else 
    { 
     throw new NotSupportedException("Type not supported"); 
    } 
} 

private Person[] SelectPersonsByPredicate(Func<Person, bool> predicate) 
{ 
    return this.collectionPersons.AsQueryable().Where(predicate).ToArray(); 
} 

private Pet[] SelectPetsByPredicate(Func<Pet, bool> predicate) 
{ 
    return this.collectionPets.AsQueryable().Where(predicate).ToArray(); 
} 

然後,如果你需要斷言的人的一個子集,你只是喜歡寫東西

object[] personsSelected = db.SelectByPredicate<Person>(p=>p.Name.Contains("Theo")); 

這是不是很優雅,我承認,在它會返回object[]而不是T[],但出於所有實際目的,它都可以。如果有人知道更好的方法,我會感興趣。