Object result from a left join is null c# - c#

Im trying to do a left join in Linq joining two different list as you can see below:
var fab = (from a in lstt
join b in lstt2 on new { a.PO, a.Line } equals new { b.PO, b.Line } into newab
from ab in newab.DefaultIfEmpty()
select new BomFabConcentrado
{
PO = a.PO,
Line = a.Line,
Fabric1 = ab.Fabric1
}).ToList();
The problem is that the result of ab is null and I got the following error message:
System.NullReferenceException: 'Object reference not set to an instance of an object.'
ab was null.
As extra information lstt contains 900 elements and lstt contains 200.
Does anyone know what is wrong?
Thanks for you help.

Left join use following query
var fab = (from a in lstt
join b in lstt2 on new { a.PO, a.Line } equals new { b.PO, b.Line } into newab
from ab in newab.DefaultIfEmpty()
select new BomFabConcentrado
{
PO = a.PO,
Line = a.Line,
Fabric1 = (ab == null || ab.Fabric1 == null) ? 0 : ab.Fabric1
//or use
Fabric1 = ab?.Fabric1 ?? 0
}).ToList();

Add a null check to your lists. For example like this:
lstt = lstt.Where(a => a != null).ToList();
lstt2 = lstt2.Where(b => b != null).ToList();
var fab = (from a in lstt
join b in lstt2 on new { a.PO, a.Line } equals new { b.PO, b.Line }
into newab
from ab in newab.DefaultIfEmpty()
select new BomFabConcentrado
{
PO = a.PO,
Line = a.Line,
Fabric1 = ab.Fabric1
}).ToList();

Related

C# Linq - Issue with joining3 tables - Error CS1941

I'm trying to fetch the records based on this query
documentList = await (from doc in _dbContext.Documents
join doce in _dbContext.StudentEnrollments on doc.ID equals doce.ID
join enr in _dbContext.Enrollments on doce.EnrollmentID equals enr.Id
where enr.name != null
&& (enr.faiedlattemps = 1 || enr.ReferenceNumber != null)
select doc);
I am getting the
"Correlates the elements of two sequences based on matching key error. The default equality comparer is used to compare keys"
I tried with this as well
var result = from x in _dbContext.Documents
join y in _dbContext.StudentEnrollments
on new { X1 = x.ID } equals new { X1 = y.ID}
join z in _dbContext.Enrollments on new { Z1 = y.EnrollmentID } equals new { Z1 = z.Id}
select new
{
/// Columns
};
Getting the same error

How do you Left join 'Is Not Null' using linq?

I am having some issues getting my LINQ statement to work. I am left joining a table, secondTable, where one of the columns can be null but I only need the records where this column is not null. I'm not sure how to get the following into a LINQ expression
LEFT JOIN secondTable b ON a.ID = b.oneTableID AND b.name IS NOT NULL
So far my LINQ is:
var list = await (from one in dbRepository.oneTable
join two in dbRepository.secondTable
on new { name = one.name, phone = one.phone, height = { is not null} } equals new
{ name = two.name, phone = two.phone, height = two.height
into temp
from two in temp.DefaultIfEmpty()
select new.....
Any Ideas?
EDIT 1: I was able to find a solution.
var list = await (from one in dbRepository.oneTable
join two in dbRepository.secondTable
on new { name = one.name, phone = one.phone, height = false } equals new
{ name = two.name, phone = two.phone, height = string.IsNullOrEmpty(two.height)}
into temp
from two in temp.DefaultIfEmpty()
select new.....
You have to use SelectMany possibility to create LEFT JOIN:
var query =
from one in dbRepository.oneTable
from two in dbRepository.secondTable
.Where(two => two.name = one.name && two.phone == one.phone
&& two.height != null)
.DefaultIfEmpty()
select new.....
Try this one:
var list = await (from one in dbRepository.oneTable
join two in dbRepository.secondTable
on new { name = one.name, phone = one.phone}
equals new
{ name = two.name, phone = two.phone}
into temp
from two in temp.DefaultIfEmpty()
where one.height == null || one.height = two.height
select new.....

EF Core 2.1 group by with skip and take

I a currently "paging" through an IQueryable that has many fields { Table1Id, Table1Label, IsActive, ...} and there can be mulitple records in IQueryable result for a PdcId, like the following:
var result = from a in _entities.table1
join b in _entities.table2 on new { a.Table1Id, a.Table1LevelId } equals new { Table1Id = b.Table2Id, Table1LevelId = b.Table2LevelId }
join c in _entities.Table3 on a.Table1Id equals c.Table1Id into cc from ccc in cc.DefaultIfEmpty()
where a.Valide == true
select new MeteringPointDetailModel
{
Table1Id = a.Table1Id,
Table1Label = a.Label,
IsActive = a.IsActive,
Table3Label = ccc.Label,
};
I tried this code but when i see the result generating in sql using sql profiler, the query is running without stop !! (generate multiple sql query ).
var data = await result.Where(i => result.GroupBy(p => p.Table1Id)
.Select(t => t.Key)
.Skip(query.Page).Take(query.PageSize)
.Contains(i.Table1Id)).ToListAsync();
How would I do this if I wanted to first group by Table1Id and then skip and take on the distinct Table1Id.
for example: if I have a result that has two records, one ends up being # 50 and the other 51, if I page by 50, I won't get record 51, and I want it.
That's what i did and worked for me :
Solution 1
var dataTable1 = (from a in _entities.table1
where a.Valide == true
select new {...}).Skip(query.Page).Take(query.PageSize);
var result = from a in dataTable1
join b in _entities.table2 on new { a.Table1Id, a.Table1LevelId } equals new { Table1Id = b.Table2Id, Table1LevelId = b.Table2LevelId }
join c in _entities.Table3 on a.Table1Id equals c.Table1Id into cc from ccc in cc.DefaultIfEmpty()
select new MeteringPointDetailModel
{
Table1Id = a.Table1Id,
Table1Label = a.Label,
IsActive = a.IsActive,
Table3Label = ccc.Label,
};
var data = await result.ToListAsync();
Solution 2
var result = from a in _entities.table1
join b in _entities.table2 on new { a.Table1Id, a.Table1LevelId } equals new { Table1Id = b.Table2Id, Table1LevelId = b.Table2LevelId }
join c in _entities.Table3 on a.Table1Id equals c.Table1Id into cc from ccc in cc.DefaultIfEmpty()
where a.Valide == true
select new MeteringPointDetailModel
{
Table1Id = a.Table1Id,
Table1Label = a.Label,
IsActive = a.IsActive,
Table3Label = ccc.Label,
};
result = !string.IsNullOrWhiteSpace(query.Table3Label) ? result.Where(c => c.Table3Label.ToLower().Contains(query.Table3Label.ToLower())) : result;
var table1Ids = await result.OrderBy(p => p.Table1Id)
.Select(p => p.Table1Id).Distinct()
.Skip(query.Page).Take(query.PageSize)
.ToListAsync();
var data = await result.Where(p => table1Ids.Contains(p.Table1Id)).ToListAsync();
Solution 2 is more useful if there are clause condition (search criteria) in join tables as specified in the example.
Hope it will help someone.

How to create LINQ from complex SQL?

I've this SQL query:
SELECT D.ID
FROM Documents AS D
INNER JOIN DocClasses AS DC WITH (NOLOCK)
ON D.DocClass = DC.ID
INNER JOIN DocSubClasses AS DSC WITH (NOLOCK)
ON D.DocSubClass = DSC.ID AND DSC.DocClassID = DC.ID
INNER JOIN DocPathFolders AS F WITH (NOLOCK)
ON D.DocPathFolderID = F.ID
WHERE
DC.ShortName = 'PAY' AND DSC.Name = 'xxxxx'
AND UPPER(F.Description) = 'READY TO SEND'
I'm trying to convert this query into LINQ. Here is what I've done so far:
from D in ctx.Documents
join DC in ctx.DocClasses on D.DocClass equals DC.ID
join DSC in ctx.DocSubClasses
on new { D.DocSubClass, DSC.DocClassID } equals new { DSC.ID, DC.ID }
join F in ctx.DocPathFolders
on D.DocPathFolderID equals F.ID
where
DC.ShortName == "PAY"
&& DSC.Name == "xxxxx"
&& (F.Description).ToUpper() == "READY TO SEND"
select D.ID;
I'm getting error in this line:
join DSC in ctx.DocSubClasses on new { D.DocSubClass, DSC.DocClassID } equals new { DSC.ID, DC.ID }
Here, I'm getting following error:
The name 'DSC' is not in scope on the left side of 'equals'
The name 'DC' is not in scope on the right side of 'equals'
I'm new in LINQ and that's why I can't solve these error. I would apperciate any suggestion on the above. Thanks.
You should rearrange the properties in the anonymous objects like this:
join DSC in ctx.DocSubClasses
on new { D.DocSubClass, DC.ID } equals new { DSC.DocClassID, DSC.ID }
Those on the right should be of the table you are joining (DSC) and those on the left can be of previous tables
You can try with Lambda Join instead of using LINQ.
Your JOIN statements look like below in Lambda.
C# Fiddle
var result = ctx.Documents.Join(ctx.DocClasses , d => new { Id = d.DocClass }, dc => new { Id = dc.ID }, (d, dc) => new { doc = d, docClass = dc }) //INNER JOIN DocClasses
.Join(ctx.DocSubClasses , d => new { sc = d.doc.DocSubClass, Id = d.docClass.ID }, dsc => new { sc = dsc.ID, Id = dsc.DocClassID }, (d, dsc) => new { doc = d, dsc = dsc } ) //INNER JOIN DocSubClasses
.Join(ctx.DocPathFolders, d => new { fId = d.doc.doc.DocPathFolderID }, f => new { fId = f.ID }, (d, f) => new { doc = d, f = f }) //INNER JOIN DocPathFolders
.Where(x => x.doc.doc.docClass.ShortName == "PAY" && x.doc.dsc.Name == "xxxxx" && x.f.Description.ToUpper() == "READY TO SEND")//Apply where clause
.Select(y => y.doc.doc.doc.ID);//Select whatever you want
Here is how I've solved my issue:
from D in ctx.Documents
join DC in ctx.DocClasses on D.DocClass equals DC.ID
join DSC in ctx.DocSubClasses on new { D.DocSubClass, DC.ID } equals new { DocSubClass = DSC.ID, ID = DSC.DocClassID }
join F in ctx.DocPathFolders on D.DocPathFolderID equals F.ID
where DC.ShortName == "PAY" && DSC.Name == "xxxx" && (F.Description).ToUpper() == "READY TO SEND"
select D.ID;
Just switch DSC into right side of equals and DC to the left, and it should work fine.
join DSC in ctx.DocSubClasses on new { D.DocSubClass, DC.ID } equals new { DSC.ID, DSC.DocClassID}
Hint: Provided you have set your relations appropriately in your database you almost never need to use "join". It is easier to write in LINQ and relations are exposed as "navigational properties". You simply use "dot notation". ie:
var result = from d in ctx.Documents
where d.DocClasses.Any( dc => dc.ShortName == "PAY"
&& dc.DocSubClasses.Any( dsc => dsc.Name == "xxxxx" )
&& d.DocPathFolders.Any( dpf => dpf.Description.ToUpper() == "READY TO SEND" )
select d.ID;
Note: We don't have any idea on your model. I assumed the relations as 1-To-Many. If it were Many-To-1 then you would simply use notation like:
where d.DocClass.ShortName == "PAY"
&& d.DocClass.DocSubClass.Name == "xxxxx"
&& d.DocPathFolder.Description.ToUpper() == "READY TO SEND"
HINT2: Download and start using Linqpad from LinqPad.net. It is a great tool where you can not only test your LINQ queries but use as a .Net scratch pad.

null reference exception on using DefaultIfEmpty()

I wrote the below sql query
SELECT *
FROM dbo.PR_ADDRESS LEFT OUTER JOIN
dbo.Book_MarkText ON dbo.PR_ADDRESS.GroupID = dbo.Book_MarkText.BMData
where
dbo.PR_ADDRESS.GroupID = dbo.Book_MarkText.BMData
OR
PR_ADDRESS.GroupID in ('001','002','003')
to LINQ as
string[] srGroupIDs = new string[] { "001", "002", "003" };
var objResult =
(from PR_ADDRESS in dtPR_ADDRESS.AsEnumerable()
join
Book_MarkText in dtBookmarkText.AsEnumerable() on
(string)PR_ADDRESS["GroupID"] equals (string)Book_MarkText["BMData"]
into Book_MarkText_join
from Book_MarkText_Temp in Book_MarkText_join.DefaultIfEmpty()
where srGroupIDs.Contains(PR_ADDRESS["Groupid"])
|| ((string)PR_ADDRESS["Groupid"] == (string)Book_MarkText_Temp["BMData"])
select new
{
ID = PR_ADDRESS["ID"],
Name1 = PR_ADDRESS["Name1"]
}).ToList();
But Gives me null reference exception on line
where srGroupIDs.Contains(PR_ADDRESS["Groupid"])
|| ((string)PR_ADDRESS ["Groupid"] == (string)Book_MarkText_Temp["BMData"])
Why ?
If i remove .DefaultIfEmpty() , this works without exception , but i need 'LEFT OUTER JOIN' so can't remove .DefaultIfEmpty() . How can i solve it ?
Edit :-
I added the OR case also , Hope now it is clear
Solved by handling Book_MarkText_Temp null as Jon Skeet's comment .Thanks for finding time to help me .
string[] srGroupIDs = new string[] { "001", "002", "003" };
var objResult =
(from PR_ADDRESS in dtPR_ADDRESS.AsEnumerable()
join
Book_MarkText in dtBookmarkText.AsEnumerable() on
(string)PR_ADDRESS ["GroupID"] equals (string)Book_MarkText["BMData"] into Book_MarkText_join
from Book_MarkText_Temp in Book_MarkText_join.DefaultIfEmpty()
where Book_MarkText_Temp == null ? srGroupIDs.Contains(PR_ADDRESS["Groupid"]) :
srGroupIDs.Contains(PR_ADDRESS["Groupid"])
|| ((string)PR_ADDRESS["Groupid"] == (string)Book_MarkText_Temp["BMData"])
select new {
ID = PR_ADDRESS ["ID"],
Name1 = PR_ADDRESS ["Name1"]
}).ToList();

Categories