Pregunta

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, agregaba a la cadena de consulta, pero quiero hacerlo con Linq.¿Cómo puedo agregar cláusulas Where condicionalmente?

¿Fue útil?

Solución

Si desea filtrar solo 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);

Hacerlo de esta manera permitirá que su árbol de expresión sea exactamente lo que desea.De esa manera, el SQL creado será exactamente lo que necesita y nada menos.

Otros consejos

Si necesita filtrar la base en una lista/matriz, utilice 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 patrones de filtros y 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;
}

Otra opción sería usar algo como el PredicateBuilder discutido aquí.Le 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 funcionar 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 el where declaración:

where (lastNameSearch && name.LastNameSearch == "smith")

significa que cuando se crea la consulta final, si lastNameSearch es false la consulta omitirá por completo cualquier SQL para la búsqueda de apellido.

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 ? source : branch(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 un 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 ? source : branch(source);
    }

No es lo más bonito, pero puedes usar una expresión lambda y pasar tus condiciones opcionalmente.En TSQL hago muchas de las siguientes cosas para que los parámetros sean opcionales:

DONDE Campo = @FieldVar O @FieldVar ES NULO

Podría duplicar el mismo estilo con la siguiente lambda (un ejemplo de verificación de autenticación):

MyDataContext db = nuevo MyDataContext();

void RunQuery(cadena parámetro1, cadena parámetro2, int?parámetro3){

Func checkUser = usuario =>

((param1.Longitud > 0)?usuario.Param1 == parámetro1:1 == 1) &&

((param2.Longitud > 0)?usuario.Param2 == parámetro2:1 == 1) &&

((param3! = nulo)?usuario.Param3 == parámetro3:1 == 1);

Usuario encontradoUsuario = db.Users.SingleOrDefault(checkUser);

}

Tuve un requisito similar recientemente y finalmente encontré esto en MSDN.Muestras de CSharp para Visual Studio 2008

Las clases incluidas en el ejemplo 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ías saber cómo condicionalmente añadir cláusulas adicionales.En ese caso no tengo ni idea.:) Lo que probablemente haría es simplemente preparar varias consultas y ejecutar la correcta, dependiendo de lo que termine necesitando.

Podrías utilizar 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 "yo", "meyou" y "mow".

Podrías optimizar eso haciendo el foreach con los predicados en una función totalmente diferente que aplica OR a todos los predicados.

Puedes crear y utilizar este método de extensión.

public static IQueryable<TSource> WhereIf<TSource>(this IQueryable<TSource> source, bool isToExecute, Expression<Func<TSource, bool>> predicate)
{
    return isToExecute ? source.Where(predicate) : source;
}
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top