LINQ Statement Doesn't Compile - linq

This statement won't compile:
query = from g in context.GridViews
join f in context.GridViewFavorites on g.ID equals f.GridViewID into gf
where g.GridTypeID == id && ( g.IsShared == true || g.RepID == me.clsRep.OID)
&& f.RepID == me.clsRep.OID
select g;
The compiler error is this (and it's underlining the last part of the where clause:
The name 'f' does not exist in the current context
It's logical SQL counterpart would be:
declare #RepID int
declare #GridTypeID int
select #RepID=15, #GridTypeID=5
select g.*,f.*
from
GridViews g
left outer join GridViewFavorites f on f.GridViewID = g.ID
where
g.GridTypeID = #GridTypeID and (g.IsShared = 1 or g.RepID == #RepID)
and f.RepID == #RepID
NOTE: per #hdv 's good catch the SQL sample should actually be:
select g.*,f.*
from
GridView g
left outer join GridViewFavorite f on f.GridViewID = g.ID and f.RepID = #RepID
where
g.GridTypeID = #GridTypeID and (g.IsShared = 1 or g.RepID = #RepID)

It's the "into" part of your join - once you've joined "into" a group, the join variable (f in this case) is out of scope - you've got to use gf instead. Alternatively, given that you're not actually using gf in your query at all, maybe you should just get rid of the into gf part entirely so it's a normal join instead of a group join.
However, that won't give you a left outer join. If you want a left outer join, you might want:
query = from g in context.GridViews
join f in context.GridViewFavorites on g.ID equals f.GridViewID into gf
from f2 in gf.DefaultIfEmpty()
where g.GridTypeID == id && (g.IsShared == true || g.RepID == me.clsRep.OID)
&& (f2 == null || f2.RepID == me.clsRep.OID)
select g;

The left-join pattern for LINQ goes like this:
join f in context.GridViewFavorites on g.ID equals f.GridViewID into gf
from f in gf.DefaultIfEmpty() //missing
More information is available on Stack Overflow and Google.

The where clause acts on the joined result, so the f variable is not in scope.

Related

Linq query - ON clause of inner join cannot compare two Guids

If the person you are searching is a CIA emplyee, take his CIAJobs.EmployerID, otherwise select People.ID
SELECT
case when CIAJobs.EmployeeID IS NULL then People.ID
else CIAJobs.EmployerID
end
FROM [FMO].[People] AS p
LEFT JOIN [FMO].[CIAJobs] j
ON (p.ID = j.[EmployeeID])
AND (j.[relationshipType] = '25a8d79d-377e-4108-8c92-0ef9a2e1ab63')
where p.ID = '1b66e032-94b2-e811-96e0-f48c508e38a2' // id of person you search for
OR
j.[EmployeeID] = '1b66e032-94b2-e811-96e0-f48c508e38a2' // id of person you search for
I tried doing this in Linq:
var a = from l in People
join x in CIAJobs
on l.Id equals x.EmployeeID && x.RelationshipTypeGuid equals Guid.Parse('25a8d79d-377e-4108-8c92-0ef9a2e1ab63')
into gcomplex
from xx in gcomplex.DefaultIfEmpty()
select (xx.EmployeeID == null) ? l.EmployeeId : x.EmployerID;
var b = a.ToList();
why does the query show an error because of this chunk: && x.RelationshipTypeGuid equals Guid.Parse('25a8d79d-377e-4108-8c92-0ef9a2e1ab63')
If I remove this part it shows no error.
Error is: operator && cannot be applied to operands of type Guid and Guid.
Can you help me correct the Linq query please logically and syntactically? Thank you.
You don't need join for multiple conditions in this scenario. Use this
var a = from l in People
join x in CIAJobs
.Where(z=>z.RelationshipTypeGuid
.Equals(Guid.Parse('25a8d79d-377e-4108-8c92-0ef9a2e1ab63')))
on l.Id equals x.EmployeeID
into gcomplex
from xx in gcomplex.DefaultIfEmpty()
select (xx.EmployeeID == null) ? l.EmployeeId : x.EmployerID;
var b = a.ToList();
But based on your problem statement this should do
var a = from l in People
join x in CIAJobs
on l.Id equals x.EmployeeID
into gcomplex
from xx in gcomplex.DefaultIfEmpty()
select (xx == null) ? l.EmployeeId : xx.EmployerID;
var b = a.ToList();

SQL to LINQ with JOIN and SubQuery

I have a query that I' struggling to convert to LINQ. I just can't get my head around the required nesting. Here's the query in SQL (just freehand typed):
SELECT V.* FROM V
INNER JOIN VE ON V.ID = VE.V_ID
WHERE VE.USER_ID != #USER_ID
AND V.MAX > (SELECT COUNT(ID) FROM VE
WHERE VE.V_ID = V.ID AND VE.STATUS = 'SELECTED')
The Closest I've come to is this:
var query = from vac in _database.Vacancies
join e in _database.VacancyEngagements
on vac.Id equals e.VacancyId into va
from v in va.DefaultIfEmpty()
where vac.MaxRecruiters > (from ve in _database.VacancyEngagements
where ve.VacancyId == v.Id && ve.Status == Enums.VacanyEngagementStatus.ENGAGED
select ve).Count()
...which correctly resolves the subquery from my SQL statement. But I want to further restrict the returned V rows to only those where the current user does not have a related VE row.
I've realised that the SQL in the question was misleading and whilst it led to technically correct answers, they weren't what I was after. That's my fault for not reviewing the SQL properly so I apologise to #Andy B and #Ivan Stoev for the misleading post. Here's the LINQ that solved the problem for me. As stated in the post I needed to show vacancy rows where no linked vacancyEngagement rows existed. The ! operator provides ability to specify this with a subquery.
var query = from vac in _database.Vacancies
where !_database.VacancyEngagements.Any(ve => (ve.VacancyId == vac.Id && ve.UserId == user.Id))
&& vac.MaxRecruiters > (from ve in _database.VacancyEngagements
where ve.VacancyId == vac.Id && ve.Status == Enums.VacanyEngagementStatus.ENGAGED
select ve).Count()
This should work:
var filterOutUser = <userId you want to filter out>;
var query = from vac in _database.Vacancies
join e in _database.VacancyEngagements
on vac.Id equals e.VacancyId
where (e.UserId != filterOutUser) && vac.MaxRecruiters > (from ve in _database.VacancyEngagements
where ve.VacancyId == vac.Id && ve.Status == Enums.VacanyEngagementStatus.ENGAGED
select ve).Count()
select vac;
I removed the join to VacancyEngagements but if you need columns from that table you can add it back in.

Join the result with new inner query sql

select A.UNIT, A.LEASE_ID, A.MONTHS_GUARANTEED, A.MONTHLY_PAYMENT_AM,
B.DATE_PAID, C.CHARGE_JOB from leasei A
left outer join eq_capture B on A.LEASE_ID = B.LEASE_ID
left outer join eq_mast C on A.UNIT = C.UNIT
where A.DATE_LEASE_EXPIRE = 0
ORDER BY A.LEASE_ID;
I want to use the result of the above query that is use the value C.CHARGE_JOB and the check with another table (job_infojc D) and get the D.STATE value with a where condition C.CHARGE_JOB = D.JOB
Any help is highly appreciated.
Like this:
select D.STATE, X.*
from job_infojc D
join (select A.UNIT, A.LEASE_ID, A.MONTHS_GUARANTEED, A.MONTHLY_PAYMENT_AM,
B.DATE_PAID, C.CHARGE_JOB from leasei A
left outer join eq_capture B on A.LEASE_ID = B.LEASE_ID
left outer join eq_mast C on A.UNIT = C.UNIT
where A.DATE_LEASE_EXPIRE = 0
) X on X.CHARGE_JOB = D.JOB
ORDER BY X.LEASE_ID;

Where Clause on Joined Table with Into Keyword

I wish to join two tables while filtering one of the tables. That works fine like
var matching = from a in ctx.A
join b in ctx.B on a.BId equals b.Id
where idList.Contains(b.Id)
select a;
However, if I also make use of the into keyword to name the joined result
var matching = from a in ctx.A
join b in ctx.B on a.BId equals b.Id into c
where idList.Contains(b.Id)
select a;
I get a compiler error telling me
The name 'b' does not exist in the current context
However, I can reference a at that point, as well as 'c', without problems.
Why is that exactly, and how can I apply a where clause to b?
Why is that exactly
Because after a join into clause, the range variable introduced by that clause isn't in scope - whereas previous variables are. Don't forget that you're joining into c, so each value of b is effectively part of the group of values (c).
and how can I apply a where clause to b?
By doing it earlier:
var matching = from a in ctx.A
join b in ctx.B.Where(x => idList.Contains(x.Id))
on a.BId equals b.Id into c
where c.Any()
select a;
EDIT: This can be put into slightly more query-expression-oriented code as:
var matchingBs = from b in ctx.B
where idList.Contains(b.Id)
select b;
var matching = from a in ctx.A
join b in matchingBs
on a.BId equals b.Id into c
where c.Any()
select a;
(You could use a nested query expression, but I'm not keen on those in general.)
Or using Any on c:
var matching = from a in ctx.A
join b in ctx.B on a.BId equals b.Id into c
where c.Any(b => idList.Contains(b.Id))
select a;
Or even:
var matching = from a in ctx.A
where ctx.B.Any(b => idList.Contains(x.Id) &&
a.BId == b.Id)
select a;
Which can be rewritten as:
var matching = ctx.A.Where(a => ctx.B.Any(b => idList.Contains(x.Id) &&
a.BId == b.Id));
It's important to understand the difference in results between join and join into - the first creates a "pairwise" join; the second creates a group join, where the result for the extra range variable is a group of matches.

Multiple Joins LINQ Query performance

I'm new to LINQ and have very little knowledge.
I have the following complex query. it runs 3 or 4 times slower than the stored procedure which i translated to LINQ.
any tips for me to make it run faster?
var result = from a in db.A
join al in db.AL.Where(q => q.CurrentLocation == 1) on a.AID equals al.AID into tmp_al
from al in tmp_al.DefaultIfEmpty()
join l in db.Lon al.LID equals l.LID into tmp_l
from l in tmp_l.DefaultIfEmpty()
join r in db.R on l.RID equals r.RID into tmp_r
from r in tmp_r.DefaultIfEmpty()
join b in db.B on r.BID equals b.BID into tmp_b
from b in tmp_b.DefaultIfEmpty()
join ap in db.AP.Where(q => q.CurrentProtocol == 1) on a.AID equals ap.AID into tmp_ap
from ap in tmp_ap.DefaultIfEmpty()
join p in db.P on ap.PID equals p.PID into tmp_p
from p in tmp_p.DefaultIfEmpty()
join s in db.S on a.SID equals s.SID into tmp_s
from s in tmp_s.DefaultIfEmpty()
join ans in db.AS on a.ASID equals ans.ASID into tmp_ans
from ans in tmp_ans.DefaultIfEmpty()
join pr in db.P on p.PI equals pr.PID into tmp_pr
from pr in tmp_pr.DefaultIfEmpty()
where a.Active == 1
group a by new { a.Active, pr.LN, pr.FN, b.BN, r.RID, r.R1, p.PN, s.S1, ans.AS1 }
into grp
orderby grp.Key.BN, grp.Key.R1, grp.Key.PN, grp.Key.S1, grp.Key.AS1
select new
{
PIName = grp.Key.LN + " " + grp.Key.FN,
BN = grp.Key.BN,
RID = grp.Key.RID,
R = grp.Key.R1,
PN = grp.Key.PN,
S = grp.Key.S1,
AS = grp.Key.AS1,
NumberOA = grp.Count()
};
Thanks for your answers. #Albin Sunnanbo: i dont know how to check the execution plans. my LINQ runs correctly and produces the required output. it is just slow. I would like to speeden it up. #usr: the original sql is as follows:
sorry about the silly table names. the original code is confidential. so i'm not posting the complete table names.
CREATE PROCEDURE [dbo].[report_CBRP] --
AS
SELECT LN + ' ' + FN As PIN, BN, R.RID, R, PN,
S, AS, COUNT(*) As NOA
FROM A
LEFT JOIN AL
ON A.AID = AL.AID
AND AL.CL = 1
LEFT JOIN L
ON AL.LID = L.LID
LEFT JOIN R
ON L.RID = R.RID
LEFT JOIN B
ON R.BID = B.BID
LEFT JOIN AP
ON A.AID = AP.AID
AND AP.CPl = 1
LEFT JOIN P
ON AP.PID = P.PID
LEFT JOIN S
ON A.SID = S.SID
LEFT JOIN AS
ON A.ASID = AS.ASID
LEFT JOIN P
ON P.PI = P.PID
GROUP BY A.A, LN , FN , B.BN, R.RID, R.R, P.PN,
S.S, AS.AS
HAVING A.A = 1
ORDER BY B.BN, R.R, P.PN, S, AS
GO
It seems you're doing SQL hard life here.
In general, try to avoid so many joins, but rather break them into few small queries.
More than that, you're performing a group by which in itself is an expensive operation, let alone with so many columns
I've noticed that you're joining all the columns in each table. Try to select only the relevant columns.
Also noticed that few of the tables aren't used in the group by like al, ap and l. Do you need them at all??
Use AsNoTracking() for readonly data from EF. In that way you speed up things.
Use SQL Views

Resources