Estoy buscando implementar un sistema por el cual use las condiciones 'compilar' y luego devolver los datos resultantes de la base de datos. En la actualidad, hay un procedimiento almacenado que genera SQL sobre la marcha y lo ejecuta. Este es un problema particular que quiero eliminar.Consulta dinámica de linq con criterios múltiples/desconocidos
Mi problema proviene del hecho de que puedo tener múltiples campos dentro de mis criterios, y para cada uno de estos campos, podría haber 1 o más valores, con diferentes operadores potenciales.
Por ejemplo,
from t in Contacts
where t.Email == "[email protected]" || t.Email.Contains ("mydomain")
where t.Field1 == "valuewewant"
where t.Field2 != "valuewedontwant"
select t
El campo, criterios y operador se almacenan en la base de datos (y List<FieldCriteria>
) y habría alguna cosa como esta (basado en más arriba);
Email, Equals, "[email protected]"
Email, Contains, "mydomain" Field1,
Equals, "valuewewant" Field2,
DoesNotEqual, "valuewedontwant"
o
new FieldCriteria
{
FieldName = "Email",
Operator = 1,
Value = "[email protected]"
}
Así, utilizando la información que tengo, quiero ser capaz de construir una consulta con cualquier número de condiciones. He visto enlaces anteriores a Dynamic Linq y PredicateBuilder, pero no puedo visualizar esto como una solución a mi propio problema.
Cualquier sugerencia sería apreciada.
actualización
A raíz de la sugerencia sobre Dynamic LINQ, se me ocurrió una solución muy básica, usando un solo operador, con 2 campos y múltiples criterios. Un poco crudo por el momento codificado en LinqPad, pero los resultados son exactamente lo que quería;
enum Operator
{
Equals = 1,
}
class Condition
{
public string Field { get; set; }
public Operator Operator { get; set;}
public string Value { get; set;}
}
void Main()
{
var conditions = new List<Condition>();
conditions.Add(new Condition {
Field = "Email",
Operator = Operator.Equals,
Value = "[email protected]"
});
conditions.Add(new Condition {
Field = "Email",
Operator = Operator.Equals,
Value = "[email protected]"
});
conditions.Add(new Condition {
Field = "Field1",
Operator = Operator.Equals,
Value = "Chris"
});
var statusConditions = "Status = 1";
var emailConditions = from c in conditions where c.Field == "Email" select c;
var field1Conditions = from c in conditions where c.Field == "Field1" select c;
var emailConditionsFormatted = from c in emailConditions select string.Format("Email=\"{0}\"", c.Value);
var field1ConditionsFormatted = from c in field1Conditions select string.Format("Field1=\"{0}\"", c.Value);
string[] conditionsArray = emailConditionsFormatted.ToArray();
var emailConditionsJoined = string.Join("||", conditionsArray);
Console.WriteLine(String.Format("Formatted Condition For Email: {0}",emailConditionsJoined));
conditionsArray = field1ConditionsFormatted.ToArray();
var field1ConditionsJoined = string.Join("||", conditionsArray);
Console.WriteLine(String.Format("Formatted Condition For Field1: {0}",field1ConditionsJoined));
IQueryable results = ContactView.Where(statusConditions);
if (emailConditions != null)
{
results = results.Where(emailConditionsJoined);
}
if (field1Conditions != null)
{
results = results.Where(field1ConditionsJoined);
}
results = results.Select("id");
foreach (int id in results)
{
Console.WriteLine(id.ToString());
}
}
Con un SQL generado de;
-- Region Parameters
DECLARE @p0 VarChar(1000) = 'Chris'
DECLARE @p1 VarChar(1000) = '[email protected]'
DECLARE @p2 VarChar(1000) = '[email protected]'
DECLARE @p3 Int = 1
-- EndRegion
SELECT [t0].[id]
FROM [Contacts].[ContactView] AS [t0]
WHERE ([t0].[field1] = @p0) AND (([t0].[email] = @p1) OR ([t0].[email] = @p2)) AND ([t0].[status] = @p3)
y la salida de la consola:
Formatted Condition For Email: Email="[email protected]"||Email="[email protected]"
Formatted Condition For Field1: Field1="Chris"
sólo necesitan limpiar esto y añadir los otros operadores y que se ve bien.
Si alguien tiene algún comentario sobre este hasta el momento, cualquier entrada sería apreciada
La idea general de "LINQ to" es convertir expresiones estáticas de tiempo de compilación de C# a algún tipo de cadena que se enviará a la base de datos subyacente para su ejecución. Su sugerencia de usar una cadena para convertirla en una expresión linq que a su vez se convierte en una cadena parece un poco redundante :) –
Tiene razón acerca de la expresión "estática" en SQL, pero en la pregunta el usuario no sabe sobre la expresión en tiempo de compilación y, por lo tanto, necesita generar expresiones en tiempo de ejecución. DLINQ es una forma de hacerlo desde "cadenas" y de otra manera es usar Expression API para crear las expresiones necesarias en tiempo de ejecución. – Ankur