Converting a LINQ Inner join with Left Join - c#

I'm trying to implement a left join into my query, at the moment I'm getting a 'Object reference is not set to an instance of an object'.
The query is working perfectly as an inner join, but I want to include all rows from the left table despite if matches are found. I have tried to follow some of the previous posts on this, most refer to DefaultIfEmpty() but I'm failing to figure it out.
INNER JOIN - SQL
SELECT TOP (1000)
FROM table1 as edc
inner join table2 as c on edc.Id = c.Id
inner join table3 as p on p.Id = c.Id
group by p.Description
INNER JOIN - SQL
SELECT TOP (1000)
FROM table1 as edc
inner join table2 as c on edc.Id = c.Id
left join table3 as p on p.Id = c.Id
group by p.Description
INNER JOIN - LINQ
from edc in table1
join q1 in table2 on __edc.Id equals q1__.Id
join q2 in _table3 on q2.Id equals q1.Id
group q1 by q2.Description
into grouped
select new MyObj
{
Label = grouped.Key,
Value = grouped.Count(),
}
LEFT JOIN - LINQ
from edc in table1
join q1 in table2 on __edc.Id equals q1__.Id
join q2 in _table3 on q2.Id equals q1.Id into leftJoin
from p in leftJoin.DefaultIfEmpty()
group q1 by p.Description
into grouped
select new MyObj
{
Label = grouped.Key,
Value = grouped.Count(),
}

Consider the following example. We have three tables, with a left join between table1 and table2, and a second left join to table3. You need to specify DefaultIfEmpty() on the two joins to include rows where there is no match in the right table.
public class Item
{
public int Id { get; set; }
public string Description { get; set; }
}
class Program
{
static void Main(string[] args)
{
var table1 = new List<Item>
{
new Item {Id = 1, Description = "a"},
new Item {Id = 2, Description = "b"},
new Item {Id = 3, Description = "c"},
new Item {Id = 4, Description = "d"}
};
var table2 = new List<Item>
{
new Item {Id = 1, Description = "e"},
new Item {Id = 2, Description = "f"},
new Item {Id = 4, Description = "g"}
};
var table3 = new List<Item>
{
new Item {Id = 1, Description = "h"},
new Item {Id = 4, Description = "h"},
new Item {Id = 5, Description = "i"},
new Item {Id = 6, Description = "j"}
};
var leftJoin = from t1 in table1
join t2 in table2 on t1.Id equals t2.Id into firstJoin
from x in firstJoin.DefaultIfEmpty()
join t3 in table3 on x?.Id equals t3.Id into secondJoin
from y in secondJoin.DefaultIfEmpty()
select new
{
Table1Id = t1?.Id,
Table1Description = t1?.Description,
Table2Id = x?.Id,
Table2Description = x?.Description,
Table3Id = y?.Id,
Table3Description = y?.Description
};
Console.WriteLine("Left Join:");
foreach (var i in leftJoin)
{
Console.WriteLine($"T1Id: {i.Table1Id}, T1Desc: {i.Table1Description}, " +
$"T2Id: {i.Table2Id}, T2Desc: {i.Table2Description}, " +
$"T3Id: {i.Table3Id}, T3Desc: {i.Table3Description}");
}
Console.WriteLine(string.Empty);
var grouped = from x in leftJoin
group x by x.Table3Description
into group1
select new
{
Label = group1.Key,
Count = group1.Count()
};
Console.WriteLine("Left Join Grouped:");
foreach (var i in grouped)
{
Console.WriteLine($"Label: {i.Label}, Count: {i.Count}");
}
Console.ReadLine();
}
}
Running the program yields the following output:
Left Join:
T1Id: 1, T1Desc: a, T2Id: 1, T2Desc: e, T3Id: 1, T3Desc: h
T1Id: 2, T1Desc: b, T2Id: 2, T2Desc: f, T3Id: , T3Desc:
T1Id: 3, T1Desc: c, T2Id: , T2Desc: , T3Id: , T3Desc:
T1Id: 4, T1Desc: d, T2Id: 4, T2Desc: g, T3Id: 4, T3Desc: h
Left Join Grouped:
Label: h, Count: 2
Label: , Count: 2
Hope this helps!

it's very easy, just change "from p" to "from q2":
from edc in table1
join q1 in table2 on __edc.Id equals q1__.Id
join q2 in _table3 on q2.Id equals q1.Id into leftJoin
from q2 in leftJoin.DefaultIfEmpty()
group q1 by p.Description
into grouped
select new MyObj
{
Label = grouped.Key,
Value = grouped.Count(),
}

Related

Building linq query that collect records by group

scenario is simple actually but handling it in linq require more exp than I have..
There is 3 table
Table1
ID Column
1 val1
2 val2
Table2
ID Column
1 val3
2 val4
Table3
ID Column
1 val5
2 val6
I need such a query that returns;
TableResult:
Row ID Column Type
1 1 val1 table1
2 2 val2 table1
3 1 val3 table2
4 2 val4 table2
5 1 val5 table3
6 2 val6 table3
Searched on net and started like below but cant figure how handle the tricks create "type", merge records etc..
from t1 in table1
join t2 in table2 on t1.id equals t2.id
join t3 in table3 on t1.id equals t3.id
select new {...}
You've already accepted an answer, so I don't know if this is what you WANT, but it generates the output you specified in your post.
Because you have only used Id values of 1 and 2, it's unclear whether you actually want to perform a Join or just get the set of all rows into a single result.
Anyway:
struct TableStructure
{
public int Id { get; set; }
public string Column { get; set; }
}
var t1 = new List<TableStructure>() { new TableStructure { Id = 1, Column = "val1" }, new TableStructure { Id = 2, Column = "val2" } };
var t2 = new List<TableStructure>() { new TableStructure { Id = 1, Column = "val3" }, new TableStructure { Id = 2, Column = "val4" } };
var t3 = new List<TableStructure>() { new TableStructure { Id = 1, Column = "val5" }, new TableStructure { Id = 2, Column = "val6" } };
var result = ((from row1 in t1 select new { row1.Id, row1.Column, SourceTable = "table1" })
.Union(from row2 in t2 select new { row2.Id, row2.Column, SourceTable = "table2" })
.Union(from row3 in t3 select new { row3.Id, row3.Column, SourceTable = "table3" }))
.AsEnumerable().Select((row, index) => new { RowNum = index + 1, row.Id, row.Column, row.SourceTable });
result.ToList().ForEach(row => Console.WriteLine($"{row.RowNum}, {row.Id}, {row.Column}, {row.SourceTable}"));
output:
1, 1, val1, table1
2, 2, val2, table1
3, 1, val3, table2
4, 2, val4, table2
5, 1, val5, table3
6, 2, val6, table3
Same as what you did try Distinct at the end. query syntax would be :
var List = (from t1 in dbContext.table1
join t2 in dbContext.table2 on t1.ID equals t2.ID
join t3 in dbContext.table3 on t1.ID equals t3.ID
select new
{
//t1.DesiredColumnName,
//t2.DesiredColumnName,
//t3.DesiredColumnName,
//so on
}).Distinct().ToList();

Linq Joins only working when all tables have data

Below you can find my problem. My view only loops through the data when both of "DocentenCompetenties" and "DocentenLocaties" is filled in. This is a problen because I want to be able to loop them even if one of them doens't have any data
var ShowCompetenties = from d in db.Docent
join dc in db.DocentenCompetenties on d.DocentID equals dc.DocentID
join c in db.Competenties on dc.CompetentiesID equals c.CompetentiesID
join dl in db.DocentenLocaties on d.DocentID equals dl.DocentID
where d.DocentID == id
join l in db.Locaties on dl.LocatieID equals l.LocatieID
select new ShowCompetenties { Docenten = d, Competenties = c, DocentenCompetenties = dc, DocentenLocaties = dl, Locaties = l };
UPDATE
Current Error:
An exception of type 'System.NullReferenceException' occurred in App_Web_xp01otas.dll but was not handled in user code
Additional information: Object reference not set to an instance of an object.
var id = Convert.ToInt32(Session["id"]);
var LeftShowCompetenties = from d in db.Docent
join g1 in db.DocentenCompetenties on d.DocentID equals g1.DocentID into group1
from dc in group1.DefaultIfEmpty()
join c in db.Competenties on dc.CompetentiesID equals c.CompetentiesID
where d.DocentID == id
select new ShowCompetenties { Docenten = d, Competenties = c, DocentenCompetenties = dc};
var RightShowCompetenties = from d in db.Docent
join g3 in db.DocentenLocaties on d.DocentID equals g3.DocentID into group3 from dl in group3.DefaultIfEmpty()
where d.DocentID == id
join l in db.Locaties on dl.LocatieID equals l.LocatieID
select new ShowCompetenties { Docenten = d, Locaties = l, DocentenLocaties = dl };
var ShowCompetenties = LeftShowCompetenties.Union(RightShowCompetenties);
VIEW
<h4>Competenties</h4>
#foreach (var item in Model)
{
if(#item.DocentenCompetenties != null && #item.DocentenCompetenties.DocentID.ToString() != null) {
#item.Competenties.Name #Html.ActionLink("Delete", "DeleteCompetenties", new { id = item.DocentenCompetenties.DocentenCompetentiesID })
}
<h4>Docenten</h4>
#foreach (var item in Model)
{
if(#item.DocentenLocaties != null && #item.DocentenLocaties .DocentID.ToString() != null)
{
#item.Locaties.Name
}
}
You are doing an Inner join this way.
It sounds like you actually want to do an outer join.
Try this:
var ShowCompetenties = from d in db.Docent
join g1 in db.DocentenCompetenties on d.DocentID equals g1.DocentID into group1
from dc in group1.DefaultIfEmpty()
join g2 in db.Competenties on dc.CompetentiesID equals g2.CompetentiesID into group2
from c in group2.DefaultIfEmpty()
join g3 in db.DocentenLocaties on d.DocentID equals g3.DocentID into group3
from dl in group3.DefaultIfEmpty()
where d.DocentID == id
join l in db.Locaties on dl.LocatieID equals l.LocatieID
select new ShowCompetenties { Docenten = d, Competenties = c, DocentenCompetenties = dc, DocentenLocaties = dl, Locaties = l };
This is only a LEFT outer join however, if you wish to do a full outer join. You must first do a left, then a right outer join and finally merge (with Union()) the two.
EDIT
As per the comments, with regards to the error you're getting after the UNION:
var id = Convert.ToInt32(Session["id"]);
var LeftShowCompetenties = from d in db.Docent
join g1 in db.DocentenCompetenties on d.DocentID equals g1.DocentID into group1
from dc in group1.DefaultIfEmpty()
join c in db.Competenties on dc.CompetentiesID equals c.CompetentiesID
where d.DocentID == id
select new ShowCompetenties { Docenten = d, Competenties = c, Locaties = null, DocentenCompetenties = dc, DocentenLocaties = null};
var RightShowCompetenties = from d in db.Docent
join g3 in db.DocentenLocaties on d.DocentID equals g3.DocentID into group3
from dl in group3.DefaultIfEmpty()
where d.DocentID == id
join l in db.Locaties on dl.LocatieID equals l.LocatieID
select new ShowCompetenties { Docenten = d, Competenties = null, Locaties = l, DocentenCompetenties = null, DocentenLocaties = dl };
var ShowCompetenties = LeftShowCompetenties.Union(RightShowCompetenties);
(Check the added Locaties = null and Competenties = null in the constructors.)
VIEW
if (#item.DocentenCompetenties != null){}
if (#item.DocentenLocaties!= null){}
If you have navigation properties set up, it's much easier to left join:
var ShowCompetenties =
from d in db.Docent
where d.DocentID == id
from dc in d.DocentenCompetenties.DefaultIfEmpty()
let c = dc.Competenty
from dl in d.DocentenLocaties.DefaultIfEmpty()
let l = dl.Locaty
select new ShowCompetenties {
Docenten = d,
Competenties = c,
DocentenCompetenties = dc,
DocentenLocaties = dl,
Locaties = l };

Translate SQL statement with SUM() in Entity

I try to translate this SQL code :
SELECT w.Id, w.LastName, w.FirstName, SUM(d.Price*dt.Number) AS somme
FROM Waiter w
INNER JOIN Client c on w.Id = c.WaiterId
INNER JOIN DisheOnTable dt on c.Id = dt.ClientId
INNER JOIN Dishe d on dt.DisheId = d.Id
GROUP BY w.Id, w.LastName, w.FirstName
ORDER BY somme DESC;
in entity framework.
I tried something like this
var query2 = (from w in db.Waiter
join c in db.Client on w.Id equals c.WaiterId
join dt in db.DisheOnTable on c.Id equals dt.ClientId
join d in db.Dishe on dt.DisheId equals d.Id
group w by new { w.Id, w.LastName, w.FirstName } into g
//orderby g.Select() descending
select new
{
id = g.Key.Id,
lastname = g.Key.LastName,
firstname = g.Key.FirstName,
total = g.Sum(q => q.)
});
but my sum doesn't work (after multiple research and try) and i don't know how to multiply my variables.
PS : The SQL statement works well, i tried it.
Thank you for helping guys ! :)
You need to group on both dish and DishOnTable alias as Price is in Dish and Number is in DishOnTable:
group new{ d,dt} by new {w.Id, w.LastName, w.FirstName} into g
and now sum the columns which you want from it
select new {
id = g.Key.Id,
lastname = g.Key.LastName,
firstname = g.Key.FirstName,
total = g.Sum(q => q.d.Price * q.dt.Number)
}).OrderBy(x=>x.total)

Multiple joins using linq

This is my query:
var results = from table1 in dtSplitDates.AsEnumerable()
join table2 in dtSplitDates.AsEnumerable() on (int)table1["FID"] equals (int)table2["FID"] into lj
from r in lj.DefaultIfEmpty()
select dtSplitDates2.LoadDataRow(new object[]
{
r["FID"],
r["SLNO"],
r == null ? string.Empty : r["Dates"]
}, false);
Currently i am joining on Column FID - due to which i am getting 36 records (duplicates):
However in order to avoid duplicates i need to join also on SLNO column but i am unable to write that query - please help.
As per my understanding you want two join condition; Try this
var results = from table1 in dtSplitDates.AsEnumerable()
join table2 in dtSplitDates.AsEnumerable()
on new {id1 =(int)table1["FID"], SLno1= (int)table1["SLNO"]}
equals new {id2=(int)table2["FID"], SLno2=(int)table2["SLNO"]} into lj
from r in lj.DefaultIfEmpty()
select dtSplitDates2.LoadDataRow(new object[]
{
r["FID"],
r["SLNO"],
r == null ? string.Empty : r["Dates"]
}, false);
Try to implement with this example:
For Multiple Joins:
var result=(from com in db.Company.AsEnumerable()
join c in db.Country.AsEnumerable() on com.CountryID equals c.CountryID
join s in db.State.AsEnumerable() on com.StateID equals s.StateID
join ct in db.City.AsEnumerable() on com.CityID equals ct.CityID
orderby com.Name
select new CompanyModel()
{
CompanyID = com.CompanyID,
Name = com.Name,
AddressLine1 = com.AddressLine1,
CountryID = com.CountryID,
StateID = com.StateID,
CityID = com.CityID,
Country = c.CountryID,
State = s.StateID,
City = ct.CityID,
Pin = com.Pin,
Phone = com.Phone,
}).Distinct().ToList();

LINQ - Left join and Group by and Sum

I have two lists                                                      
var stores = new[]
{
new { Code = 1, Name = "Store 1" },
new { Code = 2, Name = "Store 2" }
};
var orders = new[]
{
new { Code = 1, StoreCode = 1, TotalValue = 14.12 },
new { Code = 2, StoreCode = 1, TotalValue = 24.12 }
};
OUTPUT
StoreName = Store 1 | TotalValue = 38.24
StoreName = Store 2 | TotalValue = 0
How can I translate this into LINQ to SQL?                                             
var lj = (from s in stores
join o in orders on s.Code equals o.StoreCode into joined
from j in joined.DefaultIfEmpty()
group s by new
{
StoreCode = s.Code,
StoreName = s.Name
}
into grp
select new
{
StoreName = grp.Key.StoreName,
TotalValue = ???
}).ToList();
When you doing group join, all orders related to store will be in group, and you will have access to store object. So, simply use s.Name to get name of store, and g.Sum() to calculate total of orders:
var lj = (from s in db.stores
join o in db.orders on s.Code equals o.StoreCode into g
select new {
StoreCode = s.Name,
TotalValue = g.Sum(x => x.TotalValue)
}).ToList();
Note - from your sample it looks like you don't need to group by store name and code, because code looks like primary key and it's unlikely you will have several stores with same primary key but different names.

Categories