LINQ: Combine join with left join - c#

Let's say I have this query in SQL:
SELECT
....
FROM
TableA a
JOIN TableB b on a.Id = b.Id
I've got that working just fine as a linq query:
var results = (from a in db.TableA
join b in db.TableB on a.Id equals b.Id
select new MyObject {...}).ToList();
But now I want to add a left join to the mix as well. In SQL:
SELECT
....
FROM
TableA a
JOIN TableB b on a.Id = b.Id
LEFT JOIN TableC c on a.Id2 = c.Id2
I'm not sure how to handle this in the linq query.

SQL's LEFT JOIN concept is a pretty clunky abstraction when you think about what you're really trying to do. You really want, for every TableA entry, to have a list of TableC items associated with it, right?
LINQ uses a much simpler (IMO) way to represent this abstraction, because it has the concept of nested, hierarchical objects. The provider will generate a LEFT JOIN, but it'll also build the result into an object structure that's easy to work with.
var results = (from a in db.TableA
join b in db.TableB on a.Id equals b.Id
select new MyObject {
...
TableCEntries =
from c in db.TableC
where c.Id == a.Id
select c // or select specific values from c
}).ToList();
If you really want your results flattened out the way they would be with a left outer join, you could do this:
var results = (from a in db.TableA
join b in db.TableB on a.Id equals b.Id
from c in db.TableC
where c.Id == a.Id
select new MyObject {
...
}).ToList();
... but you wouldn't get any entries for TableA where there are no entries present for TableC. For that behavior you'd have to do ugly stuff with group by/DefaultIfEmpty as shown in Ivan Stoev's link.

Related

SQL to LINQ - Left Join Before Inner Join

So I have a SQL query that I would like to convert to LINQ.
Here is said query:
SELECT *
FROM DatabaseA.SchemaA.TableA ta
LEFT OUTER JOIN DatabaseA.SchemaA.TableB tb
ON tb.ShipId = ta.ShipId
INNER JOIN DatabaseA.SchemaA.TableC tc
ON tc.PostageId= tb.PostageId
WHERE tc.PostageCode = 'Package'
AND ta.MailId = 'Specification'
The problem I am struggling with is I cannot seem to figure out how to do a left join in LINQ before an inner join, since doing a left join in LINQ is not as clear to me at least.
I have found numerous examples of a LINQ inner join and then a left join, but not left join and then inner join.
If it helps, here is the LINQ query I have been playing around with:
var query = from m in tableA
join s in tableB on m.ShipId equals s.ShipId into queryDetails
from qd in queryDetails.DefaultIfEmpty()
join p in tableC on qd.PostageId equals p.PostageId
where m.MailId == "Specification" && p.PostageCode == "Package"
select m.MailId;
I have tried this a few different ways but I keep getting an "Object reference not set to an instance of an object" error on qd.PostageId.
LINQ is very new to me and I love learning it, so any help on this would be much appreciated. Thanks!
From my SQL conversion recipe:
JOIN conditions that aren't all equality tests with AND must be handled using where clauses outside the join, or with cross product (from ... from ...) and then where
JOIN conditions that are multiple ANDed equality tests between the two tables should be translated into anonymous objects
LEFT JOIN is simulated by using into joinvariable and doing another from from the joinvariable followed by .DefaultIfEmpty().
The order of JOIN clauses doesn't change how you translate them:
var ans = from ta in TableA
join tb in TableB on ta.ShipId equals tb.ShipId into tbj
from tb in tbj.DefaultIfEmpty()
join tc in TableC on tb.PostageId equals tc.PostageId
where tc.PostageCode == "Package" && ta.MailId == "Specification"
select new { ta, tb, tc };
However, because the LEFT JOIN is executed before the INNER JOIN and then the NULL PostageIds in TableB for unmatched rows will never match any row in TableC, it becomes equivalent to an INNER JOIN as well, which translates as:
var ans2 = from ta in tableA
join tb in tableB on ta.ShipId equals tb.ShipId
join tc in tableC on tb.PostageId equals tc.PostageId
where tc.PostageCode == "Package" && ta.MailId == "Specification"
select new { ta, tb, tc };
Use:
var query = from m in tableA
join s in tableB on m.ShipId equals s.ShipId
join p in tableC on s.PostageId equals p.PostageId
where m.MailId == "Specification" && p.PostageCode == "Package"
select m.MailId;
Your query uses a LEFT OUTER JOIN but it doesn't need it.
It will, in practice, function as an INNER JOIN due to your tc.PostageCode = 'Package' clause. If you compare to a column value in a table in a WHERE clause (and there are no OR clauses and you aren't comparing to NULL) then effectively all joins to get to that table will be treated as INNER).
That clause will never be true if TableB is null (which is why you use LEFT OUTER JOIN vs INNER JOIN) - so you should just use an INNER JOIN to make the problem simpler.

How to convert store procedure to linq in nopCommerce c#

How to convert store procedure to linq in nopCommerce c#
My store procedure query
SELECT p.Id
FROM Product p WITH (NOLOCK)
LEFT JOIN Discount_AppliedToProducts dap WITH (NOLOCK)
ON p.Id = dap.Product_Id
LEFT JOIN Product_Category_Mapping pcm WITH (NOLOCK)
ON p.Id = pcm.ProductId
LEFT JOIN Discount_AppliedToCategories dac WITH (NOLOCK)
ON pcm.CategoryId = dac.Category_Id
AND dac.Category_Id IN (1 ,2 ,3 ,4 ,5 ,6)
LEFT JOIN Product_Manufacturer_Mapping pmm WITH (NOLOCK)
ON p.Id = pmm.ProductId
LEFT JOIN Discount_AppliedToManufacturers dam WITH (NOLOCK)
ON pmm.ManufacturerId = dam.Manufacturer_Id
WHERE dap.Discount_Id IN (3)
OR dac.Discount_Id IN (3)
OR dam.Discount_Id IN (3)
My linq query
var productlist = (from q in _productRepository.Table
select q).ToList();
var discount_AppliedToProductIds = (from dp in _discountRepository.Table
from p in dp.AppliedToProducts
select p).ToList().DistinctBy(d => d.Id).ToList();
var discount_AppliedToCategorieIds = (from dp in _discountRepository.Table
from c in dp.AppliedToCategories
select c).ToList().DistinctBy(d => d.Id).ToList();
var discount_AppliedToManufacturerIds = (from dp in _discountRepository.Table
from m in dp.AppliedToManufacturers
select m).ToList().DistinctBy(d => d.Id).ToList();
var product_Manufacturer_Mapping = (from dp in productlist
from pm in dp.ProductManufacturers
select pm).ToList().DistinctBy(d => d.Id).ToList();
var product_Category_Mapping = (from dp in productlist
from pc in dp.ProductCategories
select pc).ToList().DistinctBy(d => d.Id).ToList();
var ss = (from p in productlist
join dap in discount_AppliedToProductIds on p.Id equals dap.Id
join pcm in product_Category_Mapping on p.Id equals pcm.ProductId
//join dac in discount_AppliedToCategorieIds on pcm.CategoryId equals dac.Id
from dac in discount_AppliedToCategorieIds
join pmm in product_Manufacturer_Mapping on p.Id equals pmm.ProductId
join dam in discount_AppliedToManufacturerIds on pmm.ManufacturerId equals dam.Id
from dapd in dap.AppliedDiscounts
from pacd in dac.AppliedDiscounts
from damd in dam.AppliedDiscounts
where discountIds.Any(d => dapd.Id == d || d == pacd.Id || d == damd.Id)
// innner join condition
where categoryIds.Any(d => d == dac.Id) && dac.Id == pcm.CategoryId
select p).ToList();
I have write this code into c# but this code not provide proper result. Now I don't know what is problem into this code. If I run this code into sql server, then I get proper result, but in c# code I don't get proper result.
It's been a long time since I wrote a query in LINQ, but I seem to recall that if you wish to model a LEFT JOIN, you have to use DefaultIfEmpty(), otherwise you end up with an INNER JOIN.
See this, it's answer shows where to apply DefaultIfEmpty:
Linq join iquery, how to use defaultifempty
Obviously if you don't correctly model a LEFT JOIN expression, you'll end up with results only when all 3 inputs produce values.
I would also suggest not using .ToList() on each of your source queries, because that's going to manifest data into memory and use LINQ to Objects for your final query. If you remove the .ToList(), LINQ will construct a single database query for the entire process.
Mark

Transform SQL query to Linq left join clause incorrect

I'm trying to select the MatchingObjects that doesn't exist in the Unlock table , I have this SQL query as following:
select a.* from MatchingObjects a
left join Unlocks b
on a.ObjectCategoryId = b.ObjectCategoryId
left join Members c
on b.StudentId = c.Id
and b.StudentId = #studentId
where b.ObjectCategoryId is null
and c.id is null
order by a.ObjectCategoryId
And a LINQ query
var query = (from d in db.ObjectCategories
join a in db.MatchingObjects on d.Id equals a.ObjectCategoryId into grp3
join b in db.Unlocks
on d.Id equals b.ObjectCategoryId into grp1
from m in grp1.DefaultIfEmpty()
join c in db.Members
on m.StudentId equals c.Id into grp2
from n in grp2.DefaultIfEmpty()
where m.ObjectCategoryId == null
&& n.Id == null
orderby d.Id).AsEnumerable()
;
However, the LINQ query is not showing the same result as that I want like in the SQL query. Could you guys tell me what I should change in my LINQ Query?
This is the model:
Better you can use below tools:
An SQL-> LINQ converter..
http://www.sqltolinq.com
http://www.linqpad.net/
try this one
var query = from m in db.MatchingObjects.Where(w => w.ObjectCategoryId == null)
join u in db.Unlocks.Where(w => w.studentId == #studentId)
on m.ObjectCategoryId equals u.ObjectCategoryId into joinedU
from ju in joinedU.DefaultIfEmpty()
join m in db.Members.Where(w => w.id == null)
on ju.StudentId equals m.Id into joinedM
from jm in joinedM.DefaultIfEmpty()
select m;
But your request is a bit Strange. You make the join by ObjectCategoryId and in the Where clause you put ObjectCategoryId == null!!!
sorry guys, the Sql and the LINQ query was a bit different in table selection. I'm sorry about that because I was trying different Linq while posting the question, but The main problem is at the join clause
(from d in db.ObjectCategories
join a in db.MatchingObjects
on d.Id equals a.ObjectCategoryId into grp3
join b in db.Unlocks
on d.Id equals b.ObjectCategoryId into grp1
from m in grp1.DefaultIfEmpty()
join c in db.Members
on m.StudentId equals studentId into grp2
from n in grp2.DefaultIfEmpty()
where m.ObjectCategoryId == null
where n.Id == null
orderby d.Id).AsEnumerable()
/* this is the correct one */
join c in db.Members
on m.StudentId equals studentId into grp2
/* the below was the original incorrect join clause*/
join c in db.Members
on m.StudentId equals c.Id into grp2

Making 1 table join on 2 other tables with LINQ

I'm trying to join 1 table onto 2 other tables with LINQ, but I can't seem to figure out how this is done.
I can make it work writing pure SQL statements within Visual studio, I'm just not sure how to convert this into LINQ.
Here's my SQL statement:
SELECT c.CustomerId, c.CustomerName, pw.Number, pc.Number FROM Customers as c
LEFT JOIN Tasks as k ON k.Id = c.Task_Id
LEFT JOIN Workers as w ON w.Id = k.Worker_Id
LEFT JOIN PersonNumbers as pw ON pw.Person_Id = w.Id
LEFT JOIN Chiefs as ch ON ch.Id = k.Chief_Id
LEFT JOIN PersonNumbers as pc ON pc.Person_Id = ch.Id
Perhaps this requires a bit of explanation.
We have a bunch of Customers and these customers can have some tasks. Within a task you will have workers and chiefs. Within the PersonNumbers table, I have some extra information about workers and chiefs, and this is the information that I need.
You should be able to do something like the following assuming all your joins are based on foreign keys that should result in navigation properties in your entities. The DefaultIfEmpty is what makes everything a left join.
var results = from c in db.Customers
from k in c.Tasks.DefaultIfEmpty()
from w in k.Workers.DefaultIfEmpty()
from pw in w.Persons.DefaultIfEmpty()
from ch in k.Chiefs.DefaultIfEmpty()
from pc in ch.Persons.DefaultIfEmpty()
select new
{
c.CustomerId,
c.CustomerName,
pw.Number,
pc.Number
};
If you don't have the navigation properties then you'll have to use join.
var results = from c in db.Customers
join xk in db.Tasks on xk.Id equals c.Task_Id
from k in xk.DefaultIfEmpty()
join xw in db.Workers on xw.Id equals k.Worker_Id
from w in xw.DefaultIfEmpty()
join xpw in db.Persons on xpw.Person_Id equals w.Id
from pw in xpw.DefaultIfEmpty()
join xch in db.Chiefs on xch.Id equals k.Chief_Id
from ch in xch.DefaultIfEmpty()
join xpc in db.Persons on xpc.Person_Id euals ch.Id
from pc in xpc.DefaultIfEmpty()
select new
{
c.CustomerId,
c.CustomerName,
pw.Number,
pc.Number
};

LINQ join queries on multiple table

I have struggling to run linq left on multiple tables.
tableA
Select all the (courseID, code, title) from courseInstances table
tableB
select (studyLevel_ID) from Courses table where courseID from tableA = CourseID from tableB. tableB has courseID
tableC
Select (StudyLevelDescription) from StudyLevel table where studyLevelID from tableB = studyLevel from tableC.
I believe I need left join on table A as I need all the records
I have done separate linq which are working fine but struggling to bring combine result
CourseInstances results
var processedCourseInstance =
(from _courseInstances in _uof.CourseInstances_Repository.GetAll()
join _courses in _uof.Courses_Repository.GetAll() on _courseInstances.CourseID equals _courses.CourseID
into a from b in a.DefaultIfEmpty()
orderby _courseInstances.CourseCode
select _courseInstances).ToList();
StudyLevel results for each course
var _CoursesStudyLevel_Lookup =
(from _course in _uof.Courses_Repository.GetAll()
join _studyLevel in _uof.StudyLevel_Repository.GetAll() on _course.StudyLevelId equals _studyLevel.StudyLevelID
select new {_course.CourseID, _course.StudyLevelId, _studyLevel.LevelDescription }).ToList();
I have managed to combine two results but NOT with LEFT join on CourseInstance table. This time I used LINQPad
from _courseInstances in CourseInstances
join _courses in Courses on _courseInstances.CourseID equals _courses.CourseID
join _studylevel in StudyLevels on _courses.StudyLevelId equals _studylevel.StudyLevelID
orderby _courseInstances.CourseCode
select new {_courseInstances.CourseID, _courseInstances.CourseCode, _courseInstances.CourseTitle, _courseInstances.UCASCode, _courses.StudyLevelId, _studylevel.LevelDescription, _studylevel.SLevelType }
for above SQL version as following;
SELECT [t0].[CourseID], [t0].[CourseCode], [t0].[CourseTitle],
[t0].[UCASCode], [t1].[StudyLevelId], [t2].[LevelDescription], [t2].[SLevelType]
FROM [CourseInstances] AS [t0]
INNER JOIN [Courses] AS [t1] ON [t0].[CourseID] = ([t1].[CourseID])
INNER JOIN [StudyLevel] AS [t2] ON [t1].[StudyLevelId] = ([t2].[StudyLevelID])
ORDER BY [t0].[CourseCode]
If i understand correctly, you want something like this?
from _courseInstances in CourseInstances
join c in Courses on _courseInstances.CourseID equals c.CourseID into courses
from _courses in courses.DefaultIfEmpty()
join sl in StudyLevels on _courses.StudyLevelId equals sl.StudyLevelID into studyLevels
from _studylevel in studyLevels.DefaultIfEmtpy()
orderby _courseInstances.CourseCode
select new {
_courseInstances.CourseID,
_courseInstances.CourseCode,
_courseInstances.CourseTitle,
_courseInstances.UCASCode,
_courses.StudyLevelId,
_studylevel.LevelDescription,
_studylevel.SLevelType
}
}
You can create a LINQ left join query with the into keyword and .DefaultIfEmpt().

Categories

Resources