Usando contém () em LINQ para SQL
-
24-09-2019 - |
Pergunta
Estou tentando implementar uma pesquisa de palavras-chave muito básica em um aplicativo usando o LINQ para SQL. Meus termos de pesquisa estão em uma matriz de strings, sendo cada item de matriz uma palavra, e eu gostaria de encontrar as linhas que contêm os termos de pesquisa. Não me importo se eles contiverem mais do que apenas os termos de pesquisa (provavelmente, eles o farão), mas todos os termos de pesquisa precisam estar presentes.
Idealmente, eu gostaria de algo semelhante ao trecho abaixo, mas sei que isso não funcionará. Além disso, eu olhei para esta questão aqui, mas o autor dessa pergunta parece contente em fazer as coisas de outra maneira ( query.Contains(part.partName)
), o que não funciona para mim.
public IQueryable<Part> SearchForParts(string[] query)
{
return from part in db.Parts
where part.partName.Contains(query)
select part;
}
Como posso reescrever essa consulta para que ela faça o que eu preciso?
Solução
Olhando para as outras tentativas me entristece :(
public IQueryable<Part> SearchForParts(string[] query)
{
var q = db.Parts.AsQueryable();
foreach (var qs in query)
{
var likestr = string.Format("%{0}%", qs);
q = q.Where(x => SqlMethods.Like(x.partName, likestr));
}
return q;
}
Suposições:
PartName parece: "ABC 123 XYZ"
Query é {"abc", "123", "xy"}
Outras dicas
Uma solução mais simples e mais correta (então de Leppie):
public IQueryable<Part> SearchForParts(string[] query)
{
var q = db.Parts.AsQueryable();
foreach (string qs in query)
{
q = q.Where(x => x.partName.Contains(qs));
}
return q;
}
Isso funcionará enquanto partName
é uma string (ou um equivalente SQL de uma string).
O importante a ser observado é partName.Contains(qs)
é diferente de query.Contains(partName)
.
Com partName.Contains(qs)
, partName
é pesquisado por qualquer ocorrência de qs
. O SQL resultante seria equivalente (ondeu003Cqs> é o valor de qs
):
select * from Parts where partName like '%<qs>%';
Também de nota são StartsWith
e EndsWith
que são semelhantes a Contains
Mas procure a string no local específico.
query.Contains(partName)
é o mesmo que um SQL in
comando. O SQL resultante seria equivalente a (ondeu003Cquery0> é o valor de query[0]
, Assim,u003Cquery1> é o valor de query[1]
, eu003CqueryN> é o último valor na matriz de consultas):
select * from Parts where partName in ( <query0>, <query1>, ..., <queryN> );
Atualizar:
Também é importante observar que a resposta de Leppie não escapa dos caracteres curinga antes de adicioná -los ao Curti declaração. Este não é um problema com o Contains
Solução Como o LINQ escapará da consulta antes de enviá -la. Uma versão escapada do SqlMethods.Like
Solução seria:
public IQueryable<Part> SearchForParts(string[] query)
{
var q = db.Parts.AsQueryable();
foreach (var qs in query)
{
string escaped_bs = qs.Replace("/", "//"),
escaped_us = escaped_bs.Replace("_", "/_"),
escaped_p = escaped_us.Replace("%", "/%"),
escaped_br = escaped_p.Replace("[", "/["),
likestr = string.Format("%{0}%", escaped_br);
q = q.Where(x => SqlMethods.Like(x.partName, likestr, '/'));
}
return q;
}
Você não precisa se preocupar com o que o Linq escapará disso para você.
Você poderia tentar:
public IQueryable<Part> SearchForParts(string[] query)
{
return from part in db.Parts
where query.All(term => part.partName.Contains(term))
select part;
}
No entanto, não tenho certeza se o LINQ para SQL poderá transformá-lo em T-SQL. Outra opção seria:
public IQueryable<Part> SearchForParts(string[] query)
{
var result = from part in db.Parts
select part;
foreach(var term in query)
{
result = from part in result
where part.partName.Contains(term)
select part;
}
return result;
}
Não é tão bonito, mas deve funcionar. Você terá uma consulta com muito AND
está na cláusula onde.
Você pode escrever como este
var result = db.Parts.Where(p => query.All(q => p.partName.Contains(q)));
Usando o Ninjanye.searchExtension O pacote Nuget permite que você execute esta pesquisa com facilidade:
string[] terms = new[]{"search", "term", "collection"};
var result = db.Parts.Search(terms, p => p.PartName);
Você também pode pesquisar várias propriedades da string
var result = db.Parts.Search(terms, p => p.PartName, p.PartDescription);
Ou execute um RankedSearch
que retorna IQueryable<IRanked<T>>
que simplesmente inclui uma propriedade que mostra quantas vezes os termos de pesquisa apareceram:
//Perform search and rank results by the most hits
var result = db.Parts.RankedSearch(terms, p => p.PartName, p.PartDescription)
.OrderByDescending(r = r.Hits);
Há um guia mais extenso na página do Github Projects: https://github.com/ninjanye/searchExtensions
Espero que isso ajude futuros visitantes
Eu sinto que isso é um pouco simples e trabalhando para mim:
string[] product = products.Split(',');
using (var context = new ProjectTrackerEntities())
{ var result = from part in context.DBAudits where product.Contains(part.TableName) select part; }
Por favor, tente isto:
public IQueryable<Part> SearchForParts(string[] query)
{
return from part in db.Parts
where Search(part.Name,query)
select part;
}
public bool Search(string partName,string[] query)
{
for (int i = 0; i < query.Length; i++)
{
if(partName.Contains(query[i]))
return true;
}
return false;
}