Using Left Outer Join in Linq - c#

I want to write one LINQ query with left outer join.For this I am able to write the corresponding SQL query which returns the desired output but the same in linq doesn't work for me.
This what my SQL looks like:
SELECT Table1.ID, Table1.Description
FROM Table1 LEFT OUTER JOIN
Table2 ON Table1.AID = Table2.AID AND Table1.TID = Table2.TID
WHERE (Table2.Status <> 'Using') OR (Table.Status IS NULL)
This query returns 7 records for me and that is what my requirement is.Now the same query I want to write with LINQ and this is what I tried with:
return (from t1 in db.Table1.AsEnumerable()
join t2 in db.Table2.AsEnumerable() on t1.AID equals t2.AID into outer
from item in outer.DefaultIfEmpty()
where item.TID == t1.TID
&& string.IsNullOrEmpty(item.Status) || item.Status != "Using"
select t1
);
But I have been facing issues with this.Here , it is not able to find item and thus returning
'Object reference not set to an instance of an object.'
What am I doing wrong over here????
I have been trying continuously but ended up with no solutions . So, any help in this would be highly appreciated.Thanks in advance..

Your SQL and LINQ statement are not the same, you have a different join clause.
Try this:
from t1 in db.Table1
join t2 in db.Table2 on new {
aID = t1.AID,
tID = t2.TID
}
equals new {
aID = t2.AID,
tID = t2.TID
} into outer
from item in outer.DefaultIfEmpty()
where item.Status == null || item.Status != "Using"
select t1
You can add multipe join relations with the new {} equals new {} statement.

DefaultIfEmpty returns a collection containing a single, default item if there is nothing in the collection.
As your entities are probably classes rather than structs, that means you get a collection with a single null in it, not a collection with a single item in it with null fields.
As well as the join clause change suggested by Loetn, you should also change your where condition to
where item == null || item.Status != "Using"

Related

Linq query is not working while sql is working properly

I am converting some SQL queries to Linq (Entity Framework). Most of queries are working fine, but I am facing little problem with the following one.
When I try this query in SQL Server Management Studio, it returns multiple records.
SELECT
bDrillDown,
Icon
FROM
dbo.Checklist
INNER JOIN
dbo.Codes ON Checklist.iCodeID = Codes.iCodeID
AND Codes.bDeleted = 0 AND Codes.bObsolete = 0
INNER JOIN
dbo.CodeGroup ON Codes.iGroupID = CodeGroup.iGroupID
AND CodeGroup.bDeleted = 0 AND CodeGroup.bInspection = 1
INNER JOIN
dbo.CodeInspectionTypeV ON Cast(LEFT(Checklist.LongKey, 6) as int) = CodeInspectionTypeV.InspectionTypeID
WHERE
Checklist.bDeleted = 0
ORDER BY
iChecklistID
When I convert it into LINQ query like:
var checkList = from checklist in db.Checklists
join code in db.Codes on checklist.iCodeID equals code.iCodeID
where code.bDeleted == false && code.bObsolete == false
join codeGroup in db.CodeGroups on code.iGroupID equals codeGroup.iGroupID
where codeGroup.bDeleted == false && codeGroup.bInspection == true
join codeInspectionType in db.CodeInspectionTypeVs on checklist.LongKey.Substring(0, 6) equals codeInspectionType.InspectionTypeID.ToString()
where checklist.bDeleted == false
orderby checklist.iChecklistID
select new
{
checklist.iChecklistID,
InspectionTypeID = checklist.LongKey.Substring(0, 6).ToString()
};
It does not return any records, only an empty array.
The problem is apparently in the following join condition
on checklist.LongKey.Substring(0, 6) equals
codeInspectionType.InspectionTypeID.ToString()
which is not equivalent to the SQL query one.
Unfortunately EF does not support string to numeric data conversions, so your attempt is good, but doesn't work when the string value contains leading zeroes as in your case.
To make it work, you need to left pad with zeroes the result of the codeInspectionType.InspectionTypeID.ToString(), which can be done (at least in the latest EF6.1.3) by using the DbFunctions.Right canonical function (similar to how to sort varchar column containing numeric values with linq lambdas to Entity):
on checklist.LongKey.Substring(0, 6) equals
DbFunctions.Right("00000" + codeInspectionType.InspectionTypeID, 6)
Try to add "into [an alias name]" at the end of join line.
After that add a from line using that alias name
after that use that alias name in the where line
from checklist in db.Checklists
join code in db.Codes on checklist.iCodeID equals code.iCodeID into Temp1
from t1 in Temp1
where t1.bDeleted == false && t1.bObsolete == false

Left Outer Join in Linq not working

I'm trying to do a left outer join in Linq but the below code is not working
var result = from dataRows1 in agdt.AsEnumerable()
join dataRows2 in hwt.AsEnumerable()
on dataRows1.Field<string>("ID") equals dataRows2.Field<string>("HWID")
where ((dataRows2.Field<string>("HWID") == null) &&
(dataRows1.Field<string>("TYPE")=="a"))
select dataRows1;
Without the where clauses I receive about 37000 rows and with it I receieve 0. The agdt table has 12000 rows and the hwt table has 6000. This is getting very frustrating. Can someone please help?
You are missing the DefaultIfEmpty method call.
From what I understand from your query, it should look something like:
var result = from dataRows1 in agdt.AsEnumerable()
join dataRows2 in hwt.AsEnumerable()
on dataRows1.Field<string>("ID") equals dataRows2.Field<string>("HWID")
into groupJoin
from leftOuterJoinedTable in groupJoin.DefaultIfEmpty()
where (leftOuterJoinedTable == null &&
(dataRows1.Field<string>("TYPE")=="a"))
select dataRows1;
It seems to me that this in essence would be the same as running the following SQL query
SELECT
DR1.*
FROM DataRows1 DR1
INNER JOIN DataRows2 DR2
ON DR1.ID=DR2.HWID
WHERE
DR2.HWID IS NULL
AND
DR1.Type='a'
Essentially your LINQ is doing an inner join and then executing the where. To truly do a left join, see the link
LEFT OUTER JOIN in LINQ

Left Join 2 tables with main table using LINQ

This is my Query in SQL :
Select distinct * from tr.Table1
Left Outer join tr.Table2 on tr.Table1.ID = tr.Table2.ID
Left Outer join tr.Table3 on tr.Table2.AId= tr.Table3.ID
where tr.Table1.Deleted =1 and tr.Table1.Ready=1 and tr.Table1.Show=0
The query is working in SQL and gives the expected results.The thing here is that I want the equivalent of this using LINQ. I have tried some variations in LINQ queries such as :
var query = from p in _ctx.Table1
join s in _ctx.Table2 on p.Id equals s.Id into bag1
from to in bag1.DefaultIfEmpty()
join tx in _ctx.Table3 on to.AId equals tx.Id into bag2
from ts in bag2.DefaultIfEmpty()
select new
{
ContactNo = to.Table1.ContactNo
};
But it always doesn't return all the field values. Some are returned as NULL. Also tried referring to some other link as well but they all focus on joining with the parent table whereas I have to join one of the joined tables with the other one. So here I am, struggling with this.
This is the output that I'm getting as of now. Some values are null. The field has values but due to some joining issue, they are returned as NULL.
Guidance here is appreciated. Thank you.
Your query looks fine to me, the reason why you must be getting the Nulls is because when we use DefaultIfEmpty, it returns null for non-matching rows, thus you need to handle that while fetching the actual results. Try doing something like this:-
var query = from p in _ctx.Table1
join s in _ctx.Table2 on p.Id equals s.Id into bag1
from to in bag1.DefaultIfEmpty()
join tx in _ctx.Table3 on to.AId equals tx.Id into bag2
from ts in bag2.DefaultIfEmpty()
select new
{
ContactNo = to == null ? String.Empty : to.Table1.ContactNo
};
Assuming, ContactNo to be of type String, I have used String.Empty you can use any default value.

LINQ equivalent for SQL

I am trying to convert a ASP.NET project to Entity framework. How to re-write the following query to its LINQ equivalent?
SELECT {Table1 objects}
FROM [Table1] tb1
INNER JOIN [Table2] tb2
ON tb1.Table1ID = tb2.fk_Table1ID
WHERE tb2.fk_attrib1 = '123' AND tb2.fk_attrb2 = '345'
ORDER BY tb1.attrib1
The result is a collection of Table1 objects.
Here Table1 and Table2 correspond to object System.Data.Objects.ObjectSet of ADO.NET Entity Framework.
var results = from tb1 in Context.Table1
join tb2 in Context.Table2 on tb1.Table1ID == tb2.fk_Table1ID
where tb2.fk_attrib1 == "123" && tb2.fk_attrb2 == "345"
orderby tb1.attrib1
select tb1;
Something like this:
context.Table1
.Where( o => o.Table2s.Any( o2 =>
o2.fk_attrib1 == '123' &&
o2.fk_attrib2 == '345' ) )
.OrderBy( o => o.attrib1 )
.ToList();
BTW, LINQPad is great for trying out L2E queries.
This should help you a little bit. I suppose the main problem is with JOIN clause - in EF you can use NavigationProperties and don't need to worry about joining tables - EF will take care of that for you.
Also you are trying to filter on column from joined table. This you can do using Any method to find all Table1 elements that are connected to Table2 where those referenced elements have certain properties/columns. You should also get familiar with All method, as it might be useful to you in future.
from t1 in context.Table1
where t1.Table2s.Any(t2.fk_attrib1 == "123" && t2 => t2.fk_attrb2 == "345")
order by t1.attrib1
select t1;
Edit:
I assume that there is 1:n relationship between Table1 and Table2 which results in enumerable collection as NavigationProperty in Table1 objects.
Edit2:
Fixed error in code - didn't noticed that both attributes are from Table2 not Table1
Should be something like this:
var result = (from tb1 in Table1
from tb2 in Table2
where tb1.Key == tb2.Key &&
tb2.fk_attrib1 = '123' &&
tb2.fk_attrb2 = '345'
select ione).OrderBy(p=>p.attrib1);
Hope this helps.

LINQ and various joining sample

i just learning LINQ. so first of all i need to be familiar with join with linq. i search google for left outer and right outer join with linq and i got answer like
left outer join
var LeftJoin = from emp in ListOfEmployees
join dept in ListOfDepartment
on emp.DeptID equals dept.ID into JoinedEmpDept
from dept in JoinedEmpDept.DefaultIfEmpty()
select new
{
EmployeeName = emp.Name,
DepartmentName = dept != null ? dept.Name : null
};
right outer join
var RightJoin = from dept in ListOfDepartment
join employee in ListOfEmployees
on dept.ID equals employee.DeptID into joinDeptEmp
from employee in joinDeptEmp.DefaultIfEmpty()
select new
{
EmployeeName = employee != null ? employee.Name : null,
DepartmentName = dept.Name
};
from then code i just could not understand how it is left outer join because no left outer key word is use here. so please tell me how to understand that the join is left outer join or right outer.
when i will use linq then how like operator can be use. 'a%' or '%a' or '%a%'. i saw there is contain method which is bit different.
please discuss the two issue. thanks
The "join ... in ... on ... into" piece of LINQ query syntax, is translated into a GroupJoin().
GroupJoin() method, for each key in the outer list (or table), returns a list of elements in the inner list (or table) having the same key, or an empty list if such key doesn't exist.
Hence, the left outer join code of your question is clearer:
If JoinedEmpDept (i.e. the list of elements having the same key of the current examined outer list entry) is empty, dept is set to null (thanks to DefaultIfEmpty() method).
Translation in pseudo code:
for each employee in ListOfEmployees
get the list of dept having ID equal to empl.DeptID
and set them into JoinedEmpDept
then for each dept in JoinedEmpDept
(if empty iterates over a single null dept)
returns an new element containing:
employee.Name and dept.Name (or null if dept is null)
The right outer join instead, is basically a left outer join with outer and inner lists exchanged.
About the "like" question, you should use string.Contains("a") for '%a%', string.StartsWith("a") for 'a%', string.EndsWith("a") for '%a'
Example:
var query = from el in listOfStrings
where el.StartsWith("AB")
select el;
EDIT:
About the IN() operator question...
well, you can use Contains() also for that, or Any():
var inGroup = new []{ "Foo", "Bar" };
var query1 = from el in listOfStrings
where inGroup.Contains(el)
select el;
// or equally
var query2 = from el in listOfStrings
where inGroup.Any(x => el.Equals(x))
select el;
The left outer join is so because of this line:
from dept in JoinedEmpDept.DefaultIfEmpty()
which will get all of the employees, even if they are not in a department. The DefaultIfEmpty turns the join into an left outer join, when the SQL is generated.
See this blog post for more details: C#: Left outer joins with LINQ
Left join Tip,
Instead of:
from user in tblUsers
join compTmp1 in tblCompanies
on user.fkCompanyID equals compTmp1.pkCompanyID into compTmp2
from comp in compTmp2.DefaultIfEmpty()
You can write:
from user in tblUsers
from comp in tblCompanies.Where(c => c.pkCompanyID == user.fkCompanyID).DefaultIfEmpty()

Categories

Resources