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

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!

Related

C# Linq return the first element of each group [duplicate]

This question already has answers here:
How to get first record in each group using Linq
(7 answers)
Closed 1 year ago.
What I try to achieve is the following: Filter the ProjektStatus with the where function, the group them by ProjektId. Order the group descending by StatusMonatJahr and then take the first element of each group and return it.
public IEnumerable<ProjektStatus> GetCurrentProjektStatus(Func<ProjektStatus, bool> where)
{
return this.db.ProjektStatus
.Where(where)
.GroupBy(x => x.ProjektId)
.Select(x =>
x.OrderByDescending(y => y.StatusMonatJahr))
.First();
}
Unfortunately, with this query I get the first whole group instead of the first element of each group.
What do I have to change to achieve this?
Thanks in advance
The code should return the first element in each group, not the content of the first group.
public IEnumerable<ProjektStatus> GetCurrentProjektStatus(Func<ProjektStatus, bool> where)
{
return this.db.ProjektStatus
.Where(where)
.GroupBy(x => x.ProjektId)
.Select(x => x.OrderByDescending(y => y.StatusMonatJahr).First());
}
Some remarks :
Language keyword should be avoided as variable name. Here it's about the where parameter. whereFunc is a good name.
The GroupBy, Select, OrderByDescending operations can be done remotely (server side), for that they should be called first. An other option is to do everything remotely, for that, the type of the whereFunc should be Expression<Func<ProjectStatus, bool>>.
Personal opinion: you should prefer to code in English, if your company doesn't do it, I feel bad for you.
Here is the result :
public IEnumerable<ProjectStatus> GetCurrentProjectStatuses(Func<ProjectStatus, bool> whereFunc)
{
return ProjectStatuses
.GroupBy(s => s.ProjectId)
.Select(g => g.OrderByDescending(s => s.MonthAndYear).First())
.AsEnumerable() // From now on the execution is done locally
.Where(whereFunc);
}

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.

Unexpected result in a LINQ to Entities query [duplicate]

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)

OrderByDescending with a date from another table(s)

Hopefully this question is not to confusing. Basically I'm looking for pointers on how to OrderByDecending with a date from relational tables. I have constructed a basic method that looks like it could possibly work but I'm getting errors:
DbSortClause expressions must have a type that is order comparable.
Parameter name: key
I understand what this is saying but I'm not entirely sure how to fix using Linq method syntax.
public BusinessEntities.Application GetLastUpdatedAppliction(int userID)
{
return context.tbl_User_To_Application
.Where(x => x.UserID == userID)
.OrderByDescending
(o => o.tbl_Application.tbl_ApplicationChanges
.Where(oo => oo.ApplicationID == o.ApplicationID)
.Select(s => s.ChangeDate))
.ThenByDescending(t => t.DateAdded)
.Select(y => new BusinessEntities.Application
{
ApplicationID = y.tbl_Application.ApplicationID,
ApplicationName = y.tbl_Application.ApplicationName
}).FirstOrDefault();
}
Basically I have a cross reference table that binds a user to a specific application(Website) Then inside I need to nest into two tables to get the latest changes to the Application with a "ChangesDate". So ideally this would return the last updated application. Then obviously populates my DTO.
I'm still trying to get to grips with Linq method syntax so any help would be greatly appreciated!
Regards,
Tez Wingfield
If you want to order by the last application change date:
(...)
.OrderByDescending(o =>
o.tbl_Application.tbl_ApplicationChanges
.Where(ac => ac.ApplicationID == o.ApplicationID)
.OrderByDescending(ac => ac.ChangeDate)
.First()
.Select(ac => ac.ChangeDate)
)
(...)

Linq to SQL order by with Distinct

My Environment: ASP.net and C# in VS 2013 Express.
I have been through many similar SO articles trying to work this out. I am amateur with Linq to SQL queries and c# in general.
I'm trying to use Linq to SQL to get the top 5 most recent distinct values from a column, then add them to a list. My application is asp.net using c# and a .dbml file for data abstraction.
I've tried it many different ways. I either get non-distinct yet sorted list, or I get a distinct unsorted list. What I have so far is below
var Top5MFG = (from mfg in db.orders
where mfg.manufacturer.Length > 0 && mfg.customerid == "blahblahblahblahblah"<br />
select new {
manufacturer = mfg.manufacturer,
date = mfg.date_created
})
.Distinct()
.OrderByDescending(s => s.date);
I'm thinking my "Distinct" is looking at the "ID" column, and perhaps I need to tell it I want it to look at the "manufacturer" column, but I haven't worked out how / if it's possible to do that.
I could do this with ease by using a storedproc, but I'm really trying to do it with c# code directly if possible. This is my first post to SO, I hope I have put it together properly. Any help much appreciated.
Thanks
No the Distinct compares manufacturer and date pairs.If you want to get distinct records by manufacturer then I recommend DistinctBy method.It's in the MoreLINQ library.Since its a third library method it's not supported in linq to sql, you still can use it by fetching the records from DB and do the rest in memory
(from mfg in db.orders
where mfg.manufacturer.Length > 0 && mfg.customerid == "blahblahblahblahblah"
select new {
manufacturer = mfg.manufacturer,
date = mfg.date_created
})
.AsEnumerable()
.DistinctBy(x => x.manufacturer)
.OrderByDescending(s => s.date)
.Take(5);
I think you can use the GroupBy to do what you want.
var Top5MFG = db.orders
.Where (x => x.manufacturer.Length > 0 && x.customerid == "blahblahblahblahblah")
.GroupBy(mfg => mfg.manufacturer)
.Select(g => g.First())
.OrderByDescending(d => d.date_created );
.Take(5);
One way you can distinct by a certain field is to replace:
...
.Distinct()
...
with:
...
.GroupBy(x => x.manufacturer )
.Select(g => g.First())
...

Categories