GroupBy with SingleOrDefault in EF - c#

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
})

Related

Optimize linq query by storing value in select

I have problem with linq query. In Select I am getting the same item twice which makes code execution much longer than I can afford. Is there any way to store x.OrderByDescending(z => z.Date).FirstOrDefault() item inside Select query?
Execution time: 180 ms
var groups = dataContext.History
.GroupBy(a => new { a.BankName, a.AccountNo })
.Select(x => new HistoryReportItem
{
AccountNo = x.FirstOrDefault().AccountNo,
BankName = x.FirstOrDefault().BankName,
IsActive = x.FirstOrDefault().IncludeInCheck,
})
.ToList();
Execution time: 1200 ms
var groups = dataContext.History
.GroupBy(a => new { a.BankName, a.AccountNo })
.Select(x => new HistoryReportItem
{
AccountNo = x.FirstOrDefault().AccountNo,
BankName = x.FirstOrDefault().BankName,
IsActive = x.FirstOrDefault().IncludeInCheck,
LastDate = x.OrderByDescending(z => z.Date).FirstOrDefault().Date,
})
.ToList();
Execution time: 2400 ms
var groups = dataContext.History
.GroupBy(a => new { a.BankName, a.AccountNo })
.Select(x => new HistoryReportItem
{
AccountNo = x.FirstOrDefault().AccountNo,
BankName = x.FirstOrDefault().BankName,
IsActive = x.FirstOrDefault().IncludeInCheck,
LastDate = x.OrderByDescending(z => z.Date).FirstOrDefault().Date,
DataItemsCount = x.OrderByDescending(z => z.Date).FirstOrDefault().CountItemsSend
})
.ToList();
You can try doing the select in two steps:
var groups = dataContext.History
.GroupBy(a => new { a.BankName, a.AccountNo })
.Select(x => new
{
first = x.FirstOrDefault();
lastDate = x.OrderByDescending(z => z.Date).FirstOrDefault();
}
.Select(x => new HistoryReportItem
{
AccountNo = x.first.AccountNo,
BankName = x.first.BankName,
IsActive = x.first.IncludeInCheck,
LastDate = x.lastDate.Date,
DataItemsCount = x.lastDate.CountItemsSend
})
.ToList();
If this fails, it might be because the engine can't convert it completely to SQL, and you can try adding an AsEnumerable() between the two Selects.

Transform SQL Query to LINQ

I would like if someone helps me to convert this SQL Query to LINQ syntax.
SELECT i.Id, i.Condomino as Condomino, i.Interno as Interno,
p.NomePiano as NomePiano, s.Nome as NomeCondominio,
m.millesimi_fabbisogno_acs, m.millesimi_fabbisogno_riscaldamento
FROM Interni i
INNER JOIN Piani p ON i.IdPiano = p.Id
INNER JOIN Stabili s ON i.IdStabile = s.Id
LEFT JOIN MillesimiTabellaC m ON i.Id = m.idInterno
WHERE s.IdCondominio = {0}
I tried using something like this, but is not working..
return _Db.Interni.Include("Piani").Where(x => x.Piani.IdCondominio == iidcond).ToList();
I made it on-the-spot (so it's not tested), but perhaps it's enough to give you the idea. I'm also assuming that your DB model has foreign keys set up.
var result = _db.Interni
.Where(i => i.Stabili.IdCondominio = [value])
.Select(i => new
{
i.Id,
Condomino = i.Condomino,
Interno = i.Interno,
NomePiano = i.Piani.NomePiano,
NomeCondominio = i.Stabili.Nome,
i.MillesimiTabellaC.millesimi_fabbisogno_acs,
i.MillesimiTabellaC.millesimi_fabbisogno_riscaldamento
})
.ToList();
update
In case you don't have a foreign key between Interni and MillesimiTabellaC, try this:
var result = _db.Interni
.Include(i => i.Piani)
.Include(i => i.Stabili)
.Where(i => i.Stabili.IdCondominio = [value])
.Select(i => new
{
Interni = i,
MillesimiTabellaC = _db.MillesimiTabellaC.Where(m => i.Id = m.idInterno)
})
.Select(x => new
{
Id = x.Interni.Id,
Condomino = x.Interni.Condomino,
Interno = x.Interni.Interno,
NomePiano = x.Interni.Piani.NomePiano,
NomeCondominio = x.Interni.Stabili.Nome,
x.MillesimiTabellaC?.millesimi_fabbisogno_acs,
x.MillesimiTabellaC?.millesimi_fabbisogno_riscaldamento
})
.ToList();

Linq group by with parent object

How do I group so that I don't loose the parent identifier.
I have the following
var grouped = mymodel.GroupBy(l => new { l.AddressId })
.Select(g => new
{
AddressId = g.Key.AddressId,
Quotes = g.SelectMany(x => x.Quotes).ToList(),
}).ToList();
this returns
{ AddressId1, [Quote1, Quote2, Quote3...]}
{ AddressId2, [Quote12, Quote5, Quote8...]}
Now I would like to group these by Quote.Code and Quote.Currency, So that Each address has 1 Object-Quote (that is if all 4 quotes belonging to the address have the same Code and Currency). I would like the sum of Currency in that object.
This works, but I can't get how to add Address to this result:
var test = grouped.SelectMany(y => y.Quotes).GroupBy(x => new { x.Code, x.Currency }).Select(g => new
{
test = g.Key.ToString()
});}
this gives compile error, whenever i try to add AddressId to result:
var test1 = grouped.SelectMany(y => y.Quotes, (parent, child) => new { parent.AddressId, child }).GroupBy(x => new { x.Provider, x.Code, x.Currency, x.OriginalCurrency }).Select(g => new
{
test = g.Key.ToString(),
Sum = g.Sum(x => x.Price)
});
compiler error as well:
var test1 = grouped.Select(x => new { x.AddressId, x.Quotes.GroupBy(y => new { y.Provider, y.Code, y.Currency, y.OriginalCurrency }).Select(g => new
{
addr = x.AddressId,
test = g.Key.ToString(),
Sum = g.Sum(q => q.Price)
};
I would do that this way:
var grouped = mymodel.GroupBy(l => new { l.AddressId })
.Select(g => new
{
AddressId = g.Key.AddressId,
QuotesByCode = g.SelectMany(x => x.Quotes)
.GroupBy(x=>x.Code)
.Select(grp=>new
{
Code = grp.Key.Code,
SumOfCurrency=grp.Sum(z=>z.Currency)
}).ToList(),
}).ToList();

The LINQ expression node type 'ArrayIndex' is not supported in LINQ to Entities

var residenceRep =
ctx.ShiftEmployees
.Include(s => s.UserData.NAME)
.Include(s => s.ResidenceShift.shiftName)
.Join(ctx.calc,
sh => new { sh.empNum, sh.dayDate },
o => new { empNum = o.emp_num, dayDate = o.trans_date },
(sh, o) => new { sh, o })
.Where(s => s.sh.recordId == recordId && s.o.day_flag.Contains("R1"))
.OrderBy(r => r.sh.dayDate)
.Select(r => new
{
dayDate = r.sh.dayDate,
empNum = r.sh.empNum,
empName = r.sh.UserData.NAME,
shiftId = r.sh.shiftId,
shiftName = r.sh.ResidenceShift.shiftName,
recordId,
dayState = r.o.day_desc.Split('[', ']')[1]
}).ToList();
I get an exception :
The LINQ expression node type 'ArrayIndex' is not supported in LINQ to
Entities
How i could find an alternative to Split('[', ']')[1] in this query
You must commit the query and do the split after loading the data:
var residenceRep =
ctx.ShiftEmployees
.Include(s => s.UserData.NAME)
.Include(s => s.ResidenceShift.shiftName)
.Join(ctx.calc,
sh => new { sh.empNum, sh.dayDate },
o => new { empNum = o.emp_num, dayDate = o.trans_date },
(sh, o) => new { sh, o })
.Where(s => s.sh.recordId == recordId && s.o.day_flag.Contains("R1"))
.OrderBy(r => r.sh.dayDate)
.Select(r => new
{
dayDate = r.sh.dayDate,
empNum = r.sh.empNum,
empName = r.sh.UserData.NAME,
shiftId = r.sh.shiftId,
shiftName = r.sh.ResidenceShift.shiftName,
recordId = r.sh.recordId,
dayState = r.o.day_desc,
})
.ToList()//Here we commit the query and load data
.Select(x=> {
var parts = x.dayState.Split('[', ']');
return new {
x.dayDate,
x.empNum,
x.empName,
x.shiftId,
x.shiftName,
x.recordId,
dayState = parts.Length > 1 ?parts[1]:"",
};
})
.ToList();
I had this Issue and the approach that I've chose was that get all element I wanted and save them into a List and then filter the actual data on that list.
I know this is not the best answer but it worked for me.

Cannot implicitly convert type 'System.Collections.Generic.List<AnonymousType#1>' to 'System.Linq.IQueryable<AnonymousType#2>'

This query is being compiled without errors:
var _entityList = context.customer
.Join(context.applications,
cust => cust.cust_id,
app => app.cust_id,
(cust, app) => new { customer = cust, application = app })
.Join(context.advices,
cust => cust.application.app_id,
sa => sa.app_id,
(cust, sa) => new { customer = cust, advice = sa })
.GroupBy(g => new { g.customer.customer.cust_id, g.customer.customer.cust_code, g.customer.customer.cust_name })
.Select(g => new { cust_id = g.Key.cust_id, cust_code = g.Key.cust_code, cust_name = g.Key.cust_name })
.ToList();
While adding a conditional where clause to the above query returns compile time type conversion error:
var _entityList = context.customer
.Join(context.applications,
cust => cust.cust_id,
app => app.cust_id,
(cust, app) => new { customer = cust, application = app })
.Join(context.advices,
cust => cust.application.app_id,
sa => sa.app_id,
(cust, sa) => new { customer = cust, advice = sa });
if (custcode != null && custcode != "")
_entityList = _entityList.Where(e => e.customer.customer.cust_code == custcode);
_entityList = _entityList
.GroupBy(g => new { g.customer.customer.cust_id, g.customer.customer.cust_code, g.customer.customer.cust_name })
.Select(g => new { cust_id = g.Key.cust_id, cust_code = g.Key.cust_code, cust_name = g.Key.cust_name })
.ToList(); // error on this line
Cannot implicitly convert type System.Collections.Generic.List<AnonymousType#1> to System.Linq.IQueryable<AnonymousType#2>
What am I missing?
Consider the following simplified code sample:
var _entityList = Enumerable.Range(0, 1)
.Select(i=>new {i1 =i, i2 = i+1});
_entityList = _entityList
//.Select(i => new { i1 = i.i1, i2 = i.i2 }) // works
//.Select(i => i) // works
.Select(i => new { i }) // fails
.ToList();
In your first scenario, there is only one anonymous type involved, hence _entityList is a List<AnonymousType#1>. In your 2nd scenario, you change the returned type from one anonymous type:
new {
customer = cust,
advice = sa
}
to another:
new {
cust_id = g.Key.cust_id,
cust_code = g.Key.cust_code,
cust_name = g.Key.cust_name
}
so a conversion error occurs.
Try this:
var _entityList = context.customer
.Join(context.applications,
cust => cust.cust_id,
app => app.cust_id,
(cust, app) => new { customer = cust, application = app })
.Join(context.advices,
cust => cust.application.app_id,
sa => sa.app_id,
(cust, sa) => new { customer = cust, advice = sa })
.Where(e => (custcode != null && custcode != "")
? e.customer.customer.cust_code == custcode : true)
.GroupBy(g => new {
g.customer.customer.cust_id,
g.customer.customer.cust_code,
g.customer.customer.cust_name })
.Select(g => new {
cust_id = g.Key.cust_id,
cust_code = g.Key.cust_code,
cust_name = g.Key.cust_name })
.ToList();
Change
_entityList = _entityList
.GroupBy(g => new { g.customer.customer.cust_id, g.customer.customer.cust_code, g.customer.customer.cust_name })
.Select(g => new { cust_id = g.Key.cust_id, cust_code = g.Key.cust_code, cust_name = g.Key.cust_name })
.ToList(); // error on this line
to
var result = _entityList
.GroupBy(g => new { g.customer.customer.cust_id, g.customer.customer.cust_code, g.customer.customer.cust_name })
.Select(g => new { cust_id = g.Key.cust_id, cust_code = g.Key.cust_code, cust_name = g.Key.cust_name })
.ToList();
When you apply the When clause you use the type
(cust, sa) => new { customer = cust, advice = sa }.
But after you change to
new { cust_id = g.Key.cust_id, cust_code = g.Key.cust_code, cust_name = g.Key.cust_name }
They are different types to compiler.

Categories