Linq To Sql Nested Select - c#

This is the SQL query I am trying to write with LINQ:
SELECT pd.Description, pd.Name, pd.SKU, pt.FileName,
(SELECT ISNULL(M1.parent, '') + '/' + M2.parent + '/' + M2.slug
FROM seo AS M1
JOIN seo AS M2
ON M1.Slug = M2.Parent
WHERE M2.SeoId = pd.seoId) as Url
FROM SEO seo
INNER JOIN Products p on seo.SeoID = p.SeoID
INNER JOIN RelatedProducts rp on p.ProductID = rp.ProductID
INNER JOIN Products pd on rp.RelatedID = pd.ProductID
INNER JOIN ProductPhotos pp on pd.ProductID = pp.ProductID
INNER JOIN Photos pt on pp.PhotoID = pt.PhotoID
WHERE seo.slug = 'SlugValue'
This is the LINQ I have:
_database.SEODataSource
.Join(_database.ProductDataSource, seo => seo.SeoID, p => p.SeoID, (seo, p) => new { seo, p })
.Join(_database.RelatedProductDataSource, #t => #t.p.ProductID, rp => rp.ProductID, (#t, rp) => new { #t, rp })
.Join(_database.ProductDataSource, #t => #t.rp.RelatedID, pd => pd.ProductID, (#t, pd) => new { #t, pd })
.Join(_database.ProductPhotoDataSource, #t => #t.pd.ProductID, pp => pp.ProductID, (#t, pp) => new { #t, pp })
.Join(_database.PhotoDataSource, #t => #t.pp.PhotoID, pds => pds.PhotoID, (#t, pds) => new { #t, pds })
.Where(#t => #t.#t.#t.#t.#t.seo.Slug == slug)
.Select(#t => new ProductLinkDTO
{
ProductDesc = #t.#t.#t.pd.Description,
ProductName = #t.#t.#t.pd.Name,
ProductSku = #t.#t.#t.pd.SKU,
ProductImageName = #t.pds.FileName,
ProductUrl = (_database.SEODataSource.Join(_database.SEODataSource, seo1 => seo1.SeoID, seo2 => seo2.SeoID, (seo1, seo2) => new {seo1, seo2})
.Where(#t1 => #t1.seo2.SeoID == #t.#t.#t.#t.#t.seo.SeoID)
.Select(#t1 => #t1.seo1.Parent ?? "" + "/" + #t1.seo2.Parent + "/" + #t1.seo2.Slug)).FirstOrDefault()
}).ToList());
This is the query it produces:
SELECT [t3].[Name] AS [ProductName], [t3].[Description] AS [ProductDesc], [t3].[SKU] AS [ProductSku], [t5].[FileName]
AS [ProductImageName], (
SELECT TOP (1) [t8].[value]
FROM (
SELECT COALESCE([t6].[Parent],((#p1 + [t7].[Parent]) + #p2) + [t7].[Slug]) AS [value], [t7].[SeoID]
FROM [dbo].[SEO] AS [t6]
INNER JOIN [dbo].[SEO] AS [t7] ON [t6].[SeoID] = [t7].[SeoID]
) AS [t8]
WHERE [t8].[SeoID] = [t0].[SeoID]
) AS [ProductUrl]
FROM [dbo].[SEO] AS [t0]
INNER JOIN [dbo].[Products] AS [t1] ON ([t0].[SeoID]) = [t1].[SeoID]
INNER JOIN [dbo].[RelatedProducts] AS [t2] ON [t1].[ProductID] = [t2].[ProductID]
INNER JOIN [dbo].[Products] AS [t3] ON [t2].[RelatedID] = [t3].[ProductID]
INNER JOIN [dbo].[ProductPhotos] AS [t4] ON [t3].[ProductID] = [t4].[ProductID]
INNER JOIN [dbo].[Photos] AS [t5] ON [t4].[PhotoID] = [t5].[PhotoID]
WHERE [t0].[Slug] = #p0',N'#p0 nvarchar(4000),#p1 nvarchar(4000),#p2
nvarchar(4000)',#p0=N'KBUIT4255E-42in-Masterpiece-Side-By-Side',#p1=N'/',#p2=N'/'
Any idea what I am doing wrong?

By changing this:
.Select(#t1 => #t1.seo1.Parent ?? "" + "/" + #t1.seo2.Parent + "/" + #t1.seo2.Slug)).FirstOrDefault()
To:
.Select(#t1 => String.Concat(#t1.seo1.Parent ?? "", "/", #t1.seo2.Parent, "/", #t1.seo2.Slug))).SingleOrDefault()
The correct SQL was generated.

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();

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

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();

Converting SQL query into LINQ - not working

The SQL query I'm trying to convert is:
select p.PhoneNumber_Id, p.State, p.Created
from PhoneNumberServiceItems p
join PhoneNumbers on p.PhoneNumber_Id = PhoneNumbers.Id
inner join (
select PhoneNumber_Id, max(Created) as MaxDate
from PhoneNumberServiceItems
group by PhoneNumber_Id
) tm on p.PhoneNumber_Id = tm.PhoneNumber_Id and p.Created = tm.MaxDate
where PhoneNumbers.NumberRangeId = {Id}
And the LINQ code I've ended up with is below, however this isn't working:
var res =
from serviceItems in _db.PhoneNumberServiceItems
join nums in _db.PhoneNumbers on serviceItems.PhoneNumber_Id equals nums.Id
where nums.NumberRangeId == id
join serviceGroup in (from ps in _db.PhoneNumberServiceItems
group ps by ps.PhoneNumber_Id into numGroup
//join tm in _db.PhoneNumbers on psg.FirstOrDefault().PhoneNumber_Id equals tm.Id
select new
{
NumId = numGroup.FirstOrDefault().PhoneNumber_Id,
MaxDate = numGroup.Max(i => i.Created)
}) on new { PNId = serviceItems.PhoneNumber_Id, serviceCreated = serviceItems.Created } equals new { PNId = serviceGroup.NumId, serviceCreated = serviceGroup.MaxDate }
select new
{
State = serviceItems.State,
NumId = serviceGroup.NumId,
Created = serviceGroup.MaxDate
};
I'm aware my LINQ is wrong, but I can't put my finger on what it is I'm doing differently. Any help would be appreciated.
Edit: This is the compiled SQL generated from the LINQ
SELECT
[Extent1].[State] AS [State],
[Project4].[C1] AS [C1],
[Project4].[C2] AS [C2]
FROM [dbo].[PhoneNumberServiceItems] AS [Extent1]
INNER JOIN [dbo].[PhoneNumbers] AS [Extent2] ON [Extent1].[PhoneNumber_Id] = [Extent2].[Id]
INNER JOIN (SELECT
[Project3].[C1] AS [C1],
(SELECT
MAX([Extent5].[Created]) AS [A1]
FROM [dbo].[PhoneNumberServiceItems] AS [Extent5]
WHERE [Project3].[PhoneNumber_Id] = [Extent5].[PhoneNumber_Id]) AS [C2]
FROM ( SELECT
[Distinct1].[PhoneNumber_Id] AS [PhoneNumber_Id],
(SELECT TOP (1)
[Extent4].[PhoneNumber_Id] AS [PhoneNumber_Id]
FROM [dbo].[PhoneNumberServiceItems] AS [Extent4]
WHERE [Distinct1].[PhoneNumber_Id] = [Extent4].[PhoneNumber_Id]) AS [C1]
FROM ( SELECT DISTINCT
[Extent3].[PhoneNumber_Id] AS [PhoneNumber_Id]
FROM [dbo].[PhoneNumberServiceItems] AS [Extent3]
) AS [Distinct1]
) AS [Project3] ) AS [Project4] ON ([Extent1].[PhoneNumber_Id] = [Project4].[C1]) AND ([Extent1].[Created] = [Project4].[C2])
WHERE [Extent2].[NumberRangeId] = {id}
This is linq equivalent of your query.
var res = from s in PhoneNumberServiceItems
join p in PhoneNumbers on s.PhoneNumber_Id equals p.Id
join tm in ( from p1 in PhoneNumberServiceItems
group p1 by p1.PhoneNumber_Id into p_g
select new {PhoneNumber_Id = p_g.Key,MaxDate = p_g.Max(i=> i.Created) } )
on new {Created = s.Created, PhoneNumber_Id = s.PhoneNumber_Id}
equals new { Created = tm.MaxDate, PhoneNumber_Id = tm.PhoneNumber_Id}
where p.NumberRangeId == {Id}
select new
{
s.PhoneNumber_Id,
s.State,
s.Created
};
Try following which is much simpler :
var res = (from nums in _db.PhoneNumbers.Where(x => NumberRangeId == id)
join serviceItems in _db.PhoneNumberServiceItems on nums.PhoneNumber_Id equals serviceItems.Id
select new {serviceItems = serviceItems, nums = nums})
.OrderByDescending(x => x.serviceItems.Created)
.GroupBy(x => x.nums.PhoneNumber_Id)
.Select(x => x.First())
.Select(x => new {Id = x.nums.PhoneNumber_Id, state = x.serviceItems.State, maxDate = x.serviceItems.Created})
.ToList();
var recentPhoneNos= from psi in _db.PhoneNumberServiceItems
group psi by psi .PhoneNumber_Id into psiTemp
select new {
PhoneNumber_Id = psiTemp.Key,
MaxDate = psiTemp.Max(i=> i.Created)
};
var res=from serviceItems in _db.PhoneNumberServiceItems
join nums in _db.PhoneNumbers on serviceItems.PhoneNumber_Id equals nums.Id
join serviceGroup in recentPhoneNos on nums.Id equals serviceGroup .PhoneNumber_Id
where nums.NumberRangeId == id && serviceGroup.MaxDate
select new {
State = serviceItems.State,
NumId = serviceGroup.NumId,
Created = serviceGroup.MaxDate
} ;

sql query to c# using northwind DB

using the northwind DB, i have to make a query to get employeename, amount of orders per employee and average price of those orders
this is what the query looks like in SQL
SELECT TOP 10
a.LastName, a.FirstName, amountOfOrders = COUNT(DISTINCT b.OrderID), AveragePricePerOrder = SUM(c.Quantity*c.UnitPrice) /COUNT(DISTINCT b.OrderID)
FROM Employees a join orders b on (a.EmployeeID = b.EmployeeID)
join [Order Details] c on b.OrderID = c.OrderID
Group BY a.EmployeeID, a.LastName, a.FirstName
ORDER BY amountOfOrders Desc
this runs fine but I have to make this in c# and I am a little stuck
So far, I have got this
var query_rx = (from c in ctx.Employees
join or in ctx.Orders on c.EmployeeID equals or.EmployeeID
join ord in ctx.Order_Details on or.OrderID equals ord.OrderID
group c by new
{
c.EmployeeID,
c.LastName,
c.FirstName,
amount = c.Orders.Count
} into c
orderby c.Key.amount descending
select new
{
c.Key.LastName,
c.Key.FirstName,
amountOfOrders = c.Key.amount
}).Take(10);
"edit" I am having trouble working the average in, tried a lot of things but I can't get it to work
"edit" I have changed the query a bit with help from Dohnal's suggestion.
This looks almost exactly like what i want in terms of columns, except that the field lastname and firstname are blank, even with ToString
var query_rx = (from or in ctx.Order_Details
join ord in ctx.Orders on or.OrderID equals ord.OrderID
group or by new
{
ord.EmployeeID
} into c
orderby c.Select(x => x.OrderID).Distinct().Count() descending
select new
{
Lastname = (from emp in ctx.Employees
where c.Key.EmployeeID == emp.EmployeeID
select emp.LastName),
Firstname = (from emp in ctx.Employees
where c.Key.EmployeeID == emp.EmployeeID
select emp.FirstName),
c.Key.EmployeeID,
AmountOfOrders = c.Select(x => x.OrderID).Distinct().Count(),
AveragePricePerOrder = c.Sum(x => x.Quantity * x.UnitPrice) / c.Select(x => x.OrderID).Distinct().Count()
}).Take(10);
Try this query:
var query = (from emp in ctx.Employers
join order in ctx.Orders on emp.EmployeeID equals order.EmployerID
join orderDet in ctx.Order_Details on order.OrderID equals orderDet.OrderID
group new { emp, order, orderDet }
by new { emp.FirstName, emp.LastName, emp.EmployeeID, order.OrderID }
into orderGroup
let a = new
{
orderGroup.Key.EmployeeID,
orderGroup.Key.FirstName,
orderGroup.Key.LastName,
orderGroup.Key.OrderID,
sum1 = orderGroup.Sum(x => x.orderDet.Quantity * x.orderDet.UnitPrice),
}
group a by new { a.FirstName, a.LastName, a.EmployeeID } into empGroup
let a2 = new
{
empGroup.Key.FirstName,
empGroup.Key.LastName,
sum = empGroup.Sum(x => x.sum1),
count = empGroup.Count()
}
orderby a2.count descending
select new
{
a2.FirstName,
a2.LastName,
amountOfOrders = a2.count,
AveragePricePerOrder = a2.sum / a2.count
}).Take(10);

Categories

Resources