Groupby, sum with multiple joins in Linq lambda - c#

I have the below Linq query, and I want to sum the Hours based on the WorkItem.Id:
EntryCategories
.Join(TimeReportEntries,
ec => ec.Id,
tr => tr.WorkItem.Id,
(ec, tr) => new { ec, tr })
.Join(Timesheets,
ecs => ecs.tr.Timesheet.Id,
t => t.Id,
(ecs, t) => new { ecs, t })
.Join(Users,
ts => ts.t.User.Id,
u => u.Id,
(ts, u) => new
{
Employee = ts.t.User.FirstName + " " + ts.t.User.LastName,
Hours = ts.ecs.tr.Hours
});

I have no idea as to how exactly you want it to work and cant compile it here, but I think you need a group by to get sum of hours by each work item, and that is what you're after.
EntryCategories
.Join(TimeReportEntries, ec => ec.Id, tr => tr.WorkItem.Id, (ec, tr) => new { ec, tr })
.Join(Timesheets, ecs => ecs.tr.Timesheet.Id, t => t.Id, (ecs, t) => new { ecs, t })
.Join(Users, ts => ts.t.User.Id, u => u.Id, (ts, u) => new
{
WorkItemId = ts.ecs.tr.WorkItem.Id
Employee = ts.t.User.FirstName + " " + ts.t.User.LastName,
Hours = ts.ecs.tr.Hours
})
.GroupBy(x => x.WorkItemId)
.Select(x => new
{
WorkItemId = x.Key,
Hours = x.Sum(y => y.Hours)
});

Related

Error report of C # groupby using AsQueryable

var commodity = _appDbContext.ArchivesCCommodity.Where(lambda)
.GroupJoin(_appDbContext.ArchivesCCommoditySpecification, a => a.Code, b => b.Commodity, (a, b) => new { a, b })
.SelectMany(a => a.b.DefaultIfEmpty(), (a, b) => new { a.a, b })
.GroupJoin(_appDbContext.ArchivesCSpecificationDetail, a => a.a.a.b.SpecificationDetail, d => d.Code, (a, d) => new { a, d })
.SelectMany(a => a.d.DefaultIfEmpty(), (a, d) => new
{
Commodity = a.a.a.Code,
CommodityName = a.a.a.Name,
SpecificationDetailName = d.Name,
OrderSN = d.OrderSN
}).AsQueryable().OrderBy(a => a.OrderSN).GroupBy(a => new { a.Commodity, a.CommodityName })
.Select(a => new
{
Commodity = a.Key.Commodity,
CommodityName = a.Key.CommodityName,
SpecificationDetailName = string.Join(" - ", a.Select(a => a.SpecificationDetailName)),
SpecificationDetailTotal = string.Join(" - ", a.Select(a => a.SpecificationDetailName)) == "" ? 0 : a.Count()
});
Where .AsQueryable() will cause an error
.AsQueryable()
.OrderBy(a => a.OrderSN)
.GroupBy(a => new { a.Commodity, a.CommodityName })
No error will be reported when changing to AsEnumerable()
.ASEnumerable()
.OrderBy(a => a.OrderSN)
.GroupBy(a => new { a.Commodity, a.CommodityName })
But I don't want to send this code to the database for the time being, because it will be sent after paging query. I don't know how to deal with it?
//////////////I pasted my complete code and talked about my actual needs
Query the code and query the database page by page. For example, only one page and 10 rows of records are checked. Here is OK.
var AA= _appDbContext.ArchivesCCommodity.Where(lambda)
.GroupJoin(_appDbContext.ArchivesCCommoditySpecification, a => a.Code, b => b.Commodity, (a, b) => new { a, b })
.SelectMany(a => a.b.DefaultIfEmpty(), (a, b) => new { a.a, b })
.GroupJoin(_appDbContext.ArchivesCSpecificationDetail, a => a.a.b.SpecificationDetail, d => d.Code, (a, d) => new { a, d })
.SelectMany(a => a.d.DefaultIfEmpty(), (a, d) => new
{
Commodity = a.a.a.a.a.Code,
CommodityName = a.a.a.a.a.Name,
SpecificationDetailName = d.Name,
OrderSN = d.OrderSN
});
PageHealper<object> page = new PageHealper<object>();
page.Start(pageNum, pageSize);
page = await page.RestPage(AA);
At this time, I grouped and sorted again, and now I found that:
It is not to operate the paging query results, but to query all the AA databases.
Based on the previous pagination query, the number of rows and page numbers are obtained. Here, the number of rows is changed by grouping and merging.
That's why I want to put grouping and sorting together, and finally pagination.
var BB = AA.AsEnumerable().OrderBy(a => a.OrderSN).GroupBy(a => new { a.Commodity, a.CommodityName, a.Specification, a.SpecificationName })
.Select(a => new
{
Commodity = a.Key.Commodity,
CommodityName = a.Key.CommodityName,
SpecificationDetailName = string.Join(" - ", a.Select(a => a.SpecificationDetailName)),
SpecificationDetailTotal = string.Join(" - ", a.Select(a => a.SpecificationDetailName)) == "" ? 0 : a.Count()
}); ;
page.Data = BB.ToList<object>();
return page;
Checkout this article https://weblogs.asp.net/zeeshanhirani/using-asqueryable-with-linq-to-objects-and-linq-to-sql about what AsQueryable does.
I think you dont really need AsQueryable there... LINQ to SQL does not like something about that query.
It does not like the String.Join(...) because it cannot translate it.
So one thing you can do is put .AsEnumerable() after the GroupBy() this will do everything up to in SQL and everything after in memory.
Ex:
var commodity = _appDbContext.ArchivesCCommodity.Where(lambda)
.GroupJoin(_appDbContext.ArchivesCCommoditySpecification, a => a.Code, b => b.Commodity, (a, b) => new { a, b })
.SelectMany(a => a.b.DefaultIfEmpty(), (a, b) => new { a.a, b })
.GroupJoin(_appDbContext.ArchivesCSpecificationDetail, a => a.a.a.b.SpecificationDetail, d => d.Code, (a, d) => new { a, d })
.SelectMany(a => a.d.DefaultIfEmpty(), (a, d) => new
{
Commodity = a.a.a.Code,
CommodityName = a.a.a.Name,
SpecificationDetailName = d.Name,
OrderSN = d.OrderSN
}).OrderBy(a => a.OrderSN).GroupBy(a => new { a.Commodity, a.CommodityName })
.AnEnumerable()
.Select(a => new
{
Commodity = a.Key.Commodity,
CommodityName = a.Key.CommodityName,
SpecificationDetailName = string.Join(" - ", a.Select(a => a.SpecificationDetailName)),
SpecificationDetailTotal = string.Join(" - ", a.Select(a => a.SpecificationDetailName)) == "" ? 0 : a.Count()
});

Mysql to Linq query

MySQL Query
select MAX(os.aggregate_date) as lastMonthDay,os.totalYTD
from (SELECT aggregate_date,Sum(YTD) AS totalYTD
FROM tbl_aggregated_tables
WHERE subscription_type = 'Subcription Income'
GROUP BY aggregate_date) as os
GROUP by MONTH(os.aggregate_date),YEAR(os.aggregate_date)
ORDER BY lastMonthDay;
converted to this LINQ query
var income = context.tbl_aggregated_tables
.Where(s => s.subscription_type == "Subcription Income")
.GroupBy(s => s.aggregate_date)
.Select(result => new
{
date = result.Key,
ytdsum = result.Select(x => x.YTD).Sum()
})
.GroupBy(s => new { month = s.date.Month, year = s.date.Year })
.Select(
// select max data and take its ytdsum value
).ToList();
The purpose of second grouping is to find the max day of each month with a year.
Now, How to select the max date of each month and its ytdsum after the second Grouping?
update
income = context.tbl_aggregated_tables
.Where(s => s.subscription_type == "Subcription Income")
.GroupBy(s => s.aggregate_date)
.Select(result => new
{
date = result.Key,
ytdsum = result.Select(x => x.YTD).Sum()
})
.GroupBy(s => new { s.date.Month, s.date.Year })
.Select(
x => x.Max(s => s.date)
).ToList()
this way it's only return the dates and i could not return the full object of the list including ytdSum.
This should work:
var income = context.tbl_aggregated_tables
.Where(s => s.subscription_type == "Subcription Income")
.GroupBy(s => s.aggregate_date)
.Select(result => new
{
date = result.Key,
ytdsum = result.Select(x => x.YTD).Sum()
})
.GroupBy(s => new { month = s.date.Month, year = s.date.Year })
.Select(
x => x.OrderByDescending(k => k.date).First()
).ToList();
income now is a list of objects, each of them have date and ytdsum
this worked good but it takes much more time than the original mysql query.
var income = context.tbl_aggregated_tables
.Where(s => s.subscription_type == "Subcription Income"
)
.GroupBy(s => s.aggregate_date)
.Select(result => new
{
date = result.Key,
ytdsum = result.Select(x => x.YTD).Sum()
})
.GroupBy(s => new { s.date.Month, s.date.Year })
.Select(
x => x.OrderByDescending(s => s.date)
)
.ToList()
.Select(el => el.FirstOrDefault())
.OrderBy(s=>s.date);

How to convert a group_concat, join, and union query into linq lambda?

im pretty new to linq-lambda. I have a MySQL query that pulls the name of items in two tables union'd together. Then pull another column of the specialties that the items fall under.
my working query is this:
Select
p.title,
GROUP_CONCAT(spec.categoryname) genre
From
(SELECT FullName AS title, TypeId as typeid, Id as id FROM programs
UNION
SELECT FullName, TypeId, Id FROM products)
AS p
Inner join specialtymembers mem on (ItemType = p.typeid AND ItemId = p.id)
Inner join specialties spec on mem.Category = spec.id
GROUP BY p.title
ORDER BY p.title
now my problem is... that i have to somehow convert this into linq lambda. My attempt is this:
var allitems =
_programContentService.Products.Select(r => r.FullName)
.Union(_programContentService.Programs.Select(q => q.FullName))
.Join(_programContentService.SpecialtyMembers, z => z.ItemType, q => q.ItemType,
(z, q) => new {z, q})
.Join(_programContentService.Specialties, x => x.Id, z => z.Category,
(x, z) => new {x,z})
.Select(#t => new SelectListItem { Name = q.FillName.ToString(), Genre = "SOME HOW HAVE TO USE A LOOPING FEATURE??" });
UPDATE:
var allitems = _programContentService
.ProgramProductViews
.Select(x => new {x.FullName, x.TypeId, x.Id})
.Join(
_programContentService.SpecialtyMembers,
type => new {type.TypeId, type.Id},
member => new {TypeId = member.ItemType, Id = member.ItemId},
(type, member) => new {type.FullName, member.Category})
.Join(
_programContentService.Specialties,
q => q.Category,
specialty => specialty.Id,
(q, specialty) => new { q.FullName, specialty.SpecialtyName })
.GroupBy(x=>x.FullName)
.AsEnumerable()
.Select(x => new SelectListItem
{
Value = x.Key,
Text = String.Join(",", x.Select(q=>q.SpecialtyName))
});
i have a feeling that I am close...
I think this will get you what you want. It is untested, if you have issues let me know and I will work it out.
var allitems =
_programContentService
.Products
.Select(x => new { x.FullName, x.TypeId, x.Id })
.Union(
_programContentService
.Programs
.Select(x => new { x.FullName, x.TypeId, x.Id }))
.Join(
_programContentService.SpecialtyMembers,
type => new { type.TypeId, type.Id },
member => new { TypeId = member.ItemType, Id = member.ItemId },
(type, member) => new { type.FullName, member.Category })
.Join(
_programContentService.Specialties,
x => x.Category,
specialty => specialty.Id,
(x, specialty) => new { x.FullName, specialty.CategoryName })
.GroupBy(x => x.FullName)
.AsEnumerable()
.Select(x => new SelectListItem
{
Value = x.Key,
Text = String.Join(",", x.Select(y => y.CategoryName))
});

GroupBy with SingleOrDefault in EF

I have 5 tables:
NazelShifts
Nazel
Tank
PersonnelNazelShifts
Shift
sql query is:
SELECT SUM(NazelShift.Eold) AS tEold, SUM(NazelShift.Er) AS tEr, SUM(NazelShift.Ecf) AS tEcf, SUM(NazelShift.Esf) AS tEsf, SUM(NazelShift.ESale) AS tESale, Tank.FuelId,
NazelShift.ShiftId, PersonnelNazelShift.PersonnelId
FROM NazelShift INNER JOIN
Nazel ON NazelShift.NazelId = Nazel.NazelId AND NazelShift.NazelId = Nazel.NazelId INNER JOIN
Tank ON Nazel.TankId = Tank.TankId INNER JOIN
PersonnelNazelShift ON Nazel.NazelId = PersonnelNazelShift.NazelId INNER JOIN
Shift ON NazelShift.ShiftId = Shift.ShiftId AND PersonnelNazelShift.ShiftId = Shift.ShiftId
WHERE (NazelShift.ShiftId = 1)
GROUP BY Tank.FuelId, NazelShift.ShiftId, PersonnelNazelShift.PersonnelId
NazelShift have pelation many to one with Nazel and Shift
also PersonnelNazelShift have relation many to one with Nazel and Shift.
diagram is http://jmp.sh/dlO3MTf
I need to run this query:
NazelShifts.Where(i => i.ShiftId == 1)
.GroupBy(i => new
{
i.ShiftId,
i.Nazel.Tank.FuelId,
i.Nazel.PersonnelNazelShifts.SingleOrDefault().PersonnelId
})
.Select(i => new
{
i.Key.ShiftId,
i.Key.PersonnelId,
i.Key.FuelId,
tEold = i.Sum(rr => rr.Eold),
tEr = i.Sum(rr => rr.Er),
tEcf = i.Sum(rr => rr.Ecf),
tEsf = i.Sum(rr => rr.Esf),
tESale = i.Sum(rr => rr.ESale)
})
This works fine in LinqPad4 but in vs2012 throws an exception:
"The methods 'Single' and 'SingleOrDefault' can only be used as a
final query operation. Consider using the method 'FirstOrDefault' in
this instance instead."
How can I solve this problem?
I find this solution.
NazelShifts.Where(i => i.ShiftId == 1)
.Join(Nazels,
ns => ns.NazelId,
n => n.NazelId,
(ns, n) => new { NS = ns, N = n })
.Join(Shifts,
nsn => nsn.NS.ShiftId,
s => s.ShiftId,
(nsn, s) => new { NSN = nsn, S = s })
.Join(PersonnelNazelShifts,
nsns =>new{ nsns.NSN.N.NazelId,nsns.S.ShiftId},
pns =>new { pns.NazelId,pns.ShiftId},
(nsns, pns) => new { NSNS = nsns, PNS = pns })
.Join(Tanks,
nsnspns => nsnspns.NSNS.NSN.N.TankId,
t => t.TankId,
(nsnspns, t) => new { NSNSpns = nsnspns, T = t })
.GroupBy(i => new { i.T.FuelId,i.NSNSpns.NSNS.NSN.NS.ShiftId,i.NSNSpns.PNS.PersonnelId })
.Select(i => new
{
i.Key.ShiftId,
i.Key.PersonnelId,
i.Key.FuelId,
tEold = i.Sum(rr => rr.NSNSpns.NSNS.NSN.NS.Eold),
tEr = i.Sum(rr => rr.NSNSpns.NSNS.NSN.NS.Er),
tEcf = i.Sum(rr => rr.NSNSpns.NSNS.NSN.NS.Ecf),
tEsf = i.Sum(rr => rr.NSNSpns.NSNS.NSN.NS.Esf),
tESale = i.Sum(rr => rr.NSNSpns.NSNS.NSN.NS.ESale)// not used
})

How to group dictionary by dateTime and do a sum on int

I have this code :
var tradeReqsBySegment = segGroups.Join(pPeriods, s => s.MarketSegmentId, p => p.EntityId, (s, p) => new
{
SegmentCode = s.SegmentCode, // string
Time = p.StartLocal, // datetime
TradeRequirement = p.Volume // double
})
.GroupBy(s => s.SegmentCode)
.ToDictionary(g => g.Key, g => g.ToDictionary(i => i.Time, i=>i.TradeRequirement));
I would like the g.ToDictionary(i => i.Time, i=>i.TradeRequirement)) to be grouped by time, and the TradeRequirement to be summed up or averaged. How do I approach this?
Also, is it possible to group the time by month by month basis, like get :
Time - TradeReq
01/2013 - 500
02/2013 - 234
...
g.GroupBy(gr => new DateTime(gr.Time.Year, gr.Time.Month, 1))
.ToDictionary(i => i.Key, i => i.Sum(s => s.TradeRequirement));
You can get both: Sum and Average at the same time, using anonymous type:
var tradeReqsBySegment = segGroups.Join(pPeriods, s => s.MarketSegmentId, p => p.EntityId, (s, p) => new
{
SegmentCode = s.SegmentCode, // string
Time = p.StartLocal, // datetime
TradeRequirement = p.Volume // double
})
.GroupBy(s => s.SegmentCode)
.ToDictionary(g => g.Key,
g => g.GroupBy(gr => new DateTime(gr.Time.Year, gr.Time.Month, 1))
.ToDictionary(gr => gr.Key.ToString("MM/yyyy"),
gr => new {
Sum = gr.Sum(s => s.TradeRequirement),
Avg = gr.Average(s => s.TradeRequirement)
}));

Categories