C# Nested Linq Group By - linq

I'd like to achive this output, ONE VIEWMODEL INSTANCE for each customer
CUSTOMER
TUI
DATE
PASSENGERS
VESSEL
01/05/2021
2
MORNING STAR
01/05/2021
7
CAPTAIN JOHN
01/05/2021
10
CAPTAIN JOHN
VESSEL
PASSENGERS
CAPTAIN JOHN
17
MORNING STAR
2
TOTAL
19
This is the model
public class Reservation {
public string Date { get; set; }
public string Destination { get; set; }
public string Customer { get; set; }
public string Vessel { get; set; }
public int TotalPersons { get; set; }
}
This is the view model so far
public class ReservationVM {
public string Customer { get; set; }
public List<Reservation> Reservations { get; set; }
}
So far, I can only create the list with the customer and the reservations
CUSTOMER
TUI
DATE
PASSENGERS
VESSEL
01/05/2021
2
MORNING STAR
01/05/2021
7
CAPTAIN JOHN
01/05/2021
10
CAPTAIN JOHN
Code
public IEnumerable<ReservationVM> Get() {
var result = dbContext.Reservations
.Where(x => x.Date == "2021-05-01")
.AsEnumerable()
.GroupBy(x => x.Customer)
.Select(x => new ReservationVM {
Customer = x.Key,
Reservations = x.ToList()
})
.OrderBy(x => x.Customer);
return result;
}
How could I include the part that displays the total passengers for each vessel?
VESSEL
PASSENGERS
CAPTAIN JOHN
17
MORNING STAR
2
TOTAL
19

The Total field must be at the level of ReservationVM (unless you want a vessel named Total, which you don't). Also, you should create another class, let's call it VesselReservationVm. You can do something as follows:
Classes:
public class Reservation {
public string Date { get; set; }
public string Customer { get; set; }
public string Vessel { get; set; }
public int TotalPersons { get; set; }
}
public class ReservationVM {
public string Customer { get; set; }
public List<Reservation> Reservations { get; set; }
public List<VesselReservationVM> VesselReservations { get; set; }
public int Total { get; set; }
}
public class VesselReservationVM {
public string Vessel { get; set; }
public int Passengers { get; set; }
}
Methods:
public IEnumerable<ReservationVM> GetCustomersReservations() {
var result = dbContext.Reservations
.Where(x => x.Date == "2021-05-01")
.AsEnumerable()
.GroupBy(x => x.Customer)
.Select(x => new ReservationVM {
Customer = x.Key,
Reservations = x.ToList(),
VesselReservations = GetCustomerVessels(x.ToList()),
Total = x.Select(r => r.TotalPersons).Sum()
})
.OrderBy(x => x.Customer);
return result;
}
public List<VesselReservationVM> GetCustomerVessels(
List<Reservation> reservations) {
var result = reservations
.GroupBy(r => r.Vessel)
.Select(g => new VesselReservationVM
{
Vessel = g.Key,
Passengers = g.Select(r => r.TotalPersons).Sum(),
})
.ToList();
return result;
}

Related

LINQ from a list inside a class

I have the following Model class:
public partial class EmployeeInfo
{
public int EmployeeInfoId { get; set; }
public string FirstName { get; set; } = null!;
public int? JobTitleLookupId { get; set; }
public virtual JobTitleLookup? JobTitleLookup { get; set; }
}
public partial class JobTitleLookup
{
public int JobTitleLookupId { get; set; }
public string Title { get; set; } = null!;
public virtual ICollection<EmployeeInfo> EmployeeInfos { get; } = new List<EmployeeInfo>();
}
I have JobTitleLookupId in my employeeeInfo database table. How can i get the name of the jobtitle if i have the corresponding ID in employeeInfo Table.
below is my JobTitleLookup table:
JobTitleLookupID title
1 title1
2 title2
3 title3
EmployeeInfo table
EmployeeInfoId FirstName JobTitleLookupID
1 test1 2
2 test3 1
3 testx 3
How can I get the job title if I have the employeeinfoId and jobTitleLookupID using LINQ.'
this is what I tried so far:
public async Task<List<string?> GetJobTitle(string employeeId)
{
var query = _ackContext.EmployeeInfos
.Include(c => c.JobTitleLookup)
.Where(c => c.EmployeeNumber == employeeId)
}
this is something I wrote in sql. I want the same in LINQ
SELECT
title
FROM
employeeInfo e
INNER JOIN JobTitleLookup j ON
e.JobTitleLookupID =j.JobTitleLookupID
AND
EmployeeInfoID = 212;
To select something precise, you have to use Select.
public Task<List<string?> GetJobTitle(string employeeId)
{
return _ackContext.EmployeeInfos
.Where(c => c.EmployeeNumber == employeeId)
.Select(c => c.JobTitleLookup.Title)
.ToListAsync();
}

Ignore Soft-Deleted records within graph

Using EF Core
Assuming Structure:
class Parent
{
public int Id { get; set; }
public List<Child> Children { get; set; }
public string Name { get; set; }
public bool IsDeleted { get; set; }
}
class Child
{
public int Id { get; set; }
List<GrandChild> Grandchildren { get; set; }
public string Name { get; set; }
public bool IsDeleted { get; set; }
public int ParentId { get; set; }
}
class Grandchild
{
public int Id { get; set; }
public string Name { get; set; }
public bool IsDeleted { get; set; }
public int ChildId { get; set; }
}
Records could have been soft-deleted at any level, although no descendant of a soft-deleted entity can itself not be soft-deleted (obviously).
List<Parent> nonDeletedParents = Context.Parents
.Include(p => p.Children).ThenInclude(c => c.Grandchildren)
.Where(p => !p.IsDeleted)
.ToList();
will get me non-deleted parents, but how do I exclude deleted children and grandchildren in a single query to the database?
One approach I have explored is to use an anonymous return type and then rebuild:
var result = await Context.Parents
.Where(p => !p.IsDeleted)
.Select(p =>
{
Parent = p,
AllChildren = p.Children
.Where(c => !c.IsDeleted)
.ToList(),
AllGrandchildren = p.Children
.Where(c => !c.IsDeleted)
.SelectMany(c => c.Grandchildren)
.Where(g => !g.IsDeleted)
.ToList()
}).ToList();
List<Parent> actualResults = new List<Parent>();
results.ForEach(r =>
{
Parent actualResult = r.Parent;
actualResult.Children = r.AllChildren;
actualResult.Children.ForEach(c =>
{
c.Grandchildren = r.AllGrandchildren.Where(g => g.ChildId == c.Id).ToList();
});
actualResults.Add(actualResult);
}
return actualResults;
But, seems pretty inelegant. Is there a preferable approach?

Fluent LINQ EF Core - Select filtered child property

I have the classes below:
public class User
{
public Guid Id { get; set; }
public string Name { get; set; }
}
public class ParentEntity
{
public Guid Id { get; set; }
public string SomeProperty { get; set; }
public ICollection<ChildEntity> ChildEntities { get; set; }
}
public class ChildEntity
{
public Guid Id { get; set; }
public int Vote { get; set; }
public Guid UserId { get; set; }
}
public class ReturnedParentDto
{
public Guid Id { get; set; }
public string SomeProperty { get; set; }
public int Vote { get; set; }
}
I want to be able to return a full list of ParenEntities, but take an Id of the User class (UserClassId), then filter the ParentEntity's ICollection where UserUid = UserClassId, so only 1 ChildEntity is always returned. Then I would want to extract a specific field from that returned ChildEntity and merge it with the ParentEntity fields. The end result should be like the ReturnedParentDto.
I want to do it in the style like
ParentEntities.Include(v => v.ChildEntities).ToList()
That seems to be possible in EF Core 5, but my project is in 3.1.
You can do this as below
Approach 1:
var result = result = parentEntities.Include(x => x.ChildEntities.Where(y => y.UserId == userId))
.Select(x => new ReturnedParentDto {
Id = x.Id,
SomeProperty = x.SomeProperty,
Vote = x.ChildEntities.FirstOrDefault()?.Vote // userId is the variable here
});
Approach 2:
var result = parentEntities.Select(x =>
new ReturnedParentDto {
Id = x.Id,
SomeProperty = x.SomeProperty,
Vote = x.ChildEntities.FirstOrDefault(y => y.UserId == userId)?.Vote // userId is the variable here
});

LINQ converting anaonymous list to strongly type with date splits in groupBY

I am trying to group together daily logs into months in a linq query from a dbset, getting above or below a percentage of allowed usage.
The dbset is on
public partial class DailyUsageForYear
{
public System.DateTime DateAdded { get; set; }
public Nullable<long> NumUsers { get; set; }
public int AllowedUsageCount { get; set; }
public string Application { get; set; }
public System.Guid ApplicationID { get; set; }
public System.Guid SupplierID { get; set; }
}
I am trying the to get a list of objects like
public class UsageDisplay
{
public int Year { get; set; }
public int Month { get; set; }
public string Application { get; set; }
public int NumUsers { get; set; }
public int AllowedUsageCount { get; set; }
public Guid ApplicationID { get; set; }
}
However the select at the end of my statement does not seem to understand the field names, can anyone tell me please, what am I missing about the linq syntax?
var predicate = PredicateBuilder.False<DailyUsageForYear>();
predicate = predicate.And (x => x.NumUsers > (x.AllowedUsageCount * (DropPercentage/100)));
if (supplier != null)
{
Guid supplierGuid = new Guid (supplier.ToString());
predicate = predicate.And (x => x.SupplierID == supplierGuid);
}
if (Direction == 0)
predicate = predicate.And (x => x.NumUsers < (x.AllowedUsageCount * (Percentage/100)));
else
predicate = predicate.And (x => x.NumUsers > (x.AllowedUsageCount * (Percentage/100)));
usageDetailModel.usage = db.DailyUsageForYears.**Where**(predicate)
.**GroupBy**(x => new { x.DateAdded.Year, x.DateAdded.Month, x.Application, x.NumUsers, x.SeatCount, x.LicenceID })
.**Select**(u => new UsageDisplay() { Year = u.DateAdded.Year, Month = u.DateAdded.Month, Application = u.Application, NumUsers = u.NumUsers, SeatCount = u.SeatCount, ApplicationId = u.ApplicationID });
Inside last select u is not DailyUsageForYear its groupping object with Key field, so try change your select like this
.Select(u => new UsageDisplay() {
Year = u.Key.Year,
Month = u.Key.Month,
Application = u.Key.Application,
NumUsers = u.Key.NumUsers,
SeatCount = u.Key.SeatCount,
ApplicationId = u.Key.ApplicationID
});
also in
.GroupBy(x => new {
x.DateAdded.Year,
x.DateAdded.Month,
x.Application,
x.NumUsers,
x.SeatCount,
x.LicenceID })
i think you need x.ApplicationId instead of x.LicenceID

Group Linq and return as class

I am querying an EF entity MatchHistory:
public partial class MatchHistory
{
public System.Guid ApplicantId { get; set; }
public int BuyerId { get; set; }
public System.DateTime AppliedOn { get; set; }
public int MatchResultId { get; set; }
public System.DateTime ReapplyOn { get; set; }
public virtual MatchBuyer MatchBuyer { get; set; }
}
I currently have this linq statement in my code.
return r.Find()
.Where(x => x.AppliedOn > cutoff && x.MatchResultId == (int)MatchResult.Accepted)
.ToList();
This will return all rows of the type MatchHistory matching the criteria.
However, what I want to do is group by BuyerId and return a count by BuyerId.
Here's the class, I want to output to:
public class QuotaCount
{
public int BuyerId { get; set; }
public int Count { get; set; }
}
Haven't quite managed to get the right syntax together yet - any advice appreciated.
return r.Find()
.Where(x => x.AppliedOn > cutoff && x.MatchResultId == (int)MatchResult.Accepted)
.GroupBy(x => x.BuyerId)
.Select(x => new QuotaCount { BuyerId = x.Key, Count = x.Count() })
.ToList();
return r.Find()
.Where(x => x.AppliedOn > cutoff && x.MatchResultId == (int)MatchResult.Accepted)
.GroupBy(mh=>mh.BuyerId).Select(gr=>new QuotaCount{BuyerId=gr.Key,Count=gr.Count});

Resources