Entity Framework Core Where All - c#

I can do this fine in EF.Net, but not in EFCore
List<string> keyList = string.IsNullOrEmpty(keywords) ? new List<string>() : keywords.Split(' ').ToList();
collections = await db.ProductCollections
.Where(m => m.Children.Count == 0 && (!keyList.Any() ? true : keyList.All(x => m.Name.Contains(x))))
.ToListAsync();
I changed it into:
collections = await db.ProductCollections
.Where(m => m.Children.Count == 0 && (keyList.Count == 0 || keyList.All(x => m.Name.Contains(x))))
.ToListAsync();
So I guess the problem is in keyList.All(). How can I achieve this in EFCore?
The error message:
The LINQ expression 'DbSet()
.Where(p => DbSet()
.Where(p0 => EF.Property<Nullable>(p, "ID") != null && object.Equals(
objA: (object)EF.Property<Nullable>(p, "ID"),
objB: (object)EF.Property<Nullable>(p0, "ParentId")))
.Count() == 0 && False || __keyList_0
.All(x => p.Name.Contains(x)))' 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.

Alternative approach to build query based on key list, before executing it.
var keys = string.IsNullOrEmpty(keywords) ? Array.Empty<string>() : keywords.Split(' ');
var query = db.ProductCollections.Where(p => p.Children.Any() == false);
foreach (var key in keys)
{
query = query.Where(p => p.Name.Contains(key));
}
var collections = await query.ToListAsync();

Related

Could not be translated. Either rewrite the query in a form that can be translated,

I have C# application (.NET Core 6) and I have written the following LINQ expression.
var data = query.OrderByDescending(a => a.CreatedOn).GroupBy(b => new { b.PackageID, b.PatientId }).ToList();
I get following error
The LINQ expression 'DbSet<LAB_ValueBasedResult>()
.Where(item => item.GroupId == 58)
.Where(item => item.HospitalId == 59)
.Where(x => x.IsActive)
.Where(x => x.CreatedOn >= __AddDays_0)
.Where(x => x.CreatedOn <= __AddDays_1)
.Where(x => __lstPatientID_2.Contains(x.PatientId))
.OrderByDescending(a => a.CreatedOn)
.GroupBy(b => new {
PackageID = b.PackageID,
PatientId = b.PatientId
})' 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'.
I want to Add OrderByDescending and GroupBy in LINQ execute. how could I do it?
Please advise how can I resolve this.
Thank you.

Find in Entity Framework multiple OR parameters

Suppose I have a product in my database with the description “white shirt size 50”.
The search parameter would be “shirt 50”. I have a more complex query in which I add several “OR”s and I can't get them to work.
I get the following error:
The LINQ expression
'DbSet()
.Where(p => p.IdTienda == __request_IdTienda_0)
.Join(
inner: DbSet(),
outerKeySelector: p => p.IdArticulo,
innerKeySelector: a => a.Id,
resultSelector: (p, a) => new TransparentIdentifier<Publicacion, Articulo>(
Outer = p,
Inner = a
))
.Where(ti => __arrayrequest_1
.Any(s => ti.Outer.Descripcion.Contains(s)) || ti.Outer.Codigo == __request_Filtro_SearchText_2 || ti.Inner.Codigo == __request_Filtro_SearchText_2 || ti.Inner.CodigoUniversal == __request_Filtro_SearchText_2 || ti.Inner.CodigoUniversalBulto == __request_Filtro_SearchText_2)'
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.
My code so far is the following:
var arrayrequest = request.Filtro.SearchText.Split().ToList();
var query = from publicacion in _dbContext.Publicaciones.Where(p => p.IdTienda == request.IdTienda)
join articulo in _dbContext.Articulos
on publicacion.IdArticulo equals articulo.Id
where
arrayrequest.Any(s => publicacion.Descripcion.Contains(s))
|| publicacion.Codigo == request.Filtro.SearchText
|| articulo.Codigo == request.Filtro.SearchText
|| articulo.CodigoUniversal == request.Filtro.SearchText
|| articulo.CodigoUniversalBulto == request.Filtro.SearchText
select publicacion;
var publicaciones = await query
.Include(p => p.Articulo)
.Include(p => p.TributoPublicacion)
.ToArrayAsync();
The error occurs in the section
arrayrequest.Any(s => publicacion.Descripcion.Contains(s))`
I use Entity Framework Core 5 - any help is welcome
Don't want to repeat myself, but it is good to show how it can be solved.
EF do not supports complex predicates with local collections and here you need to build expression tree dynamically. This answer has GetItemsPredicate function which helps in building needed condition.
Then you can rewrite your query in this way:
var arrayrequest = request.Filtro.SearchText.Split().ToList();
var query = from publicacion in _dbContext.Publicaciones.Where(p => p.IdTienda == request.IdTienda)
join articulo in _dbContext.Articulos
on publicacion.IdArticulo equals articulo.Id
select publicacion;
var descriptionPredicate = query.GetItemsPredicate(arrayrequest, (publicacion, s) => publicacion.Descripcion.Contains(s));
Expression<Func<Publicacion, bool>> otherPredicate = publicacion => publicacion.Codigo == request.Filtro.SearchText
|| articulo.Codigo == request.Filtro.SearchText
|| articulo.CodigoUniversal == request.Filtro.SearchText
|| articulo.CodigoUniversalBulto == request.Filtro.SearchText;
query = query.Where(descriptionPredicate.CombineOr(otherPredicate)));
var publicaciones = await query
.Include(p => p.Articulo)
.Include(p => p.TributoPublicacion)
.ToArrayAsync();

Problem in Search From One List in Another List in Ef .Net Core

List<string> groupId = request.GroupId.Split(',').ToList();
ENTITIES.ProductGroup
.Where(p => p.IsDisplay)
.Where(p => p.FK_GroupNavigation.IsDisplay)
.Where(p => groupId.Any(g => g == (p.FK_Group ?? 0) + "")
.ToList();
The value in request.GroupId is "12,15" and the same values are in the table, but give the following error.
In Ef Core I want to search for some value in another list but it gives the following error What is the problem?
TargetFramework=5.0
The LINQ expression 'DbSet()
.Where(p => p.IsDisplay)
.LeftJoin(
inner: DbSet(),
outerKeySelector: p => EF.Property<Nullable>(p, "FK_Group"),
innerKeySelector: g => EF.Property<Nullable>(g, "PK_Group"),
resultSelector: (o, i) => new TransparentIdentifier<ProductGroup, Group>(
Outer = o,
Inner = i
))
.Where(p => p.Inner.IsDisplay)
.Count(p => __groupId_0
.Any(g => (g ?? "").Equals((object)(p.Outer.FK_Group ?? 0) + "")))' 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'.
Any with local collection is not translatable to SQL, use Contains instead. Also compare integers by integer values, query will use indexes if they are exists.
List<int> groupId = request.GroupId.Split(',').Slect(s => int.Parse(s)).ToList();
SGP_PRODUCT.ProductGroup
.Where(p => p.IsDisplay)
.Where(p => p.FK_GroupNavigation.IsDisplay)
.Where(p => groupId.Contains(p.FK_Group))
.ToList();

Rewriting an SQL statement as a LINQ query results in runtime error

As a follow-up to this question of mine I have got this SQL:
SELECT Documents.*
FROM Documents
WHERE Documents.ID IN
(
SELECT Keywords.DocumentID
FROM Keywords
WHERE
Keywords.Keyword = 'KeywordA' OR
Keywords.Keyword = 'KeywordB'
GROUP BY Keywords.DocumentID
HAVING COUNT(Keywords.Keyword) = 2
)
I did use Linqer to convert the query to C# to use with Entity Framework Core 5:
from Document in db.Document
where
(from Keyword in db.Keyword
where
Keyword.Value == "KeywordA" ||
Keyword.Value == "KeywordB"
group Keyword by new {
Keyword.DocumentId
} into g
where g.Count(p => p.Value != null) == 2
select new {
g.Key.DocumentId
}).Contains(new { DocumentId = Document.DocumentId })
select Document
This compiles successfully, but upon running the query, I get an error:
The LINQ expression '<see below>' 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.
The formatted LINQ expression from the above error message reads:
DbSet<Keyword>()
.Where(k => k.Value == "KeywordA" || k.Value == "KeywordB")
.GroupBy(
keySelector: k => new { DocumentId = k.DocumentId },
elementSelector: k => k)
.Where(e => e
.Count(p => p.Value != null) == 2)
.Select(e => new { DocumentId = e.Key.DocumentId })
.Any(p => p == new { DocumentId = EntityShaperExpression:
EntityType: Document
ValueBufferExpression:
ProjectionBindingExpression: EmptyProjectionMember
IsNullable: False
.DocumentId })
I really do not understand what's wrong here. I could only imagine that Linqer is too old to generate valid C# code for use in EF Core 5.
My question
Could someone give me a hint on what I'm doing wrong here and how to resolve the issue? (I.e. how to rewrite the C# query)
EF Core query translation still doesn't support many LINQ constructs, and all that without documentation of what exactly is/is not supported really puts us on the trial-and-error path, thus it's not surprising that external tools cannot produce a "proper" translation.
In this particular case the problem is Contains call with complex argument (event though it is a class with single member). Because they support only Contains with primitive argument.
So the minimal change needed to make this work is to replace
select new
{
g.Key.DocumentId
}).Contains(new { DocumentId = Document.DocumentId })
with
select g.Key.DocumentId).Contains(Document.DocumentId)
It looks like the .Contains(new { DocumentId = Document.DocumentId }) part is your problem. It's having trouble translating it into an expression because Document hasn't been evaluated at that point.
If you have the FK setup, you could refactor it this way:
from d in db.Documents
where d.Keywords
.Where(k => (new[] { "keyword A", "keyword B" }).Contains(k.Keyword))
.Count() == 2
select d
Try this:
var result =
from Document in db.Document
where
(from Keyword in db.Keyword
where
Keyword.Value == "KeywordA" ||
Keyword.Value == "KeywordB"
group Keyword by Keyword.DocumentId
into g
where g.Count(p => p.Value != null) == 2
select g.Key).Any(d => d == Document.DocumentId)
select Document;
If having 2 separate queries, 1 for the keywords and 1 for the documents, is not an issue, this should work too:
var matchedDocumentIds = db.Keywords
.Where(keyword => keyword.Keyword == "KeywordA" || keyword.Keyword == "KeywordB")
.GroupBy(keyword => keyword.DocumentID)
.Where(grp => grp.Count() > 2)
.Select(grp => grp.Key);
var filteredDocs = db.Documents.Where(doc => matchedDocumentIds.Any(matchedDocId => matchedDocId == doc.ID));
This assumes there is a foreign key in place between the 2 tables.

LINQ expression error could not be translated

I'm getting this error with trying to execute my query.
System.InvalidOperationException:
.Where(ti => (int)ti.Inner.OptionType == 1 && ti.Inner.QualifierId == null && ti.Inner.CreditingMethod != "xxx" && __ToList_0
.Any(e => e.Vdate== (Nullable)ti.Outer.ADate))' 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 either AsEnumerable(), AsAsyncEnumerable(), ToList(), or ToListAsync(). See https://go.microsoft.com/fwlink/?linkid=2101038 for more information.'
The object that I'm populating is
public class OCList
{
public DateTime? Vdate{ get; set; }
public Double? VWeight{ get; set; }
}
/// example of adding to the list
List<OCList> ocList= new List<OCList>();
if (request.Date1 != null && request.Weight1 != null)
{
ocList.Add(new OCList{ Vdate = request.Date1.Value, VWeight = request.Weight1.Value });
}
it error's here:
&& ocList.ToList().Any(e => e.Vdate == x.Tb2.ADate))
Linq expression:
var results = _context.Tb1
.Join(_context.Tb2, oc => oc.OptionId, o => o.OptionId, (oc, o) => new { OptionCost = oc, Option = o })
.Where(x => x.Tb2.Val1 == 1
&& x.Tb2.Val2 == null
&& ocList.ToList().Any(e => e.Vdate == x.Tb2.ADate))
////.........
Check client and server side evaluation here: https://learn.microsoft.com/en-us/ef/core/querying/client-eval
EF Core supports partial client evaluation in the top-level projection (essentially, the last call to Select()). If the top-level projection in the query can't be translated to the server, EF Core will fetch any required data from the server and evaluate remaining parts of the query on the client. If EF Core detects an expression, in any place other than the top-level projection, which can't be translated to the server, then it throws a runtime exception.
Your ToList and then Any can't be translated to sql and thus you get the error.
This would work
var results = _context.Tb1
.Join(_context.Tb2, oc => oc.OptionId, o => o.OptionId, (oc, o) => new { OptionCost = oc, Option = o })
.AsEnumerable()
.Where(x => x.Tb2.Val1 == 1
&& x.Tb2.Val2 == null
&& ocList.ToList().Any(e => e.Vdate == x.Tb2.ADate))
BUT it would first fetch everything from the server and then apply the where clause, resulting to poor performance.
You could make it a bit better like this
var results = _context.Tb1
.Join(_context.Tb2, oc => oc.OptionId, o => o.OptionId, (oc, o) => new { OptionCost = oc, Option = o })
.Where(x => x.Tb2.Val1 == 1
&& x.Tb2.Val2 == null)
.AsEnumerable()
.Where(ocList.ToList().Any(e => e.Vdate == x.Tb2.ADate))

Categories

Resources