Consultas de Linq Conditional

Estamos trabalhando em um visualizador de logs. O uso terá a opção de filtrar por usuário, severidade, etc. Nos dias do Sql eu adicionaria a string de consulta, mas eu quero fazê-lo com o Linq. Como posso adicionar condicionalmente cláusulas where?

se você quiser filtrar somente se certos critérios forem passados, faça algo como isto

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); 

Fazer isso desta maneira permitirá que sua tree de expressões seja exatamente o que você deseja. Dessa forma, o SQL criado será exatamente o que você precisa e nada menos.

Se você precisar filtrar base em uma lista / matriz, use o seguinte:

  public List GetData(List Numbers, List Letters) { if (Numbers == null) Numbers = new List(); if (Letters == null) Letters = new List(); 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(); } 

Acabei usando uma resposta semelhante à de Daren, mas com uma interface IQueryable:

 IQueryable 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(); 

Isso cria a consulta antes de atingir o database. O comando não será executado até .ToList () no final.

Quando se trata de linq condicional, gosto muito do padrão de filtros e canos.
http://blog.wekeroad.com/mvc-storefront/mvcstore-part-3/

Basicamente, você cria um método de extensão para cada caso de filtro que considera o IQueryable e um parâmetro.

 public static IQueryable HasID(this IQueryable query, long? id) { return id.HasValue ? query.Where(o => i.ID.Equals(id.Value)) : query; } 

Outra opção seria usar algo como o PredicateBuilder discutido aqui . Ele permite que você escreva um código como o seguinte:

 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; 

Note que eu só tenho isso para trabalhar com o SQL 2 do Linq. EntityFramework não implementa Expression.Invoke, que é necessário para este método funcionar. Eu tenho uma pergunta sobre esse assunto aqui .

Fazendo isso:

 bool lastNameSearch = true/false; // depending if they want to search by last name, 

tendo isso na declaração where :

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

significa que quando a consulta final é criada, se lastNameSearch for false a consulta omitirá completamente qualquer SQL para a pesquisa do último nome.

Não é a coisa mais bonita, mas você pode usar uma expressão lambda e passar suas condições opcionalmente. No TSQL, faço muitos dos seguintes para tornar os parâmetros opcionais:

WHERE Field = @FieldVar OU @FieldVar IS NULL

Você poderia duplicar o mesmo estilo com o seguinte lambda (um exemplo de verificação de autenticação):

MyDataContext db = new MyDataContext ();

void RunQuery (string param1, string param2, int? param3) {

Func checkUser = usuário =>

((param1.Length> 0)? user.Param1 == param1: 1 == 1) &&

((param2.Length> 0)? user.Param2 == param2: 1 == 1) &&

((param3! = null)? user.Param3 == param3: 1 == 1);

Usuário foundUser = db.Users.SingleOrDefault (checkUser);

}

Eu tive uma exigência semelhante recentemente e, eventualmente, encontrei isso no MSDN. Amostras do CSharp para o Visual Studio 2008

As classs incluídas na amostra DynamicQuery do download permitem que você crie consultas dinâmicas em tempo de execução no seguinte formato:

 var query = db.Customers. Where("City = @0 and Orders.Count >= @1", "London", 10). OrderBy("CompanyName"). Select("new(CompanyName as Name, Phone)"); 

Usando isso, você pode construir uma string de consulta dinamicamente em tempo de execução e passá-la para o 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; 

Eu resolvi isso com um método de extensão para permitir que o LINQ fosse habilitado condicionalmente no meio de uma expressão fluida. Isso elimina a necessidade de dividir a expressão com instruções if .

Método de extensão .If() :

 public static IQueryable If( this IQueryable source, bool condition, Func, IQueryable> branch) { return condition ? source : branch(source); } 

Isso permite que você faça isso:

 return context.Logs .If(filterBySeverity, q => q.Where(p => p.Severity == severity)) .If(filterByUser, q => q.Where(p => p.User == user)) .ToList(); 

Apenas use o operador && do C #:

 var items = dc.Users.Where(l => l.Date == DateTime.Today && l.Severity == "Critical") 

Edit: Ah, precisa ler com mais cuidado. Você queria saber como adicionar condicionalmente cláusulas adicionais. Nesse caso, não faço ideia. 🙂 O que eu provavelmente faria é apenas preparar várias consultas e executar o caminho certo, dependendo do que acabei precisando.

Você poderia usar um método externo:

 var results = from rec in GetSomeRecs() where ConditionalCheck(rec) select rec; ... bool ConditionalCheck( typeofRec input ) { ... } 

Isso funcionaria, mas não pode ser dividido em trees de expressão, o que significa que o Linq to SQL executaria o código de verificação em todos os registros.

Alternativamente:

 var results = from rec in GetSomeRecs() where (!filterBySeverity || rec.Severity == severity) && (!filterByUser|| rec.User == user) select rec; 

Isso pode funcionar em trees de expressão, o que significa que o Linq to SQL seria otimizado.

Bem, o que eu pensei foi que você poderia colocar as condições do filtro em uma lista genérica de predicados:

  var list = new List { "me", "you", "meyou", "mow" }; var predicates = new List>(); predicates.Add(i => i.Contains("me")); predicates.Add(i => i.EndsWith("w")); var results = new List(); foreach (var p in predicates) results.AddRange(from i in list where p.Invoke(i) select i); 

Isso resulta em uma lista contendo “me”, “meyou” e “mow”.

Você poderia otimizar isso fazendo o foreach com os predicados em uma function totalmente diferente que ORs todos os predicados.