Quiero crear dinámicamente una consulta LINQ para poder hacer algo como
var list = n.Elements().Where(getQuery("a", "b"));en lugar de
var list = n.Elements().Where(e => e.Name = new "a" || e.Name == "c");(La mayoría de las veces, necesito pasar XNames con espacios de nombres, no solo nombres locales...)
Mi problema es acceder a los elementos de la matriz:
private static Func<XElement, bool> getQuery(XName[] names) { var param = Expression.Parameter(typeof(XElement), "e"); Expression exp = Expression.Constant(false); for (int i = 0; i < names.Length; i++) { Expression eq = Expression.Equal( Expression.Property(param, typeof(XElement).GetProperty("Name")!.Name), /*--->*/ Expression.Variable(names[i].GetType(), "names[i]") ); } var lambda = Expression.Lambda<Func<XElement, bool>>(exp, param); return lambda.Compile(); }Obviamente, la expresión Variable es incorrecta, pero tengo dificultades para crear una expresión capaz de acceder a los valores de la matriz.
¿Necesita crear una expresión y compilarla? A menos que me falte algún matiz en esto, todo lo que necesita es una función que devuelva un Func<XElement, bool> .
private Func<XElement, bool> GetQuery(params string[] names) { return element => names.Any(n => element.Name == n); } Esto toma una matriz de cadenas y devuelve Func<XElement> . Esa función devuelve verdadero si el nombre del elemento coincide con cualquiera de los argumentos.
Luego puede usar eso como lo describió:
var list = n.Elements.Where(GetQuery("a", "b"));Hay muchas maneras de hacer algo como esto. Para una mayor legibilidad, una extensión como esta podría ser mejor:
public static class XElementExtensions { public static IEnumerable<XElement> WhereNamesMatch( this IEnumerable<XElement> elements, params string[] names) { return elements.Where(element => names.Any(n => element.Name == n)); } }Entonces el código que lo usa se convierte en
var list = n.Elements.WhereNamesMatch("a", "b"); Eso es especialmente útil cuando tenemos otros filtros en nuestra consulta LINQ. All the Where y otros métodos pueden volverse difíciles de leer. Pero si los aislamos en sus propias funciones con nombres claros, entonces el uso es más fácil de leer y podemos reutilizar la extensión en diferentes consultas.
Si desea escribirlo como Expresión, puede hacerlo así:
public static Expression<Func<Person, bool>> GetQuery(Person[] names) { var parameter = Expression.Parameter(typeof(Person), "e"); var propertyInfo = typeof(Person).GetProperty("Name"); var expression = names.Aggregate( (Expression)Expression.Constant(false), (acc, next) => Expression.MakeBinary( ExpressionType.Or, acc, Expression.Equal( Expression.Constant(propertyInfo.GetValue(next)), Expression.Property(parameter, propertyInfo)))); return Expression.Lambda<Func<Person, bool>>(expression, parameter); }Si compila o no la expresión está determinado por los medios que desea lograr. Si desea pasar la expresión a un proveedor de consultas (cf. Queryable . Where ) y hacer que, por ejemplo, la base de datos filtre sus valores, entonces no puede compilar la expresión.
Si desea filtrar una colección en la memoria, es decir, enumera todos los elementos (cf. Enumerable . Where ) y aplica el predicado a todos los elementos, entonces debe compilar la expresión. En este caso, probablemente no debería usar la API de expresión, ya que agrega complejidad a su código y, por lo tanto, es más vulnerable a los errores de tiempo de ejecución.