He estado buscando mucho sobre mi problema actual, pero no pude encontrar una respuesta real para resolver ese problema.
Estoy tratando de crear una consulta LINQ que produzca el siguiente SQL:
SELECT * FROM TABLE WHERE (Field1 = X, Field2 = Y ... ) or (Field3 = Z)En una situación normal, simplemente haría esto:
Object.Where(c => (c.Field1 == X && c.Field2 == Y) || (c.Field3 == Z)) No puedo usar este enfoque porque la consulta se crea usando varias llamadas .Where() .
Teniendo un ejemplo:
// This is a short example, the real world situation has 20 fields to check and they are all connected with an AND. if (model.Field1.HasValue) { Query = Query.Where(c => c.Field1 == X) } if (model.Field2.HasValue) { Query = Query.Where(c => c.Field2 == X) } [...] like 20 more of these .Where() calls. y así es como se me complica. Todas estas llamadas .Where() están creando una consulta Linq que está conectada con AND , lo cual está bien.
¿Cómo les permito ejecutar con paréntesis y agregar un OR simple ahora usando la API?
¿Hay alguna manera de guardar el predicado en algunas variables para poder hacer algo como:
Query = Query.Where(c => previousPredicates || c.Field3 == X)o como solucionar ese problema?
Creo que debe haber una buena solución para ese problema en particular y no soy el único que la necesita, pero no estoy absolutamente seguro de cómo lograrlo.
PD: Realmente no puedo eliminar las múltiples .Where() y escribir SQL directo no es una opción.
EDITAR StackOverflow quiere que diga por qué mi pregunta es diferente de las demás. Bueno, la cosa va de Parentheses . No quiero conectar todos los .Where() con una sola cláusula OR, quiero dejarlos con AND y agregar otra cláusula OR mientras todas las consultas AND están entre paréntesis.
Primero, cree algunos métodos auxiliares de extensión para combinar más fácilmente dos predicados Func<T,bool> :
public static Func<T, bool> And<T>(this Func<T, bool> left, Func<T, bool> right) => a => left(a) && right(a); public static Func<T, bool> Or<T>(this Func<T, bool> left, Func<T, bool> right) => a => left(a) || right(a);Luego puedes usarlos para encadenar predicados:
var list = Enumerable.Range(1, 100); Func<int, bool> predicate = v => true; // start with true since we chain ANDs first predicate = predicate.And(v => v % 2 == 0); // numbers dividable by 2 predicate = predicate.And(v => v % 3 == 0); // numbers dividable by 3 predicate = predicate.Or(v => v % 31 == 0); // numbers dividable by 31 var result = list.Where(predicate); foreach (var i in result) Console.WriteLine(i);Producción:
6 12 18 24 30 31 36 42 48 54 60 62 66 72 78 84 90 93 96Puede usar Expression para crear en un solo paso como este:
Expression<Func<Model, bool>> exp = (model => ((model.Field1.HasValue && c.Field1 == X) && (model.Field2.HasValue && c.Field2 == X)) || model.Field3 == X )Una vez que haya definido sus predicados, es muy fácil usarlos en una consulta.
var result = Query.AsQueryable().Where(exp)Verifique el código en esta esencia: mi url esencial
ACTUALIZACIÓN 1: si tiene que usar pasos para crear su expresión, puede usar esto:
Expression<Func<Model, bool>> exp = c => true; if (model.Field1.HasValue) { var prefix = exp.Compile(); exp = c => prefix(c) && c.Field1 == X; } if (model.Field2.HasValue) { var prefix = exp.Compile(); exp = c => prefix(c) && c.Field2 == X; } [...] like 20 more of these .Where() calls.Si desea crear su consulta mediante programación y hacer que se ejecute en su servidor SQL en lugar de obtener todos los registros y consultar en la memoria, debe usar el conjunto de métodos estáticos en la clase Expression y crear su consulta con ellos. En tu ejemplo:
public class Query // this will contain your 20 fields you want to check against { public int? Field1; public int? Field2; public int? Field3; public int Field4; } public class QueriedObject // this is the object representing the database table you're querying { public int QueriedField; } public class Program { public static void Main() { var queryable = new List<QueriedObject>().AsQueryable(); var query = new Query { Field2 = 1, Field3 = 4, Field4 = 2 }; // this represents the argument to your lambda expression var parameter = Expression.Parameter(typeof(QueriedObject), "qo"); // this is the "qo.QueriedField" part of the resulting expression - we'll use it several times later var memberAccess = Expression.Field(parameter, "QueriedField"); // start with a 1 == 1 comparison for easier building - // you can just add further &&s to it without checking if it's the first in the chain var expr = Expression.Equal(Expression.Constant(1), Expression.Constant(1)); // doesn't trigger, so you still have 1 == 1 if (query.Field1.HasValue) { expr = Expression.AndAlso(expr, Expression.Equal(memberAccess, Expression.Constant(query.Field1.Value))); } // 1 == 1 && qo.QueriedField == 1 if (query.Field2.HasValue) { expr = Expression.AndAlso(expr, Expression.Equal(memberAccess, Expression.Constant(query.Field2.Value))); } // 1 == 1 && qo.QueriedField == 1 && qo.QueriedField == 4 if (query.Field3.HasValue) { expr = Expression.AndAlso(expr, Expression.Equal(memberAccess, Expression.Constant(query.Field3.Value))); } // (1 == 1 && qo.QueriedField == 1 && qo.QueriedField == 4) || qo.QueriedField == 2 expr = Expression.OrElse(expr, Expression.Equal(memberAccess, Expression.Constant(query.Field4))); // now, we combine the lambda body with the parameter to create a lambda expression, which can be cast to Expression<Func<X, bool>> var lambda = (Expression<Func<QueriedObject, bool>>) Expression.Lambda(expr, parameter); // you can now do this, and the Where will be translated to an SQL query just as if you've written the expression manually var result = queryable.Where(lambda); } }Ok, has tenido tu propia respuesta sobre linq.
Permítanme presentarles un enfoque diferente usando Dynamic.linq
// You could build a Where string that can be converted to linq. // and do if sats and append your where sats string. as the example below var query = "c => (c.Field1 == \" a \" && c.Field2 == Y) || (c.Field3 == \" b \")"; var indicator = query.Split('.').First(); // the indicator eg c // assume TABLE is the name of the class var p = Expression.Parameter(typeof(TABLE), indicator); var e = DynamicExpression.ParseLambda(new[] { p }, null, query); // and simple execute the expression var items = Object.Where(e);