Eu tenho a seguinte aula:
public class Article
{
long Id;
List<Category> Categories;
}
Estou usando o EF Core 5 e o que preciso é uma consulta LINQ no SQLite que retorne todos os artigos que possuem todas as categorias que eu especificar.
Tentei o seguinte código:
List<long> cIds = c.Select (x => x.Id).ToList ();
query.Where (art => cIds.All (cId => art.Categories.Select (c => c.Id).Contains (cId)));
mas o compilador diz
InvalidOperationException: The LINQ expression 'DbSet<Article>()
.Where(a => __cIds_0
.All(cId => DbSet<Dictionary<string, object>>("ArticleCategory")
.Where(a0 => EF.Property<Nullable<long>>(a, "Id") != null && object.Equals(
objA: (object)EF.Property<Nullable<long>>(a, "Id"),
objB: (object)EF.Property<Nullable<long>>(a0, "ArticlesId")))
.Join(
inner: DbSet<Category>(),
outerKeySelector: a0 => EF.Property<Nullable<long>>(a0, "CategoriesId"),
innerKeySelector: c => EF.Property<Nullable<long>>(c, "Id"),
resultSelector: (a0, c) => new TransparentIdentifier<Dictionary<string, object>, Category>(
Outer = a0,
Inner = c
))
.Select(ti => ti.Inner.Id)
.Any(p => p == cId)))' could not be translated. Either rewrite the query in a form that can be translated, or switch to client evaluation explicitly by inserting a call to 'AsEnumerable', 'AsAsyncEnumerable', 'ToList', or 'ToListAsync'. See https://go.microsoft.com/fwlink/?linkid=2101038 for more information.
Como posso obtê-lo?
Uma possível solução que encontrei é a seguinte:
List<long> cIds = c.Select (x => x.Id).ToList ();
query = query.Where (art => art.Categories.Select (c => c.Id).Any (x => cIds.Contains (x)));
query = query.Include (x => x.Categories);
result = await query.ToListAsync ();
result = result.Where (art => cIds.All (cId => art.Categories.Select (c => c.Id).Contains (cId))).ToList ();
Mas eu queria saber se poderia obter o mesmo resultado com uma única consulta LINQ.
desde já, obrigado
ATUALIZAR:
Vou apenas adicionar a função onde este código será usado e fazer um exemplo para tornar as coisas mais claras:
Esta é a função onde o código será usado:
public async Task<List<Article>> SearchAsync (string search, Section s, Website w,
List<Category> c)
{
List<Article> result = new List<Article> ();
if (
search == ""
&& s == null
&& w == null
&& c.Count == 0
)
return result;
IQueryable<Article> query = dbSet.AsQueryable ();
if (search != "")
query = query.Where (x => x.Title.Contains (search) || x.Summary.Contains (search));
if (s != null)
query = query.Where (x => x.SectionId == s.Id);
if (w != null)
query = query.Where (x => x.WebsiteId == w.Id);
if (c.Count > 0)
{
List<long> cIds = c.Select (x => x.Id).ToList ();
query = query.Where (art => art.Categories.Select (c => c.Id).Any (x => cIds.Contains (x)));
}
query = query.Include (x => x.Categories);
result = await query.ToListAsync ();
if (c.Count > 0)
{
List<long> cIds = c.Select (x => x.Id).ToList ();
result = result.Where (art => cIds.All (cId => art.Categories.Select (c => c.Id).Contains (cId))).ToList ();
}
return result;
}
E aqui está um exemplo:
Digamos que c
irá conter ids 9,10,11
e a coleção de artigos é o seguinte pseudocódigo:
List<article> articles = new List<Article> ()
{
new Article () {Id = 1, Categories = "12,44,55"}
new Article () {Id = 2, Categories = "7,8,9,10,11"}
new Article () {Id = 3, Categories = "9,10,11"}
}
A consulta linq deve retornar Artigo com Id 2
e 3
porque ambos contêm todos os ids presentes em c
.
Uma das soluções usando Intersect
, mas temos que preparar os dados para a interseção.
// articles query
var query = ...
var cIds = c.Select(x => x.Id).ToList();
var idsCount = cIds.Count();
// translating list of IDs to IQueryable
var categoryIdsQuery = dbContext.Categories
.Where(c => cIds.Contains(c.Id))
.Select(c => c.Id);
query = query
.Where(art => art.Categories
.Select(c => c.Id)
.Intersect(categoryIdsQuery)
.Count() == idsCount
)
.Include(x => x.Categories);
Este artigo é coletado da Internet.
Se houver alguma infração, entre em [email protected] Delete.
deixe-me dizer algumas palavras