I have a question about joins when using Linq to Entities. According to the documentation the use on the join without a qualifier performs like a left outer join. However when I execute the code below, I get a count returned of zero. But if I comment out the three join lines I get a count of 1. That would indicate that the join are acting as inner join. I have two questions. One which is right inner or outer as the default? Second how do I do the other one i.e. inner or outer? The key words on inner and outer do not work.
var nprs = (from n in db.FMCSA_NPR
join u in db.FMCSA_USER on n.CREATED_BY equals u.ID
join t in db.LKUP_NPR_TYPE on n.NPR_TYPE_ID equals t.ID
join s in db.LKUP_AUDIT_STATUS on n.NPR_STATUS_ID equals s.ID
where n.ROLE_ID == pRoleId
&& n.OWNER_ID == pOwnerId
&& n.NPR_STATUS_ID == pNPRStatusId
&& n.ACTIVE == pActive
select n).ToList();
if (nprs.Count() == 0)
return null;
joins without keys operate like inner joins. I can't find it in the documentation at the moment, but I'll post an edit when I do. However, to perform a left outer join, here is a sample from 101 LINQ Samples: http://msdn.microsoft.com/en-us/vcsharp/ee908647#leftouterjoin
Joins in LINQ are inner by default. To do an outer join, you have to either do a manual "if-null-then" or write your own custom join operation.
Left-outer example with manual if-null:
var query = from person in people
join pet in pets on person equals pet.Owner into gj //inner
from subpet in gj.DefaultIfEmpty()
select new { person.FirstName, PetName = (subpet == null ? String.Empty : subpet.Name) }; //left outer
How to: Perform Left Outer Joins
How to: Perform Custom Join Operations
Related
Thanks in advance for any help.
Not an expert in Linq to Sql by any means.
I have 4 tables.
The main lb_item table which defines, unsurprisingly, an item.
Many fields but holds 3 ID fields.
itemID (key)
categoryID (not null)
patternID (can be null)
lb_pattern table which is keyed off the lb_item patternID.
lb_category table which is keyed off the lb_item categoryID.
lb_animal table which is keyed off the lb_item item ID.
So I need a select from the lb_item table joining to these other 3 tables to bring back varchar fields as I'm building a DTO.
A single left outer join works fine thus:
from lbi in lbContext.lb_item
join lbp in lbContext.lb_pattern on lbi.patternID equals lbp.patternID into g1
from j1 in g1.DefaultIfEmpty()
join lbc in lbContext.lb_category on lbi.categoryID equals lbc.categoryID
where lbi.itemID == id
select new lb_itemDTO..........
I now need to add a 2nd left outer join for the lb_animal table.
So I started to do this:
from lbi in lbContext.lb_item
join lbp in lbContext.lb_pattern on lbi.patternID equals lbp.patternID into g1
from j1 in g1.DefaultIfEmpty()
join lba in lbContext.lb_animal on j1.
But the options in VS for j1 give me only the fields within the lb_pattern table.
I need the join to read:
join lba in lbContext.lb_animal on j1.itemID equals lba.itemID
or
join lba in lbContext.lb_animal on lbi.itemID equals lba.itemID
Neither works and gives me an exception along the lines of "'NavigationExpandingExpressionVisitor' failed. This may indicate either a bug or a limitation in EF Core".
So how can I add a left outer join to the lb_animal table?
I've spent the last hour looking at various SO posts to suss it out but I just cannot seem to get my head around the solution for some reason. Feel like a newbie. And I'm sure the solution is going to be obvious!
Any help or pointers to a solution would be much appreciated.
This should work:
var ans = from lbi in lbContext.lb_item
where lbi.itemID == id
join lbp in lbContext.lb_pattern on lbi.patternID equals lbp.patternID into lbpj
from lbp in lbpj.DefaultIfEmpty()
join lba in lbContext.lb_animal on lbi.itemID equals lba.itemID into lbaj
from lba in lbaj.DefaultIfEmpty()
join lbc in lbContext.lb_category on lbi.categoryID equals lbc.categoryID
select new {
};
Persvered and finally came across a SO post which approached it in a different way and it worked.
Original SO is here:
My working code is now thus:
from lbi in lbContext.lb_item
from lbc in lbContext.lb_category
.Where(c => c.categoryID == lbi.categoryID)
from lbp in lbContext.lb_pattern
.Where(p => p.patternID == lbi.patternID)
.DefaultIfEmpty()
from lba in lbContext.lb_animal
.Where(a => a.itemID == lbi.itemID)
.DefaultIfEmpty()
where lbi.itemID == id
select new lb_itemDTO
Would still be interested in comments on this solution as it's not breaking the outer joins into grouped segments. So is this solution I found any less efficient in terms of the SQL it generates compared to how I was originally proposing to do it?
Please note below is entirely made up for example sake. I have a similar query based on an sql code but couldn't translate it to LINQ to get correct value.
The sql basically looks like this:
select * from customers c
join proucts p on c.id = p.customerid
join credit r on r.customerid=c.id and ISNULL(r.trandate, c.registeredDate) >= c.registeredDate
I also tried to tweak the above sql and put the condition inside where and it also returns the same value I am getting in my #2 LINQ below(which is incorrect).
How can I use c (customer) inside .Where of credit? see code
1.
from c in customers
join p in products on c.id = p.customerid
join cr in credit.Where(r=> r.tranDate => c.registeredDate!=null?c.registeredDate : r.purchaseDate) on c.id=cr.customerid
...
2.
I know you would suggest why not just put it in a where below like below but I am getting incorrect value.
from c in customers
join p in products on c.id = p.customerid
join cr in credit on c.id=cr.customerid
where r.tranDate => c.registeredDate!=null?c.registeredDate : r.purchaseDate
Is there a workaround? I have tried tons of others but won't get me the correct one.
LINQ supports only equijoins. Any additional criteria should go to where clause. And yes, the other range variables are inaccessible from the join inner sequence, so the filtering should happen before or after the join.
So this SQL query:
select * from customers c
join products p on c.id = p.customerid
join credit r on r.customerid = c.id
and ISNULL(r.trandate, c.registeredDate) >= c.registeredDate
directly translates to this LINQ query:
from c in customers
join p in products on c.id equals p.customerid
join cr in credit on c.id equals cr.customerid
where (cr.tranDate ?? c.registeredDate) >= c.registeredDate
select new { c, p, cr };
Optionally, the condition
(cr.tranDate ?? c.registeredDate) >= c.registeredDate
can be replaced with
(cr.tranDate == null || cr.tranDate >= c.registeredDate)
I have the following statement in LINQ:
var eventsWithTag = (from occurence in occurrences1
join eventTag in serviceContext.CreateQuery("adx_eventtag_event")
on occurence.Event.GetAttributeValue<Guid>("adx_eventid") equals eventTag.GetAttributeValue<Guid>("adx_eventid")
join tag in serviceContext.CreateQuery("adx_eventtag")
on eventTag.GetAttributeValue<Guid>("adx_eventtagid") equals tag.GetAttributeValue<Guid>("adx_eventtagid")
where tag.GetAttributeValue<string>("adx_name") == "scotland"
select occurence).OrderBy(x => x.Start);
This works fine with the fixed string search of "Scotland" in this instance. However I need to replace that to reflect the current page topic. So essentially I need to replace
== "scotland"
with
== getBranch()
where getBranch returns the relevant branch name as a string.
This would lead me to try
eventsWithTag = (from occurence in occurrences1
join eventTag in serviceContext.CreateQuery("adx_eventtag_event") on occurence.Event.GetAttributeValue<Guid>("adx_eventid") equals eventTag.GetAttributeValue<Guid>("adx_eventid")
join tag in serviceContext.CreateQuery("adx_eventtag") on eventTag.GetAttributeValue<Guid>("adx_eventtagid") equals tag.GetAttributeValue<Guid>("adx_eventtagid")
where tag.GetAttributeValue<string>("adx_name") == getBranch()
select occurence).OrderBy(x => x.Start);
But this doesnt work, and from what little I know of LINQ it is because you cant use variables in this way.
So my question is: How can I use the LINQ query above with a dynamic value for the branch.
Please note: I have seen other posts about this but I evidently don't have the LINQ knowledge to transfer them to my specific needs. Just yet!
Everything looks right so I'd double check your getBranch()...
if this works:
var branch = "scotland";
eventsWithTag =
(from occurence in occurrences1
join eventTag in serviceContext.CreateQuery("adx_eventtag_event") on occurence.Event.GetAttributeValue<Guid>("adx_eventid") equals eventTag.GetAttributeValue<Guid>("adx_eventid")
join tag in serviceContext.CreateQuery("adx_eventtag") on eventTag.GetAttributeValue<Guid>("adx_eventtagid") equals tag.GetAttributeValue<Guid>("adx_eventtagid")
where tag.GetAttributeValue<string>("adx_name") == branch
select occurence)
.OrderBy(x => x.Start);
Then p.s.w.g's answer should work, and your issue lies with getBranch returning a value that doesn't match any records...
On a slightly different matter, I'm slightly confused as to why you need to get the actual attributes in your equals statement, because according to this, it should work fine like this:
var branch = "scotland";
eventsWithTag =
(from occurence in occurrences1
join eventTag in serviceContext.CreateQuery("adx_eventtag_event") on occurence.Event["adx_eventid"] equals eventTag["adx_eventid"]
join tag in serviceContext.CreateQuery("adx_eventtag") on eventTag["adx_eventtagid"] equals tag["adx_eventtagid"]
where tag["adx_name"] == branch
select occurence)
.OrderBy(x => x.Start);
You can just make the call beforehand:
var branch = getBranch();
eventsWithTag =
(from occurence in occurrences1
join eventTag in serviceContext.CreateQuery("adx_eventtag_event") on occurence.Event.GetAttributeValue<Guid>("adx_eventid") equals eventTag.GetAttributeValue<Guid>("adx_eventid")
join tag in serviceContext.CreateQuery("adx_eventtag") on eventTag.GetAttributeValue<Guid>("adx_eventtagid") equals tag.GetAttributeValue<Guid>("adx_eventtagid")
where tag.GetAttributeValue<string>("adx_name") == branch
select occurence)
.OrderBy(x => x.Start);
I am creating a game, and my table layout looks like this:
dbPlayer:
Id (int)
...
dbGame:
Id (int)
Finished (bool)
...
dbGamePlayer:
GameId
PlayerId
...
Given a Players ID, how can I select all games that the player is involved in, but has not (true) finished?
This is what I've go so far:
from g in dbGame
join gp in dbGamePlayer on gp.GameId equals g.Id
join p in dbPlayer on p.Id equals gp.PlayerId
where p.Id == 1 && g.Finished == false
select g
But I'm getting errors all over the place. Sorry, I'm new at LINQ
Your LINQ Statement is wrong.
The joint object (gp) has to be on the right side of the equals-Statement.
join gp in dbGamePlayer on g.Id equals gp.GameId
Same applies to the second join:
join p in dbPlayer on gp.PlayerId equals p.Id
So the full statement should look like:
IEnumerable query = (from g in dbGame
join gp in dbGamePlayer on g.Id equals gp.GameId
join p in dbPlayer on gp.PlayerId equals p.Id
where p.Id == 1 && g.Finished == false
select g);
But the error
Name model is not in scope on the left side of equals.
Consider swapping the expression on the either side of equals.
should have told you that.
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.