Respostas:
se você quiser filtrar apenas se certos critérios forem passados, faça algo assim
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 dessa forma permitirá que sua árvore de expressão seja exatamente o que você deseja. Dessa forma, o SQL criado será exatamente o que você precisa e 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.
Se você precisar filtrar com base em uma lista / matriz, use o seguinte:
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();
}
Terminei usando uma resposta semelhante à de Daren, mas com uma interface 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();
Isso cria a consulta antes de chegar ao banco de dados. O comando não será executado até .ToList () no final.
Quando se trata de linq condicional, gosto muito do padrão de filtros e tubos.
http://blog.wekeroad.com/mvc-storefront/mvcstore-part-3/
Basicamente, você cria um método de extensão para cada caso de filtro que leva em IQueryable e um 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;
}
Resolvi isso com um método de extensão para permitir que LINQ seja habilitado condicionalmente no meio de uma expressão fluente. Isso elimina a necessidade de dividir a expressão com if
instruções.
.If()
método de extensão:
public static IQueryable<TSource> If<TSource>(
this IQueryable<TSource> source,
bool condition,
Func<IQueryable<TSource>, IQueryable<TSource>> branch)
{
return condition ? branch(source) : source;
}
Isso permite que você faça o seguinte:
return context.Logs
.If(filterBySeverity, q => q.Where(p => p.Severity == severity))
.If(filterByUser, q => q.Where(p => p.User == user))
.ToList();
Esta é também uma IEnumerable<T>
versão que irá lidar com a maioria das outras expressões LINQ:
public static IEnumerable<TSource> If<TSource>(
this IEnumerable<TSource> source,
bool condition,
Func<IEnumerable<TSource>, IEnumerable<TSource>> branch)
{
return condition ? branch(source) : source;
}
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;
Observe que eu só tenho isso para funcionar com o Linq 2 SQL. EntityFramework não implementa Expression.Invoke, que é necessário para que este método funcione. Eu tenho uma pergunta sobre este assunto aqui .
Fazendo isso:
bool lastNameSearch = true/false; // depending if they want to search by last name,
tendo isso na where
declaração:
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 de sobrenome.
Não é a coisa mais bonita, mas você pode usar uma expressão lambda e passar suas condições opcionalmente. No TSQL, faço muitas das seguintes ações para tornar os parâmetros opcionais:
WHERE Field = @FieldVar OU @FieldVar IS NULL
Você pode 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 = user =>
((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 um requisito semelhante recentemente e finalmente encontrei isso no MSDN. Amostras CSharp para Visual Studio 2008
As classes 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;
Basta usar o operador && do C #:
var items = dc.Users.Where(l => l.Date == DateTime.Today && l.Severity == "Critical")
Edit: Ah, preciso ler com mais atenção. Você queria saber como adicionar cláusulas adicionais condicionalmente . Nesse caso, não tenho ideia. :) O que eu provavelmente faria seria apenas preparar várias consultas e executar a correta, dependendo do que eu acabasse precisando.
Você pode 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 árvores de expressão, o que significa que o Linq to SQL executaria o código de verificação em cada registro.
Alternativamente:
var results =
from rec in GetSomeRecs()
where
(!filterBySeverity || rec.Severity == severity) &&
(!filterByUser|| rec.User == user)
select rec;
Isso pode funcionar em árvores de expressão, o que significa que Linq para SQL seria otimizado.
Bem, pensei que você poderia colocar as condições do filtro em uma 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);
Isso resulta em uma lista contendo "me", "meyou" e "mow".
Você poderia otimizar isso fazendo o foreach com os predicados em uma função totalmente diferente que executa o OR de todos os predicados.