Translating my SQL Query to c# linq/lambda. Multiple parameter GroupBy - c#

I've been puzzling over this problem all morning and can't figure out how to do it in C#.
My SQL query as follows:
select a.CourseID,
a.UserID
from audit a
inner join results r on a.UserID = r.UserID
inner join Course c on a.CourseID = c.CourseID
where c.CourseType = 9 and a.Guid = 'A123F123D123AS123123'
and a.Result = 'Passed' and r.Class = 'Maths'
group by a.CourseID, a.UserID
order by a.UserID
returns exactly what I want, but I can't seem to translate it into linq format. (the format being used here is what is required in my job at the moment so please advise on this format)
So far I have the following:
var audits = auditRepository.Get(a => a.Course.CourseType == 9 && a.GUID == this.Company.GUID && a.Result == "Passed", null, null,
a => a.Course, a => a.User)
.Join(resultsRepository.Get(r => r.GUID == this.Company.GUID && r.Class == class),
a => a.UserID,
r => r.UserID,
(a, r) => new Audit
{
User = a.User,
Course = a.Course,
Result = a.Result,
Timestamp = a.Timestamp,
AuditID = a.AuditID,
UserID = a.UserID
}
)
.OrderByDescending(o => o.Timestamp)
.GroupBy(u => new { u.User, u.Course })
.Select(grp => grp.ToList())
.ToList();
However this returns duplicates.
Any advice is appreciated, thanks
H

Instead of
.Select(grp => grp.ToList())
Select only the first element from each group to exclude duplicates:
.Select(grp => grp.First())
If you need a count also:
.Select(t => new{grp = t.First(),cnt = t.Count()} )
Fix:
.Select(t => new { grp = t.First(), cnt = t.Select(s => s.AuditID).Distinct().Count() })

Related

Linq an aggregate with a grouping

I'm translating a query and here's the original:
select top 5 t.usrID, u.FirstName, u.LastName, t.cnt as sCount
from (
select usrID, COUNT(rID) as cnt
from sessions as s where s.sDate > DATEADD(yy, -1, getdate())
group by usrID
) as t
inner join users as u on t.usrID = u.usrID
order by t.cnt desc
Here's what I have so far:
var topUser = (from p in _context.Sessions
where p.SDate > DateTime.Now.AddYears(-1)
join c in _context.Users on p.UsrId equals c.UsrId into j1
from j2 in j1.DefaultIfEmpty()
// group j2 by p.UsrId into grouped
select new
{
p.UsrId,
j2.FirstName,
j2.LastName,
cnt = p.RId
})
//.OrderBy(d => d.cnt)
//.GroupBy(o => o.UsrId)
.Take(5);
I'm having trouble figuring out how to include count() and group by clauses. When I include groupBy my other columns disappear. Thank you.
This is the answer to your answer - not to your original query. I would put it as comment, but without formatting it's hard to explain
Assuming User object has collection of Session your first statement can be drastically simplified:
var topUsers = _context.Sessions
.Where(s => s.SDate > DateTime.Now.AddYears(-1))
.Select(s => new
{
s.UsrId,
cnt = s.User.Sessions.Count(u => u.UsrId == s.UsrId)
})
.OrderByDescending(s => s.cnt)
.Take(5);
You can shape the results to get a ViewModel that also has FirstName and LastName. It all boils down to defining a model with one-to-many relationship
var topUser = (from p in _context.Sessions
where p.SDate > DateTime.Now.AddYears(-1)
join c in _context.Users on p.UsrId equals c.UsrId into j1
from j2 in j1.DefaultIfEmpty()
group j2 by p.UsrId into g
select new
{
UsrId = g.Key,
FirstName = g.Select(x => x.FirstName).FirstOrDefault(),
LastName = g.Select(x => x.LastName).FirstOrDefault(),
sCount = g.Count()
})
.OrderByDescending(d => d.sCount)
.Take(5);
Alright so it's not the most efficient but it works:
var topUsers = _context.Sessions
.Where(s => s.SDate > DateTime.Now.AddYears(-1))
.GroupBy(s => s.UsrId)
.Select(ws => new { ws.Key, cnt = ws.Count() })
.OrderByDescending(s => s.cnt)
.Take(5);
var topNamedUsers = topUsers.Join(_context.Users, ws => ws.Key, ud => ud.UsrId, (ws, ud) => new { ws.Key, ud.FirstName, ud.LastName, ws.cnt });

Linq to Sql distinct with left join, Lambda

I'm trying to rewrite the query below into a Linq statement.
The query should return a List of "User" objects containing the four properties (Id, FirstName, LastName, Email).
SELECT DISTINCT Id, FirstName, LastName, Email FROM User
LEFT JOIN Booklet ON Booklet.UserId = User.Id
WHERE Booklet.YearReleased = #YearOfRelease
ORDER BY LastName ASC
Now I have something like this, but the Group by is incorrect and it all doesn't look very good coding...i feel like this can be optimized a lot into a single statement but I fail to do so. (demo year '18').
List<User> = new List<User>();
List<int?> lstUserIds = db.Booklet.GroupBy(x => x.UserId && x.YearReleased)
.Where(x => x.YearReleased == 18)
.OrderBy(x => x.LastName)
.Select(x => x.UserId)
.ToList();
foreach (int iUserId in lstUserIds )
lst.Add(db.User.First(x => x.Id == iUserId));
If you use Any() you don't need distinct nor left join to booklet.
var users = db.User
.Where(u => db.Booklet.Any(b => u.Id == b.UserId && b.YearReleased == 18))
.OrderBy(u => u.LastName);
Tr this:
List<User> lst = db.User.Include(B => B.Booklet)
.Where(B => B.YearReleased == 18)
.OrderBy(x => x.LastName)
.Distinct()
.ToList();
I would use a join to get Left Outer Join :
List<int?> lstUserIds = (from b in db.Booklet.Where(x => x.YearReleased == 18)
join on u in User on b.Id equal u.Id into ids
from id in ids.DEfaultIfEmpty
select new { user = u, books = b})
.OrderBy(x => x.user.LastName)
.GroupBy(x -> x.user.UserId)
.Select(x => x.FirstOrDefault())
.ToList();

Having problems with a Lambda LINQ Query

I'm trying to convert the following SQL expression into a Lambda LINQ query and I seem to be going round in circles at the moment:
select m.MemberExternalPK FROM Member.Member AS m INNER JOIN Member.Account AS a ON m.MemberID = a.MemberID where m.MemberExternalPK in
(
SELECT m.MemberExternalPK
FROM Member.Member AS m INNER JOIN Member.Account AS a ON m.MemberID = a.MemberID
group by MemberExternalPK
having Count(AccountID) = 1
)
and AccountStatusID = 3
So far I have managed to get the following syntax that returns the correct number of rows I am after but all columns (except the MemberExternalPK one I want)!
Members.Join(Accounts, m => m.MemberID, a => a.MemberID, (m, a) => new { m, a })
.GroupBy(t => t.m.MemberExternalPK, t => t.a)
.Where(grp => grp.Count(p => p.AccountID != null) == 1)
.SelectMany(sublist => sublist).Where(x => x.AccountStatusID == 3)
I think this is fairly close:
var query =
from m in Member_Member
join a in Member_Account on m.MemberID equals a.MemberID
group a by m.MemberExternalPK into gas
where gas.Count(ga => ga.AccountID != null) == 1
from ga in gas
where ga.AccountStatusID == 3
select gas.Key;
The only concern is the ga.AccountID != null which means that the gas group may have more than one record so you might end up with more than one gas.Key at the end.
Something like this? Splitting it up could also improve performance.
var externalMembers =
Members.Join(Accounts, m => m.MemberID, a => a.MemberID, (m, a) => new { m, a })
.GroupBy(grp => grp.MemberExternalPK)
.Where(grp => grp.Count() > 1)
.Select(grp => grp.Key);
var result =
Members.Where(w => externalMembers.Contains(w.MemberExternalPK) && w.AccountStatusID == 3)
.Select(s => s.MemberExternalPK)

Entity Framework with Linq, inner Join, Group By, Order By

I have a SQL Query
select Firma.Name as companyName,
Taetigkeit.Taetigkeit as skillName,
SUM(Zeit) as time
from Zeiterfassung
inner join Firma On ZEiterfassung.FirmenID = Firma.ID
inner join Taetigkeit on Zeiterfassung.TaetigkeitID = Taetigkeit.ID
group by Taetigkeit, Firma.Name
order by Firma.Name
And want to "translate" it to linq. Here is what I tried:
var query = db.Zeiterfassung
.Where(x => x.Firma.ID == x.FirmenID && x.TaetigkeitID == x.Taetigkeit.ID)
.GroupBy(x => x.Taetigkeit.Taetigkeit1)
.Select(x => new Evaluation() { skillName = x.Key, time = x.Sum(y => y.Zeit), //skillName = x.Sum(x => x.Zeit), })
.OrderBy(x => x.skillName);
I dont know who to solve this with joins and the group by because all the time when i do a groupBy i cant access the other members.
From data you provided, I think query should look like
from z in db.Zeiterfassung
join f in db.Firma on z.FirmenID equals f.ID
join t in db.Taetigkeit on z.TaetigkeitID equals t.ID
select new { f.Name, t.Taetigkeit, z.Zeit) into x
group x by new { x.Taetigkeit, f.Name } into g
select new {
CompanyName = g.Key.Name,
SkillName = g.Key.Taetigkeit,
Time = g.Sum(i => i.Zeit)
}
Or with navigation properties:
db.Zeiterfassung
.Select(z => new { z.Zeit, z.Taetigkeit.Taetigkeit1, z.Firma.Name })
.GroupBy(x => new { x.Taetigkeit1, x.Name })
.Select(g => new Evaluation {
companyName = g.Key.Name,
skillName = g.Key.Taetigkeit1,
time = g.Sum(y => y.Zeit)
});

convert linq to lambda with multiple joins

I'm trying to convert this linq query to lambda
var query = (from a in context.Table_A
join u in context.Table_B on a.scored equals u.userid
join u2 in context.Table_B on a.scorer equals u2.userid
from cs in a.Table_C //(Table_A is related to Table_C)
where (a.date>= startdate && a.date < enddate)
select new MSViewModel
{
scored= u.User.name,
scorer= u2.User.name,
subject_name = cs.Subject.name,
score = cs.score,
categoryid = cs.id,
})
.AsEnumerable()
.GroupBy(t => t.scored)
.ToList();
so far this is what i have. I'm kinda lost what to do next.
var tobi = db.Table_A.Join(db.Table_B,a=>a.scored,u=>u.userid,
(a,u) => new {scored=u.User.name });
db.Table_A.Join(db.Table_B,a1=>a1.scorer,u2=>u2.userid,
(a,u2)=> new {scorer= u2.User.name});
Firstly, I'd agree with Arran's comment: the query expression is going to be much simpler to deal with. I'm absolutely on board with using the lambda form where it makes sense, but joins are generally much simpler in query expressions.
Having said that, you basically need to imitate transparent identifiers. The code below is untested, but looks vaguely plausible to me.
var query = context.Table_A
.Join(context.Table_B, a => a.scored, u => u.userid,
(a, u) => new { a, u })
.Join(context.Table_B, p => p.a.scorer, u2 => u2.userid,
(p, u2) => new { p, u2 })
.SelectMany(q => q.p.a.TableC, (q, cs) => new { q, cs })
.Where(r => r.q.p.a.date >= startdate && r.q.p.a.date < enddate)
.Select(q => new MSViewModel {
scored= r.q.p.u.User.name,
scorer= r.q.u2.User.name,
subject_name = r.cs.Subject.name,
score = r.cs.score,
categoryid = r.cs.id,
})
.AsEnumerable()
.GroupBy(t => t.scored)
.ToList();
Basically p and q and r are the transparent identifiers here. You've got three of them because you've got two joins and a subsequent from clause.
To expand on my comment, If you have Resharper:
Resharper showing option to convert LINQ to Lambada
After the conversion:
Try this one:
var query = (_context.Table_A.Join(_context.Table_B, a => a.scored, u =>
u.userid,
(a, u) => new {a, u}).Join(_context.Table_B,
#t => #t.a.scorer,
u2 => u2.userid,
(#t, u2) => new {#t, u2}).
SelectMany(#t => _context.Table_A, (#t, cs) => new MSViewModel()
{
scored = #t.#t.u.User.name,
scorer= #t.u2.User.name,
subject_name = #t.cs.Subject.name,
score = #t.cs.score,
categoryid = #t.cs.id,
})).AsEnumerable()
.ToList();

Categories

Resources