I have 2 inner joins (3 tables) but I don't know and I find it hard to implement my research about outer join in LINQ. How do I change the last inner join to outer join, such that column will still join even if the column (Role) is null?
Here's an existing SQL version of this which I want to convert to LINQ:
SELECT dbo.EmployeeAccess.id, dbo.EmployeeAccess.EmpNo, dbo.EmployeeAccess.RoleID, dbo.EmployeeAccess.Active, dbo.EmployeeAccessLevel.Role,
dbo.View_HCM.LNameByFName
FROM dbo.EmployeeAccess LEFT OUTER JOIN
dbo.EmployeeAccessLevel ON dbo.EmployeeAccess.RoleID = dbo.EmployeeAccessLevel.id INNER JOIN
dbo.View_HCM ON dbo.EmployeeAccess.EmpNo = dbo.View_HCM.EmpNo
LINQ I now have with 2 inner joins:
(from ea in context.EmployeeAccesses
join vh in context.View_HCM on (Int16)ea.EmpNo equals vh.EmpNo
join rl in context.EmployeeAccessLevels on ea.RoleID equals rl.id
select new EmployeeWithEmail{
EmpNum = ea.EmpNo ?? 0,
EmailAddress = vh.EmailAddress,
LNameByFname = vh.LNameByFName,
Active2 = ea.Active ?? false
}).ToList();
}
Linq's outer join syntax uses 2 parts. First an into then DefaultIfEmpty
In your case, an outer join might look like this:
(from ea in context.EmployeeAccesses
join vh in context.View_HCM on (Int16)ea.EmpNo equals vh.EmpNo
join rl in context.EmployeeAccessLevels on ea.RoleID equals rl.id into outer_join
from subjoin in outer_join.DefaultIfEmpty()
select new EmployeeWithEmail{
EmpNum = ea.EmpNo ?? 0,
EmailAddress = vh.EmailAddress,
LNameByFname = vh.LNameByFName,
Active2 = ea.Active ?? false
}).ToList();
There are many tutorials on how to create the outer join in LINQ.
Related
I am not trying to join my table using left outer join in LINQ where one of my join conditions does not satisfy. How can I still get the default record in my dataset? My SQL query runs perfectly fine but my join query is not returning the data.
Below is my SQL query where my left outer join works fine:
select
w.issueid as Issue
from worklog w
join jiraissue as j on w.issueid=j.ID
join issuetype AS ty ON ty.ID = j.issuetype
join project AS p on p.ID=j.PROJECT
left outer join customfieldvalue cfv on cfv.ISSUE = w.issueid
Below is my LINQ query in C# where if cfv.issue in the last join is not available in the table, I want to still return the default column.
var data = (from w in worklogs
join i in issuetypes on j.issuetype equals i.ID
join p in projects on j.PROJECT equals p.ID into table0
from c in table0.DefaultIfEmpty()
join cfv in customfieldvalues on w.issueid equals cfv.ISSUE into table1
from d in table1.DefaultIfEmpty()
{
IssueKey = l.Key.pkey + '-' + l.Key.issuenum,
Hours = l.Sum(w => w.timeworked)
}).ToList();
Any help is appreciated.
Thank you.
I am trying to use the "into" statement with DefaultIfEmpty() for the left outer join, but when I try to join to the 3rd collection, it doesn't like it (can't see /use it / find it )
It doesn't seem to like personRole on the line below
join roleTypes in roles on personRole.ContactRoleTypeId equals roleTypes.ContactRoleTypeId into r2
the query:
findPersonResultsViewModelNew =
from azed in findPersonViewModel.findPersonResultsViewModel
join personRole in personContactRoles on azed.PersonID equals personRole.PersonId into r1
join roleTypes in roles on personRole.ContactRoleTypeId equals roleTypes.ContactRoleTypeId into r2
from p in r1.DefaultIfEmpty()
from g in r2.DefaultIfEmpty()
select
//…. other code
Change the order of your statements. When doing left join, you must immediately follow the join with a new from...DefaultIfEmpty():
findPersonResultsViewModelNew =
from azed in findPersonViewModel.findPersonResultsViewModel
join personRole in personContactRoles on azed.PersonID equals personRole.PersonId into prj
from personRole in prj.DefaultIfEmpty()
join roleTypes in roles on personRole.ContactRoleTypeId equals roleTypes.ContactRoleTypeId into rtj
from roleTypes in rtj.DefaultIfEmpty()
select
I've been following a few examples from this website on how to create Linq Left Outer Join queries but I haven't found any examples of where the "outer key in the left join doesn't point to the inner key but instead points to a previous key". Bear with me for that phrasing I know it's not correct but have a look at the following snippets of code and maybe it will be clearer.
Specifically, see the first left join where sp.SalesPersonID = j.SalesPersonID.
select rt.Name as ResourceType, s.FirstName + ' ' + s.Surname as Supervisor, sp.FirstName + ' ' + sp.LastName as SalesPerson, tr.OrderCodeID, tr.SkillID
, j.CustomerName, j.JobNumber
from dbo.TaskResource tr join projects.Task t on t.ID = tr.taskiD
join dbo.ResourceType rt on rt.ID = tr.ResourceTypeID
join projects.projecttask pt on pt.taskid = tr.taskid
join projects.jobproject jp on jp.projectid = pt.projectid
join crm.tbljobs j on j.jobid = jp.jobid
left join common.tblSalesPersons sp on sp.SalesPersonID = j.SalesPersonID
left join common.tblSupervisors s on s.SupervisorID = j.SupervisorID
where JobDeleted is null or JobDeleted = 0
order by ResourceType
When converted to Linq it would make
...from j in temp1.DefaultIfEmpty()
join sp in dbc.tblSalesPersons on j.SalesPersonID equals sp.SalesPersonID into temp2
So far so good. But when I do the next left join I though it would just be the same thing but pointing to one of the previous keys as I mentioned earlier so instead of using the sp variable which I've seen several examples of, I use the j variable which is from a previous join:
from sp in temp2.DefaultIfEmpty()
join s in dbc.tblSupervisors on j.SupervisorID equals s.SupervisorID
Here is the full code snippet:
List<ResourceTreeObject> resourceTreeObjects = (
from tr in dbc.TaskResources
join t in dbc.Tasks on tr.TaskID equals t.ID
join rt in dbc.ResourceTypes on tr.ResourceTypeID equals rt.ID
join pt in dbc.ProjectTasks on tr.TaskID equals pt.TaskID
join jp in dbc.JobProjects on pt.ProjectID equals jp.ProjectID
join j in dbc.tblJobs on jp.JobID equals j.JobID into temp1
from j in temp1.DefaultIfEmpty()
join sp in dbc.tblSalesPersons on j.SalesPersonID equals sp.SalesPersonID into temp2
from sp in temp2.DefaultIfEmpty()
join s in dbc.tblSupervisors on j.SupervisorID equals s.SupervisorID
where j.JobDeleted == null || j.JobDeleted == 0
select new ResourceTreeObject
{
TaskResourceID = tr.ID
,
TaskID = tr.TaskID
,
ResourceTypeID = tr.ResourceTypeID
,
ResourceType = rt.Name
,
SkillID = tr.SkillID
,
OrderCodeID = tr.OrderCodeID
,
PermissionID = tr.PermissionID
,
JobID = j.JobID
,
JobNumber = j.JobNumber
,
CustomerName = j.CustomerName
,
Salesperson = sp.FirstName + " " + sp.LastName
,
Supervisor = s.FirstName + " " + s.Surname
}).ToList();
And this results in the wrong query. The last "left join" is treated like an inner join and returns the wrong number of rows. So in essence what I'm asking is, how do I (in LinQ) do two consecutive left outer joins after doing several consecutive inner joins but use the key from one of the previous tables in my left out join?
Also I'm not sure what the correct terminology for inner/outer keys etc. hence the awkward phrasing and title. Perhaps someone could correct that so it would be more beneficial to others. Thank you.
Your LINQ translation is just a little off.
The SQL has an inner join on crm.tbljobs followed by outer joins on common.tblSalesPerson and common.tblSupervisors.
The LINQ has outer joins on dbc.tblJobs and dbc.tblSalesPersons followed by an inner join on dbc.tblSupervisors.
into temp1 ... from j in in temp1.DefaultIfEmpty() makes the outer join happen on the table introduced prior to the into, which is dbc.tblJobs.
So it should be:
...
// inner join
join j in dbc.tblJobs on jp.JobID equals j.JobID
// left outer join
join sp in dbc.tblSalesPersons on j.SalesPersonID equals sp.SalesPersonID into salesPersons
from sp in salesPersons.DefaultIfEmpty()
// left outer join
join s in dbc.tblSupervisors on j.SupervisorID equals s.SupervisorID into supervisors
from s in supervisors.DefaultIfEmpty()
...
I changed temp1 and temp2 to more meaningful names to demonstrate what they represent in the outer join syntax. Note the relationship and relative position of dbc.tblSalesPersons to salesPersons, for example.
One more thing to remember is that sp and s can be null, so make sure you check for that before accessing their FirstName, LastName, and Surname properties.
I have this query
SELECT t.NomeTipo, sum(v.QtdProduto)
FROM [dbo].[Vendas] AS V
RIGHT JOIN [dbo].[Produtos] AS P ON V.IdProduto = P.IdProduto
INNER JOIN [dbo].[Tipos] AS T ON P.IdTipo = T.IdTipo
group by t.NomeTipo
order by t.NomeTipo
I have tried this
var queryTipos = from vendas in repositorioVendas.Vendas
join produtos in repositorioProduto.Produtos.DefaultIfEmpty()
on vendas.IdProduto equals produtos.IdProduto
join tipos in repositorioTipo.Tipos
on produtos.IdTipo equals tipos.IdTipo
group vendas by new { tipos.NomeTipo, vendas.QtdProduto }
into novoGrupo
select new
{
NomeTipo = novoGrupo.Key.NomeTipo,
QtdProduto = novoGrupo.Sum(x => x.QtdProduto)
};
With this query I got only two results, but when I run straight from the database I get something like this:
Bebidas 16
Bolos 14
Frios 16
Pães 21
The trick is to realize that you can rewrite your query with a left join instead of a right join by swapping the order of the first two tables and that Linq doesn't have a way to really handle right joins. Also you're grouping was wrong.
var queryTipos = from produtos in repositorioProduto.Produtos
join vendas_pj in repositorioVendas.Vendas
on vendas_pj.IdProduto equals produtos.IdProduto into joined
from vendas in joined.DefaultIfEmpty()
join tipos in repositorioTipo.Tipos
on produtos.IdTipo equals tipos.IdTipo
group vendas by tipos.NomeTipo
into novoGrupo
select new
{
NomeTipo = novoGrupo.Key,
QtdProduto = novoGrupo.Sum(x => x.QtdProduto)
};
Basically a Left join in SQL
From TableA
Left Join TableB
On TableA.ID = TableB.ID
is done in Linq like this
from a in repo.TableA
join b_pj in repo.TableB
on a.ID equals b_pj.ID into joined
from b in joined.DefaultIfEmpty()
I have to query the database and to do so requires that I do quite a few inner joins and a couple of left outer joins. I've generated the SQL in a view but I am now having a bit of difficulty rewriting it to LINQ in my applications data layer.
FROM dbo.Organisation
INNER JOIN
dbo.EducationCourseVenue
INNER JOIN
dbo.EducationCourseVenueLocation ON dbo.EducationCourseVenue.Id = dbo.EducationCourseVenueLocation.EducationCourseVenueId ON
dbo.Organisation.GlobalEntityGUID = dbo.EducationCourseVenue.GlobalEntityGUID
INNER JOIN
dbo.CommunicationType
INNER JOIN
dbo.CommunicationTypeGlobalEntityMap ON dbo.CommunicationType.Id = dbo.CommunicationTypeGlobalEntityMap.CommunicationTypeId ON
dbo.EducationCourseVenue.GlobalEntityGUID = dbo.CommunicationTypeGlobalEntityMap.GlobalEntityGUID
INNER JOIN
dbo.Country
INNER JOIN
dbo.Address ON dbo.Country.Id = dbo.Address.CountryId
INNER JOIN
dbo.CountryRegion ON dbo.Country.RegionId = dbo.CountryRegion.Id ON
dbo.CommunicationTypeGlobalEntityMap.CommunicationTypeItemId = dbo.Address.Id
LEFT OUTER JOIN
dbo.AddressPostalDistrictMap
INNER JOIN
dbo.RegionItemDistrictMap ON dbo.AddressPostalDistrictMap.Id = dbo.RegionItemDistrictMap.Id
INNER JOIN
dbo.RegionTypeItem ON dbo.RegionItemDistrictMap.RegionTypeItemId = dbo.RegionTypeItem.Id ON
dbo.Address.Id = dbo.AddressPostalDistrictMap.AddressId
LEFT OUTER JOIN
dbo.RHSGarden
INNER JOIN
dbo.AddressGeographics ON dbo.RHSGarden.Id = dbo.AddressGeographics.NearestRHSGardenId ON dbo.Address.Id = dbo.AddressGeographics.AddressId
WHERE (dbo.CommunicationType.Code = 'AD')
This particular line of SQL is a problem for in LINQ
FROM dbo.Organisation
INNER JOIN
dbo.EducationCourseVenue
INNER JOIN
dbo.EducationCourseVenueLocation ON dbo.EducationCourseVenue.Id = dbo.EducationCourseVenueLocation.EducationCourseVenueId ON
dbo.Organisation.GlobalEntityGUID = dbo.EducationCourseVenue.GlobalEntityGUID
I don't know how to do a join in LINQ without specifying key joins and then doing another join below that.
Any ideas?
var q = from o in context.Organisation
join v in context.EducationCourseVenue on o.GlobalEntityGUID equals v.GlobalEntityGUID
join l in context.EducationCourseVenueLocation on v.Id equals l.EducationCouseVenueId
Is how i think of it, since your:
FROM dbo.Organisation
INNER JOIN
dbo.EducationCourseVenue
INNER JOIN
dbo.EducationCourseVenueLocation ON dbo.EducationCourseVenue.Id = dbo.EducationCourseVenueLocation.EducationCourseVenueId ON
dbo.Organisation.GlobalEntityGUID = dbo.EducationCourseVenue.GlobalEntityGUID
Corresponds to:
FROM dbo.Organisation as o
INNER JOIN dbo.EducationCourseVenue as v ON o.GlobalEntityGUID = v.GlobalEntityGUID
INNER JOIN dbo.EducationCourseVenueLocation as l ON v.Id = l.EducationCourseVenueId