LINQ to Entities Join on Nullable Field where Null Implies "Match All" - c#

I am attempting to run the following LINQ query using Entity Framework 5:
int taskId = 2;
query = from a in Table_A
where a.StatusCode != "DONE"
&& a.Inbound
join b in Table_B
on a.Id equals b.Id_Table_A
join c in Table_C
on a.State equals (c.State ?? a.State)
where 2 == c.Id_Task
&& b.DataType == c.DataType
select a.Id;
The line that is causing me problems is:
on a.State equals (c.State ?? a.State)
The "State" field in Table_C is nullable... and when it is null, it is used to imply "all states". As such, when "c.State" is null I want the record to be matched. If I were to write this in SQL, I would use the following:
JOIN Table_C ON Table_A.State = ISNULL(Table_C.State, Table_A.State)
Unfortunately, I am being given the following error:
The name 'a' is not in scope on the right side of 'equals'. Consider swapping the expressions on either side of 'equals'.
I will be grateful to anybody who can let me in on the secret to getting this working.
Thanks.

You can modify your code like:
int taskId = 2;
query = from a in Table_A
where a.StatusCode != "DONE"
&& a.Inbound
join b in Table_B
on a.Id equals b.Id_Table_A
from c in Table_C
where 2 == c.Id_Task
&& b.DataType == c.DataType
&& (c.State == null || a.State.Equals(c.State))
select a.Id;
A join is essentially a where clause, so here we can use the where clause due to the restrictions with join.

I managed to get this to work by moving the "DataType" check from the WHERE to the JOIN, and moving the "State" check from the JOIN to the WHERE. The resulting code that worked as I expected is as follows:
query = from a in Table_A
where a.StatusCode != "DONE"
&& a.Inbound
join b in Table_B
on a.Id equals b.Id_Table_A
join c in Table_C
on b.DataType equals c.DataType
where 2 == c.Id_Task
&& (c.State ?? a.State) == a.State
select a.Id;
Many thanks to everybody who has taken a look at this for me. :)

You can use "from" syntax instead of "join"
from a in TableA
from b in TableB
.Where(x => (x.Buy ?? a.Buy) == a.Buy
&& (x.Parity ?? a.Parity) == a.Parity)

Related

How to use LINQ query using OR condition in join clause [duplicate]

I want to do a JOIN with LINQ using an OR statement.
Here is the SQL query I'm starting with:
SELECT t.id
FROM Teams t
INNER JOIN Games g
ON (g.homeTeamId = t.id OR g.awayTeamId = t.id)
AND g.winningTeamId != 0
AND g.year = #year
GROUP BY t.id
I'm having trouble converting that ON clause to LINQ. This is where I'm at:
var y = from t in db.Teams
join g in db.Games on t.ID equals g.AwayTeamID //missing HomeTeamID join
where g.WinningTeamID != 0
&& g.Year == year
group t by t.ID into grouping
select grouping;
I think I could use:
join g in db.Games on 1 equals 1
where (t.ID == g.HomeTeamID || t.ID == g.AwayTeamID)
and this works but seems kind of seems hacky. Is there a better way?
I struggled with this as well until I found the following solution, which worked well for my situation:
var y = from t in db.Teams
from g in db.Games
where
(
t.ID == g.AwayTeamID
|| t.ID == g.HomeTeamID
)
&& g.WinningTeamID != 0
&& g.Year == year
group t by t.ID into grouping
select grouping;
Under the covers, your solution probably works very close to this one. However, I bet this one is just a bit faster if you benchmark it since it is not JOINING every item in the first dataset with every item in the second dataset, which could be a disaster if either (or both) dataset were really big.
The where clause applies a boolean condition, so using "||" is the way to go. You can chain multiple where clauses but I believe that will give you a "and" operation, rather than an "or".
I think you can do like this:
from t1 in db.Table1
// inner join with OR condition
from t2 in db.Table2 where t1.col1 == t2.col1 || t1.col2 == t2.col2
// normal inner join
join t3 in db.Table3 on t1.col1 equals t3.col1
// inner join with complex condition
join t4 in db.Table4 on t2.col4 equals t4.col4 where t2.col5.Contains(t4.col5)
// left join with OR condition
from t5 in db.Table5.Where(x => x.col5 == t1.col5 || x.col6 == t1.col6).DefaultIfEmpty()
select new {
x = 1 // select whatever you want here
}
The underlying SQL query probably won't use native sql joins but the above is just a way to make your code look pretty and organized.

let statement and outer join in LINQ to SQL

How to use the let statement in outer join of LINQ to SQL?
Something like below,
from a in tableA
from b in tableB.where(o => a.Key == o.Key).DefaultIfEmpty() //outer join
let x = b.objectBB
where b != null && x != null && x.FilterCode == "X"
So,
Does the above query is fine?
Will the 'let' fail if 'b' is null ?
what will happen with the where clause on 'x'? Does this filter work fine?
Any better way to achieve this also much welcome.!
Seems to me that if you want both sides to be not null, don't you just want an inner join?
from a in tableA
join b in tableB on a.Key equals o.Key
where b.FilterCode == "X"

Linq outer join syntax

I have the following query:
var query =
from modules in _Context.Modules
join moduleStrings in _Context.ModuleStrings on modules.MID equals moduleStrings.MID
join strings in _Context.Strings on moduleStrings.SID equals strings.SID
join stringTexts in _Context.StringTexts on strings.SID equals stringTexts.SID into stringsEmpty
from stringTexts in stringsEmpty.DefaultIfEmpty()
join languages in _Context.Languages on stringTexts.LID equals languages.LID
where modules.MID == MID && LID == languages.LID
select new GridData6S()
{
Name = strings.Name,
Text = stringTexts != null ? stringTexts.Text : ""
};
I want to join it so that I see the empty text if stringTexts is null.
It's probably some small thing, but I have been looking at this for an hour and can't figure it out. This is the closest I got.
You are filtering out any language that is not LID=languages.LID so when languages/stringTexts are null this condition will never be true.
Also as the only reason you seem to be joining to the languages table is to get the LID which is also the join condition, you could just skip joining to languages
The same is true for modules.....
This might help and is a little simpler
var query =
from moduleStrings in _Context.ModuleStrings
join strings in _Context.Strings on moduleStrings.SID equals strings.SID
join stringTexts1 in _Context.StringTexts.Where(x=>x.LID==LID) on strings.SID equals stringTexts.SID into stringsEmpty
from stringTexts in stringsEmpty.DefaultIfEmpty()
where moduleStrings.MID == MID
select new GridData6S()
{
Name = strings.Name,
Text = stringTexts != null ? stringTexts.Text : ""
};

Linq outer join using inequality?

In SQL I'd say:
select a.*
from TableA a
left join TableB b on a.Type = b.Type and a.SomeDate < b.AnotherDate
where b.ID is null
This would select all records in TableA where no record exists in TableB of the same Type and later date.
In Linq, how do you do this?
from a in TableA
join b in TableB on a.Type equals b.Type into j // what about the comparator?
from x in j.DefaultIfEmpty()
where x == null
select a;
Thanks!
EDIT:
A few good answers have been proposed, all of which address the specific need expressed in this question, but they're all basically workarounds. They all translate to a nested "exists" queries in one way or another, whereas the SQL in the question is one neat query without any nesting. The case given here is just an example of a general principle; what I'd really like to see is a Linq expression that will translate to (roughly) the syntax of the above SQL query.
Something like this ought to help:
var results =
(from itemA in TableA
from itemB in TableB
where itemA.Type != itemB.Type && itemA.Date < itemB.Date
select itemA).Distinct();
from a in tableA
let rights =
from b in tableB
where a.Type == b.Type && a.Date < b.Date
select b
where !rights.Any()
select a;
It's translated into:
SELECT [t0].[Type] AS [Type], [t0].[SomeDate] AS [SomeDate]
FROM [TableA] AS [t0]
WHERE NOT (EXISTS(
SELECT NULL AS [EMPTY]
FROM [TableB] AS [t1]
WHERE ([t0].[Type] = [t1].[Type]) AND ([t0].[SomeDate] < [t1].[AnotherDate])))
var results = TableA.Where(a =>
!TableB.Any(b => a.Type == b.Type && a.Date < b.Date))
If you want the linq query to be exactly as your SQL you can write:
var result = from a in TableA
from b in TableB.Where(b => a.Type = b.Type && a.SomeDate < b.AnotherDate).DefaultIfEmpty()
where b == null
select a;
But I would say that the first solution is better as the where b == null would result in a filter operation in the queryplan.

How to do a SQL "Where Exists" in LINQ to Entities?

I really want to do something like this:
Select *
from A join B on A.key = B.key join C on B.key = C.key -- propagated keys
where exists (select null from B where A.key = B.key and B.Name = "Joe") and
exists (select null from C where B.key = C.key and C.Name = "Kim")
What would the linq statement look like using Entity Framework 4 and C#?
Update:
Apparently .Contains() will produce "Where Exists" results. So, another attempt (I don't know if this will even compile LOL):
var inner1 = from recordB in B
where recordB.Name = "Joe"
select recordB.key;
var inner2 = from recordC in C
where recordC.Name = "Kim"
select recordC.key;
var result = from recordA in A
where inner1.Contains( recordA.key) &&
inner2.Contains( recordA.key)
select recordA;
EDIT: WOW this is what actually worked:
var result = from A in Products
where A.kfield1 == 1 && A.kfield2 == 2 &&
( from B in Btable
where B.otherid == "Joe" && // field I want to select by
B.kfield1 == A.kfield1 &&
B.kfield2 == A.kfield2 // Can keep adding keys here
select A.identifier // unique identity field
).Contains(A.identifier) &&
( from C in Ctable
where C.otherid == "Kim" && // field I want to select by
C.kfield1 == A.kfield1 &&
C.kfield2 == A.kfield2 // Can keep adding keys here
select A.identifier // unique identity field
).Contains(A.identifier)
select A;
This produced this SQL:
SELECT [t0].[identifier], [t0].*
FROM [A] AS [t0]
WHERE ([t0].[kfield1] = #p0) AND ([t0].[kfield2] = #p1) AND (EXISTS(
SELECT NULL AS [EMPTY]
FROM [B] AS [t1]
WHERE ([t0].[identifier] = [t0].[identifier]) AND ([t1].[otherid] = #p2) AND
([t1].[kfield1] = [t0].[kfield1]) AND
([t1].[kfield2] = [t0].[kfield2]))) AND (EXISTS(
SELECT NULL AS [EMPTY]
FROM [C] AS [t2]
WHERE ([t0].[identifier] = [t0].[identifier]) AND ([t2].[otherid] = #p3) AND
([t2].[kfield1] = [t0].[kfield1]) AND
([t2].[kfiekd2] = [t0].[kfield2]) ))
Which is what I wanted. Notice the [t0].[identifier] = [t0].[identifier], which filters out null values because null doesn't compare equal to anything including itself (in SQL)
The .Any() extension method typically maps to exists.
Have you tried adding your exists conditioning to your joins?
from a in context.AEntity
Join B in context.BEntity on A.Key equals B.Key && B.Name == "Joe"
Join C in context.CEntity on B.Key equals C.Key && C.Name == "Kim";
Not sure if that will work, but worth a shot.

Categories