2012-02-29 10 views
25

Tenemos un objetodinámicamente generar LINQ consulta

public class SomeObject 
{ 
    public Name {get;set;} 
    public City {get;set;} 
    public State {get;set} 
    //various other parameters. Let's say there's ~20 
} 

¿Es posible crear dinámicamente nuevas consultas LINQ sin recompilación del código fuente? En cambio, los parámetros de consulta provienen de una estructura XML que se almacena y actualiza en la base de datos.

var result = from i in someObj 
      where 
      //XML requests Name = 'Bob'...so append this where clause 
      name = 'Bob' 

¿Se puede hacer esto?

+1

Hay un par de formas diferentes de hacerlo. Siempre será Propiedad == Valor. ¿Son menores que, mayores que, no iguales posibles? ¿El XML puede especificar más de un valor? 'Nombre = 'Bob' || Nombre = 'Fred''. ¿Las condiciones solo se combinan con &&? 'Name = 'Bob' && State = 'OH''. ¿O pueden las condiciones combinarse con ||? 'Nombre = 'Bob' || Estado = 'OH''. Todos estos deben tenerse en cuenta antes de poder proponer una solución. – cadrell0

+1

La pregunta carece de claridad; no informa que la estructura XML contendrá expresiones condicionales (en lugar de solo los valores deseados que se emparejarán, como parece implicar 'Name =' Bob''). Es estúpido e inútil que alguien encuentre apropiado dar la vuelta y rechazar todas las respuestas que no hayan inferido esta información faltante. – Douglas

+0

@ Cadrell0 - Las consultas más complejas serían una muy buena adición. Traté de comenzar la pregunta de forma simple con solo una cláusula where. No sabía si esa sería una solución muy compleja en sí misma. A juzgar por las respuestas, parece que hay una biblioteca completa disponible para realizar las tareas. Dynamic LINQ by ScottGU parece prometedor. –

Respuesta

89

Aquí es una solución con árboles de expresión:

var param = Expression.Parameter(typeof(SomeObject), "p"); 
var exp = Expression.Lambda<Func<SomeObject, bool>>(
    Expression.Equal(
     Expression.Property(param, "Name"), 
     Expression.Constant("Bob") 
    ), 
    param 
); 
var query = someObj.Where(exp); 

Sé que es mucho más complejo, pero esto puede ser útil en ocasiones.

+10

Esta respuesta merece más amor. – Askolein

+3

+1 porque aunque los árboles de expresión pueden ser un poco difíciles de entender al principio, pero son una buena solución para este problema. Una vez que haya pasado los ejemplos iniciales que están disponibles, simplemente tenga cuidado con el manejo de valores nulos y asegúrese de que los tipos utilizados en sus expresiones coincidan entre sí. Dependiendo de cuán "dinámico" sea el origen de sus consultas (por ejemplo, todos los Expression.Constants() vienen como cadenas, aunque se comparen con un int), es posible que deba realizar algunas conversiones adicionales. Este artículo de MSDN también es una buena referencia: http://msdn.microsoft.com/en-us/library/bb882637.aspx –

+1

Te amo, hombre.simplificaste este lío en dos líneas de código. Permítanme señalar que 'someObj' tiene que ser IQueryable para aceptar' Where (exp) ' –

2

Sí, en realidad es bastante fácil:

var name = GetBobNameFromXml(); 
var result = someObj.Where(i => i.Name == name); 

También puede elegir si desea o no aplicar criterios poco a poco.

var result = someObj; 
var name = xmlCriteria.Name; 
if(!string.IsNullOrEmpty(name)) 
{ 
    result = result.Where(i => i.Name == name); 
} 
// follow the same pattern for city, state, etc. 

Incluso puede utilizar un patrón que utiliza un diccionario nombre de la carrocería de funcs criterio, para evitar un montón de if declaraciones.

foreach(var criterionPair in xmlCriteria) 
{ 
    var value = criterionPair.Value; 
    result = result.Where(i => propGetters[criterionPair.PropertyName](i, value)); 
} 

Básicamente, hay mucho que puede hacer en esta línea. Si desea una respuesta más específicamente adaptada a su situación, deberá proporcionar una pregunta más específica.

+0

no, te perdiste su pregunta. ¿cómo se crean consultas de linq desde un campo de texto? – jcolebrand

+0

@jcolebrand: ¿Puedes aclarar a qué te refieres con "crear consultas de linq desde un campo de texto?" Por lo que puedo decir, he respondido la pregunta tan bien como se puede esperar, teniendo en cuenta la vaguedad de la pregunta. – StriplingWarrior

10

Tal dinámica LINQ puede ayudarle a: Dynamic linq part 1: Using the linq dynamic query library

query = query.Where("Id = 123 And Age > 18"); 

o se puede manipular su consulta LINQ directamente:

query = query.Where(x=>x.Id == 5); 
+0

Sí, pero ¿puedes pegar un código de ejemplo para mostrar cómo funcionaría esto utilizando dynamic linq? – jcolebrand

+0

Puede usar cadenas en la cláusula where, p. query = query.Where ("Id = 123 y edad> 18"); – Antineutrino

+5

Mi palabra hombre, ¿necesito hacer las ediciones por usted? Edita tu publicación e incluye esa información. Luego obtén votos especiales. Luego haz gamification. – jcolebrand

26

lo más seguro desea echar un vistazo a lo que permitirá Dynamic Linq usted para definir las condiciones de consulta como texto.

En cuanto a agregar condiciones dinámicamente, puede agregar condiciones a una consulta utilizando una sintaxis similar a;

if(CategoryIsImportant) 
    myQuery = myQuery.Where("CategoryId=2"); 

todos los cuales se puede (con bastante facilidad) codificar en un formato XML de su elección.

+0

'Where (" SomeText ")' Sí. Has logrado el concepto que busco lograr. –

+5

Esta es una publicación anterior, por lo que puede no ser relevante para esta pregunta específica, pero Dynamic Linq no es una característica incorporada del framework. Fue lanzado como un código fuente que puede descargar y compilar en su aplicación (https://github.com/kahanu/System.Linq.Dynamic). Dependiendo de su aplicación, esto puede no ser adecuado, así que solo quería aumentar la conciencia sobre esto. –

6

Creo que realmente tendrá que profundizar en Expression Trees. No he profundizado demasiado en esto, así que no puedo crear una muestra para usted, pero sí sé que puede usar Expression Trees para construir dinámicamente sus consultas y luego llamar a .Compile (en el código) para que pueda ejecutarse.

En realidad, aquí hay un mejor enlace Building Dynamic Queries with Expression Trees. Debería darte exactamente lo que quieres, y es bastante sucinto para lo que es. Esto debería actuar como un buen ejemplo para usted :)

+0

La respuesta de Antineutrino es probablemente la más fácil, donde puedes pasar cadenas. Sin embargo, creo que el método .pile en Expression Tree te da una verificación de tipo, por lo que es un poco más seguro en ese sentido. Sin embargo, implementar árboles de expresión no es trivial por lo que he escuchado. –

+4

¿Por qué se bajó este valor? Definitivamente parece encajar en el problema, por lo que agradecería una razón para el downvote? –

+1

Árboles de expresión: diversión para toda la familia :) Votaron porque ninguna de las otras soluciones los menciona. –

4

Supongo que desea introducir filtros opcionales, según el contenido de su XML. Para continuar con el ejemplo por StriplingWarrior:

var name = GetNameFromXml(); 
var city = GetCityFromXml(); 
var state = GetStateFromXml(); 

var result = someObj; 
if (name != null) 
    result = result.Where(i => i.Name == name); 
if (city != null) 
    result = result.Where(i => i.City == city); 
if (state != null) 
    result = result.Where(i => i.State == state); 

esta manera, se estaría aplicando cualquier número de filtros (de ninguno a los tres), dependiendo de lo que realmente está especificada en el código XML.

19

Es difícil para mí contar en función de su pregunta, pero en algunos casos no necesita un Linq dinámico y simplemente puede hacer esto ...

var result = from o in someObj 
      where (Name == null || o.Name == Name) 
      && (City == null || o.City == City) 
      && (State == null || o.State == State) 
      select o; 

Esto esencialmente evitará que los datos se filtren cuando el parámetro en cuestión es nulo. Y todavía funciona bien gracias al comportamiento de cortocircuito en C#.

+2

Tu idea me dio una pista para resolver mi problema sin tener que recurrir a complicadas bibliotecas dinámicas de Linq. Lo resolvió de la manera nativa. Gracias – Ananda

+0

Esto funcionó bien para mí. Olvidé que solíamos hacer esto en SQL Stored Procs así que es fácil ver por qué funcionaría en Linq. – Caverman