2012-01-12 75 views
7

我試圖使用動態linq查詢從對象集合(Linq到Object)檢索IEnumerable <T>,每個對象都在該集合具有一個內部集合,其中包含另一組存儲數據的對象,這些值可通過外部集合中的索引器訪問Dynamic Linq - 對包含「dynamic」類型的成員執行查詢

當您使用強類型對象時,動態linq查詢將按預期返回過濾集但我的對象將數據存儲在動態類型的成員中,請參閱下面的示例:

public class Data 
{ 
    public Data(string name, dynamic value) 
    { 
     this.Name = name; 
     this.Value = value; 
    } 

    public string Name { get; set; } 
    public dynamic Value { get; set; } 
} 

public class DataItem : IEnumerable 
{ 
    private List<Data> _collection; 

    public DataItem() 
    { _collection = new List<Data>(); } 

    public dynamic this[string name] 
    { 
     get 
     { 
      Data d; 
      if ((d = _collection.FirstOrDefault(i => i.Name == name)) == null) 
       return (null); 

      return (d.Value); 
     } 
    } 

    public void Add(Data data) 
    { _collection.Add(data); } 

    public IEnumerator GetEnumerator() 
    { 
     return _collection.GetEnumerator(); 
    } 
} 

public class Program 
{ 
    public void Example() 
    { 
     List<DataItem> repository = new List<DataItem>(){ 
      new DataItem() { 
       new Data("Name", "Mike"), 
       new Data("Age", 25), 
       new Data("BirthDate", new DateTime(1987, 1, 5)) 
      }, 
      new DataItem() { 
       new Data("Name", "Steve"), 
       new Data("Age", 30), 
       new Data("BirthDate", new DateTime(1982, 1, 10)) 
      } 
     }; 

     IEnumerable<DataItem> result = repository.AsQueryable<DataItem>().Where("it[\"Age\"] == 30"); 
     if (result.Count() == 1) 
      Console.WriteLine(result.Single()["Name"]); 
    } 

當我運行上面的例子,我得到:操作「==」與操作數類型的「對象」不兼容和「的Int32」

動態成員動態LINQ查詢不兼容?或者是有處理動態

與會員處理正當評估時的另一種構建表達方式非常感謝您的幫助。

回答

2

動態成員動態LINQ查詢不兼容?或者是有建設有型動態成員打交道時,將正確計算表達式的另一種方式?

兩者都可以一起工作。只是做一個轉換的Int32做像這樣比較之前:

IEnumerable<DataItem> result = 
      repository.AsQueryable<DataItem>().Where("Int32(it[\"Age\"]) == 30"); 

編輯1:說了這麼多,動態操作都沒有表達允許使用動態的LINQ的連接結合在一般限制樹木。考慮以下Linq-To-Objects查詢:

IEnumerable<DataItem> result = repository.AsQueryable(). 
                Where(d => d["Age"] == 30); 

由於上述原因,此代碼段不會編譯。

編輯2:在你的情況下(和動態Linq一起),有一些方法可以解決編輯1和原始問題中提到的問題。例如:

// Variant 1: Using strings all the way 
public void DynamicQueryExample(string property, dynamic val) 
{ 
    List<DataItem> repository = new List<DataItem>(){ 
     new DataItem() { 
      new Data("Name", "Mike"), 
      new Data("Age", 25), 
      new Data("BirthDate", new DateTime(1987, 1, 5)) 
     }, 
     new DataItem() { 
      new Data("Name", "Steve"), 
      new Data("Age", 30), 
      new Data("BirthDate", new DateTime(1982, 1, 10)) 
     } 
    }; 

    // Use string comparison all the time   
    string predicate = "it[\"{0}\"].ToString() == \"{1}\""; 
    predicate = String.Format(whereClause , property, val.ToString()); 

    var result = repository.AsQueryable<DataItem>().Where(predicate); 
    if (result.Count() == 1) 
     Console.WriteLine(result.Single()["Name"]); 
} 

Program p = new Program(); 

p.DynamicQueryExample("Age", 30); // Prints "Steve" 
p.DynamicQueryExample("BirthDate", new DateTime(1982, 1, 10)); // Prints "Steve" 
p.DynamicQueryExample("Name", "Mike"); // Prints "Steve" (nah, just joking...) 

或:

// Variant 2: Detecting the type at runtime. 
public void DynamicQueryExample(string property, string val) 
{ 
    List<DataItem> repository = new List<DataItem>(){ 
     new DataItem() { 
      new Data("Name", "Mike"), 
      new Data("Age", 25), 
      new Data("BirthDate", new DateTime(1987, 1, 5)) 
     }, 
     new DataItem() { 
      new Data("Name", "Steve"), 
      new Data("Age", 30), 
      new Data("BirthDate", new DateTime(1982, 1, 10)) 
     } 
    }; 

    string whereClause = "{0}(it[\"{1}\"]) == {2}"; 


    // Discover the type at runtime (and convert accordingly) 
    Type type = repository.First()[property].GetType(); 
    string stype = type.ToString(); 
    stype = stype.Substring(stype.LastIndexOf('.') + 1); 

    if (type.Equals(typeof(string))) { 
     // Need to surround formatting directive with "" 
     whereClause = whereClause.Replace("{2}", "\"{2}\""); 
    } 
    string predicate = String.Format(whereClause, stype, property, val); 

    var result = repository.AsQueryable<DataItem>().Where(predicate); 
    if (result.Count() == 1) 
     Console.WriteLine(result.Single()["Name"]); 
} 

var p = new Program(); 
p.DynamicQueryExample("Age", "30"); 
p.DynamicQueryExample("BirthDate", "DateTime(1982, 1, 10)"); 
p.DynamicQueryExample("Name", "Mike"); 
+1

那他們不是真的'兼容'嗎? – 2012-01-12 02:38:43

+0

謝謝,當我們事先知道該值的運行時類型時,該解決方案工作正常,但如何以編程方式構建查詢時,我們不會真正知道如何在事先手動轉換值。有沒有其他方式可以建立一個表達式? – Darsegura 2012-01-12 03:33:45

+0

@ M.Babcock:取決於你對'兼容'的定義。 – afrischke 2012-01-12 10:38:34

1

你試過it[\"Age\"].Equals(object(30))

使得:

IEnumerable<DataItem> result = 
    repository.AsQueryable<DataItem>().Where("it[\"Age\"].Equals(object(30))"); 

編輯:更新正確投30到對象。

+0

引發異常」類型'System.Int32'的表達式不能用於'System'類型的參數。對象'方法'布爾等於(System.Object)'「。它它[\」Age \「]。Equals(object(30))''雖然。 – svick 2012-01-12 11:36:15

+0

答案更新相應。 – Seph 2012-01-12 12:01:14

+0

謝謝,此解決方案也運作良好。此解決方案與@afrischke提供的解決方案之間的任何性能考慮事項 – Darsegura 2012-01-12 19:13:34

1

下面的代碼是否適合你?

IEnumerable<DataItem> result = repository.AsQueryable<DataItem>().Where("it[\"Age\"].ToString() == \"30\""); 

但對於這個工作,你的所有類型的可分配給您的Data類的Value成員需要有ToString方法的一個有用的執行。