Multiple Null Foreign keys group join error in Linq - c#

I have this following LINQ statement which giving the following error
Type inference failed in the call to Group join
var EmployeeList = (from requestemployee in _context.BulkTransportRequestEmployees
join request in _context.BulkTransportRequests on requestemployee.BulkRequestBulkRequestId equals request.BulkRequestId
join employee in _context.Employees on requestemployee.EmployeeEmployeeId equals employee.EmployeeId
join deptemp in _context.EmployeeDepartments on employee.EmployeeId equals deptemp.EmployeeEmployeeId
join dept in _context.Departments on deptemp.DepartmentDepartmentId equals dept.DepartmentId
//Error is in here
join routepath in _context.RootPaths on new { Key1 = requestemployee.CityCityId, Key2 = requestemployee.RootRootId } equals new { Key1 = routepath.CityId, Key2 = routepath.RootId } into join_routepath
from routepath in join_routepath.DefaultIfEmpty()
join route in _context.Roots on routepath.RootId equals route.RootId
join destination in _context.Cities on routepath.CityId equals destination.CityId
join pass in _context.TransportPasses on employee.EmployeeId equals pass.IssuedToEmployeeId into join_employee_pass
from pass in join_employee_pass.DefaultIfEmpty()
where deptemp.IsDefaultDepartment == true &&
request.RequestType == 1 &&
employee.EmployeeType == emptype &&
DbFunctions.TruncateTime(request.TripDateAndTime) >= DbFunctions.TruncateTime(FromDate) &&
DbFunctions.TruncateTime(request.TripDateAndTime) <= DbFunctions.TruncateTime(ToDate) &&
(pass.IsHold == true || pass.IsCanceled == true || pass.IssuedDate == null || pass.PassType == 1)
group new { employee, request, routepath } by new
{
dept.DepartmentName,
employee.EmployeeId,
employee.EmpCode,
employee.EmployeeCallingName,
employee.EmployeeSurname,
employee.NameWithInitials
} into g
select new
{
g.Key.EmployeeId,
g.Key.EmpCode,
Employee = g.Key.EmployeeCallingName + " - " + g.Key.NameWithInitials,
g.Key.DepartmentName
}).ToList();
requestemployee keys are nullable types. So how could I establish this group join without error?

I figured it out. Just write down for others' use.
because requestemployee.CityCityIdandrequestemployee.RootRootId keys are nullable I need to change routepath.CityId and routepath.RootId also to accept null as follow
join routepath in _context.RootPaths on new { Key1 = requestemployee.CityCityId, Key2 = requestemployee.RootRootId } equals new { Key1 = (int?)routepath.CityId, Key2 = (int?)routepath.RootId } into join_routepath
from routepath in join_routepath.DefaultIfEmpty()

Related

Pass parameters to linq queries

I have the following part of code that works perfectly.
public Task<List<RsvItemsViewModel>> GetReservations(int company_ID)
{
try
{
// we cannot pass parameter as company_id . we use hard coded values as 1 for CompanyId.Must find a way to overcome this
return ((from a in gTravelDbContext.Set<Frsvitem>()
join d in gTravelDbContext.Set<Freserv>()
on a.Rsinum equals d.Rsvnum into gd from d in gd.Where(p1 => p1.CompanyId == 1)
join c in gTravelDbContext.Set<Fsupplier>()
on a.SupplId equals c.SupplId into gc from c in gc.Where(p1=>p1.CompanyId == 1)
join i in gTravelDbContext.Set<Fzone>()
on a.Rsizone equals i.Zoneid into gi
from i in gi.DefaultIfEmpty() where i.CompanyId == 1
join g in gTravelDbContext.Set<Fservtype>()
on a.Rsisertype equals g.Stypecode
join b in gTravelDbContext.Set<Fcustomer>()
on d.CustId equals b.CustId
join e in gTravelDbContext.Set<Fpaykind>()
on d.Rsvpaymethod equals e.Payid into ge
from e in ge.DefaultIfEmpty()
join f in gTravelDbContext.Set<Fsalesman>()
on d.Rsvsalescode equals f.Salescode into gf
from f in gf.DefaultIfEmpty()
join h in gTravelDbContext.Set<Fpinake>()
on d.Rsvakirosi equals h.Tblid into gh
from h in gh.Where(p => p.Tblcd == "yesno")
select new RsvItemsViewModel
{
Rsinum = a.Rsinum,
Stypename = g.Stypename,
Cuname = b.Cuname,
Rsvcuname = d.Rsvcuname,
Sumame = c.Suname,
Rsvakirosi = d.Rsvakirosi,
Yesno = h.Tbltext ,
Paytext = e.Paytext,
Salesname = f.Salesname,
Stypegroup = g.Stypegroup,
Company_id =d.CompanyId.GetValueOrDefault(),
Zonename = i.Zonename,
Rsisertype = a.Rsisertype,
Suppl_id = a.SupplId,
Xrhsh = d.Xrhsh,
Bpar_id = a.BparId
}
).ToListAsync());
}
catch (Exception)
{
throw;
}
}
The problem is that I want to pass company_ID as a parameter in linq and I want to substitute the Where(p1 => p1.CompanyId == 1) with Where(p1 => p1.CompanyId == company_ID )
I would be grateful if someone could help me.
Thank you
You have used GroupJoin in way, which is not supported by EF Core. GroupJoin has a lot of limitations and use it only if you need LEFT JOIN.
I have rewritten your query to be translatable:
var query =
from a in gTravelDbContext.Set<Frsvitem>()
from d in gTravelDbContext.Set<Freserv>().Where(d => d.Rsvnum == a.Rsinum && d.CompanyId == company_ID)
from c in gTravelDbContext.Set<Fsupplier>().Where(c => a.SupplId == c.SupplId && c.CompanyId == company_ID)
from i in gTravelDbContext.Set<Fzone>().Where(i => a.Rsizone == i.Zoneid && c.CompanyId == company_ID).DefaultIfEmpty()
join g in gTravelDbContext.Set<Fservtype>()
on a.Rsisertype equals g.Stypecode
join b in gTravelDbContext.Set<Fcustomer>()
on d.CustId equals b.CustId
join e in gTravelDbContext.Set<Fpaykind>()
on d.Rsvpaymethod equals e.Payid into ge
from e in ge.DefaultIfEmpty()
join f in gTravelDbContext.Set<Fsalesman>()
on d.Rsvsalescode equals f.Salescode into gf
from f in gf.DefaultIfEmpty()
from h in gTravelDbContext.Set<Fpinake>().Where(h => d.Rsvakirosi == h.Tblid && h.Tblcd == "yesno")
select new RsvItemsViewModel
{
Rsinum = a.Rsinum,
Stypename = g.Stypename,
Cuname = b.Cuname,
Rsvcuname = d.Rsvcuname,
Sumame = c.Suname,
Rsvakirosi = d.Rsvakirosi,
Yesno = h.Tbltext ,
Paytext = e.Paytext,
Salesname = f.Salesname,
Stypegroup = g.Stypegroup,
Company_id =d.CompanyId.GetValueOrDefault(),
Zonename = i.Zonename,
Rsisertype = a.Rsisertype,
Suppl_id = a.SupplId,
Xrhsh = d.Xrhsh,
Bpar_id = a.BparId
}

How to add a '.Contains()' to the where clause of a query?

I have this query but I need to add a where clause that uses 'Contains()' so I can check the db for a list of accountIDs, the reason I need to do it this way is because I have to first check if there are any accountID's and if there are it must execute the 'Contains()' in the where clause but the compiler complains if I try do it this way but given what the method has to do I can't figure out another way to do it given the fact that there are so many tables joined in the query.
My code:
bool UseAcc = false;
if (accountIds != null && accountIds.Count > 0)
{
UseAcc = true;
}
var query = (from i in db.Incidents
join s in db.Sites on i.SiteID equals s.SiteID
join a in db.Accounts on i.AccountID equals a.AccountID
join st in db.Status on i.StatusID equals st.StatusID
join currentUser in db.Users on i.CurrentAssignedUser equals currentUser.UserID
join loggedByUser in db.Users on i.LoggedByUser equals loggedByUser.UserID
join createdByUser in db.Users on i.CreatedByUser equals createdByUser.UserID
join l in db.Locations on i.Location equals l.LocationID into locList
from loc in locList.DefaultIfEmpty()
join q in db.QuestionCategories on i.QuestionCategoryID equals q.QuestionCategoryID
join ia in db.IncidentActions on i.IncidentID equals ia.IncidentID into iaList
from actions in iaList.DefaultIfEmpty()
//If 'UseAcc' gets set to true then execute the contains section of the or statement
where (i.Active == true) &&
(UseAcc = false || a => accountIds.Contains(a.AccountID))
select new
{
Title = i.Title,
IncidentID = i.IncidentID,
StatusName = st.StatusName,
StatusID = i.StatusID,
ReferenceNumber = i.ReferenceNo,
AccountName = a.AccountName,
AccountID = i.AccountID,
SiteName = s.SiteName,
SiteID = i.SiteID,
LocationName = loc.LocationName,
LocationID = i.Location,
CatName = q.QuestionCategoryName,
CatID = i.QuestionCategoryID,
CurrentAssignedUser = currentUser.FirstName + " " + currentUser.LastName,
AssignedUserID = i.CurrentAssignedUser,
CreatedByUser = createdByUser.FirstName + " " + createdByUser.LastName,
DateCreated = i.LoggedDate,
DepartmentID = i.DepartmentID,
Logger = loggedByUser.FirstName + " " + loggedByUser.LastName,
LoggedBy = i.LoggedByUser,
EscalationCount = i.EscalationCount)
});
I would paste the whole method but its really big.
I have looked at examples online but they can't work either because alot of them use an AsQueryable with a single table and this query has many tables joined.
Please Help.
var accounts = db.Accounts.AsQueryable();
if (accountIds != null && accountIds.Count > 0)
{
accounts = accounts.Where(a => accountIds.Contains(a.AccountID));
}
var query = (from i in db.Incidents
join s in db.Sites on i.SiteID equals s.SiteID
join a in accounts on i.AccountID equals a.AccountID
join st in db.Status on i.StatusID equals st.StatusID
join currentUser in db.Users on i.CurrentAssignedUser equals currentUser.UserID
join loggedByUser in db.Users on i.LoggedByUser equals loggedByUser.UserID
join createdByUser in db.Users on i.CreatedByUser equals createdByUser.UserID
join l in db.Locations on i.Location equals l.LocationID into locList
from loc in locList.DefaultIfEmpty()
join q in db.QuestionCategories on i.QuestionCategoryID equals q.QuestionCategoryID
join ia in db.IncidentActions on i.IncidentID equals ia.IncidentID into iaList
from actions in iaList.DefaultIfEmpty()
select new
{
Title = i.Title,
IncidentID = i.IncidentID,
StatusName = st.StatusName,
StatusID = i.StatusID,
ReferenceNumber = i.ReferenceNo,
AccountName = a.AccountName,
AccountID = i.AccountID,
SiteName = s.SiteName,
SiteID = i.SiteID,
LocationName = loc.LocationName,
LocationID = i.Location,
CatName = q.QuestionCategoryName,
CatID = i.QuestionCategoryID,
CurrentAssignedUser = currentUser.FirstName + " " + currentUser.LastName,
AssignedUserID = i.CurrentAssignedUser,
CreatedByUser = createdByUser.FirstName + " " + createdByUser.LastName,
DateCreated = i.LoggedDate,
DepartmentID = i.DepartmentID,
Logger = loggedByUser.FirstName + " " + loggedByUser.LastName,
LoggedBy = i.LoggedByUser,
EscalationCount = i.EscalationCount)
});

Entity Framework forcing Inner Join when Left Join DefaultIfEmpty() Syntax used

I'm trying to create a fairly complex query with the Oracle EF framework (Oracle.ManagedDataAccess.EntityFramework nuget package, version 12.1.2400) with numerous left joins, utilizing the .DefaultIfEmpty() syntax:
from i in dbHR.Identities
join p in dbHR.Personals
on new {
key1 = i.ID,
key2 = true,
key3 = true
} equals new {
key1 = p.EID_ID,
key2 = (DbFunctions.TruncateTime(p.EFFECTIVE).Value.CompareTo(DbFunctions.TruncateTime(DateTime.Now).Value) <= 0),
key3 = (DbFunctions.TruncateTime(p.EXPIRY).Value.CompareTo(DbFunctions.TruncateTime(DateTime.Now).Value) >= 0)
}
into pj from p in pj.DefaultIfEmpty()
join s in dbHR.States on p.DSP_ID_ADDRESS equals s.ID into sj from s in sj.DefaultIfEmpty()
join e in dbHR.Employments
on new {
key1 = i.ID,
key2 = true
}
equals new {
key1 = e.EID_ID,
key2 = (!e.TERMINATION_DATE.HasValue || DbFunctions.TruncateTime(e.TERMINATION_DATE.Value).Value.CompareTo(DbFunctions.TruncateTime(DateTime.Now).Value) >= 0)
}
into ej from e in ej.DefaultIfEmpty()
join a in dbHR.Assignments
on new {
key1 = e.ID,
key2 = true
} equals new {
key1 = a.EEM_ID,
key2 = ((!a.ASSIGNMENT_END_DATE.HasValue || DbFunctions.TruncateTime(a.ASSIGNMENT_END_DATE.Value).Value.CompareTo(DbFunctions.TruncateTime(DateTime.Now).Value) >= 0)
&& (DbFunctions.TruncateTime(a.ASSIGNMENT_START_DATE).Value.CompareTo(DbFunctions.TruncateTime(DateTime.Now).Value) <= 0)
&& (a.PRIME_ASSIGNMENT != null))
}
into aj from a in aj.DefaultIfEmpty()
join ad in dbHR.AssignmentDetails
on
new { key1 = a.ID, key2 = true }
equals
new
{
key1 = ad.EAS_ID,
key2 = (
DbFunctions.TruncateTime(ad.EXPIRY).Value.CompareTo(DbFunctions.TruncateTime(DateTime.Now).Value) >= 0
&& DbFunctions.TruncateTime(ad.EFFECTIVE).Value.CompareTo(DbFunctions.TruncateTime(DateTime.Now).Value) <= 0
)
}
into adj from ad in adj.DefaultIfEmpty()
join j in dbHR.Jobs
on ad.DJB_ID equals j.ID
into jj from j in jj.DefaultIfEmpty()
join jd in dbHR.JobDetails
on new {
key1 = j.ID, key2 = true
} equals new {
key1 = jd.DJB_ID,
key2 =
(
DbFunctions.TruncateTime(jd.EFFECTIVE).Value.CompareTo(DbFunctions.TruncateTime(DateTime.Now).Value) <= 0
&& DbFunctions.TruncateTime(jd.EXPIRY).Value.CompareTo(DbFunctions.TruncateTime(DateTime.Now).Value) >= 0
)
}
into jdj from jd in jdj.DefaultIfEmpty()
join d in dbHR.Departments
on ad.DDP_ID equals d.ID
into dj from d in dj.DefaultIfEmpty()
join dd in dbHR.DepartmentDetails
on new {
key1 = d.ID, key2 = true
} equals new {
key1 = dd.DDP_ID,
key2 =
(
DbFunctions.TruncateTime(dd.EFFECTIVE).Value.CompareTo(DbFunctions.TruncateTime(DateTime.Now).Value) <= 0
&& DbFunctions.TruncateTime(dd.EXPIRY).Value.CompareTo(DbFunctions.TruncateTime(DateTime.Now).Value) >= 0
)
} into ddj from dd in ddj.DefaultIfEmpty()
select new HREmployeeRecord
{
i=i,
p= p,
s=s,
e=e,
a=a,
ad=ad,
j=j,
jd=jd,
d=d,
dd=dd
}
The resulting SQL generated by EF has left joins on most of the tables, but 3 of them keep reverting to inner joins:
FROM "DB"."DB_HR_IDENTITIES" "Extent1"
LEFT OUTER JOIN "DB"."DB_HR_PERSONALS" "Extent2" ON ("Extent1"."ID" = "Extent2"."EID_ID") AND [...datetime comparison removed for brevity...]
LEFT OUTER JOIN "DB"."DB_CM_STATE_PROVINCES" "Extent3" ON "Extent2"."DSP_ID_ADDRESS" = "Extent3"."ID"
LEFT OUTER JOIN "DB"."DB_HR_EMPLOYMENTS" "Extent4" ON ("Extent1"."ID" = "Extent4"."EID_ID") AND [...datetime comparison removed for brevity...]
LEFT OUTER JOIN "DB"."DB_HR_ASSIGNMENTS" "Extent5" ON ("Extent4"."ID" = "Extent5"."EEM_ID") AND [...datetime comparison removed for brevity...]
INNER JOIN "DB"."DB_HR_ASSIGNMENT_DETAILS" "Extent6" ON ("Extent5"."ID" = "Extent6"."EAS_ID") AND ((TRUNC("Extent6"."EXPIRY")) >= (TRUNC(LOCALTIMESTAMP))) AND ((TRUNC("Extent6"."EFFECTIVE")) <= (TRUNC(LOCALTIMESTAMP)))
INNER JOIN "DB"."DB_CM_JOBS" "Extent7" ON "Extent6"."DJB_ID" = "Extent7"."ID"
LEFT OUTER JOIN "DB"."DB_CM_JOB_DETAILS" "Extent8" ON ("Extent7"."ID" = "Extent8"."DJB_ID") AND [...datetime comparison removed for brevity...]
INNER JOIN "DB"."DB_CM_DEPARTMENTS" "Extent9" ON "Extent6"."DDP_ID" = "Extent9"."ID"
LEFT OUTER JOIN "DB"."DB_CM_DEPARTMENT_DETAILS" "Extent10" ON ("Extent9"."ID" = "Extent10"."DDP_ID") AND [...datetime comparison removed for brevity...]
I've tried replacing some of the joins with the following, but unfortunately it results in identical output:
from ad in dbHR.AssignmentDetails.Where(x=>
a.ID == x.EAS_ID &&
(
DbFunctions.TruncateTime(x.EXPIRY).Value.CompareTo(DbFunctions.TruncateTime(DateTime.Now).Value) >= 0
&& DbFunctions.TruncateTime(x.EFFECTIVE).Value.CompareTo(DbFunctions.TruncateTime(DateTime.Now).Value) <= 0
)
).DefaultIfEmpty()
I'm beginning to pull my hair out.
I read in this answer:
EF seems to use INNER JOIN for including a required and LEFT OUTER JOIN for including an optional navigation property.
While that makes sense, I don't think it applies to my AssignmentDetails table, as it's the Many (FK) side of a One (Assignments) to Many (AssignmentDetails) relationship, where there could possibly not be an AssignmentDetails record.
It may be true, however, for the Departments and Jobs tables, since they're the primary key table referenced by a foreign key - there should always be a record. However, this still breaks the results because if in the join the FK is on a table that was NULL due to a left join, then the entire row is then removed due to the INNER join.
Any assistance would be greatly appreciated!
My mistake. I was wrapping this query in a function called GetEmployees() which returned an iQueryable list, and was filtering it:
results = HRFunctions.GetEmployees().Where(x => x.ad != null);
Because there was a WHERE clause added that assumed AD should not be null, EntityFramework intelligently altered the LEFT JOIN to an INNER JOIN.
After removing the .Where(), AssignmentDetails is now a Left Join.

Why the following query results in an exception?

The following query results in the exception:
"Unable to cast object of type 'System.Linq.Expressions.TypedConstantExpression' to type 'SD.LLBLGen.Pro.LinqSupportClasses.ExpressionClasses.SetExpression'.".
What could be the problem?
return (from ubt in meta.TableUbt
join tc in meta.TableTc on ubt.TCId equals tc.Id
where ubt.Ar110aid == ar110AId && tc.IsPayment
group ubt by new { ubt.Ar110aid, ubt.TCId } into tempTrans
join pyd in meta.TablePyd on tempTrans.Key.Ar110aid equals pyd.Ar110Aid
join pm in meta.TablePm on pyd.Ar110Id equals pm.Id
join tly in TableTly on new { pyd.TyId, ChrgTransId = tempTrans.Key.TCId }
equals new { tly.TyId, tly.ChrgTransId }
join cr in meta.TableCr on
new
{
TyId = (int?)pyd.TyId,
TxLevId = (int?)tly.TxLevId,
Ar101Id = (int?)pm.Ar101Id
}
equals
new
{
cr.TyId,
cr.TxLevId,
cr.Ar101Id
}
join crd in meta.TableCrd on cr.Id equals crd.TableCrId
where crd.StartingLimit <= tempTrans.Sum(b => b.Amount) &&
tempTrans.Sum(b => b.Amount) <= crd.EndingLimit
select crd.Id).FirstOrDefault();
Probably problem is in joining on TableTly. It seems to be local data set. and it's prohibited to join Db data and in-memory objects.

Entity Framework - Handle null value in Linq

I'm writing two LINQ queries where I use my first query's result set in my second query.
But in some cases when there is no data in the database table my first query returns null,
and because of this my second query fails since wsdetails.location and wsdetails.worklocation are null causing an exception.
Exception:
Object reference not set to an instance of an object
My code is this:
var wsdetails = (from assetTable in Repository.Asset
join userAsset in Repository.UserAsset on
assetTable.Asset_Id equals userAsset.Asset_Id
join subLocationTable in Repository.SubLocation on
assetTable.Sub_Location_Id equals subLocationTable.Sub_Location_ID
where userAsset.User_Id == userCode
&& assetTable.Asset_TypeId == 1 && assetTable.Asset_SubType_Id == 1
select new { workstation = subLocationTable.Sub_Location_Name, location = assetTable.Location_Id }).FirstOrDefault();
result = (from emp in this.Repository.Employee
join designation in this.Repository.Designation on
emp.DesignationId equals designation.Id
where emp.Code == userCode
select new EmployeeDetails
{
firstname = emp.FirstName,
lastname = emp.LastName,
designation = designation.Title,
LocationId = wsdetails.location,
WorkStationName = wsdetails.workstation
}).SingleOrDefault();
As a workaround I can check
if wsdetails == null
and change my second LINQ logic, but I believe there are some ways to handle null values in LINQ itself like the ?? operator.
But I tried this and it didn't work for me.
Any help?
The problem is EF can't translate the null-coalescing operator to SQL. Personally I don't see what's wrong with checking the result with an if statement before executing the next query. However, if you don't want to do that, then because your result is always going to be a single query why not do something like:
var wsdetails = (from assetTable in Repository.Asset
join userAsset in Repository.UserAsset on
assetTable.Asset_Id equals userAsset.Asset_Id
join subLocationTable in Repository.SubLocation on
assetTable.Sub_Location_Id equals subLocationTable.Sub_Location_ID
where userAsset.User_Id == userCode
&& assetTable.Asset_TypeId == 1 && assetTable.Asset_SubType_Id == 1
select new { workstation = subLocationTable.Sub_Location_Name, location = assetTable.Location_Id }).FirstOrDefault();
result = (from emp in this.Repository.Employee
join designation in this.Repository.Designation on
emp.DesignationId equals designation.Id
where emp.Code == userCode
select new EmployeeDetails
{
firstname = emp.FirstName,
lastname = emp.LastName,
designation = designation.Title
}).SingleOrDefault();
result.LocationId = wsdetails != null ? wsdetails.location : "someDefaultValue";
result.WorkStationName = wsdetails != null ? wsdetails.workstation ?? "someDefaultValue";
Instead of the "binary" operator ?? maybe you should use the good old ternary one, ? :, like in
wsdetails != null ? wsdetails.location : null
Try not evaluating the first query, and use it in the second query. This should result in a single SQL statement.
var wsdetails = (from assetTable in Repository.Asset
join userAsset in Repository.UserAsset on
assetTable.Asset_Id equals userAsset.Asset_Id
join subLocationTable in Repository.SubLocation on
assetTable.Sub_Location_Id equals subLocationTable.Sub_Location_ID
where userAsset.User_Id == userCode
&& assetTable.Asset_TypeId == 1 && assetTable.Asset_SubType_Id == 1
select new { workstation = subLocationTable.Sub_Location_Name, location = assetTable.Location_Id });
// wsdetails is still an IEnumerable/IQueryable
result = (from emp in this.Repository.Employee
join designation in this.Repository.Designation on
emp.DesignationId equals designation.Id
where emp.Code == userCode
select new EmployeeDetails
{
firstname = emp.FirstName,
lastname = emp.LastName,
designation = designation.Title,
LocationId = wsdetails.First().location,
WorkStationName = wsdetails.First().workstation
}).SingleOrDefault();

Categories