left join linq when right list is empty - c#

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.

Related

Linq query: inner join with a count() group by subquery

We have some Machines having Devices attached. Not all the machines have devices atached and the devices cand be moved between machines. The devices generate Errors and we need to count those errors occured in the past day.
We have four tables: Machines (with Id and Code), Devices (with Id and Code), a pairing table DevicesMachines (Id, IdMachine, IdDevice, From datetime, To datetime) and Errors(Id, IdDevice, Moment datetime, Description).
The working SQL query is this:
Select m.Id, m.Code,
Coalesce(d.Code, 'NA') As DeviceCode,
Coalesce(Err.ErrorCnt,0) As ErrorCnt
From Machines As m
Left Outer Join (Select IdMachine, IdDevice From DevicesMachines as dm
Where GetDate() Between dm.From And dm.To) As dm on m.Id=dm.IdMachine
Left Outer Join Devices As d on dm.IdDevice=d.Id
Left outer join
( Select IdMachine, Count(Id) As ErrorCnt From Errors as er
Where er.Moment >= DateAdd(day,-1,GetUtcDate())
Group By IdMachine) As Err
On m.Id=Err.IdMachine
I have tried many syntaxes, one of which is below:
using ( DataContextM dcMachines = new dataContextM())
{
IEnumerable<MachineRow> lstM =
from m in dcMachines.Machines
from dm in dcMachines.DevicesMachines.Where(dm => (dm.IdMachine == m.Id) && (dm.From <= DateTime.Now) && (dm.To >= DateTime.Now)).DefaultIfEmpty()
from d in dcMachines.Devices.Where(d => d.Id == dm.IdDevice).DefaultIfEmpty()
from er in dcMachines.Errors
.Where(er => (er.Moment >= DateTime.Now) && (er.Moment <= DateTime.Now.AddDays(-1)))
.GroupBy(er => er.IdMachine)
.Select(er => new { IdMachine = er.Key, ErrorCnt = er.Count() })
.Where(er=> er.IdMachine==m.Id).DefaultIfEmpty()
select new MachineRow
{
Id = amId,
Code = m.Code,
DeviceCode = (d == null) ? "NA" : d.DeviceCode,
IdDevice = (d == null) ? 0: d.Id,
ErrorCnt = (er == null) ? 0 : er.ErrorCnt
};
}
I failed to find the right Linq syntax and I need your help.
Thank you,
Daniel
Based on the SQL you provided, I created what I think is the equivalent EF LINQ expression:
using (var dcMachines = new DataContextM())
{
var now = DateTime.Now;
var utcYesterday = DateTime.UtcNow.AddDays(-1);
var devicesMachinesQuery =
from dm in dcMachines.DevicesMachines
where dm.From <= now && dm.To >= now
join d in dcMachines.Devices on dm.IdDevice equals d.Id into dItems
from d in dItems.DefaultIfEmpty()
select new
{
dm.IdMachine,
dm.IdDevice,
DeviceCode = d != null ? d.Code : "NA"
};
var errorsQuery =
from err in dcMachines.Errors
where err.Moment >= utcYesterday
select err;
IEnumerable<MachineRow> lstM =
from m in dcMachines.Machines
join dm in devicesMachinesQuery on m.Id equals dm.IdMachine into dmItems
from dm in dmItems.DefaultIfEmpty()
select new MachineRow
{
Id = m.Id,
Code = m.Code,
DeviceCode = dm != null ? dm.DeviceCode : "NA",
IdDevice = dm != null ? dm.IdDevice : 0,
ErrorCnt = (
from err in errorsQuery
where err.IdMachine == m.Id
select err.Id
)
.Count()
};
}
I made some tests in memory and it seems to yield the same behavior as your provided SQL query.

Linq Sub Select Data Type

Learning Linq is going to be the death of me, hahahaha.
I have a large query to retrieve data for a report. I need to add a sub query to get related data into the result set. The data in the subquery is a float in the database. My error is "Cannot convert type 'System.Linq.IQueryable' to 'float'"
The query is:
var results = from d in db.Deliveries
join j in db.Jobs on d.Job equals j.Job1
join c in db.Customers on j.Customer equals c.Customer1
join ml in db.Material_Locations on j.Part_Number equals ml.Material into t1
from t2 in t1.DefaultIfEmpty()
join uv in db.User_Values on j.User_Values equals uv.User_Values into t3
from t4 in t3.DefaultIfEmpty()
where d.Promised_Date >= calFrom.SelectedDate && d.Promised_Date <= calTo.SelectedDate
where d.Remaining_Quantity > 0
where (t2.Location_ID ?? "") != "MSSICONSMT"
orderby d.Job, c.Name, d.Promised_Date
select new
{
d.Promised_Date,
d.Job,
LocationID = t2.Location_ID ?? "",
d.Shipped_Quantity,
d.Remaining_Quantity,
d.Promised_Quantity,
j.Unit_Price,
on_Hand_Qty = ((double?)t2.On_Hand_Qty) ?? 0.0,
Part_Number = j.Part_Number ?? "",
c.Name,
c.Ship_Lead_Days,
SafetyStk = t4.Decimal1 ?? 0.0,
ShipDate = d.Promised_Date.AddDays(-1 * (c.Ship_Lead_Days ?? 0)),
RemainValue = d.Remaining_Quantity * j.Unit_Price,
Balance = d.Remaining_Quantity > 0 ? d.Remaining_Quantity - (((double?)t2.On_Hand_Qty) ?? 0.0) : 0,
Consignment = ((float)(from x in db.Material_Locations
join jx in db.Jobs on x.Material equals jx.Part_Number
where jx.Job1 == d.Job
&& x.Location_ID == "MSSICONSMT"
select new {x.On_Hand_Qty}))
};
The problem is the "Consignment" line in the select. How do I deal with the anonymous type and get to converted into a float?
Thanks!
Try following :
Consignment = (from x in db.Material_Locations
join jx in db.Jobs on x.Material equals jx.Part_Number
where jx.Job1 == d.Job && x.Location_ID == "MSSICONSMT"
select new {qty = (float)x.On_Hand_Qty}).ToList();
Consignment = (from x in db.Material_Locations
where x.Material == j.Part_Number
&& x.Location_ID == "MSSICONSMT"
select (float?)x.On_Hand_Qty ?? 0.0).ToList()
and I had to add this;
newRow["Consignment"] = thisRow.Consignment.FirstOrDefault();
to store it in the datatable.
Thanks to all who nudge me in the right direction.

NullReferenceException on outer join

I am trying to grab all data from FDerive, however I am try to set a filter with a where clause. Unfortunately I am getting a nullreferencexpection when I touch spd when a row in spd is null.
var Result = from fpd in FDerive
join spd in SDerive
on new { fpd.PId, fpd.SId }
equals new { spd.PId, spd.SId } into allRows
from spd in allRows.DefaultIfEmpty()
where spd.SId == ""
|| spd.PId == ""
select new { fpd, spd };
How do I get around the null error?
DefaultIfEmpty<T> will return a set containing only one element with the default value of T - in this case null - if the source collection is empty. So spd will be null if there are no items returned in the join.
Try to a null check in the where clause
var Result =
...
where spd == null || spd.SId == "" || spd.PId == ""
select new { fpd, spd };
I found the answer at the bottom of the code in the following question
LINQ double left join
var results =
from person in students
join entry in subquery on person.FullName equals entry.AuthorFullName into personEntries
from personEntry in personEntries.DefaultIfEmpty()
orderby person.FullName
select new
{
PersonName = person.FullName,
BlogTitle = personEntry == null ? "" : personEntry.Title
};

Entity Framework - Results from subquery, speed issue?

I've made a query to get a list of articles, each bound to a "header",
so i retrieve the header plus the related articles and their properties.
The query works, however in its current style it is
Somewhat messy ( in my opinion)
The .ToList() takes way longer than i would expect.
Does anyone see any obvious reason for the speed-issue?
var offerheaders =
from o in dbcontext.F_CAB_OFFER_HEADERS
where
o.OFHD_FK_BUYER == userinfo.orgaTypeSequence
&& o.OFHD_VALID_FROM <= userinfo.selectedDate
&& o.OFHD_VALID_TO >= userinfo.selectedDate
&& o.OFHD_DELETED_YN == 0
&& o.OFHD_DELETED_BY_OWNER_YN == false
&& o.OFHD_OFFER_TYPE == userinfo.offerType
orderby o.OFHD_NO ascending
select o;
var offerlist =
from ofhd in offerheaders
select new {
ofhd = new {
OfferNo = ofhd.OFHD_NO,
OfferSequence = ofhd.OFHD_SEQUENCE,
ValidFrom = ofhd.OFHD_VALID_FROM,
ValidTo = ofhd.OFHD_VALID_TO,
OfferType = ofhd.OFHD_OFFER_TYPE,
Maingroup = new { cdmg_seq = ofhd.F_CAB_CD_MAIN_GROUP_TYPE.CDMG_SEQUENCE, Desc = ofhd.F_CAB_CD_MAIN_GROUP_TYPE.CDMG_DESC },
Supplier = new {
Name = ofhd.F_CAB_GROWER.F_CAB_ORGANISATION.ORGA_NAME,
Pic = ofhd.F_CAB_GROWER.F_CAB_ORGANISATION.ORGA_FK_PICTURE,
Seq = ofhd.F_CAB_GROWER.GROW_SEQUENCE
},
Caption = ofhd.OFHD_CAPTION,
Seperate = ofhd.OFHD_SHOW_SEPARATE_YN,
//ofdts = (from ofdt in dbcontext.F_CAB_OFFER_DETAILS.Where(x => x.OFDT_FK_OFFER_HEADER == ofhd.OFHD_SEQUENCE && x.OFDT_NUM_OF_ITEMS > 0 && x.OFDT_LATEST_DELIVERY_DATE_TIME > compareDateTime && x.OFDT_LATEST_ORDER_DATE_TIME > compareDateTime)
ofdts = from ofdt in dbcontext.F_CAB_OFFER_DETAILS
join props in dbcontext.F_CAB_CAB_PROP on ofdt.OFDT_FK_CAB_CODE equals props.PROP_FK_CABC_SEQ
join cabcode in dbcontext.F_CAB_CD_CAB_CODE on ofdt.OFDT_FK_CAB_CODE equals cabcode.CABC_SEQUENCE
join cabgroup in dbcontext.F_CAB_CD_CAB_GROUP on cabcode.CABC_FK_CAB_GROUP equals cabgroup.CDGR_SEQUENCE
join grouptype in dbcontext.F_CAB_CD_GROUP_TYPE on cabgroup.CDGR_FK_GROUP_TYPE equals grouptype.CDGT_SEQUENCE
join maingrouptype in dbcontext.F_CAB_CD_MAIN_GROUP_TYPE on grouptype.CDGT_FK_MAIN_GROUP equals maingrouptype.CDMG_SEQUENCE
join caca in dbcontext.F_CAB_CAB_CASK_MATRIX on ofdt.OFDT_FK_CACA_SEQ equals caca.CACA_SEQUENCE
join cask in dbcontext.F_CAB_CD_CASK on caca.CACA_FK_CASK equals cask.CDCA_SEQUENCE
join vbncode in dbcontext.F_CAB_CAB_VBN_MATRIX on cabcode.CABC_SEQUENCE equals vbncode.CVMA_FK_CAB_CODE
join grel in dbcontext.F_CAB_GENERAL_RELATIONS on ofdt.OFDT_FK_GREL_SEQ equals grel.GREL_SEQUENCE into greltable
from g_loj in greltable.DefaultIfEmpty()
where
ofdt.OFDT_FK_OFFER_HEADER == ofhd.OFHD_SEQUENCE
&& ofdt.OFDT_NUM_OF_ITEMS > 0
&& props.PROP_FK_CDLA_SEQ == userinfo.lang.CDLA_SEQUENCE
orderby props.PROP_CAB_DESC ascending
select new {
Desc = props.PROP_CAB_DESC,
Group = new { cdgr_seq = cabgroup.CDGR_SEQUENCE, Desc = cabgroup.CDGR_DESC },
Grouptype = new { grouptype.CDGT_SEQUENCE, Desc = grouptype.CDGT_DESC },
Properties = new CABProperties { props = props },
Price = ofdt.OFDT_ITEM_PRICE,
PIC_SEQ = ofdt.OFDT_FK_PICTURE ?? ((cabcode.CABC_FK_PICTURE ?? cabcode.CABC_SEQUENCE)),
PIC_URL = ofdt.OFDT_EXT_PICTURE_REF ?? "",
Seq = ofdt.OFDT_SEQUENCE,
Available = ofdt.OFDT_NUM_OF_ITEMS,
CabCode = ofdt.F_CAB_CD_CAB_CODE.CABC_CAB_CODE,
VBNCode = vbncode.CVMA_FK_VBN_CODE,
Remark = ofdt.OFDT_REMARK,
IsSpecial = ofdt.OFDT_SPECIAL_YN,
Arrived = inTransit ? ofdt.OFDT_ARRIVAL_DATE < DateTime.Now : true,
Cask = new CABCask { cask = cask, caca = caca },
Supplier = g_loj == null ? (ofdt.OFDT_SUPPLIER ?? "") : g_loj.GREL_NAME,
SupplierWeb = g_loj == null ? "" : g_loj.GREL_WEBSITE_URL,
SupplierLogo = g_loj == null ? ofhd.F_CAB_GROWER.F_CAB_ORGANISATION.ORGA_FK_PICTURE : g_loj.GREL_FK_PICT_SEQ,
SupplierSeq = g_loj == null ? -1 : g_loj.GREL_SEQUENCE,
}
}
};
userinfo.mainofferlist = offerlist.ToList();
As Daniel Kelly also mentioned the ToList function is where your query is executed, because these LinqToEntities queries are executed at the point where they are first enumerated, and ToList does that to be able to create a list.
Basically the reason why your querying takes so much time can be separated into two different reasons:
you are using too much projections and I thine (the parts with new {
})
your query has an incredible amount of join clauses
I would recommend to separate your query into subqueries, and run them separately like the first part in
...
select o
use
...
select o).ToList()
by breaking down the main query where you have a lot of subqueries it will be faster and much more readable, so you have less "messiness".
And last but not least you should create mapping for the anonymous objects, and use those classes other than projection that should speed up your query.

Left join is not working for complex query

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.

Categories

Resources