I'm having trouble with a LINQ query.
var cRecords = context.ClassificationRecords
.GroupBy(p => p.ImageRecordId)
.Select(g => g.OrderByDescending(c => c.Created).FirstOrDefault())
.ToList();
What I'm trying to do is get a list of the last dated classification record for each user. I'll include the user and some other stuff, but that's the stripped down version.
The problem is that the above is giving me an error:
System.InvalidOperationException: 'The LINQ expression '(GroupByShaperExpression:
KeySelector: (c.ImageRecordId),
ElementSelector:(EntityShaperExpression:
EntityType: ClassificationRecord
ValueBufferExpression:
(ProjectionBindingExpression: EmptyProjectionMember)
IsNullable: False
)
)
.OrderByDescending(c => c.Created)' 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.'
I've seen this query used in several places on here and other websites, so it should be working..?
I'm using .Net Core 3.1 with EntityFrameworkCore 3.1.11
The query is being ran inside a hangfire worker program, so not a Blazor server - not sure if it makes a difference.
One possible work around is to modify your query into something EF 3.1 can translate - how efficient this will be depends on your database engine. I think this should work but you need the primary key to match the records:
var cRecords = context.ClassificationRecords
.Where(c => c.PrimaryKey == context.ClassificationRecords.Where(c2 => c2.ImageRecordId == c.ImageRecordId)
.OrderByDescending(c2 => c2.Created)
.First().PrimaryKey)
.ToList();
This query is not possible with EFC. After GroupBy you can select only grouping key or aggregation result. Faster query here is Window Functions usage which are not supported by EF.
So there is workaround:
var grouped =
from c in ontext.ClassificationRecords
group c by c.ImageRecordId into g
select new
{
ImageRecordId = g.Key,
Created = g.Max(x => x.Created)
};
var query =
from g in grouped
join c in context.ClassificationRecords
on new { g.Created, g.ImageRecordId } equals { c.Created, c.ImageRecordId }
select c;
var result = query.ToList();
Related
I am trying to query my Database for a single record ins three steps and I am having problems getting the result that I am looking for. These are the steps that I created:
client = client
.Where(s => s.CompanyName.Contains(name));
var res = client.Select(x => x.ID);
Tracker = Tracker
.Where(s => s.ClientId.Equals(client.Select(x => x.ID)));
Debugging the code indicated that steps one and two worked correctly and generated the data that I needed to run my third query, which should provide the whole record, utilizing the result of the second step.
The third and last steps generated the following error:
"The LINQ expression DbSet<TimeTrackerViewModel>().Where(t => t.ClientId.Equals(DbSet<ClientsViewModel>().Where(c => c.CompanyName.Contains(__name_0)).Select(c => c.ID))) 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."
How do I query my database, utilizing the query result from the second step?
If you want to use the IDs in res to filter Tracker then you can use Any():
var res = client.Select(x => x.ID);
trackers = Tracker.Where(s => res.Any(r => r == s.ClientId));
The above query will return a collection.
I am trying to query my Database for a single record
If you want to return a single record then you could to use FirstOrDefault(), either in place of the Where clause (using the same predicate), or after the Where (you could consider Single() if you know there's exactly 1 matching record). But you should also consider what you expect to happen if multiple records match the name parameter in your first query and how you would handle that.
You should try this:
var trakers = (from c in client.Where(s => s.CompanyName.Contains(name))
join t in tracker
on c.ID
equals t.ClientId
select t).ToList();
So you do only a query on db.
After reading several related posts, I was able to combine thier ideas into a single working solution as posted below:
var client = _context.ClientsViewModel
.Where(s => s.CompanyName.Contains(name))
.Select(x => x.ID).ToList();
Tracker = Tracker
.Where(s => s.ClientId == client[0])
.OrderByDescending(x => x.Id);
I want to get items from the sqlite database (There are about 6,000 items in the database)
Some items are duplicates Therefore, I want to remove this duplicate information when I get items
I used the following code
public async static Task<List<myModel>> GetAllItems()
{
using var db = new dbContext();
var query =
from item in db.myTable.GroupBy(x => x.Id).Select(x => x.First())
select new myModel
{
Id = item.Id,
Name = item.Name,
...
};
return await query.ToListAsync();
}
But I get the following error
System.InvalidOperationException: 'The LINQ expression 'GroupByShaperExpression:
KeySelector: m.Id,
ElementSelector:EntityShaperExpression:
EntityType: myTable
ValueBufferExpression:
ProjectionBindingExpression: EmptyProjectionMember
IsNullable: False
.First()' 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.'
Update (EF Core 6.0):
EF Core 6.0 did add support for some additional operators to GroupBy result set, including the one in question, so now the original LINQ query should work intact.
Original:
Since currently (hopefully v6.0 would add some) EF Core does not support GroupBy result operators other than key/aggregates projections, it cannot be used to implement the top N items per group function, which is what you basically are trying to do (for N == 1).
So as a workaround (without 3rd party extensions), you have to do that manually by (1) using subquery for selecting the unique keys, and then (2) using it as filter for correlated limiting subquery. e.g. something like
var query = db.myTable.Select(x => new { x.Id }).Distinct() // (1)
.SelectMany(key => db.myTable.Where(x => x.Id == key.Id).Take(1)) // (2)
// The rest is the same as the original
.Select(item => new myModel
{
Id = item.Id,
Name = item.Name,
...
});
First, change your var to IEnumerable<HWGPackageModel>
Second, add AsEnumerable() before GroupBy
public static IEnumerable<myModel> GetAllItems()
{
var db = new dbContext();
IEnumerable<myModel> query =
from item in db.myTable.AsEnumerable().GroupBy(x => x.Id).Select(x => x.FirstOrDefault())
select new myModel
{
Id = item.Id,
...
};
return query;
}
I have a table, Items, which has a many to one relationship with two distinct parents.
I want to select the counts of ParentA for each ParentB.
In SQL this is simple:
SELECT "ParentBId", count(distinct "ParentAId")
FROM "Items"
GROUP BY "ParentBId"
In Linq I have this statement:
var itemCounts = await _context.Items
.GroupBy(item => item.ParentBId,
(parentBId, items) => new
{
ParentBId = parentBId,
Count = items.Select(item => item.ParentAId).Distinct().Count(),
}).ToDictionaryAsync(group => group.ParentBId, group => group.Count);
When running this query, EF is blowing up with this error:
System.InvalidOperationException: Processing of the LINQ expression 'AsQueryable<string>(Select<Item, string>(
source: NavigationTreeExpression
Value: default(IGrouping<string, Item>)
Expression: (Unhandled parameter: e),
selector: (item) => item.ParentAId))' by 'NavigationExpandingExpressionVisitor' failed. This may indicate either a bug or a limitation in EF Core. See https://go.microsoft.com/fwlink/?linkid=2101433 for more detailed information.
at Microsoft.EntityFrameworkCore.Query.Internal.NavigationExpandingExpressionVisitor.VisitMethodCall(MethodCallExpression methodCallExpression)
at Microsoft.EntityFrameworkCore.Query.Internal.NavigationExpandingExpressionVisitor.VisitMethodCall(MethodCallExpression methodCallExpression)
...
The Items table does use Table per hierarchy with a discriminator column to determine what the item type is. I do not know if this is a factor.
I have seen lots of people recommend the items.Select(i => i.Field).Distinct().Count() option, but this doesn't seem to be working here. Any other suggestions?
Thanks!
Currently any kind of distinction inside groups (like Distinct inside ElementSelector of GroupBy or another GroupBy inside ElementSelector of GroupBy) isn't supported by EF Core. If you insist on using EF in this case, you have to fetch some data in memory:
var result = (await _context.Items
.Select(p => new { p.ParentAId, p.ParentBId })
.Distinct()
.ToListAsync()) // When EF supports mentioned cases above, you can remove this line!
.GroupBy(i => i.ParentBId, i => i.ParentAId)
.ToDictionary(g => g.Key, g => g.Distinct().Count());
In my project I am currently having an issue with the following LINQ query:
context.WebPages.GroupBy(x => x.Url).Select(x => new { x.Key, x.FirstOrDefault()?.LogoId } ).ToList();
Basically, I am trying to get from our DB web pages distinct by their URL and the first logo ID that is assigned to them. However, I am struggling with the warning saying: The LINQ expressio could not be translated and will be evaluated locally. Since I am storing a couple of million web pages, I don't want to load unnecessary data. And I definitely don't want the expression to be evaluated locally.
I have tried several optimizations of the LINQ expressions, e.g.:
context.WebPages.GroupBY(x => x.Url, x => new {x.LogoId}).Select(x => new { x.Key, x.FirstOrDefault()?.LogoId } ).ToList();
context.WebPages.GroupBy(x => x.Url).Select(x => new { x.Key, x.First().LogoId } ).ToList();
context.WebPages.GroupBy(x => x.Url).Select(x => x.First()).ToList();
context.WebPages.GroupBy(x => x.Url).ToList();
but I always ended up with the same warning. The only query that could be translated (but is useless to me), was:
context.WebPages.GroupBy(x => x.Url).Select(x => x.Key).ToList();
Is there any alternative LINQ expression that could work (or even a set of LINQ expressions)? Or do I need to use plain SQL expression?
Side note: We are also planning to move to .NET Core 3.0, but that is a couple of months distant future... and I cannot wait until "then".
How about if we try an exclusion join? Assuming LogoId is a comparable type (e.g. int):
var ans = from a in context.WebPages
where !(from b in context.WebPages where a.Url == b.Url && a.LogoId > b.LogoId select b).Any()
select new {
a.Url,
a.LogoId
};
Update: I tested this with EF 2.2.6 and it generated (possibly inefficient) SQL fine.
Update 2: I also tested with EF 3 and it worked. My earlier Distinct/GroupJoin failed SQL translation in EF 3.
I am using entity framework core 2.1, I have a database context with an accessor for a model containing a boolean field represented as a non nullable bit field in an MS SQL database. I want to construct a query that evaluates in SQL efficiently that provides me a count of all rows in the table, and those with the bit column enabled.
var groups = await this.context.Models
.AsNoTracking()
.GroupBy(i => 1)
.Select(g => new ViewModel
{
Count = g.Count(),
Revoked = g.Count(p => p.IsRevoked)
})
.ToArrayAsync();
In order to force the query to consume all rows, I use ToArray, however the group by, count and where clauses log they cannot be evaluated remotely.
Other attempts such as:
var query = await this.context.Models
.AsNoTracking()
.GroupBy(i => i.IsRevoked)
.ToArrayAsync();
Produces two groups which I can later inspect but they fail to evaluate the bit column the same.
How can I generate a single expression that produces a new object with the count of all rows and the count of the subset which have the bit field enabled?
The first technique (group by constant) worked well in EF6. Just instead of predicate based Count which has not direct SQL equivalent, using the conditional Sum produced a nice GROUP BY SQL.
Unfortunately, this doesn't translate to SQL in EF Core, even in 2.1.
Fortunately, combining it with intermediate projection produces the desired SQL translation in EF 2.1:
var counts = await this.context.Models
.Select(e => new { Revoked = e.IsRevoked ? 1 : 0 })
.GroupBy(e => 1)
.Select(g => new ViewModel
{
Count = g.Count(),
Revoked = g.Sum(e => e.Revoked)
})
.ToArrayAsync();