2012-01-12 10 views
7

Sto cercando di utilizzare una query dinamica linq per recuperare un oggetto IEnumerable <T> da una raccolta di oggetti (da Linq a oggetto), ciascuno degli oggetti in la collezione ha una raccolta interna con un'altra serie di oggetti in cui sono memorizzati i dati, questi valori si accede attraverso un indicizzatore dalla collezione esternoLinq dinamico - Esegui una query su un oggetto con membri di tipo "dinamico"

la query LINQ dinamica restituisce il filtrato impostato come previsto quando si lavora con oggetti fortemente tipizzati ma il mio oggetto memorizza i dati in un membro di tipo dinamico, vedere l'esempio di seguito:

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

Quando eseguo l'esempio di cui sopra ottengo: '==' operatore incompatibile con 'Oggetto' tipi di operando e 'Int32'

Sono dinamiche membri incompatibili con le query dinamica LINQ ?, o c'è un altro modo di costruire espressioni che valuterà correttamente quando si tratta di membri di tipo dinamico

Grazie mille per il vostro aiuto.

risposta

2

Sono dinamiche membri incompatibili con le query dinamica LINQ ?, o c'è un altro modo di costruire espressioni che sarebbe valutare correttamente quando si tratta di membri di tipo dinamica?

Entrambi possono funzionare insieme. Basta fare una conversione a Int32 prima di fare il confronto in questo modo:

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

Edit 1: Detto questo, l'uso di vincolanti per LINQ dinamica è limitato, in generale, le operazioni dinamiche non sono ammessi in espressione alberi. Considera la seguente query Linq-To-Objects:

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

Questo snippet di codice non verrà compilato per il motivo sopra indicato.

Modifica 2: Nel tuo caso (e in congiunzione con Dynamic Linq) ci sono alcuni modi per violare i problemi menzionati in Modifica 1 e nella domanda originale. Per esempio:

// 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...) 

o:

// 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

Quindi non sono veramente "compatibili"? –

+0

Grazie, le soluzioni funzionano bene quando conosciamo in anticipo il tipo di runtime del valore, ma per quanto riguarda quando la query viene creata in modo programmatico, non sapremo veramente come convertire i valori in anticipo. C'è un altro modo in cui un'espressione può essere costruita? – Darsegura

+0

@ M.Babcock: dipende dalla tua definizione di 'compatibile'. – afrischke

1

Hai provato it[\"Age\"].Equals(object(30))?

Tale che:

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

Edit: aggiornato per lanciare correttamente 30 di opporsi.

+0

Che genera un'eccezione" Espressione di tipo 'System.Int32' non può essere utilizzata per il parametro di tipo 'Sistema. Oggetto 'di metodo' Boolean Equals (System.Object) '".' It [\ "Età \"]. Uguale (oggetto (30)) 'funziona, tuttavia. – svick

+0

risposta aggiornata di conseguenza – Seph

+0

Grazie, questa soluzione funziona anche bene Eventuali considerazioni sulle prestazioni tra questa soluzione e quelle fornite da @afrischke – Darsegura

1

Il seguente codice è utile?

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

Ma per far funzionare tutto questo, tutti i tipi di che possono essere assegnati alla Value membro della vostra classe Data ha bisogno di avere un utile implementazione del metodo ToString.

Problemi correlati