Estamos trabajando en un visor de registros. El uso tendrá la opción de filtrar por usuario, gravedad, etc. En los días de Sql agregaría a la cadena de consulta, pero quiero hacerlo con Linq. ¿Cómo puedo agregar condicionalmente cláusulas where?
Respuestas:
si solo desea filtrar si se pasan ciertos criterios, haga algo como esto
var logs = from log in context.Logs
select log;
if (filterBySeverity)
logs = logs.Where(p => p.Severity == severity);
if (filterByUser)
logs = logs.Where(p => p.User == user);
Si lo hace de esta manera, permitirá que su árbol de expresiones sea exactamente lo que desea. De esa manera, el SQL creado será exactamente lo que necesita y nada menos.
LINQ to Entities does not recognize the method 'System.String get_Item(System.String)' method, and this method cannot be translated into a store expression.
Si necesita filtrar la base en una lista / matriz, use lo siguiente:
public List<Data> GetData(List<string> Numbers, List<string> Letters)
{
if (Numbers == null)
Numbers = new List<string>();
if (Letters == null)
Letters = new List<string>();
var q = from d in database.table
where (Numbers.Count == 0 || Numbers.Contains(d.Number))
where (Letters.Count == 0 || Letters.Contains(d.Letter))
select new Data
{
Number = d.Number,
Letter = d.Letter,
};
return q.ToList();
}
Terminé usando una respuesta similar a la de Daren, pero con una interfaz IQueryable:
IQueryable<Log> matches = m_Locator.Logs;
// Users filter
if (usersFilter)
matches = matches.Where(l => l.UserName == comboBoxUsers.Text);
// Severity filter
if (severityFilter)
matches = matches.Where(l => l.Severity == comboBoxSeverity.Text);
Logs = (from log in matches
orderby log.EventTime descending
select log).ToList();
Eso construye la consulta antes de llegar a la base de datos. El comando no se ejecutará hasta .ToList () al final.
Cuando se trata de linq condicional, me gustan mucho los filtros y el patrón de tuberías.
http://blog.wekeroad.com/mvc-storefront/mvcstore-part-3/
Básicamente, crea un método de extensión para cada caso de filtro que incluye IQueryable y un parámetro.
public static IQueryable<Type> HasID(this IQueryable<Type> query, long? id)
{
return id.HasValue ? query.Where(o => i.ID.Equals(id.Value)) : query;
}
Resolví esto con un método de extensión para permitir que LINQ se habilite condicionalmente en medio de una expresión fluida. Esto elimina la necesidad de dividir la expresión con if
declaraciones.
.If()
método de extensión:
public static IQueryable<TSource> If<TSource>(
this IQueryable<TSource> source,
bool condition,
Func<IQueryable<TSource>, IQueryable<TSource>> branch)
{
return condition ? branch(source) : source;
}
Esto le permite hacer esto:
return context.Logs
.If(filterBySeverity, q => q.Where(p => p.Severity == severity))
.If(filterByUser, q => q.Where(p => p.User == user))
.ToList();
Aquí también hay una IEnumerable<T>
versión que manejará la mayoría de las otras expresiones LINQ:
public static IEnumerable<TSource> If<TSource>(
this IEnumerable<TSource> source,
bool condition,
Func<IEnumerable<TSource>, IEnumerable<TSource>> branch)
{
return condition ? branch(source) : source;
}
Otra opción sería utilizar algo como el PredicateBuilder que se analiza aquí . Te permite escribir código como el siguiente:
var newKids = Product.ContainsInDescription ("BlackBerry", "iPhone");
var classics = Product.ContainsInDescription ("Nokia", "Ericsson")
.And (Product.IsSelling());
var query = from p in Data.Products.Where (newKids.Or (classics))
select p;
Tenga en cuenta que solo tengo esto para trabajar con Linq 2 SQL. EntityFramework no implementa Expression.Invoke, que es necesario para que este método funcione. Tengo una pregunta sobre este tema aquí .
Haciendo esto:
bool lastNameSearch = true/false; // depending if they want to search by last name,
teniendo esto en la where
declaración:
where (lastNameSearch && name.LastNameSearch == "smith")
significa que cuando se crea la consulta final, si lastNameSearch
es así, false
la consulta omitirá completamente cualquier SQL para la búsqueda de apellido.
No es lo más bonito, pero puede usar una expresión lambda y pasar sus condiciones opcionalmente. En TSQL, hago muchas de las siguientes acciones para que los parámetros sean opcionales:
DONDE Field = @FieldVar O @FieldVar ES NULO
Puede duplicar el mismo estilo con la siguiente lambda (un ejemplo de verificación de autenticación):
MyDataContext db = new MyDataContext ();
void RunQuery (cadena param1, cadena param2, int? param3) {
Func checkUser = usuario =>
((param1.Length> 0)? user.Param1 == param1: 1 == 1) &&
((param2.Length> 0)? user.Param2 == param2: 1 == 1) &&
((param3! = null)? user.Param3 == param3: 1 == 1);
Usuario encontradoUser = db.Users.SingleOrDefault (checkUser);
}
Recientemente tuve un requisito similar y finalmente encontré esto en MSDN. Ejemplos de CSharp para Visual Studio 2008
Las clases incluidas en la muestra DynamicQuery de la descarga le permiten crear consultas dinámicas en tiempo de ejecución en el siguiente formato:
var query =
db.Customers.
Where("City = @0 and Orders.Count >= @1", "London", 10).
OrderBy("CompanyName").
Select("new(CompanyName as Name, Phone)");
Con esto, puede crear una cadena de consulta dinámicamente en tiempo de ejecución y pasarla al método Where ():
string dynamicQueryString = "City = \"London\" and Order.Count >= 10";
var q = from c in db.Customers.Where(queryString, null)
orderby c.CompanyName
select c;
Simplemente use el operador && de C #:
var items = dc.Users.Where(l => l.Date == DateTime.Today && l.Severity == "Critical")
Editar: Ah, necesito leer más detenidamente. Quería saber cómo agregar condicionalmente cláusulas adicionales. En ese caso, no tengo ni idea. :) Lo que probablemente haría es preparar varias consultas y ejecutar la correcta, dependiendo de lo que termine necesitando.
Podrías usar un método externo:
var results =
from rec in GetSomeRecs()
where ConditionalCheck(rec)
select rec;
...
bool ConditionalCheck( typeofRec input ) {
...
}
Esto funcionaría, pero no se puede dividir en árboles de expresión, lo que significa que Linq to SQL ejecutaría el código de verificación en cada registro.
Alternativamente:
var results =
from rec in GetSomeRecs()
where
(!filterBySeverity || rec.Severity == severity) &&
(!filterByUser|| rec.User == user)
select rec;
Eso podría funcionar en árboles de expresión, lo que significa que Linq to SQL estaría optimizado.
Bueno, lo que pensé fue que podrías poner las condiciones del filtro en una lista genérica de Predicados:
var list = new List<string> { "me", "you", "meyou", "mow" };
var predicates = new List<Predicate<string>>();
predicates.Add(i => i.Contains("me"));
predicates.Add(i => i.EndsWith("w"));
var results = new List<string>();
foreach (var p in predicates)
results.AddRange(from i in list where p.Invoke(i) select i);
Eso da como resultado una lista que contiene "me", "meyou" y "mow".
Puede optimizar eso haciendo el foreach con los predicados en una función totalmente diferente que OR todos los predicados.