Is there a way to write this LINQ query using lambda expressions? - c#

I have a linq query that I use to get data from 3 tables using 2 joins. I would prefer to write this as a lambda expression so my team finds it more readable. Is there a way to do this though? I can't find any decent examples.
var q = (
from sus in susManager.Get()
join su in suManager.Get() on sus.SUId equals su.Id
join p in pManager.Get() on su.PId equals p.Id
where sus.EndTimeStamp >= oneDayAgo
select new
{
Name = p.FirstName + " " + p.LastName,
Email = su.Email,
LastLogIn = sus.StartTimeStamp,
LastSessionDurationInMinutes =
DbFunctions.DiffMinutes(sus.StartTimeStamp, sus.EndTimeStamp),
LastActive = sus.EndTimeStamp
}).ToList();

The lambda equivalent would look something like this, assuming that the Get() functions return Lists:
var q = systemUserSessionManager.Get()
.Where(sus => sus.EndTimeStamp >= oneDayAgo)
.Join(systemUserManager.Get(),
sus => sus.SystemUserId,
su => su.Id,
(sus, su) => new { sus, su })
.Join(personManager.Get(),
j => j.su.PersonId,
p => p.Id,
(j, p) => new { sus = j.sus, su = j.su, p })
.Select(x => new
{
Name = p.FirstName + " " + p.LastName,
Email = su.Email,
LastLogIn = sus.StartTimeStamp,
LastSessionDurationInMinutes =
DbFunctions.DiffMinutes(sus.StartTimeStamp, sus.EndTimeStamp),
LastActive = sus.EndTimeStamp
})
.ToList();

Try something like this
var q = (
from sus in systemUserSessionManager.Get()
join su in systemUserManager.Get() on sus.SystemUserId equals su.Id
join p in personManager.Get() on su.PersonId equals p.Id
select new { sus = sus, su = su, p = p})
.Where(x => x.sus.EndTimeStamp >= oneDayAgo)
.Select(x => new {
Name = x.p.FirstName + " " + x.p.LastName,
Email = x.su.Email,
LastLogIn = x.sus.StartTimeStamp,
LastSessionDurationInMinutes =
DbFunctions.DiffMinutes(x.sus.StartTimeStamp, x.sus.EndTimeStamp),
LastActive = x.sus.EndTimeStamp
}).ToList();

Related

LINQ to Entities after group by, COUNT and WHERE not working

I have the following LINQ-to-Entities query for MySQL DB
var data = (from agent in db.User
join agentrole in db.UserRole.DefaultIfEmpty() on agent.Id equals agentrole.UserId
join role in db.Role.DefaultIfEmpty() on agentrole.RoleId equals role.Id
join department in db.Department.DefaultIfEmpty() on role.DepartmentId equals department.Id
join client in db.Client.DefaultIfEmpty() on agent.Id equals client.AssignedUserId
join aggclient in db.AggClient.DefaultIfEmpty() on client.Id equals aggclient.ClientId
group new { agent, department, aggclient} by agent.Id into grp
select new
{
grp.Key,
agentName = grp.Max(a => a.agent.FirstName + " " + a.agent.LastName),
departmentNames = "",
newDepositorsCount = 0,
FTDSum = grp.Sum(a => a.aggclient.FirstDepositAmountEuro),
depcount =grp.Count(a => a.department != null),
aggclientfilter = grp.Where(a => a.aggclient != null && a.aggclient.FirstDepositAmount>0).Sum(a => a.aggclient.FirstDepositAmount)
});
On the current query, the last two operations are not working.
The entity cannot parse count and where operations.
change select clause to :
select new
{
grp.Key,
agentName = grp.agent.Max(a => a.FirstName + " " + a.LastName),
departmentNames = "",
newDepositorsCount = 0,
FTDSum = grp.aggclient.Sum(a => a.FirstDepositAmountEuro),
depcount = grp.department.Count(),
aggclientfilter = grp.aggclient.Where(a => a.FirstDepositAmount>0).Sum(a => a.FirstDepositAmount)
});
I assume that you do not use EF Core 5.x, because it supports filtered count.
Problem that there is no correct translation to SQL of such LINQ query. But there are tricks which can return needed result. Also corrected bad LEFT join.
var data =
from agent in db.User
join agentrole in db.UserRole on agent.Id equals agentrole.UserId into ga
from agentrole in ga.DefaultIfEmpty()
join role in db.Role on agentrole.RoleId equals role.Id into gr
from role in gr.DefaultIfEmpty()
join department in db.Department on role.DepartmentId equals department.Id into dg
from department in dg.DefaultIfEmpty()
join client in db.Client on agent.Id equals client.AssignedUserId
join aggclient in db.AggClient on client.Id equals aggclient.ClientId into acg
from aggclient in acg.DefaultIfEmpty()
group new { agent, department, aggclient} by agent.Id into grp
select new
{
grp.Key,
agentName = grp.Max(a => a.agent.FirstName + " " + a.agent.LastName),
departmentNames = "",
newDepositorsCount = 0,
FTDSum = grp.Sum(a => a.aggclient.FirstDepositAmountEuro),
depcount = grp.Sum(a => a.department != null ? 1 : 0),
aggclientfilter = grp.Sum(a => a.aggclient.FirstDepositAmount > 0 ? a.aggclient.FirstDepositAmount : 0)
};

How to USE LEFT JOIN, GROUP BY and SUM in LINQ and C#

I have 4 tables (Group, Student, Mark, StudentFile, and want to write my SQL query using LINQ in C#.
Here is my query
SELECT
S.Id,
S.FirstName,
S.LastName,
S.MiddleName,
SUM(M.CountOfPasses) CountOfPasses,
(SELECT SUM(CountOfPassesWithARespectful) FROM StudentFile WHERE StudentId = S.Id) WithARespectful
FROM Student S
LEFT JOIN Mark M ON S.Id = M.StudentId
GROUP BY S.Id, S.FirstName, S.LastName, S.MiddleName
I've already tried something like this:
var students = (from G in context.Group
let v = G.Id
from S in context.Student.Where(x => v == x.GroupId)
from F in context.StudentFile.Where(x => x.StudentId == S.Id).DefaultIfEmpty()
from M in context.Mark.Where(x => x.StudentId == S.Id).DefaultIfEmpty()
group new
{
F.CountOfPassesWithArespectful,
M.CountOfPasses,
S.Id
}
by new
{
S.Id,
S.FirstName,
S.LastName,
S.MiddleName,
S.StartCourse,
G.Name,
S.Alphagroup
} into GSF
select new DTOStudent
{
Id = GSF.Key.Id,
FirstName = GSF.Key.FirstName,
LastName = GSF.Key.LastName,
MiddleName = GSF.Key.MiddleName,
CountOfPasses = (int)GSF.Sum(p=>p.CountOfPasses),
WithRespectful = (int)GSF.Sum(x => x.CountOfPassesWithArespectful),
WithOutRespectful = (int)GSF.Sum(x => x.CountOfPasses) - (int)GSF.Sum(x => x.CountOfPassesWithArespectful),
Course = ClassMethods.GetAgeFromDates((DateTime)GSF.Key.StartCourse).ToString() +
ClassMethods.GetShortGroupName(GSF.Key.Name) + GSF.Key.Alphagroup.ToUpper()
}).Distinct().ToList();

How to write IQueryable join in Entity Framework 6

I do have this code but I want to do it in IQueryable:
var select = (from g in this.context.Grades
join sm in this.context.SubjectMaintenance on g.SubjectCode equals sm.SubjectCode
join st in this.context.Students on g.StudentId equals st.StudentId
where sm.TeacherId == sTeacherId
select new
{
SubjectCode = g.SubjectCode,
SubjectName = g.SubjectName,
StudentName = st.LastName + ", " + st.Firstname,
PrelimGrade = g.PrelimGrade,
MidtermGrade = g.PrelimGrade,
FinalGrade = g.PrelimGrade,
}).ToList();
How do I write it using IQueryable? My start code for IQueryable is this:
IQueryable<Grades> query = this.context.Grades;
IQueryable<SubjectMaintenance> query1 = this.context.SubjectMaintenance;
IQueryable<Students> query2 = this.context.Students;
Please do help as I need it in my project. Thank you in advance for your help.
You can follow the below logic to write your query.
var query1 =
var query2 =
var join = query1.Join(query2, x => x.ParentId, y => y.ParentId, (query1, query2) => new { query1, query2 })
Please follow this document also.

Attempted Left Join in Query Syntax and Lambda results in "The method or operation is not implemented."

I've attempted to write this query two different ways, and no matter how I write it, I can't get the join to pan out. It just keeps tossing "The method or operation is not implemented." at me. What I'm trying to do is, in SQL, simple: Obtain a list of items from table A (below - Customer) where there is no corresponding listing in table B (below - SalesReps).
Attempted Lamda:
var customers =
_sms.CurrentSession.Query<customer>()
.GroupJoin(_sms.CurrentSession.Query<salesReps>(),
c => c.Id,
sr => sr.CustomerId,
(x, y) => new {c = x, sr = y})
.SelectMany(xy => xy.sr.DefaultIfEmpty(),
(x, y) => new {c = x.c, sr = y})
.Where(data => data.sr.SalesRepId == null)
.Select(data => new CustomerDTO
{
Id = data.c.Id,
FullName = data.c.FirstName + " " + data.c.LastName,
FirstName = data.c.FirstName,
LastName = data.c.LastName
});
var custList = customers .ToList();
Attempted Query Syntax:
var customers = from c in _sms.CurrentSession.Query<customer>()
join sr in _sms.CurrentSession.Query<salesReps>()
on c.Id equals sr.CustomerId
into joinedData
from jd in joinedData.DefaultIfEmpty()
where c.IsEmployee == false
&& c.CustomerOptions.Company.Id == companyGuid
&& jd.SalesRepId == null
select
new CustomerDTO
{
Id = c.Id,
FullName = c.FirstName + " " + c.LastName,
FirstName = c.FirstName,
LastName = c.LastName
};
var custList = customers .ToList();
I get the impression that the issue is on the check for null sales reps, but I'm not sure.

C# linq group by on different keys in the same entity

I've been toying with this for a while and just can't get it. I'm new to Linq, C# and these Lambda things.
What I want to do is group entities according to two properties on each entity. It's a Message entity:
Message
{
int UserId; //The user generating the message
int UserIdTo; //The receiver of the message
|...| // Other stuff
}
So, I want it so that these UserId=5, UserIdTo=6 and UserId=6, UserIdTo=5 would be in the same group.
Here's my start:
var groupList = (from m in db.Messages
where m.UserId == userId || m.UserIdTo == userId
join u in db.Users on m.UserId equals u.UserId
join w in db.Users on m.UserIdTo equals w.UserId
orderby m.MessageTimestamp descending
select new DomMessage
{
MessageId = m.MessageId,
MessageContent = m.MessageContent,
MessageTimestamp = m.MessageTimestamp,
UserId = m.UserId,
UserIdTo = m.UserIdTo,
ScreenName = u.ScreenName,
ScreenName2 = w.ScreenName
}).GroupBy(m=>m.UserId == userId)
.ToList();
This does the first bit of grouping by UserId, but I'm stuck on trying to extend this so that where any UserId value in the resulting group equals the UserIdTo somewhere else add that to this group?
EDIT: I need the result to go to a List because there is other stuff I need to do with it...
Thanks!
Try this:
var payload = new[]
{
new{ To = 1, From = 2, Message = "msj1" },
new{ To = 1, From = 2, Message = "msj2" },
new{ To = 2, From = 1, Message = "msj3" },
new{ To = 4, From = 1, Message = "msj4" },
new{ To = 1, From = 3, Message = "msj5" }
};
var groupped = payload.Select(x => new { Key = Math.Min(x.To, x.From) + "_" + Math.Max(x.To, x.From), Envelope = x }).GroupBy(y => y.Key).ToList();
foreach (var item in groupped)
{
Console.WriteLine(String.Format(#"Group: {0}, messages:", item.Key));
foreach (var element in item)
{
Console.WriteLine(String.Format(#"From: {0} To: {1} Message: {2}", element.Envelope.From, element.Envelope.To, element.Envelope.Message));
}
}
Try the following GroupBy expression:
.GroupBy(m => Math.Min(m.UserId, m.UserIdTo) + ',' + Math.Max(m.UserId, m.UserIdTo))
I think this is the easiest way:
var groupList = from a in (
from m in db.Messages
where m.UserId == userId || m.UserIdTo == userId
join u in db.Users on m.UserId equals u.UserId
join w in db.Users on m.UserIdTo equals w.UserId
select new
{
MessageId = m.MessageId,
MessageContent = m.MessageContent,
MessageTimestamp = m.MessageTimestamp,
UserId = m.UserId,
UserIdTo = m.UserIdTo,
ScreenName = u.ScreenName,
ScreenName2 = w.ScreenName
})
group a by new {
UserId = a.UserId,
UserIdTo = a.UserIdTo
} into grp
orderby grp.Max(a => a.MessageTimestamp) descending
select new
{
UserId = grp.Key.UserId
UserIdTo = grp.Key.UserIdTo,
MessageId = grp.Max(a => a.MessageId),
MessageContent = grp.Max(a => a.MessageContent),
MessageTimestamp = grp.Max(a => a.MessageTimestamp),
ScreenName = grp.Max(a => a.ScreenName),
ScreenName2 = grp.Max(a => a.ScreenName2)
}
You have to tell it what to do with the fields you are not grouping by. In this case I got the MAX value for each.

Categories

Resources