2012-01-12 15 views
7

Estoy tratando de usar una consulta dinámica linq para recuperar un IEnumerable <T> de una colección de objetos (Linq a Object), cada uno de los objetos en la colección tiene una colección interna con otro conjunto de objetos donde se almacenan los datos, se accede a estos valores a través de un indexador de la colección externaDinámico Linq - Realiza una consulta sobre un objeto con miembros del tipo "dinámico"

La consulta dinámica linq devuelve el conjunto filtrado como se esperaba cuando se trabaja con objetos muy tipados pero mi objeto almacena los datos en un miembro del tipo dinámico, consulte el siguiente ejemplo:

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

Cuando ejecuto el ejemplo anterior me sale: '==' Operador incompatible con 'objeto' tipos de operando y 'Int32'

son dinámicas miembros incompatibles con las consultas LINQ dinámica ?, o hay otra forma de construir expresiones que evaluarían correctamente cuando se trata de miembros del tipo dinámico

Muchas gracias por su ayuda.

Respuesta

2

son dinámicas miembros incompatibles con las consultas LINQ dinámica ?, o hay otra forma de construir expresiones que evaluar adecuadamente cuando se trata de miembros de tipo dinámico ?

Ambos pueden funcionar juntos. Sólo hacer una conversión a Int32 antes de hacer la comparación de este modo:

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

Edición 1: Una vez dicho esto, el uso de la unión en relación con LINQ dinámica se limita, en general, como operaciones dinámicas no están permitidos en la expresión árboles. Tenga en cuenta la siguiente consulta Linq-To-Objects:

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

Este fragmento de código no se compilará por el motivo mencionado anteriormente.

Edit 2: En su caso (y en conjunto con Dynamic Linq) hay algunas formas de piratear los problemas mencionados en Edit 1 y en la pregunta original. Por ejemplo:

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

Entonces, ¿realmente no son 'compatibles'? –

+0

Gracias, que las soluciones funcionan bien cuando sabemos de antemano el tipo de tiempo de ejecución del valor, pero qué ocurre cuando la consulta se crea programáticamente, realmente no sabremos cómo convertir los valores de antemano. ¿Hay alguna otra manera en que se pueda construir una expresión? – Darsegura

+0

@ M.Babcock: Depende de su definición de 'compatible'. – afrischke

1

¿Ha intentado it[\"Age\"].Equals(object(30))?

tal que:

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

Editar: Actualización para emitir correctamente 30 de objetar.

+0

Eso arroja una excepción" Expresión de tipo 'System.Int32' no se puede usar para el parámetro de tipo 'Sistema'. Objeto 'del método' Boolean Equals (System.Object) '".' It [\ "Age \"]. Igual (object (30)) 'funciona, aunque. – svick

+0

respuesta actualizada en consecuencia. – Seph

+0

Gracias, esta solución también funciona bien . Cualquier consideración de rendimiento entre esta solución y las proporcionadas por @afrischke – Darsegura

1

¿El código siguiente es útil para usted?

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

Pero para que esto funcione, todos los tipos que pueden ser asignados a la Value miembro de su clase Data necesita tener una aplicación útil del método ToString.

Cuestiones relacionadas