Unexpected result in a LINQ to Entities query [duplicate] - c#

This question already has an answer here:
Entity Framework v6 GroupBy Losing Original Ordering
(1 answer)
Closed 6 years ago.
I have following Entities in my DbContext:
I wrote following LINQ to Entities query in LINQPad to find all areas that an order passes(An order passes some areas to produced, an area may repeat):
OrderItems.Include("NominalRoutings.OperationCatalog.WorkCenter.AreaSpecification")
.Where(x=>x.Id == 799730)
.Select(r=>r.NominalRoutings.Where(t => t.OperationCatalog.IsActive)
.Select(t=>new
{
AreaTitle=t.OperationCatalog.WorkCenter.AreaSpecification.Title,
t.ItemSequence
})
.OrderBy(t => t.ItemSequence)
.Select(g => new{g.ItemSequence,g.AreaTitle})
).FirstOrDefault()
And this is its result:
Now, I want to find areas that my Order passed so I changed above query to:
OrderItems.Include("NominalRoutings.OperationCatalog.WorkCenter.AreaSpecification")
.Where(x=>x.Id == 799730)
.Select(r=>r.NominalRoutings.Where(t => t.OperationCatalog.IsActive)
.Select(t=>new
{
AreaTitle=t.OperationCatalog.WorkCenter.AreaSpecification.Title,
t.ItemSequence
})
.OrderBy(t => t.ItemSequence)
.GroupBy(routing => routing.AreaTitle)
.Select(t=>t.Key)
).FirstOrDefault()
The result is:
But I expect following result:
1 Melt
2 LAB
3 PU09
4 LSM
Because I orderd the areas by ItemSequence and then grouped it by Title. Am I mistaken? or there is any problem in my code?

OrderBy returns an IOrderedEnumerable and obviously keeps the order but when using GroupBy it isn't required to keep any order (if it would it would have returned IOrderedEnumerable too to make it clear).
What I suggest you do is first GroupBy and then Order
.Select(t => new { AreaTitle = t.OperationCatalog.WorkCenter.AreaSpecification.Title, t.ItemSequence })
.GroupBy(routing => routing.AreaTitle)
.OrderBy(t => t.Min(x => x.ItemSequence))
.Select(t=>t.Key)

Related

Using GroupBy in LINQ to get last inserted rows [duplicate]

This question already has an answer here:
Problem with EF OrderBy after migration to .net core 3.1
(1 answer)
Closed 2 years ago.
I have a simple table that I would like to get the last inserted rows from using GroupBy.
I have the following code that works without grouping but would like to make a new function with grouping
public Session FindLastFinished(IdentityUser user)
{
return _dbContext.Sessions.OrderByDescending(o => o.CreatedAt).FirstOrDefault(s => s.User.Equals(user) && s.Progress.Equals(Status.Finished.ToString()));
}
I need to add a grouping for the column ScenarioId so that it will return the last inserted row for each ScenarioId but am having trouble adding the GroupBy syntax.
public List<Session> FindLastFinishedByScenarios(IdentityUser user)
_dbContext.Sessions.GroupBy(s => s.ScenarioId)
.ToDictionary(e => e.Key, e => e.OrderByDescending(o => o.CreatedAt).First())
In that case you will have a dictionary that have the scenario id as a key and the last inserted item as value.
This should do:
_dbContext.Sessions.ToList()
.GroupBy(x => x.ScenarioId)
.Select(groupOrderedByCreatedAt =>
{
var orderedGroup = groupOrderedByCreatedAt.OrderByDescending(x => x.CreatedAt);
//Work with the orderedGroup
});
Works like this:
You group them by scenarioId
Select on it takes each group
You order each group by createdAt
Then you work with the ordered group
I have used this to solve it for now since efficiency isn't the biggest issue.
_dbContext.Sessions.ToList().GroupBy(s => s.ScenarioId).ToList().
Select(e => e.OrderByDescending(o => o.CreatedAt).First())
.Where(w => w.User.Equals(user) && w.Progress.Equals(Status.Finished.ToString())).ToList();
If anyone has a better idea I am all ears!

How to combine a query of the LINQ and a list? [duplicate]

This question already has answers here:
Linq version of SQL "IN" statement
(6 answers)
Closed 4 years ago.
[Sorry if it is a duplicate]
I couldn't find properly solution, so I decided to ask a question.
I have an object companies which returns list of elements.
I would like to write a query which will select these all CompanyId which we have in our list. I don't want to select only one record by using FirstOrDefault().
Example:
var companiesSummary = _context.Company
.Where(c => c.CompanyId == companies.Select(cs => cs.ID))
.Include(country => country.Country)
How can I cope with it? Do you have any ideas?
Select the ids of the companies from your in-memory list and then pass that into the query in the where method:
var ids = companies.Select(cs => cs.ID).ToList();
var companiesSummary =
_context.Company
.Where(c => ids.contains(c.ID))
.Include(country => country.Country)
Assuming your companies contains a list of objects with an ID property you want to compare to Company.CompanyId, your query should look like
int[] ids = companies.Select(cs => cs.ID).ToArray();
var companiesSummary = _context.Company
.Where(c => ids.Contains(c.CompanyId))
.Include(company => company.Country);
var matchingCompanies = companies.Where(c => companyIds.Contains(c.Id))
Make companyIds a HashSet<T> for an efficient Contains.

C# LINQ Filter deep nested list

I've a structure based of list containing other list. I need to filter the list based on the value of a property based in the deepest part of the list.
Right now I'm doing this:
queryable = queryable
.Include(x => x.Carriers)
.ThenInclude(c => c.CarrierActions)
.ThenInclude(ca => ca.Action)
.ThenInclude(ac => ac.ActionFacts);
queryable = queryable
.Where(x => x.Carriers.Any(
carriers => carriers.CarrierActions.Any(
carrieractions =>
carrieractions.Action.ActionTypeId ==
ActionTypeEnum.DosemeterCalculateDeepDose)));
I join the needed tables, then I filter them based on the ActionTypeId based 3 levels below the top list.
First off all, is it possible to do this in 1 step ( include the filtering with the joins ), second of all, the second part is not working as my list gets empty, but I'm certain that actions with that type get values.
Using .NET Core 2.0.3 btw!
To answer your first part, you can do this
queryable = queryable
.Include(x => x.Carriers)
.ThenInclude(c => c.CarrierActions)
.ThenInclude(ca => ca.Action)
.ThenInclude(ac => ac.ActionFacts)
.Where(x => x.Carriers.Any(
carriers => carriers.CarrierActions.Any(
carrieractions =>
carrieractions.Action.ActionTypeId ==
ActionTypeEnum.DosemeterCalculateDeepDose)))
To your second part, it should be working, id check your data, as this is pretty straight forward

Entity Framework Take() returns more elements [duplicate]

This question already has answers here:
Entity Framework Include OrderBy random generates duplicate data
(6 answers)
Closed 6 years ago.
Can someone please explain why following query returns list of 8 vessels?
var vessels = await db.Vessels
.Include(m => m.Images.Select(c => c.Activity))
.Include(m => m.VesselAddresses.Select(c => c.Address))
.Where(m => m.Images.Any(c => c.Activity.Active))
.Where(m => m.Activity.Active)
.Where(m => m.Listed)
.Where(m => m.Activity.User.Active)
.OrderBy(m => Guid.NewGuid())
.Take(4)
.ToListAsync();
If i remove Include(m => m.VesselAddresses.Select(c => c.Address)) or OrderBy from the query, then it works just fine and returns 4 records, but if i leave it as it is, then it returns 8 records, even i specified Take(4)
EDIT
This is almost the same query for apartments table, but this query works just fine and always returns 4 rows:
var apartments = await db.Apartments
.Include(m => m.Images.Select(c => c.Activity))
.Include(m => m.Address)
.Where(m => m.Images.Any(c => c.Activity.Active))
.Where(m => m.Activity.Active)
.Where(m => m.Listed)
.Where(m => m.Activity.User.Active).OrderBy(m => Guid.NewGuid())
.Take(4)
.ToListAsync();
Entity Framework doesn't run the query you are making until you call the ToListAsync, hence my guess would be that you Include can't be translated into SQL so its being ignored by the query builder until after it executes the SQL which because take converts into sql as TOP, means that the include is being applied after Take
moving the .Take(4) after the .ToListAsync() should correct
also i assumed you are using .OrderBy(m => Guid.NewGuid()) to randomise the results i would suggest instead Random.NextDouble() guid is overkill for randomisation

LINQ - Preserve order across objects with .Any()? [duplicate]

This question already has answers here:
List sort based on another list
(3 answers)
Closed 9 years ago.
I am building a search function which needs to return a list ordered by relevance.
IList<ProjectDTO> projects = new List<ProjectDTO>();
projects = GetSomeProjects();
List<ProjectDTO> rawSearchResults = new List<ProjectDTO>();
//<snip> - do the various search functions here and write to the rawSearchResults
//now take the raw list of projects and group them into project number and
//number of search returns.
//we will sort by number of search returns and then last updated date
var orderedProjects = rawSearchResults.GroupBy(x => x.ProjectNbr)
.Select(x => new
{
Count = x.Count(),
ProjectNbr = x.Key,
LastUpdated = x.First().UpdatedDateTime
})
.OrderByDescending(x => x.Count)
.ThenByDescending(x => x.LastUpdated);
So far so good; the "orderedProjects" variable returns my list in the correct order. However, I need the entire object for the next step. When I try to query back to get the original object type, my results lose their order. In retrospect, this makes sense, but I need to find a way around it.
projects = (from p in projects
where orderedProjects.Any(o => o.ProjectNbr == p.ProjectNbr)
select p).ToList();
Is there a LINQ-friendly method for preserving the order in the above projects query?
I can loop through the orderedProject list and get each item, but that's not very efficient. I can also rebuild the entire object in the original orderedProjects query, but I'd like to avoid that if possible.
You need to do it the other way around:
Query orderedProjects and select the corresponding items from projects:
var projects =
orderedProjects
.Select(o => projects.SingleOrDefault(p => p.ProjectNbr == o.ProjectNbr))
.Where(x => x != null) // This is only necessary if there can be
// ProjectNbrs in orderedProjects that are not in
// projects
.ToList();
You shouldn't use "Select" in the middle there as that operator transforms the object into another type and you say that you need the original object.
var orderedProjects = rawSearchResults.GroupBy(x => x.ProjectNbr)
.OrderByDescending(x => x.Count)
.ThenByDescending(x => x.First().UpdatedDateTime);
Do they come in chronological order or something? Otherwise, I'm pretty sure you want the "ThenByDescending" to be performed on the newest or oldest project update like so:
var orderedProjects = rawSearchResults.GroupBy(x => x.ProjectNbr)
.OrderByDescending(x => x.Count)
.ThenByDescending(x => x.Max(p=>p.UpdatedDateTime));

Categories