Left join is not working for complex query - c#

I have very complex linq query which is working fine for inner join, i.e., without z_temp.DefaultIfEmpty(). But when I use this for left join, The query is not yielding results.
var q = from x in db.EmployeesList
where x.EmployeesListStartDate >= startDate && x.EmployeesListStartDate <= endDate
join y in db.Survey on x.Survey.SurveyID equals y.SurveyID
join z in
(from a in db.Commit
join b in
(from commit in db.Commit
where
commit.CommitListID != null &&
commit.CommitType.ToUpper() != "PREVIEW"
group commit by new
{
commit.CommitListID
} into g
select new
{
CommitListID = (Int32?)g.Key.CommitListID,
CommitId = (Int32?)g.Max(p => p.CommitId)
})
on new { a.CommitListID, a.CommitId }
equals new { b.CommitListID, CommitId = (Int32)b.CommitId }
select new
{
CommitListID = (Int32?)a.CommitListID,
CommitUsername= a.CommitUsername,
CommitStartDateTime=a.CommitStartDateTime,
CommitType=a.CommitType,
CommitSuccessCount=a.CommitSuccessCount
}) on new { EmployeesListID = x.EmployeesListID } equals new { EmployeesListID = (Int32)z.CommitListID }
into z_temp
from _z in z_temp.DefaultIfEmpty()
select new CustomEmployeesList
{
SurveyId = x.Survey.SurveyID != null ? (int)x.Survey.SurveyID : 0,
EmployeesListId = x.EmployeesListID != null ? (int)x.EmployeesListID : 0,
EmployeesListName = x.EmployeesListName,
SpecificMessage = x.SpecificMessage,
ListCriteria = x.ListCriteria,
Channel = x.Channel,
EmployeesListStartDate = (DateTime)x.EmployeesListStartDate,
EmployeesListEndDate = (DateTime)x.EmployeesListEndDate,
Records = x.Records != null ? (int)x.Records : 0,
QueryId = x.AppSqlQueries.QueryId != null ? (int)x.AppSqlQueries.QueryId : 0,
//AuditId = (Int32?)x.AuditEntry.AuditId,
StatusCommonCode = x.CommonCode.CommonCodeId != null ? (int)x.CommonCode.CommonCodeId : 0,
SurveyName = y.SurveyName,
LastCommitDateTime = _z.CommitStartDateTime.HasValue ? (DateTime)_z.CommitStartDateTime : DateTime.MinValue,
LastCommitType = _z.CommitType != null ? _z.CommitType : "",
LastCommitUsername = _z.CommitUsername != null ? _z.CommitUsername : "",
LastCommitCount = _z.CommitSuccessCount.HasValue ? (int)_z.CommitSuccessCount : 0
};
This is returning no results and
I am getting this exception message while viewing results in debug mode:
LINQ to Entities does not recognize the method
'System.Collections.Generic.IEnumerable1[<>f_AnonymousType351[<>f_AnonymousType35%5bSystem.Nullable1[System.Int32],System.String,System.Nullable1%5bSystem.DateTime%5d,System.String,System.Nullable`1%5bSystem.Int32%5d%5d%5d">System.Nullable1[System.Int32],System.String,System.Nullable1[System.DateTime],System.String,System.Nullable1[System.Int32]]]
DefaultIfEmpty[<>f__AnonymousType35' method, and this method cannot be
translated into a store expression.
Can anyone suggest the where the problem would be, this would be really helpful!

The problem is in this line:
from _z in z_temp.DefaultIfEmpty()
Calling DefaultIfEmpty() will return null if no rows matches the join. Ok, you a left join, but you have to test if _z is null before access its members:
...
LastCommitDateTime = _z == null ? DateTime.MinValue : (_z.CommitStartDateTime.HasValue ? (DateTime)_z.CommitStartDateTime : DateTime.MinValue),
LastCommitType = _z == null ? "" : (_z.CommitType != null ? _z.CommitType : ""),
...
etc.
A more elegant alternative is create a class that defines the fields you want and call _z.DefaultIfEmpty(new ZRow()), so you don't need to test if _z is null every time you need it. But in this case you'll need to change the select that produces the result for z_temp and replace it to select new ZRow(a.CommitListID, etc..). Not a big deal.

Related

Left Outer Join on Multiple Columns in LINQ not receiving and rows

I'm trying to do a left outer join with linq on on 2 Lists of the same type. I've been following the syntax I see in examples online, but my left join is ending up with a count of 0 values. The 2 lists being joined are not empty so I should definitely be getting some results however I can't seem to determine what is wrong with the syntax. Any help would be greatly appreciated.
var leftOuterJoin = from r in received
join rs in reserved.DefaultIfEmpty()
on new {a = r.ProductId, b = r.WarehouseSectionId } equals new { a = rs.ProductId, b = rs.WarehouseSectionId } into joinedL
from rs in joinedL.DefaultIfEmpty()
select new
{
SKU = r.SKU,
ProductName = r.ProductName,
QTY = r.QTY,
PhysicalStock = (rs.QTY != null && rs.QTY > 0) ? r.QTY + rs.QTY : r.QTY,
WarehouseSection = r.WarehouseSection,
WarehouseName = r.WarehouseName,
ProductId = r.ProductId,
WarehouseSectionId = r.WarehouseSectionId
};
Edit:
I am able to make the query return values by commenting out Physical Stock in the select but I can still not figure out a reason for this. It looks like this error is caused by using the rs.qty variable, if I change any of the fields to rs.qty, it will trigger the same error. All of the rs.qty fields have values however there are more r items than rs items
//PhysicalStock = (rs.QTY != null && rs.QTY > 0) ? r.QTY + rs.QTY : r.QTY,
I managed to fix this problem. The issue was with this line:
PhysicalStock = (rs.QTY != null && rs.QTY > 0) ? r.QTY + rs.QTY : r.QTY,
Instead of doing the null check on qty I should have been doing the null check on rs to make sure the object was not null
Fix:
PhysicalStock = rs != null ? rs.QTY + r.QTY : r.QTY,

How to combine this two LINQ Query in a single var result

Here's my code. I would like to combine this 2 LINQ Query in a single var result. I use these techniques:
var result = queryOne.Union(queryTwo);
var result = queryOne.Concat(queryTwo);
var result = Enumerable.Union(queryOne, queryTwo);
This doesn't work. How to do this right? I am newly in LINQ and C#.
var queryOne = await (from x in _context.DwPropertyMasters
where
x.LandId == 2
select new
{
x.LandId,
x.MapPointX,
x.MapPointY,
x.Location,
a = x.Development == null || x.Development == "" ? x.Location : x.Development,
AreaSize = x.AreaSize ?? 0,
Premium = x.Premium ?? 0,
b = (((x.Premium == 0 ? null : x.Premium) * 100000000) / (x.AreaSize == 0 ? null : x.AreaSize)) ?? 0,
x.Developer,
x.YearTender,
c = x.Development ?? x.Location,
AreaSize2 = x.AreaSize2 ?? 0,
d = (((x.Premium == 0 ? null : x.Premium) * 100000000) / (x.AreaSize2 == 0 ? null : x.AreaSize2)) ?? 0,
}).ToArrayAsync();
var queryTwo = await (from y in _context.DwPropertyDetails
where
y.LandId == 2
orderby
y.Block,
y.Asp descending
select new
{
y.LandDetailId,
y.LandId,
a = y.Pasp ?? "",
b = y.Asp ?? "",
c = y.Tasp ?? "",
y.Block,
y.Floor,
y.Unit,
d = y.CarParking ?? "",
y.SalePrice,
e = y.Revision ?? "",
y.VendorRelate,
y.TransactionPrice,
y.FlatType,
y.ActualSize,
f =
((y.TransactionPrice == 0 ? null : y.TransactionPrice) / (y.ActualSize == 0 ? null : y.ActualSize)) ??
0
}).ToArrayAsync();
var singleQuery = queryOne.Union(queryTwo);
var result = queryOne.Union(queryTwo);
var result = queryOne.Concat(queryTwo);
var result = Enumerable.Union(queryOne, queryTwo);
This doesn't work. How to do this right? I am newly in LINQ and C#.
Your first LINQ returns an enumerable of different objects than the second one, and there is no way you can concatenate those two. They have to return the same type of objects.
var query1 = Enumerable.Range(1, 10)
.Select(i => new
{
Id = i,
Ids = 0 // See, this object has more properties than the second query's object.
});
var query2 = Enumerable.Range(11, 20)
.Select(i => new
{
Id = i,
});
query1.Concat(query2); // Won't compile!
You have to use the same number of properties for each in the same order and type, or you can define a custom class for that.
Your question lacks context right now, but since the only reason why you wouldn't be able to concatinate 2 IEnumerable<T> is because they are of different type.
If you are using an anonymous type on 2 places that's a good time to make this anonymous type into it's own class, this way you should be able to .Concat the 2 results.
But again you can see some obvious differences in your 2 query types. Another option would be to create third type which accepts contains all of the members of the other 2 queries.

Linq conditional select with null member collection

I have searched through a lot of similar questions, but cannot find one that solves this problem. Good chance I am overlooking something obvious, but please suggest how to do this with a single iteration.
var dataWithResults =
from d in data
where d.QCResults != null
from q in d.QCResults
select new QCAMaterial
{
Name = q == null ? nodata : q.ComponentName,
Value = q == null ? nodata : q.Result
};
var dataWithNullResults =
from d in data
where d.QCResults == null
select new QCAMaterial
{
Name = nodata,
Value = nodata
};
You can do a combination of Enumerable.Empty and Enumerable.DefaultIfEmpty:
var allData =
from d in data
from q in d.QCResults ?? Enumerable.Empty<QCResult>().DefaultIfEmpty()
select new QCAMaterial
{
Name = q == null ? nodata : q.ComponentName,
Value = q == null ? nodata : q.Result
};
In case you want to treat an empty list (QCResults) the same as a null list, you can modify the query:
var allData =
from d in data
from q in (d.QCResults ?? Enumerable.Empty<QCResult>()).DefaultIfEmpty()
select new QCAMaterial
{
Name = q == null ? nodata : q.ComponentName,
Value = q == null ? nodata : q.Result
};
Since both the classes without QCResults as well as QCResults with no specific data in their properties get the nodata value, you could omit the 'where' and combine them as:
IEnumerable<QCResult> dummycol = new[] {new QCResult()}; //assuming QCResult is the class exposed by the QCResults property
var res = from d in data
from q in d.QCResults ?? dummycol
select new QCAMaterial
{
Name = q == null ? nodata: q.ComponentName, //side note: can also be written as q?.ComponentName ?? nodata
Value = q == null ? nodata : q.Result
};
If QCResults is empty, a dummy collection is used with a single entry.
The dummy-collection could be created inline as well, but this should be more efficient.
You can use a SelectMany:
var result = data.SelectMany(d =>
{
return d.QCResults != null
? d.QCResults.Select(q => new QCAMaterial { Name = q.ComponentName, Value = q.Result})
: new List<QCAMaterial>{new QCAMaterial { Name = "no data", Value = "no data" }};
});

left join linq when right list is empty

pdlist = (from a in context.EMPLOYEES
join b in context.PERSONS on a.PERSON_ID equals b.PERSON_ID
where a.SUPERVISOR_ID == empId
select new Pollidut.Models.Pollidut
{
PollidutId = a.EMPLOYEE_ID,
PollidutName = b.PERSON_NAME,
DistributionHouseId = a.DISTRIBUTION_HOUSE_ID == null ? 0 : (int)a.DISTRIBUTION_HOUSE_ID
}).ToList();
DateTime dt = DateTime.Now.Date;
var pdTargets = (from p in context.PALLYDUT_TARGET
where p.Active == true && p.StartDate <= dt && p.EndDate >= dt
group p by p.PallydutId into g
select new
{
PollidutId = g.Key,
Start = g.Select(x => x.StartDate).Min(),
End = g.Select(y => y.EndDate).Max(),
Target = g.Select(z => z.Target).Sum()
}).ToList();
var PdTargetsList = (from m in pdlist
join n in pdTargets on m.PollidutId equals n.PollidutId into t
from l in t.DefaultIfEmpty()
select new
{
PallydutId = m.PollidutId,
PallydutName = m.PollidutName,
DistributionId = m.DistributionHouseId,
StartDate = l.Start == null ? dt : l.Start,
EndDate = l.End == null ? dt : l.End,
Target = l.Target == null ? 0 : l.Target
}).ToList();
pdlist is employee list where pdTarget may set or not. pdTarget can be empty. When I left join pdlist with pdTargets I get Object reference not set error. pdTargets collection returns null.How to fix it. Anybody helps me greatly appreciated.
The reason I suspect why you are getting a Null reference exception is because your are directly accessing the properties on a null object (l in your case), even though you are using a DefaultIfEmpty() method which is going to return null if no matching rows are found. You should change your code like this:-
select new
{
PallydutId = m.PollidutId,
PallydutName = m.PollidutName,
DistributionId = m.DistributionHouseId,
StartDate = l == null ? dt : l.Start,
EndDate = l == null ? dt : l.End,
Target = l == null ? 0 : l.Target
}).ToList();
Also, If you think that your one object pdTarget can be null, then I don't think its about a left join or a normal join, your code will throw an exception in either case. Please check this MSDN documentation to handle nulls in query expressions.

linq - left outer join with where returning list of nulls

Here is the full query(without the where), that works:
public List<TipoResiduo> filtro(TipoResiduo objent)
{
using (var db = new DBEntities())
{
var consulta = (from a in db.tb006_tipo_residuo
join cr in db.tb016_classe_residuo on a.fk_id_classe_residuo equals cr.id into trcr
from classeresiduo in trcr.DefaultIfEmpty()
join gr in db.tb017_grupo_residuo on a.fk_id_grupo_residuo equals gr.id into trgr
from gruporesiduo in trgr.DefaultIfEmpty()
join tfg in db.tb008_tipo_fonte_geradora on a.fk_id_tipo_fonte_geradora equals tfg.id into trtfg
from tipofontegeradora in trtfg.DefaultIfEmpty()
join civig in db.tb051_categoria_ivig on a.fk_id_categoria_ivig equals civig.id into trci
from categoriaivig in trci.DefaultIfEmpty()
join icap in db.tb048_ibama_capitulo on a.fk_id_ibama_capitulo equals icap.id into tric
from ibamacapitulo in tric.DefaultIfEmpty()
join isubcap in db.tb049_ibama_subcapitulo on a.fk_id_ibama_subcapitulo equals isubcap.id into tris
from ibamasubcapitulo in tris.DefaultIfEmpty()
join ires in db.tb050_ibama_residuo on a.fk_id_ibama_residuo equals ires.id into trir
from ibamaresiduo in trir.DefaultIfEmpty()
//where a.fk_id_classe_residuo == objent.fk_id_classe_residuo
select new TipoResiduo()
{
id = a.id,
no_tipo_residuo = a.no_tipo_residuo,
dt_cadastro = a.dt_cadastro,
fk_id_classe_residuo = a.fk_id_classe_residuo,
fk_id_grupo_residuo = a.fk_id_grupo_residuo,
nu_densidade = a.nu_densidade,
fk_id_tipo_fonte_geradora = a.fk_id_tipo_fonte_geradora,
fk_id_categoria_ivig = a.fk_id_categoria_ivig,
fk_id_ibama_capitulo = a.fk_id_ibama_capitulo,
fk_id_ibama_subcapitulo = a.fk_id_ibama_subcapitulo,
fk_id_ibama_residuo = a.fk_id_ibama_residuo,
no_classe_residuo = classeresiduo == null ? String.Empty : classeresiduo.no_classe_residuo,
no_grupo_residuo = gruporesiduo == null ? String.Empty : gruporesiduo.no_grupo_residuo,
no_tipo_fonte_geradora = tipofontegeradora == null ? String.Empty : tipofontegeradora.no_tipo_fonte_geradora,
no_categoria_ivig = categoriaivig == null ? String.Empty : categoriaivig.no_categoria_ivig,
no_ibama_capitulo = ibamacapitulo == null ? String.Empty : ibamacapitulo.de_ibama_capitulo,
no_ibama_subcapitulo = ibamasubcapitulo == null ? String.Empty : ibamasubcapitulo.de_ibama_subcapitulo,
no_ibama_residuo = ibamaresiduo == null ? String.Empty : ibamaresiduo.de_ibama_residuo
});
return consulta.OrderBy(a => a.no_tipo_residuo).ToList();
}
}
My object TipoResiduo has some virtual fields just for convenience and can have some null fields.
And when I try to filter with the where(removing the //) the query shows nothing. The ObjEnt of the function has some fields previously populated, and when the field is null, I want to show all.
I tried this too, and nothing:
where a.fk_id_classe_residuo ==
(objent.fk_id_classe_residuo == null
? 0 : objent.fk_id_classe_residuo)
I'm new at this, what should I do to make this work?
You have no left outer join so I don't understand the title
If objent.fk_id_classe_residuo is null this will find a.fk_id_classe_residuo = 0 - not all
where a.fk_id_classe_residuo == (objent.fk_id_classe_residuo == null ? 0 : objent.fk_id_classe_residuo)
try this
where objent.fk_id_classe_residuo == null
or a.fk_id_classe_residuo == objent.fk_id_classe_residuo
After hitting my head into the wall, I found the answer:
where objent.fk_id_classe_residuo == 0 ? true : classeresiduo.id == null
&& classeresiduo.id == null ? false : a.fk_id_classe_residuo == objent.fk_id_classe_residuo
The problem is this objent.fk_id_classe_residuo comes from a dropdownlist, and I set this fk_id as 0 when no option is given. Well, would be very difficult to someone help me with this. :|
But I thank Blam for his attention. ;)

Categories

Resources