I wrote this SQL request with multiple JOIN
(including a LEFT JOIN
).
It gives me the expected result.
SELECT DISTINCT c.Id,
c.Title,
COUNT(v.Id) AS 'Nb_V2',
COUNT(DISTINCT v.IdUser) AS 'Nb_V1',
r.cnt AS 'Nb_R'
FROM TABLE_C c
JOIN TABLE_V v on c.Id = v.Id
LEFT JOIN (
SELECT Id, COUNT(*) AS cnt
FROM TABLE_R
GROUP BY Id
) r ON c.Id = r.Id
WHERE c.IdUser = '1234'
GROUP BY c.Id, c.Title, r.cnt
However, 'Id like the Linq equivalent of this request, to put it my application's Data Access layer.
I tried something like :
var qResult = from c in dbContext.TABLE_C
join v in dbContext.TABLE_V on c.IdC equals v.IdC
join r in dbContext.TABLE_R on v.IdC equals r.IdC into temp
from x in temp.DefaultIfEmpty()
group x by new { c.IdC, c.Title /*miss something ?*/} into grouped
select new
{
IdC = grouped.Key.IdC, --good result
Title = grouped.Key.Title, --good result
NbR = grouped.Distinct().Count(t => t.IdC > 0), --good, but "t.Id > 0" seems weird
Count = --I'm lost. No idea how to get my COUNT(...) properties (Nb_V1 and Nb_V2)
};
I tried to adapt this SO question but I can't figure it out. I'm lost with the Count
inside the groupped sub-request.
Can anyone explain me where i'm wrong ?
Pro tip : Bonus point if someone can write the equivalent with a lambda expression
For translating SQL to LINQ query comprehension:
DISTINCT
,TOP
,MIN
,MAX
etc) into functions applied to the whole LINQ query.new {
...}
) for multiple columns (e.g. ingroupby
).First().field
to get non-key values from thegroupby
aggregate range variable (e.g. as with MySQL).JOIN
conditions that aren't all equality tests withAND
must be handled usingwhere
clauses outside the join, or with cross product (from
...from
...) and thenwhere
. If you are doingLEFT JOIN
, add a lambdaWhere
clause between the join range variable and theDefaultIfEmpty()
call.JOIN
conditions that are multipleAND
ed equality tests between the two tables should be translated into anonymous objectsLEFT JOIN
is simulated by usinginto
joinvariable and doing another fromfrom
the joinvariable followed by.DefaultIfEmpty()
.COALESCE
with the conditional operator (?:
)and anull
test.IN
to.Contains()
andNOT IN
to!
...Contains()
, using literal arrays or array variables for constant lists.BETWEEN
lowAND
high to low<=
x&&
x<=
high.CASE
to the ternary conditional operator?:
.SELECT *
must be replaced with select range_variable or for joins, an anonymous object containing all the range variables.SELECT
fields must be replaced withselect new {
...}
creating an anonymous object with all the desired fields or expressions.IIF
to the C# ternary conditional operator.FULL OUTER JOIN
must be handled with an extension method.Applying these rules to your SQL query, you get:
The lambda translation is tricky, but the conversion of
LEFT JOIN
toGroupJoin
...SelectMany
is what is needed: