Linq query join is not working - c#

Hi i am trying to join two tables in c#. the join code is given below. The problem is that when there is null value for tourid in tb_abc then in will not include that row from tb_abc in the list.
return (from p in context.tb_abc
from o in context.tb_Second
where o.id==p.tourId
where p.driverId == driverId
select new abcBean
{
id=p.id,
name=o.name
}).ToList<abcBean>();
Can anyone tell me what i am doing wrong

You are not doing an inner join in that query. You are doing a cross join, its where you have two tables and join each record to every other record.
If you want to include rows that return null on one of the constraints you need a left outer join.
return (from p in tb_abc
join o in tb_Second on p.tourId equals o.id into po
where p.driverId == driverId
from subpo in po.DefaultIfEmpty()
select new abcBean
{
id=p.id,
name=(subpo == null ? String.Empty : subpo.Name)
}).ToList();
Consider these two sql statements:
The first a cross join:
select id, name
from tb_abc o,
tb_Second p
where
o.id = p.tourID
and p.driverID = #driverID
The second a left outer join:
select id, name
from tb_abc o
LEFT OUTER JOIN tb_Second p on o.id = p.tourID
where
p.driverId = #driverID
The second will give you one set of the records, that include the null value of o.id.
The first will give you something of a Cartesian product which you rarely want.
Linq's DefaultIfEmpty() puts the default value (null) into the record if it doesnt find a match for the one side, so it behaves like the left outer join.

you can use left outer join like
return (from p in context.tb_abc
join o in context.tb_Second on o.id==p.tourId into gt
where p.driverId == driverId
from subsecond in gt.DefaultIfEmpty()
select new abcBean
{
id=p.id,
name=(subsecond == null ? String.Empty : subsecond.Name)
}).ToList<abcBean>();

Related

Linq left join returns inner join

I am trying to perform an outer join in C# using Linq, the person mentoring me keeps saying I shouldn't try to do an outer join which isn't really an answer.
What I got from the other threads is that I need the .DefaultIfEmpty() where ever I may not have a record.
I tried it first on just the lines where there may be a missing information then added it to every line just to see if that was the problem.
Every time I run this I get only the inner join records. It works great other than it is not including the two records from my DB that only have information in the first two tables.
var sqlQuery =
from s in ctx.Suppliers
from sp in ctx.SupplierParts
.Where(sp => sp.SupplierID == s.SupplierID)
.DefaultIfEmpty()
from sm in ctx.SupplierManufacturerRelations
.Where(sm => sm.SupplierPNID == sp.SupplierPNID)
.DefaultIfEmpty()
from mp in ctx.ManufacturerParts
.Where(mp => mp.MfgPNID.Equals(sm.MfgPNID))
.DefaultIfEmpty()
from m in ctx.Manufacturers
.Where(m => m.ManufacturerID.Equals(mp.ManufacturerID))
.DefaultIfEmpty()
from im in ctx.ItemMasters
.Where(im => im.PreID == mp.PreID)
.Where(im => im.PartNumber == mp.PartNumber)
.DefaultIfEmpty()
from c in ctx.ComponentClasses
.Where(c => c.CCID == im.CCID)
.DefaultIfEmpty()
from um in ctx.UnitsOfMeasures
.Where(um => um.UOMID == sp.UOMID)
.DefaultIfEmpty()
select new
{ my variables}
var querylist = sqlQuery.Where(n => n.SupplierID == thisSupplier).ToList();
I also tried
from s in ctx.Suppliers
join sp in ctx.SupplierParts on s.SupplierID equals sp.SupplierID
join sm in ctx.SupplierManufacturerRelations on sp.SupplierPNID equals sm.SupplierPNID into spartgroup
from sm in spartgroup.DefaultIfEmpty()
join mp in ctx.ManufacturerParts on sm.MfgPNID equals mp.MfgPNID into mpartgroup
from mp in mpartgroup.DefaultIfEmpty()
join m in ctx.Manufacturers on mp.ManufacturerID equals m.ManufacturerID into mgroup
from m in mgroup.DefaultIfEmpty()
join im in ctx.ItemMasters
on new { key1 = (int)mp.PreID, key2 = (int)mp.PartNumber }
equals new { key1 = im.PreID, key2 = im.PartNumber }
into tpartgroup
from im in tpartgroup.DefaultIfEmpty()
join c in ctx.ComponentClasses on im.CCID equals c.CCID into fullgroup
from c in fullgroup.DefaultIfEmpty()
join um in ctx.UnitsOfMeasures on sp.UOMID equals um.UOMID
This SQL query works and doesn't omit the rows
SELECT Supplier.SupplierID
, SupplierPart.SupplierPNID
, SupplierPart.SupplierPN
, SupplierPart.Description
, SupplierManufacturerRelation.MfgPNID
, ManufacturerPart.PreID
, ManufacturerPart.PartNumber
, ItemMaster.CCID
, ItemMaster.Description AS Expr1
FROM Supplier
Inner JOIN SupplierPart
ON Supplier.SupplierID = SupplierPart.SupplierID
Left JOIN SupplierManufacturerRelation
ON SupplierPart.SupplierPNID = SupplierManufacturerRelation.SupplierPNID
Left JOIN ManufacturerPart
ON SupplierManufacturerRelation.MfgPNID = ManufacturerPart.MfgPNID
Left JOIN ItemMaster
ON ManufacturerPart.PreID = ItemMaster.PreID
AND ManufacturerPart.PartNumber = ItemMaster.PartNumber
WHERE Supplier.SupplierID = 9
For translating SQL to LINQ query comprehension:
Translate FROM subselects as separately declared variables.
Translate each clause in LINQ clause order, translating monadic operators (DISTINCT, TOP, etc) into functions applied to the whole LINQ query.
Use table aliases as range variables. Use column aliases as anonymous type field names.
Use anonymous types (new { ... }) for multiple columns.
Left Join is simulated by using a into join_variable and doing another from from the join variable followed by .DefaultIfEmpty().
Replace COALESCE with the conditional operator and a null test.
Translate IN to .Contains() and NOT IN to !...Contains()
SELECT * must be replaced with select range_variable or for joins, an anonymous object containing all the range variables.
SELECT fields must be replaced with select new { ... } creating an anonymous object with all the desired fields or expressions.
Proper FULL OUTER JOIN must be handled with an extension method.
So from your SQL, your query should look like:
var ans = from s in ctx.Suppliers
join sp in ctx.SupplierParts on s.SupplierID equals sp.SupplierID
join sm in ctx.SupplierManufacturerRelations on sp.SupplierPNID equals sm.SupplierPNID into smj
from sm in smj.DefaultIfEmpty()
join mp in ctx.ManufacturerParts on sm?.MfgPNID equals mp.MfgPNID into mpj
from mp in mpj.DefaultIfEmpty()
join im in ctx.ItemMasters on new { key1 = (int)mp.PreID, key2 = (int)mp.PartNumber } equals new { key1 = im.PreID, key2 = im.PartNumber } into imj
from im in imj.DefaultIfEmpty()
select new {
s.SupplierID, sp.SupplierPNID, sp.SupplierPN, sp.Description, sm.MfgPNID, mp.PreID, mp.PartNumber, im.CCID, Expr1 = im.Description
};

convert SQL to LINQ not working

I want to convert this SQL code to LINQ. Here is my SQL code:
SELECT Rooms.RoomName AS RoomNo, Beds.BedName AS Beds, Rooms.RoomType, ISNULL(CheckIn.CheckIntatus,'') AS Status
FROM CheckIn
INNER JOIN GuestBeds ON CheckIn.GuestBedId = GuestBeds.Id
AND (CheckIn.CheckInStatus = 1 OR CheckIn.CheckIntatus = 2 OR CheckIn.CheckSIntatus = 3)
RIGHT JOIN Beds ON GuestBeds.BedId = Beds.Id
INNER JOIN Rooms ON Beds.RoomId = Rooms.Id
LEFT JOIN Guests ON CheckIn.GuestId = Guests.Id
WHERE Beds.Active = 1 AND Rooms.Active = 1
ORDER BY RoomName, Beds
It works well which means it shows all the RoomName with CheckInStatus. If the Room is not presence in CheckIn table, ot will return the status as Null.
So I want to convert the code to LINQ. SO here is my LINQ code:
from b in Beds
join w in Rooms on b.RoomsId equals w.Id
where (a.CheckInStatus == 3 || a.CheckInStatus == 1 || a.CheckInStatus == 2)
join p in GuestBeds on b.Id equals p.BedId
join a in CheckIn on p.Id equals a.GuestBedId
join t in Guests on a.GuestId equals t.Id
where b.Active == true && w.Active == true
orderby w.RoomName
select new
{
RoomName = w.RoomName,
BedName = b.BedName,
Status = a.CheckInStatus
}
It didnt worked like the first code. It only show the data which contain CheckInStatus. I want it to show all the RoomName inside Room database
Normally I would post some rules for converting SQL to LINQ but this is complicated enough I think I'd need to make new rules. I commented out the references to Guests because as a LEFT JOIN it has no bearing on the answer.
Pull out the WHERE on individual tables and make them sub-queries:
var ActiveBeds = Beds.Where(b => b.Active == 1);
var ActiveRooms = Rooms.Where(r => r.Active == 1);
In LINQ, a RIGHT JOIN must be done by flipping the join to be a left join, so we will create the two sides as sub-queries.
Left side of RIGHT JOIN:
Translate the JOIN conditions that aren't part of an equi-join into a LINQ where clause on the appropriate tables (alternately this could be a subquery as above). The LEFT JOIN becomes a LINQ join/from ... DefaultIfEmpty() phrase, but as noted above isn't needed.
var CheckInsGuestBedsGuests = from c in CheckIn
where (c.CheckInStatus == 1 || c.CheckInStatus == 2 || c.CheckInStatus == 3)
join gb in GuestBeds on c.GuestBedId equals gb.Id
//join g in Guests on c.GuestId equals g.Id into gj
//from g in gj.DefaultIfEmpty()
select new { c, gb /*, g */ };
Right side of RIGHT JOIN:
The other side of the RIGHT JOIN includes an INNER JOIN so put them together in a sub-query:
var ActiveBedsRooms = from b in ActiveBeds
join r in ActiveRooms on b.RoomId equals r.Id
select new { b, r };
Finally, flip the sub-queries to create a left join using the same idiom as above:
var ans = from br in ActiveBedsRooms
join cgbg in CheckInsGuestBedsGuests on br.b.Id equals cgbg.gb.BedId into cgbgj
from cgbg in cgbgj.DefaultIfEmpty()
select new {
RoomNo = br.r.RoomName,
Beds = br.b.BedName,
br.r.RoomType,
Status = cgbg.c.CheckInStatus
};
NOTE: If you were not using LINQ to SQL, the Status expression would fail when cgbg is null and you would need
Status = cgbg?.c.CheckInStatus
but unfortunately LINQ to SQL/EF doesn't handle the null conditional operators yet.
BTW, nice query - brings back memories of when I used to write hotel front desk software :)

To determine if outer join has a match using linq

I have two tables A and B,which I can outer join with linq. I need a way to find if it is possible to have a boolean along with the query if the outer join has a match. For example, I need a boolean to be true if a record is present in tableA and not in tableB. This can be done in SQL using IF, I was wondering if there was something similar in Linq
var result = from a in tableA
join b in tableB on a.Id equals b.userId into group1
from g1 in group1.DefaultIfEmpty()
select new{id = g1.Id,userId = g1.userId,boolIsPresent =(present in tableA not in tableB)}.ToList();
Currently, you're performing an inner join, not a left outer join, which means only records/objects that exist in both tables are retrieved. thus your bool boolIsPresent would always be true.
edit:
to test if a record is present in tableA and does not have a matching record in tableB just check if g1 != null, i.e:
var result = from a in tableA
join b in tableB on a.Id equals b.userId into group1
from g1 in group1.DefaultIfEmpty()
select new
{
id = g1 != null? g1.Id : enterDefault,
userId = g1 != null? g1.userId : enterDefault,
boolIsPresent = g1 != null
}.ToList();
It will be something like
boolisPresent = b != null
Once you get an Outer join working!

LINQ JOIN different tables based on conditions

How can I join two different tables based on a condition?
I have my query as under:
var myquery = from p in db.tbl1
join q in db.tbl2 on p.field1 equals q.field1
join r in db.tbl3 on q.field2 equals r.field2
Till here everything is fine, now I want to add 1 more join to a table but it should be based on a condition like:
if(q.field3 == 1)
join s in db.tbl4 on q.field4 equals s.field4
else if(q.field3 == 2)
join s in db.tbl5 on ....
So basically I want to join to different tables based on the value of q.field3.
You're not going to be able to conditionally join based on the value of a single row. The idea of joining is that you're joining based off of all of the rows. Conditionally determining what/how to join based on some value outside of the query would make sense.
What you can do is do both joins unconditionally, but choose which result to use conditionally for each row. Obviously this will only work if the tables are of the same type, or if you first project s1 and s2 into a common type (using let)
var myquery = from p in db.tbl1
join q in db.tbl2 on p.field1 equals q.field1
join r in db.tbl3 on q.field2 equals r.field2
join s1 in db.tbl4 on q.field4 equals s1.field4
join s2 in db.tbl5 on q.field5 equals s2.field5
let s = q.field3 == 1 ? s1 :
q.field3 == 2 ? s2 : null
This should be able to be translated by the query provider into a CASE statement.

LINQ Left Outer Join combined with Let

In a previous question I asked how I would get a Customers first Order, it was answered thus :
var minOrders = from customer in DataSet.Customers
let order = (from o in DataSet.Orders where o.CustomerId == customer.CustomerId
order by o.OrderTimestamp
select o).first()
select new {
customer.Name,
order.OrderAmount
});
This is great, but how do I include a Left Outer Join onto the above? That is, return all Customers even if they have no orders, so something like :
var minOrders = from customer in DataSet.Customers LEFT OUTER JOIN
let order = (from o in DataSet.Orders where o.CustomerId == customer.CustomerId
order by o.OrderTimestamp
select o).first()
select new {
customer.Name,
order.OrderAmount
});
I know, in hindsight I should of asked this at the same time..
Thanks, Joe
Firstly, using let to do the join like this isn't ideal in the first place. There's a join clause in LINQ for a reason :)
Left outer joins aren't specifically supported in LINQ, but you can fake them like this:
var minOrders = from customer in DataSet.Customers
join order in DataSet.Orders.OrderBy(o => o.OrderTimestamp)
on customer.CustomerId equals o.CustomerId
into customerOrders
let order = customerOrders.FirstOrDefault()
select new {
customer.Name,
OrderAmount = order == null ? 0m : order.OrderAmount
};
Usually a left outer join uses from foo in bar.DefaultIfEmpty instead of let foo = bar.FirstOrDefault() but in this case you're only after the first match anyway, hence the different approach.
I'm pretty sure this works logically - whether the SQL translation will work or not is a different matter.

Categories