OrderBy deletes object property - c#

I have the following code:
var test = _fitDbContext.MvLatestTestResult
.Include(r => r.LastTest)
.Include(r => r.LastTest.Testrun)
.Include(r => r.LastTest.Testrunconfig)
.Where(r => r.LastTest.Testrunconfig.Started.Value.Hour >= 0 && r.LastTest.Testrunconfig.Started.Value.Hour < 6)
.Where(r => r.LastTest.Testrun.Userc == "build")
.Where(r => r.ProductId == productId)
.GroupBy(r => r.LastTest.Testrunconfig.Started.Value.Date)
.OrderByDescending(r => r.Key.Date)
.Take(3)
.ToDictionary(group => group.Key, group => group.GroupBy(r => r.Configfilename));
Variable test looks like this:
You can see that LastTest is null. If I remove .OrderByDescending(r => r.Key.Date), it contains value.
Why does OrderByDescending remove LastTest value?
EDIT
Interesting thing I just found out. If I change order of GroupBy and OrderBy, everything works as expected. The problem is that OrderBy is executed with all records and takes a long time.
var test = _fitDbContext.MvLatestTestResult
.Include(r => r.LastTest)
.Include(r => r.LastTest.Testrun)
.Include(r => r.LastTest.Testrunconfig)
.Where(r => r.LastTest.Testrunconfig.Started.Value.Hour >= 0 && r.LastTest.Testrunconfig.Started.Value.Hour < 6)
.Where(r => r.LastTest.Testrun.Userc == "build")
.Where(r => r.ProductId == productId)
.OrderByDescending(r => r.Key.Date)
.GroupBy(r => r.LastTest.Testrunconfig.Started.Value.Date)
.Take(3)
.ToDictionary(group => group.Key, group => group.GroupBy(r => r.Configfilename));

Related

GroupBy in subquery with MAX() in C#

I Have this code. It works fine but when I have two same maximal values it appear 2 times. So I need to use OrderBy. But I dont know how. Thanks for any help.
IQueryable<PerformanceRealization> pr = _context.PerformanceRealization
.Where(u => u.Deadline == _context.PerformanceRealization
.Where(x => x.GroupRealizationId == u.GroupRealizationId)
.Max(x => x.Deadline)
)
.Select(u => u);
Here is the SQL code with GROUP BY
SELECT PR.GroupRealizationId
FROM Stores.PerformanceRealization PR
LEFT JOIN Stores.GroupRealization ON Stores.GroupRealization.Id = PR.GroupRealizationId
WHERE PR.Deadline = (SELECT MAX(Deadline)
FROM Stores.PerformanceRealization PR2
WHERE PR.GroupRealizationId = PR2.GroupRealizationId)
GROUP BY PR.GroupRealizationId
You can select the first object from the group
IQueryable<PerformanceRealization> pr2 = pr
.GroupBy(x => x.GroupRealizationId)
.Select(g => g.First());
If you need a specific object from the group, then you can order by another column
IQueryable<PerformanceRealization> pr2 = pr
.GroupBy(x => x.GroupRealizationId)
.Select(g => g.OrderBy(x => x.SomeColumn).First());
for SomeColumn having the smallest value. For the greatest value, use OderByDescending instead.
Of course, you can integrate this approach into the first query:
IQueryable<PerformanceRealization> pr = _context.PerformanceRealization
.Where(u => u.Deadline == _context.PerformanceRealization
.Where(x => x.GroupRealizationId == u.GroupRealizationId)
.Max(x => x.Deadline)
)
.GroupBy(x => x.GroupRealizationId)
.Select(g => g.OrderBy(x => x.SomeColumn).First());
Note, you don't need to have a Select at the end like .Select(u => u). Since it has no effect, you can just drop it.
If your EF Core version cannot handle it (as revealed in a comment), then transition to LINQ-to-Objects with AsEnumerable(), but do the filtering in EF Core to minimize the number of records sent to the front-end:
IQueryable<PerformanceRealization> pr = _context.PerformanceRealization
.Where(u => u.Deadline == _context.PerformanceRealization
.Where(x => x.GroupRealizationId == u.GroupRealizationId)
.Max(x => x.Deadline)
)
.AsEnumerable() // <===== transition from LINQ-to-EF-Core to LINQ-to-Objects
.GroupBy(x => x.GroupRealizationId)
.Select(g => g.OrderBy(x => x.SomeColumn).First());

LINQ expression to filter

I have the following LINQ expression where I am fetching Courses, Students that belong to that Course, then the School's where the Student's goes to. The following LINQ expression works fine.
However, I need to further, filter it where I need to get Students with the City == 'Colarado'. How can I alter the following LINQ to my use case.
_dbContext.Courses
.Where(c => c.Id == Id)
.Include(c => c.Students)
.ThenInclude(c => c.Schools)
.OrderByDescending(c => c.Id)
.ToListAsync();
If you need all courses and only filter students - since EF Core 5.0 you can use filtered include:
_dbContext.Courses
.Where(c => c.Id == Id)
.Include(c => c.Students.Where(s => s.City == "Colarado"))
.ThenInclude(c => c.Schools)
.OrderByDescending(c => c.Id)
.ToListAsync();
You can do the filter in the Where method.
_dbContext.Courses
.Where(c => c.Id == Id && c.Students.All(s => s.City == "Colarado"))
.Include(c => c.Students)
.ThenInclude(c => c.Schools)
.OrderByDescending(c => c.Id)
.ToListAsync();

Starting select after specific value

here is my problem. I'm trying to select a list of records after a specific one, so starting from this value I want to select the next one (and not the starting value).
My "filter" is a primary key, something like "NP-A6666".
Here is my code actually:
var listVU = _context.AVu
.OrderByDescending(r => r.UpdateDate)
.Select(x => new AVut
{
VuId = x.VuId
})
.Take(50)
.AsNoTracking()
.ToListAsync();
return await listVU;
I would do something like:
var listVU = _context.AVu
.OrderByDescending(r => r.UpdateDate)
".StartingFromButExluding(p => p.VuId == "NP-A6666")"
.Select(x => new AVut
{
VuId = x.VuId
})
.Take(50)
.AsNoTracking()
.ToListAsync();
return await listVU;
So I will get listVU as a list of 50 values right after NP-A6666 (from NP-A6667 to NP-A6717 ordered for UpdateDate).
Use SkipWhile
var listVU = _context.AVu
.OrderByDescending(r => r.UpdateDate)
.SkipWhile(p => !p.VuId.Equals("NP-A6666"))
.Select(x => new AVut
{
VuId = x.VuId
})
.Take(50)
.AsNoTracking()
.ToListAsync();
Skipwhile runs sequentially through the enumerable, skipping elements where the predicate returns true - once the predicate returns false, in this case when the VuID = "NP-A6666", the remaining elements are returned as a new enumerable.
If you then want to get everything up to VuID.Equals("NP-A6717"), you can do the same thing with TakeWhile instead of Take(50):
var listVU = _context.AVu
.OrderByDescending(r => r.UpdateDate)
.SkipWhile(p => !p.VuId.Equals("NP-A6666"))
.Select(x => new AVut
{
VuId = x.VuId
})
.TakeWhile(p => !p.Equals("NP-A6717"))
.AsNoTracking()
.ToListAsync();
And If you're wanting to exclude the row that the SkipWhile is stopping on, just do .Skip(1) right after the SkipWhile(p => !p.VuId.Equals("NP-A6666"))
var listVU = _context.AVu
.OrderByDescending(r => r.UpdateDate)
.SkipWhile(p => !p.VuId.Equals("NP-A6666"))
.Skip(1)
.Select(x => new AVut
{
VuId = x.VuId
})
.Take(50)
.AsNoTracking()
.ToListAsync();

LinQ to get the latest group of records satisfying a condition

I have below stated 2 tables:
now I want to get the set of Child Table objects for whichever their parent table entries are latest(wr.r.t lastmodified). It should be something like....
List<Child_Table> List = ChildsList.Where(x=>x.name =="pqr" && status == "done")
.Select(x=>x.Parent.lastmodified == recent record).....ToList();
You can use GroupBy on the date, then OrderByDescending on the Key then take the First followed by SelectMany to flatten the results.
var result = ChildsList.Where(x => x.name == "pqr" && x.status == "done")
.GroupBy(x => x.Parent.lastmodified)
.OrderByDescending(g => g.Key)
.First()
.SelectMany(g => g)
.ToList();
You could use a join to accomplish it:
var results = children
.Join(parents.OrderByDescending(p => p.lastmodified).Take(1),
c => c.parent_id,
p => p.id,
(c, p) => c)
.Where(x => x.name == "pqr" && x.status == "done")
.ToList();

return only objects with count more than X

I'm not sure how to do this with the entity framework. I got the following:
return this.enrollments
.Where(e => e.em_enrolled == false && e.em_result < _settings.PassMark)
.GroupBy(e => e.em_subject_id)
.Select(e => e.em_subject_id)
.ToList();
how do i only retrieve records that are present x times.
Do you mean groups with x or more items?
return this.enrollments
.Where(e => e.em_enrolled == false && e.em_result < _settings.PassMark)
.GroupBy(e => e.em_subject_id)
.Where(g => g.Count() >= x)
.Select(g => g.Key)
.ToList();
I suspect you want:
return this.enrollments
.Where(e => !e.em_enrolled && e.em_result < _settings.PassMark)
.GroupBy(e => e.em_subject_id)
.Where(g => g.Count() >= x)
.Select(g => g.Key)
.ToList();
Note that I've changed the Select part to reflect the fact that you want to extract the group key from the group. (I've also avoided comparison with false, changing e.em_enrolled == false into !e.em_enrolled. They mean the same thing of course, but I find the latter more idiomatic in C#.)

Categories

Resources