I've got some legacy code where there is a lot of raw SQL statements which I try to port to LINQ.
One of them looks like this
select s.Id, s.Name, s.Enabled, s.Tags, s.Description, s.ImageUrl, s.ImageFullUrl, sss.Id as StoneSupplierStoneId
from Stone s, StoneSupplierStone sss
where s.Id = sss.Stone_id and s.Enabled = 1
and sss.Id IN (select MAX(sss.Id)
from Stone s, StoneSupplierStone sss
where sss.StoneSupplier_id = 6142
and s.Id = sss.Stone_id and s.Enabled = 1
group by s.Name, (case when sss.CustomStoneName is null then s.Name else sss.CustomStoneName end))
order by s.Name
which I managed to port to LINQ using something like this
var sssq = from s in Stone
from sss in StoneSupplierStone
where sss.StoneSupplier_id == 6142
&& s.Id == sss.Stone_id
&& s.Enabled == true
let res = new { s.Name, sssName = sss.CustomStoneName == null ? s.Name : sss.CustomStoneName, sss.Id }
group res by new { res.Name, res.sssName } into g
select new { sssId = g.Max(_ => _.Id) };
var q = from s in Stone
from sss in StoneSupplierStone
where s.Id == sss.Stone_id
&& s.Enabled == true
&& sssq.Any(_ => _.sssId == sss.Id)
orderby s.Name
select new { s.Id, s.Name, s.Enabled, s.Tags, s.Description, s.ImageUrl, s.ImageFullUrl, StoneSupplierStoneId = sss.Id };
while I do get the same results, the underlying generated query looks pretty weird
-- Region Parameters
DECLARE #p0 Int = 1
DECLARE #p1 Int = 6142
DECLARE #p2 Int = 1
-- EndRegion
SELECT [t0].[Id], [t0].[Name], [t0].[Enabled], [t0].[Tags], [t0].[Description], [t0].[ImageUrl], [t0].[ImageFullUrl], [t1].[Id] AS [StoneSupplierStoneId]
FROM [Stone] AS [t0], [StoneSupplierStone] AS [t1]
WHERE (([t0].[Id]) = [t1].[Stone_id]) AND ([t0].[Enabled] = #p0) AND (EXISTS(
SELECT NULL AS [EMPTY]
FROM (
SELECT MAX([t4].[Id2]) AS [value]
FROM (
SELECT [t2].[Id], [t2].[Name], [t2].[Enabled], [t3].[Id] AS [Id2], [t3].[StoneSupplier_id], [t3].[Stone_id],
(CASE
WHEN [t3].[CustomStoneName] IS NULL THEN [t2].[Name]
ELSE CONVERT(NVarChar(256),[t3].[CustomStoneName])
END) AS [value]
FROM [Stone] AS [t2], [StoneSupplierStone] AS [t3]
) AS [t4]
WHERE ([t4].[StoneSupplier_id] = #p1) AND (([t4].[Id]) = [t4].[Stone_id]) AND ([t4].[Enabled] = #p2)
GROUP BY [t4].[Name], [t4].[value]
) AS [t5]
WHERE [t5].[value] = [t1].[Id]
))
ORDER BY [t0].[Name]
is there a way to rewrite my LINQ query to make it look more like the original SQL query?
P.S. I use LinqPad to test LINQ and generated query.
Related
Can anyone please guide me on how I can write the LINQ for below SQL query
DECLARE #now DATETIME = dbo.GetInstanceDate(NULL);
select *
FROM [dbo].[creatives] AS [t0]
INNER JOIN [dbo].[contracts] AS [t1] ON [t0].[campaign_id] = [t1].[campaign_id]
LEFT OUTER JOIN [dbo].[vouchers] AS [t2] ON ([t1].[contract_id] = ([t2].[contract_id])) AND t0.creative_id = t2.creative_id
AND ([t2].[contract_id] = 29980)
AND (NOT ([t2].[removed] = 1))
AND ([t2].[active] = 1)
AND ((NOT ([t2].[start_date] IS NOT NULL)) OR (([t2].[start_date]) <= #now)) AND ((NOT ([t2].[expiration_date] IS NOT NULL)) OR (([t2].[expiration_date]) > #now))
AND (NOT ([t2].[creative_id] IS NOT NULL))
where [t1].contract_id = 29980
If you are looking for how to convert LEFT JOIN with complex filter there is simple way:
var query =
from t in context.Table
from o in context.OtherTable
.Where(o => o.id == t.Id && (o.Some == t.Some || o.Another == t.Another))
.DefaultIfEmpty()
select new { t, o };
I would like to convert following sql query into Linq to SQL
select distinct r.CompanyLogo, j.JobName, j.JobId, ur.UserId, r.JobSeekerID
, ur.FirstName, j.JobType, j.JobCareerLevel, j.JobLocation
from UserInterest m
join job j on m.FunctionalId = j.FunctionId or m.Careerlevel = j.CarrerLevelId or m.SalId = j.SalaryRangeId
join UserRegistration ur on j.UserId = ur.UserId
join EmplrRegistration r on j.UserId = r.JobSeekerID
where m.Status = 1 and m.UserId = 1
going through this I have so far tried following which didn't work out
var list = (from m in entities.UserInterests
from j in entities.Jobs
where m.FunctionalId == j.FunctionId || m.SalId == j.SalaryRangeId || m.Careerlevel == j.CarrerLevelId
&& m.Status == true && m.UserId == 1
join ur in entities.UserRegistrations on m.UserId equals ur.UserId
join r in entities.EmplrRegistrations on m.UserId equals r.JobSeekerID
select new { r.CompanyLogo, j.JobName, j.JobId, ur.UserId, r.JobSeekerID
, ur.FirstName, j.JobType, j.JobCareerLevel, j.JobLocation }).Distinct().ToList();
Edit:
following query is being generated against Svyatoslav Danyliv answer which is returning 7 rows instead of 6
SELECT 1 AS [C1], [Extent4].[CompanyLogo] AS [CompanyLogo], [Extent2].[JobName] AS [JobName]
,[Extent2].[JobId] AS [JobId], [Extent3].[UserId] AS [UserId]
,[Extent4].[JobSeekerID] AS [JobSeekerID], [Extent3].[FirstName] AS [FirstName]
,[Extent2].[JobType] AS [JobType], [Extent2].[JobCareerLevel] AS [JobCareerLevel]
,[Extent2].[JobLocation] AS [JobLocation]
FROM [dbo].[UserInterest] AS [Extent1]
INNER JOIN [dbo].[Job] AS [Extent2] ON ([Extent1].[FunctionalId] = [Extent2].[FunctionId])
OR (([Extent1].[FunctionalId] IS NULL) AND ([Extent2].[FunctionId] IS NULL))
OR ([Extent1].[Careerlevel] = [Extent2].[CarrerLevelId])
OR (([Extent1].[Careerlevel] IS NULL) AND ([Extent2].[CarrerLevelId] IS NULL))
OR ([Extent1].[SalId] = [Extent2].[SalaryRangeId])
OR (([Extent1].[SalId] IS NULL)
AND ([Extent2].[SalaryRangeId] IS NULL))
INNER JOIN [dbo].[UserRegistration] AS [Extent3] ON [Extent2].[UserId] = [Extent3].[UserId]
INNER JOIN [dbo].[EmplrRegistration] AS [Extent4] ON [Extent2].[UserId] = [Extent4].[JobSeekerID]
WHERE (1 = [Extent1].[Status]) AND (1 = [Extent1].[UserId])
Join which contains not just AND expressions is possible via from x in entities.Where(x => ..). You have did that partially and made mistake in where condition.
Corrected query, looks the same as original SQL
var query =
from m in entities.UserInterests
from j in entities.Jobs.Where(j =>
m.FunctionalId != null && m.FunctionalId == j.FunctionId ||
m.Careerlevel != null && m.Careerlevel == j.CarrerLevelId ||
m.SalId != null && m.SalId == j.SalaryRangeId)
join ur in entities.UserRegistrations on j.UserId equals ur.UserId
join r in entities.EmplrRegistrations on j.UserId equals r.JobSeekerID
where m.Status == true && m.UserId == 1
select new { r.CompanyLogo, j.JobName, j.JobId, ur.UserId, r.JobSeekerID
, ur.FirstName, j.JobType, j.JobCareerLevel, j.JobLocation };
var list = query.Distinct().ToList();
I try to convert T-SQL to lambda expression but I met a problem. Data is not correct.
This is my query
SELECT A.*
FROM
(SELECT UserId, MIN(ID) AS ID
FROM FingerMachineUsers
GROUP BY UserId ) A
LEFT OUTER JOIN
FingerTimeSheets B ON A.ID = B.UserNo
AND B.DayOfCheck = '2018-08-02 00:00:00.000'
WHERE
B.UserNo IS NULL
This is my lambda expression
dbContext.FingerMachineUsers
.GroupBy(x => x.UserId)
.Select(g => new { ID = g.Min(p => p.ID), UserId=g.Select(p => p.UserId) })
.GroupJoin(dbContext.FingerTimeSheets.Where(x=>x.DayOfCheck==shortDate),x=>x.ID,y=>y.UserNo,(x,y)=> new { ID = x, UserNo = y })
.SelectMany(x=>x.UserNo.DefaultIfEmpty(),(x,y)=>new { x.ID,y.UserNo});
Data returned is not correct.
The linq query should be:
DateTime date = DateTime.Today;
var innerQuery = from x in db.FingerMachineUsers
group x.UserId by x.UserId into y
select new { UserId = y.Key, ID = y.Min() };
var query = from x in innerQuery
join y in db.FingerTimeSheets on x.ID equals y.UserNo into z
from y in z.Where(a => a.DayOfCheck == date).DefaultIfEmpty()
where y == null || y.UserNo == null
select x;
The query is more or less equivalent to:
SELECT
[GroupBy1].[K1] AS [UserId],
[GroupBy1].[A1] AS [C1]
FROM (SELECT
[Extent1].[UserId] AS [K1],
MIN([Extent1].[UserId]) AS [A1]
FROM [dbo].[FingerMachineUsers] AS [Extent1]
GROUP BY [Extent1].[UserId] ) AS [GroupBy1]
LEFT OUTER JOIN [dbo].[FingerTimeSheets] AS [Extent2] ON (([GroupBy1].[A1] = [Extent2].[UserNo]) OR (([GroupBy1].[A1] IS NULL) AND ([Extent2].[UserNo] IS NULL))) AND ([Extent2].[DayOfCheck] = #p__linq__0)
WHERE [Extent2].[TimeSheetId] IS NULL OR [Extent2].[UserNo] IS NULL
(where TimeSheetId is the primary key of FingerTimeSheets)
There are some open points about the nullability of FingerTimeSheets.UserNo and about the meaning of B.UserNo IS NULL. Is FingerTimeSheets.UserNo nullable? Then the query is correct as is. If FingerTimeSheets.UserNo isn't nullable then change the where to:
where y == null
Another small problem that needs fixing is if both FingerMachineUsers.ID is nullable and FingerTimeSheets.UserNo is nullable. Change the moddile from y in z.Where() to:
from y in z.Where(a => UserNo != null && a.DayOfCheck == date).DefaultIfEmpty()
So basically, I try here to transfer this query in LINQ.
DECLARE #p1 UniqueIdentifier SET #p1 = 'AC1D85C1-28F1-46A3-9C6A-3B7446609A2A'
DECLARE #p2 UniqueIdentifier SET #p2 = NEWID()
SELECT
[MTD].[Description],
[MTD].[MessageTypeID],
ISNULL([AMT].[ApplicationMessageTypeID], NEWID()),
ISNULL([AMT].[EventForwardingRuleID], '1001')
FROM [dbo].[MessageType] as [MT]
INNER JOIN [dbo].[MessageTypeDescription] AS [MTD]
ON [MT].[MessageTypeID] = [MTD].[MessageTypeID]
LEFT OUTER JOIN [dbo].[ApplicationMessageType] AS [AMT]
ON [AMT].[MessageTypeID] = [MT].[MessageTypeID]
AND ( [AMT].[ApplicationID] = #p1 OR [AMT].[ApplicationID] IS NULL )
WHERE [MTD].[Culture] = 'fr'
I know for the most part that the query should look like something like that:
(from mt in db.MessageTypes
join mtd in db.MessageTypeDescriptions
on mt.MessageTypeID equals mtd.MessageTypeID
join amt in db.ApplicationMessageTypes
on new { mt.MessageTypeID, (applicationId || null) } equals new { amt.MessageTypeID, amt.ApplicationID }
into appMessageTypes
from amt in appMessageTypes.DefaultIfEmpty()
where mtd.Culture == culture
select new ApplicationEditEventTypeModel
{
ApplicationMessageTypeID = amt.ApplicationMessageTypeID == null ? Guid.NewGuid() : amt.ApplicationMessageTypeID,
Description = mtd.Description,
MessageTypeID = mtd.MessageTypeID,
EventForwardingRuleID = amt.EventForwardingRuleID == null ? 0 : amt.EventForwardingRuleID
});
The part here where I'm really not sure is the "ApplicationMessageTypes" part. For a multiple left join query I'd use the new {} equals new {} construct but in this case, I have 2 clauses ( [AMT].[ApplicationID] = #p1 OR [AMT].[ApplicationID] IS NULL ).
Should I use something like new { mt.MessageTypeID, new { applicationId ,null }} equals new { amt.MessageTypeID, amt.ApplicationID }? This seems too strange to be real.
(from mt in db.MessageTypes
join mtd in db.MessageTypeDescriptions
on mt.MessageTypeID equals mtd.MessageTypeID
from amt in db.ApplicationMessageTypes
.Where(a => a.MessageTypeID == mt.MessageTypeID &&
(a.ApplicationID == applicationId || !a.ApplicationID.HasValue)).DefaultIfEmpty()
where mtd.Culture == culture
select new ApplicationEditEventTypeModel
{
ApplicationMessageTypeID = amt.ApplicationMessageTypeID ?? Guid.NewGuid(),
Description = mtd.Description,
MessageTypeID = mtd.MessageTypeID,
EventForwardingRuleID = amt.EventForwardingRuleID ?? 0
});
I think the ApplicationId clause doesn't really look like its part of the JOIN - i.e. it's not really part of the foreign key relationship - instead it's really just a normal WHERE condition.
So I'd recommend moving the ApplicationId out to WHERE in both the SQL and in the LINQ
DECLARE #p1 UniqueIdentifier SET #p1 = 'AC1D85C1-28F1-46A3-9C6A-3B7446609A2A'
DECLARE #p2 UniqueIdentifier SET #p2 = NEWID()
SELECT
[MTD].[Description],
[MTD].[MessageTypeID],
ISNULL([AMT].[ApplicationMessageTypeID], NEWID()),
ISNULL([AMT].[EventForwardingRuleID], '1001')
FROM [dbo].[MessageType] as [MT]
INNER JOIN [dbo].[MessageTypeDescription] AS [MTD]
ON [MT].[MessageTypeID] = [MTD].[MessageTypeID]
LEFT OUTER JOIN [dbo].[ApplicationMessageType] AS [AMT]
ON [AMT].[MessageTypeID] = [MT].[MessageTypeID]
WHERE [MTD].[Culture] = 'fr'
AND (AMT IS NULL OR ([AMT].[ApplicationID] = #p1 OR [AMT].[ApplicationID] IS NULL ))
and
(from mt in db.MessageTypes
join mtd in db.MessageTypeDescriptions
on mt.MessageTypeID equals mtd.MessageTypeID
join amt in db.ApplicationMessageTypes
on new mt.MessageTypeID equals amt.MessageTypeID
into appMessageTypes
from amt in appMessageTypes.DefaultIfEmpty()
where mtd.Culture == culture
&& amt==null || (amt.ApplicationID == null || amt.ApplicationID == applicationId)
select new ApplicationEditEventTypeModel
{
ApplicationMessageTypeID = amt.ApplicationMessageTypeID == null ? Guid.NewGuid() : amt.ApplicationMessageTypeID,
Description = mtd.Description,
MessageTypeID = mtd.MessageTypeID,
EventForwardingRuleID = amt.EventForwardingRuleID == null ? 0 : amt.EventForwardingRuleID
});
You could probably also use a nested select (or a view) if you wanted to keep the ApplicationId clause closer to the original ApplicationMessageType table
I have a stored procedure that creates a field bases on a CASE value. How do I do the same in LINQ? Any ideas?
Basically this is the old stored procedure (truncated for ease)
SELECT M.Period AS 'Period' ,
C.Code AS 'Group' ,
C.ClientCode AS 'Code' ,
C.ClientName AS 'Name' ,
CASE WHEN ( SELECT SUM(Amount) AS Expr1
FROM M
WHERE ( ClientCode = C.ClientCode )
GROUP BY ClientCode
) = 0 THEN 'Balanced'
WHEN ( SELECT SUM(Amount) AS Expr1
FROM M
WHERE ( ClientCode = C.ClientCode )
GROUP BY ClientCode
) > 0 THEN 'Pending'
END AS 'Status' ,
As you can see from above the case picks a value like so
CASE WHEN ( SELECT SUM(Amount) AS Expr1
FROM M
WHERE ( ClientCode = C.ClientCode )
GROUP BY ClientCode
) = 0 THEN 'Balanced'
WHEN ( SELECT SUM(Amount) AS Expr1
FROM M
WHERE ( ClientCode = C.ClientCode )
GROUP BY ClientCode
) > 0 THEN 'Pending'
END AS 'Status' ,
So I have done all my joins and I have this so far and it works.
var test = from c in C join h in H on c.Code
equals h.Code join m in M on c.ClientCode
equals m.ClientCode
select new
{
Period=m.Period,
Group=c.Code,
Code= c.ClientCode,
Name= c.ClientName,
<-- Here is where I need the to display the correct case value-->
};
I would appreciate any feedback or help.
Try this one out
var test = from c in db.C
select new {
Period = c.M.Period,
Group = c.Code,
Code = c.ClientCode,
Name = c.ClientName,
Status =
((from m0 in db.M
where
m0.ClientCode == c.ClientCode
group m0 by new {
m0.ClientCode
} into g
select new {
SumOfAmount = (System.Int32)g.Sum(p => p.Amount)
}).First().SumOfAmount) == 0 ? "Balanced" :
((from m0 in db.M
where
m0.ClientCode == c.ClientCode
group m0 by new {
m0.ClientCode
} into g
select new {
SumOfAmount = (System.Int32)g.Sum(p => p.Amount)
}).First().SumOfAmount) > 0 ? "Pending" : null
}
If I understand the question correctly then something like below should work for your needs.
var test = from c in C join h in H on c.Code
equals h.Code join m in M on c.ClientCode
equals m.ClientCode
select new
{
Period=m.Period,
Group=c.Code,
Code= c.ClientCode,
Name= c.ClientName,
Status = M.Where(x => x.ClientCode == c.ClientCode).Sum(x => x.Amount) > 0 ? "Pending" : "Balanced"
};
Something like this?
var test = from c in C join h in H on c.Code
equals h.Code join m in M on c.ClientCode
equals m.ClientCode
select new
{
Period=m.Period,
Group=c.Code,
Code= c.ClientCode,
Name= c.ClientName,
CaseValue = c.Where(x => x.ClientCode == c.ClientCode)
.Sum(x => x.Amount) == 0
? "Balanced" : "Pending"
};
You might need to adjust the sum field (i don't know which table it's stored), and convert the conditional operator to maybe an extension method for cleanliness, but it should get you on the right track.