Find maximum value of all properties in list of objects - entity-framework

I have the following Entity Framework Core query:
IQueryable<Product> products = context.Products;
var maximums = new MaximumModel {
PA = await products.MaxAsync(x => x.Composition.PA)
PB = await products.MaxAsync(x => x.Composition.PB)
PC = await products.MaxAsync(x => x.Composition.PC)
PD = await products.MaxAsync(x => x.Composition.PD)
// Other 36 properties ...
}
This executes 40 queries, one for each query ...
Is there a way to execute only one query?

I haven't tested this with EF async versions, but the LINQ to SQL solution is to use a singleton group from a GroupBy to combine the query into one query:
var maximums = await products.GroupBy(p => 1)
.Select(pg => new MaximumModel {
PA = pg.Max(x => x.Composition.PA),
PB = pg.Max(x => x.Composition.PB),
PC = pg.Max(x => x.Composition.PC),
PD = pg.Max(x => x.Composition.PD),
// Other 36 properties ...
}).FirstAsync();

Related

Entity Framework ThenInclude filtered from original dataset

I have a query where my ThenInclude Where needs to access data in the original data set e.g.
var bikes = context.Bikes
.Include(bike => bikes.Models)
.ThenInclude(model => model.Spec.Where(spec=> spec.SpecYear == bike.YearCreated ))
.ToList();
I currently have this in two separate queries but it would save a lot of time if I completed this request on the database.
Try to use projection instead of Include:
var bikes = context.Bikes
.Select(bike = new Bike
{
Id = bike.Id,
// ... other fields
Models = bike.Models.Select(m => new Model
{
Id = m.Id
// ... other fields
Spec = m.Spec
.Where(spec => spec.SpecYear == bike.YearCreated)
.ToList()
})
})
.ToList();

MongoDB .NET Group by with list result of whole objects

I was digging into the documentation of MongoDB http://mongodb.github.io/mongo-csharp-driver/2.7/reference/driver/crud/linq/ and I saw, using .NET drive is possible to make group by on the database.
For 1 ProductId I can have few elements in a database. I want to get whole Last element.
So I am trying to do something like:
var query = _collection.AsQueryable()
.GroupBy(p => p.ProductId, (k, s) =>
new { Name = k, Result = s.First() })
.Select(x => new { Name = x.Name, Result = x.Result }).First();
The problem is that I see an error message like:
System.NotSupportedException: Specified method is not supported.
at
MongoDB.Driver.Linq.Processors.AccumulatorBinder.GetAccumulatorArgument(Expression node)
I know that for now in my example i didnt order by the result.. But this will be my second step. For now i see that i cannot group by. Is it possible to do this kind of group by?
My solution for that is:
var query = _collection.Aggregate(new AggregateOptions { AllowDiskUse = true })
.Match(x => ElementIds.Contains(x.ElementId))
.SortByDescending(x => x.StartDate).ThenByDescending(x => x.CreatedAt)
.Group(x => x.ElementId, x => new
{
StartDate = x.First().StartDate,
Grades = x.First().Grades,
SellingId = x.First().SellingId,
CreatedAt = x.First().CreatedAt,
ModifiedAt = x.First().ModifiedAt,
Id = x.First().Id,
ElementId = x.First().ElementId
})
.ToEnumerable(token);
After that, I parsed it into my model.
AllowDiskUse = true, because in my case MongoDB's memory is not enough to handle this operation.

In EF Core, how can I perform a group by and select a list of comma separated values?

In this little ASP.Net Core application, I am attempting to group students by their enrollment date and return the students' names in a comma separated string, as opposed to another kind of aggregation.
When using SQL, I've used the stuff function in the past, and I haven't been able to perform the equivalent operation as a subquery in LINQ.
Core won't allow me to perform client-side GroupBys, which I tried at first.
Client side GroupBy is not supported.
I've tried the following code and received an InvalidOperationException:
IQueryable<EnrollmentDateGroup> data =
_context.Students
.GroupBy(s => s.EnrollmentDate)
.Select(s => new EnrollmentDateGroup()
{
EnrollmentDate = s.Key,
StudentCount = s.Count(),
//BELOW IS NOT WORKING
StudentNamesCSV = string.Join(",", s.Select(x => x.FirstMidName + " " + x.LastName))
});
Another attempt and error message, moving away from using the already grouped data:
SqlException: Column 'Student.EnrollmentDate' is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause.
//ALSO NOT WORKING
...
StudentNamesCSV = string.Join(",", _context.Students
.Where(x => x.EnrollmentDate == s.Key)
.Select(x => x.FirstMidName + " " + x.LastName))
...
Any ideas are welcome! Thank you.
Try to use below linq code:
var result = ((from uu in _context.Students.AsEnumerable()
select new​
{​
EnrollmentDate = uu.EnrollmentDate,​
FullName = uu.FirstMidName + " " + uu.LastName​
}).GroupBy(cc => cc.EnrollmentDate).​
Select(s => new EnrollmentDateGroup()​
{​
EnrollmentDate = s.Key,​
StudentCount = s.Count(),​
StudentNamesCSV = string.Join(",", s.Select(ee => ee.FullName).ToList())​
})​
).ToList();
You can try this way
var data = _context.Students
.GroupBy(s => s.EnrollmentDate)
.Select(s => new
{
Key = s.Key,
listOfStudents = s.ToList()
}).ToList();
var result = data.Select(s => new EnrollmentDateGroup
{
EnrollmentDate = s.Key,
StudentCount = s.listOfStudents.Count,
//BELOW IS NOT WORKING
StudentNamesCSV = string.Join(",", s.listOfStudents.Select(x => x.FirstMidName + " " + x.LastName))
});

Evaluate EF query with MAX on the server

Using Entity Framework Core 2.2 I have the following query:
var user = await context.Users.AsNoTracking()
.Include(x => x.Lessons).ThenInclude(x => x.LessonLevel)
.FirstOrDefaultAsync(x => x.Id == userId);
var lessons = context.Lessons.AsNoTracking();
.Where(x => x.LessonLevelId < user.Lessons.Max(y => y.LessonLevelId));
Thus query evaluates locally and I get the message:
The LINQ expression 'Max()' could not be translated and will be evaluated locally.'
How can I make this query evaluate on the server?
Update
Based on DavigG answer I made it work using:
var maxLessonLevelId = user.Lessons.Max(y => y.LessonLevelId););
var lessons = context.Lessons.AsNoTracking();
.Where(x => x.LessonLevelId < maxLessonLevelId);
I know the following evaluates locally but shouldn't evaluate on the server?
var lessons = context.Lessons.AsNoTracking();
.Where(x => x.LessonLevelId <
context.Users.AsNoTracking()
.Where(y => y.Id == userId)
.Select(y => y.Lessons.Max(z => z.LessonLevelId))
.FirstOrDefault());
Is it possible to use a child queries that evaluates on the server?
Get the max value as a separate query, for example:
var maxLessonLevelId = user.Lessons.Max(y => y.LessonLevelId);
Then you can can get the lessons like this:
var lessons = context.Lessons.AsNoTracking()
.Where(x => x.LessonLevelId < maxLessonLevelId);

How to do aggregate functions such as count in Entity Framework Core

I have this SQL that I would like to execute in Entity Framework Core 2.1:
Select ItemTypeId, Count(ItemTypeId) as Count from Items i
where i.ParentId = 2
group by ItemTypeId
How do I do that?
This is what I came up with, but it returns zero:
var query = this._context.Items.Where(a => a.ParentId == 2)
.GroupBy(i => new { i.ItemTypeId })
.Select(g => new { g.Key.ItemTypeId, Count = g.Count(i=> i.ItemTypeId == g.Key.ItemTypeId) });
var items = query.ToList();
The only example I could find was here
You don't need Count = g.Count(i=> i.ItemTypeId == g.Key.ItemTypeId), instead use g.Count().