How to replace .Take() method in linq query? - c#

I have a linq query:
from cc in _db.CC
from pn in _db.AlertDbEntities
.Where(x => x.CompanyId == cc.Id)
.DefaultIfEmpty()
.Take(1)
join ns in _db.NS
on cc.Id equals ns.CompanyId
...
'Take()' part of which is translated into the following SQL code:
ROW_NUMBER() OVER (PARTITION BY ...)
The problem is that this function appeared only in Mysql 8.0, and the server for which I am writing has a version of Mysql 5.7. A lot of workarounds of this problem either cannot be transferred to SQL
The LINQ expression 'ProjectionBindingExpression: 1' 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'
Maybe you had a similar problem? How can it be solved?
I tried also this code:
from cc in _db.CC
from pn in _db.AlertDbEntities
.Where(x => x.CompanyId == cc.Id)
.OrderByDescending(n => n.CreatedAt)
.AsEnumerable()
.DefaultIfEmpty()
.Take(1)
.AsQueryable()
join ns in _db.NS
on cc.Id equals ns.CompanyId
...
//------------------------------------------
from cc in _db.СС
from pn in _db.AlertDbEntities
.Where(x => x.CompanyId == cc.Id)
.AsEnumerable()
.DefaultIfEmpty()
.OrderByDescending(n => n.CreatedAt)
.GroupBy(n => n.CompanyId)
.Select(g => g.FirstOrDefault())
join ns in _db.NS
on cc.Id equals ns.CompanyId
...

Related

EF Complex query throw me System.InvalidOperationException

I want to do DB query with EF. The LINQ expression 'DbSet() and the query look slike
.Where(p => p.SellerCustomerId == __sellerId_0)
.Where(p => p.Quantity > 0)
.Join(
inner: DbSet<Product>(),
outerKeySelector: p => EF.Property<long?>(p, "ProductId"),
innerKeySelector: p0 => EF.Property<long?>(p0, "Id"),
resultSelector: (o, i) => new TransparentIdentifier<ProductInstance, Product>(
Outer = o,
Inner = i
))
.Where(p => (bool)DbSet<ProductCategory>()
.Where(p1 => EF.Property<long?>(p.Inner, "Id") != null && object.Equals(
objA: (object)EF.Property<long?>(p.Inner, "Id"),
objB: (object)EF.Property<long?>(p1, "ProductId")))
.Join(
inner: DbSet<Category>(),
outerKeySelector: p1 => EF.Property<long?>(p1, "CategoryId"),
innerKeySelector: c => EF.Property<long?>(c, "Id"),
resultSelector: (o, i) => new TransparentIdentifier<ProductCategory, Category>(
Outer = o,
Inner = i
))
.Where(p1 => p1.Inner.Name == "ASD")
.Select(p1 => p1.Outer))'
but I am getting back "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."
What I am doing wrong? I think problem is with ".Where(p1 => p1.Inner.Name == "ASD")"
Thank you for any ideas

How to write this linq query so it isn't so slow

I have this SQL statement which is pretty instantaneous when running it:
select Distinct statuses.Description, count(*) as count
from referrals
inner join statuses on referrals.StatusId = statuses.id
group by statuses.Description
But when I run the below linq code with Entity Framework Core, it takes almost 5 minutes to run and there are only 680 rows in the database.
var data = context.Referrals
.Include(s => s.Status).AsEnumerable()
.GroupBy(r => r.Status)
.Select(g => new StatusCountItem
{
Status = g.Key.Description,
Count = g.Select(r => r).Count()
}).ToList();
Is there a way to write a similar Linq statement that won't take forever to run or do I need to figure out a different way to do what I want?
EDIT: when I don't have the AsEnumerable I get this error message which is why I added it:
The LINQ expression 'DbSet().Join(inner: DbSet(),
outerKeySelector: r => EF.Property<int?>(r, "StatusId"),
innerKeySelector: s => EF.Property<int?>(s, "Id"),
resultSelector: (o, i) => new TransparentIdentifier<Referral, Status>(Outer = o, Inner = i))
.GroupBy(r => r.Inner)' 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
Try this:
var data = context.Referrals
.GroupBy(r => r.StatusId) // notice the change here, you need to group by the id
.Select(g => new StatusCountItem()
{
Status = g.First().Status.Description,
Count = g.Count()
}).ToList();
Your Sql query is built based on context.Referrals.Include(s => s.Status).AsEnumerable(), which is equivalent to:
select *
from referrals
inner join statuses on referrals.StatusId = statuses.id
Note the star, you're querying every column. In other words, remove the random AsEnumerable() in the middle of your query.
Use this one, it is simple and will improve query performance.
from r in context.Referrals
join s in context.statuses on r.StatusId equals s.Id
select new { s.Description, r.StatusId , S.Id) into result
group result by new { s.Description } into g
select new {
CompanyName = g.Key.Description,
Count = g.Count()
}

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.

Suggestions on why this linq ef core query wont execute

Good morning,
I have the following query in EF Core which works as required if I do not add into the the where clause a condition for the createdtimestamp. If I add other conditions clauses to filter down the data as shown below the query will execute without issue.
var q = (
from trckhead in DbContext.TrackingBatchHeader
from userdets in DbContext.UserDetails
.Where(u => u.UserId == trckhead.ClosedByUserId).DefaultIfEmpty()
from trckkeys in DbContext.TrackingBatchesItemKey
.Where(t => t.TrackingNo == trckhead.TrackingNo).DefaultIfEmpty()
from trcklink1 in DbContext.TrackingBatchesLink
.Where(x => x.TrackingNo == trckhead.TrackingNo && x.TrackingType == "I").DefaultIfEmpty()
from trcklink2 in DbContext.TrackingBatchesLink
.Where(x => x.TrackingNo == trckhead.TrackingNo && x.TrackingType == "T").DefaultIfEmpty()
join trckref in DbContext.TrackingBatchesReference on trckhead.TrackingBatchType equals trckref
.TrackingBatchType
join mthsite in DbContext.SiteDetail on trckhead.SiteCode equals mthsite.SiteCode
join userdets2 in DbContext.UserDetails on trckhead.CreatedByUserId equals userdets2.UserId
where
(string.IsNullOrEmpty(searchRequest.Status) || trckhead.Status == searchRequest.Status) &&
trckhead.CreatedTimestamp >= DateTime.Now.AddMonths(-11)
select new TrackingBatchSearchResultDto
{
TrackingBatchId = trckhead.TrackingNo,
SiteCode = trckhead.SiteCode,
TrackingBatchType = trckhead.TrackingBatchType,
Status = trckhead.Status,
Created = trckhead.CreatedTimestamp,
CreatedById = trckhead.CreatedByUserId,
ClosedById = trckhead.ClosedByUserId,
UserDescription = trckhead.UsersDescription,
TrackingBatchDescription = trckref.TrackingBatchTypeDescription,
SiteName = mthsite.Title,
CreatedByName = userdets2.FullUserName ?? string.Empty,
ClosedByName = userdets.FullUserName ?? string.Empty,
Link1 = trcklink1.TrackingData ?? string.Empty,
Link2 = trcklink2.TrackingData ?? string.Empty
}).Distinct();
return await q.ToListAsync();
If I keep the date clause in the query the exception that is raised shows the following. I would have thought the server evaluation wouldnt be happy if I was passing a function into the query but checking against a simple date has thrown me. I could filter the results by date after obtaining the whole data set but the number of records returned can be massive so I would rather filter it down from a database query. Any help would be appreciated.
The LINQ expression 'DbSet<TrackingBatchHeader>
.SelectMany(
source: t => DbSet<UserDetail>
.Where(u => u.UserId == t.ClosedByUserId)
.DefaultIfEmpty(),
collectionSelector: (t, c) => new TransparentIdentifier<TrackingBatchHeader, UserDetail>(
Outer = t,
Inner = c
))
.SelectMany(
source: ti => DbSet<TrackingBatchesItemKey>
.Where(t0 => t0.TrackingNo == ti.Outer.TrackingNo)
.DefaultIfEmpty(),
collectionSelector: (ti, c) => new TransparentIdentifier<TransparentIdentifier<TrackingBatchHeader, UserDetail>, TrackingBatchesItemKey>(
Outer = ti,
Inner = c
))
.SelectMany(
source: ti0 => DbSet<TrackingBatchesLink>
.Where(t1 => t1.TrackingNo == ti0.Outer.Outer.TrackingNo && t1.TrackingType == "I")
.DefaultIfEmpty(),
collectionSelector: (ti0, c) => new TransparentIdentifier<TransparentIdentifier<TransparentIdentifier<TrackingBatchHeader, UserDetail>, TrackingBatchesItemKey>, TrackingBatchesLink>(
Outer = ti0,
Inner = c
))
.SelectMany(
source: ti1 => DbSet<TrackingBatchesLink>
.Where(t2 => t2.TrackingNo == ti1.Outer.Outer.Outer.TrackingNo && t2.TrackingType == "T")
.DefaultIfEmpty(),
collectionSelector: (ti1, c) => new TransparentIdentifier<TransparentIdentifier<TransparentIdentifier<TransparentIdentifier<TrackingBatchHeader, UserDetail>, TrackingBatchesItemKey>, TrackingBatchesLink>, TrackingBatchesLink>(
Outer = ti1,
Inner = c
))
.Join(
outer: DbSet<TrackingBatchesReference>,
inner: ti2 => ti2.Outer.Outer.Outer.Outer.TrackingBatchType,
outerKeySelector: t3 => t3.TrackingBatchType,
innerKeySelector: (ti2, t3) => new TransparentIdentifier<TransparentIdentifier<TransparentIdentifier<TransparentIdentifier<TransparentIdentifier<TrackingBatchHeader, UserDetail>, TrackingBatchesItemKey>, TrackingBatchesLink>, TrackingBatchesLink>, TrackingBatchesReference>(
Outer = ti2,
Inner = t3
))
.Join(
outer: DbSet<SiteDetail>,
inner: ti3 => ti3.Outer.Outer.Outer.Outer.Outer.SiteCode,
outerKeySelector: s => s.SiteCode,
innerKeySelector: (ti3, s) => new TransparentIdentifier<TransparentIdentifier<TransparentIdentifier<TransparentIdentifier<TransparentIdentifier<TransparentIdentifier<TrackingBatchHeader, UserDetail>, TrackingBatchesItemKey>, TrackingBatchesLink>, TrackingBatchesLink>, TrackingBatchesReference>, SiteDetail>(
Outer = ti3,
Inner = s
))
.Join(
outer: DbSet<UserDetail>,
inner: ti4 => ti4.Outer.Outer.Outer.Outer.Outer.Outer.CreatedByUserId,
outerKeySelector: u0 => u0.UserId,
innerKeySelector: (ti4, u0) => new TransparentIdentifier<TransparentIdentifier<TransparentIdentifier<TransparentIdentifier<TransparentIdentifier<TransparentIdentifier<TransparentIdentifier<TrackingBatchHeader, UserDetail>, TrackingBatchesItemKey>, TrackingBatchesLink>, TrackingBatchesLink>, TrackingBatchesReference>, SiteDetail>, UserDetail>(
Outer = ti4,
Inner = u0
))
.Where(ti5 => ti5.Outer.Outer.Outer.Outer.Outer.Outer.Outer.CreatedTimestamp >= (Nullable<DateTime>)DateTime.Now.AddMonths(-11))' 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.
Plainly speaking, neglecting either establish navigation properties for the relationships between entities and instead using manual outer joins using "from" plus then adding the ultimate insult to poorly formed queries with Distinct is trying to use EF like a hammer to pound a square peg through a round hole.
With proper relationships, that query should be trivial.
Your datetime condition error message does not match your query.
** Edit: Removed details on Date comparison - This does not appear to be directly related as I did verify that the Nullable Date/DateTime comparisons do work with .AddMonth() in EF Core.
Expanding out conditional logic can help simplify the queries being executed.
For example:
var query = (/*build your base query*/);
if (!string.IsNullOrEmpty(searchRequest.Status))
query = query.Where(x => x.Status == searchRequest.Status);
if (searchRequest.DateFrom.HasValue)
{
var fromDate = searchRequest.DateFrom.Value.AddMonths(-11);
query = query.Where(x => x.CreatedTimestamp >= fromDate);
}
var results = query.Select(...).Distinct().ToList();
EF can perform some magic to build queries provided you give it enough information to do it properly. Seeing it build something like ti5.Outer.Outer.Outer.Outer.Outer.Outer.Outer.CreatedTimestamp should be sending off alarm bells. There may be something deep in your query lurking that is tripping a Client-side evaluation, or it is simply a case that a combination of joins and such has tipped over a complexity limit or exposed a bug in EF Core. The starting point would be to look to simplify that query expression by leveraging navigation properties rather than joins, and externalizing the conditional logic where you can.
If you have no recourse but to try and query across relationships that you are not prepared to map out properly with navigation properties, I would recommend exploring building your query in SQL as a Stored Procedure and then defining an Entity that can map to the resulting record from that Sproc.

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

Categories

Resources