Linq left outer join across multiple tables retrieving info from each table - linq

I am new to linq and want to perform joins across three tables. The sql equivalent of what I am trying to do is:
SELECT PM.Id, PM.new_clientId, .....
C.new_firstname, c.new_surname,.....
A.new_addressid, A.new_addressline1, A.new_addressline2.....
CY.new_county
FROM partialMatch PM
INNER JOIN new_client C ON PM.new_clientId = C.new_clientId
LEFT OUTER JOIN new_address A ON C.new_addressId = A.new_addressId
LEFT OUTER JOIN new_county CY ON A.new_countyId = CY.new_countyId
WHERE PM.ownerId = #UserId AND PM.new_reviewed <> true
The linq code I have developed is this, but these don't seem to be outer join as it is not returning any results unless I comment out the joins to the address and county table
var partialMatches = from pm in ContextService.CreateQuery<new_partialmatch>()
join c in ContextService.CreateQuery<new_client>() on pm.new_ClientId.Id equals c.new_clientId
join a in ContextService.CreateQuery<new_address>() on c.new_AddressID.Id equals a.new_addressId
join cy in ContextService.CreateQuery<new_county>() on a.new_CountyID.Id equals cy.new_countyId
where pm.OwnerId.Id == _currentUserId && pm.new_Reviewed != true
select new
{
Id = pm.Id,
new_ClientID = pm.new_ClientId,
new_MatchingCRMClientID = pm.new_MatchingCRMClientId,
new_MatchingVulcanClientID = pm.new_MatchingVulcanClientID,
new_name = pm.new_name,
firstname = c.new_FirstName,
surname = c.new_Surname,
dob = c.new_DateOfBirth,
addressId = a.new_addressId,
address1 = a.new_AddressLine1,
address2 = a.new_AddressLine2,
address3 = a.new_AddressLine3,
address4 = a.new_AddressLine4,
county = cy.new_County
};
Any help would be greatly appreciated,
Thanks,
Neil
EDIT:
I also tried using the 'into' statement but then on my second join the alias isn't recognised.
from pm in ContextService.CreateQuery<new_partialmatch>()
join c in ContextService.CreateQuery<new_client>() on pm.new_ClientId.Id equals c.new_clientId into pmc
from x in pmc.DefaultIfEmpty()
join a in ContextService.CreateQuery<new_address>() on c.new_AddressID.Id equals a.new_addressId
So for the c.newIddressID.Id equals a.new_addressId I get this error message:
The name 'c' is not in scope on the left side of 'equals'. Consider swapping the expressions on either side of 'equals'.

var addressCountryQuery = from a in ContextService.CreateQuery<new_address>()
from cy in ContextService.CreateQuery<new_county>().Where( cy => cy.new_countyId == a.new_CountyID.Id).DefaultIfEmpty()
select new
{
addressId = a.new_addressId,
address1 = a.new_AddressLine1,
address2 = a.new_AddressLine2,
address3 = a.new_AddressLine3,
address4 = a.new_AddressLine4,
county = cy.new_County
}
var partialMatches = from pm in ContextService.CreateQuery<new_partialmatch>()
join c in ContextService.CreateQuery<new_client>() on pm.new_ClientId.Id equals c.new_clientId
from a in addressCountryQuery .Where( a => a.addressId == c.new_AddressID.Id).DefaultIfEmpty()
where pm.OwnerId.Id == _currentUserId && pm.new_Reviewed != true
select new
{
Id = pm.Id,
new_ClientID = pm.new_ClientId,
new_MatchingCRMClientID = pm.new_MatchingCRMClientId,
new_MatchingVulcanClientID = pm.new_MatchingVulcanClientID,
new_name = pm.new_name,
firstname = c.new_FirstName,
surname = c.new_Surname,
dob = c.new_DateOfBirth,
addressId = a.addressId,
address1 = a.AddressLine1,
address2 = a.AddressLine2,
address3 = a.AddressLine3,
address4 = a.AddressLine4,
county = a.County
};
See how I modified the join for a and cy so I could add the DefultIfEmpty method.
I split the two outer joins in to one query and then join that query back into the original query.

Related

Group Concatenation with Linq

I have this:
var lstAssignmentDetails =
(from t1 in _unitOfWork.ReferralDetailsRepository.Get()
.Where(m => (m.ChartStatusID == (int)Utility.ReferralChartStatus.NotStaffed
|| m.ChartStatusID == (int)Utility.ReferralChartStatus.Partially_Staffed
|| m.ChartStatusID == (int)Utility.ReferralChartStatus.Restaff)
&& m.IsDeleted == false).OrderByDescending(x => x.ReferralDetailsId)
join t2 in _unitOfWork.ReferralRepository.Get() on t1.ReferralId equals t2.ReferralId
join t3 in _unitOfWork.PatientRepository.Get() on t2.PatientId equals t3.PatientId
join t4 in _unitOfWork.ClientRepository.Get() on t2.ClientId equals t4.ClientID
join t5 in _unitOfWork.DisciplineRepository.Get() on t1.DesciplineID equals t5.DesciplineID
join t6 in _unitOfWork.UserRepository.Get() on t2.CreatedBy equals t6.UserID
join t7 in _unitOfWork.PersonRepository.Get() on t6.PersonID equals t7.PersonID
join rt in _unitOfWork.ReferralTherapistRepository.Get() on t2.ReferralId equals rt.ReferralId
join t in _unitOfWork.TherapistRepository.Get() on rt.TherapistId equals t.TherapistId
join u in _unitOfWork.UserRepository.Get() on t.UserId equals u.UserID
join p in _unitOfWork.PersonRepository.Get() on u.PersonID equals p.PersonID
select new ReferralTempModel()
{
ReferralId = t1.ReferralId,
ClientName = t4.ClientName,
PatientName = t3.LastName + "," + t3.FirstName,
RefferalDate = t2.RefferalDate,
DisciplineID = t1.DisciplineID,
ReferralDetailsId = t1.ReferralDetailsId,
PatientId = t2.PatientId,
ClientID = t4.ClientID,
DiscName = t5.DesciplineType,
IsRejected = t1.IsRejected,
CreatedBy = t7.LastName + "," + t7.FirstName,
ChartstatusId = t1.ChartStatusID.Value
}).ToList();
return lstAssignmentDetails;
There are multiple ReferralTherapists for each referral. I need all the ReferralTherapists for each referral to be concatenated into one field. Essentially what I want to do is a GROUP_CONCAT using LINQ.
I know this is probably a duplicate, but none of the previous questions handles anything as complicated as this. Trying to add GroupBy always seems to create an error no matter where I place it.
Start by not joining other referrals as t2 when constructing your flat list, and adding therapist's name to the anonymous type:
var flatListAssignmentDetails =
(from t1 in _unitOfWork.ReferralDetailsRepository.Get()
.Where(m => (m.ChartStatusID == (int)Utility.ReferralChartStatus.NotStaffed
|| m.ChartStatusID == (int)Utility.ReferralChartStatus.Partially_Staffed
|| m.ChartStatusID == (int)Utility.ReferralChartStatus.Restaff)
&& !m.IsDeleted)
join t2 in _unitOfWork.ReferralRepository.Get() on t1.ReferralId equals t2.ReferralId
join t3 in _unitOfWork.PatientRepository.Get() on t2.PatientId equals t3.PatientId
join t4 in _unitOfWork.ClientRepository.Get() on t2.ClientId equals t4.ClientID
join t5 in _unitOfWork.DisciplineRepository.Get() on t1.DesciplineID equals t5.DesciplineID
join t6 in _unitOfWork.UserRepository.Get() on t2.CreatedBy equals t6.UserID
join t7 in _unitOfWork.PersonRepository.Get() on t6.PersonID equals t7.PersonID
select new {
t1.ReferralId,
t4.ClientName,
PatientName = t3.LastName + "," + t3.FirstName,
t2.RefferalDate,
t1.DisciplineID,
t1.ReferralDetailsId,
t2.PatientId,
t4.ClientID,
DiscName = t5.DesciplineType,
t1.IsRejected,
CreatedBy = t7.LastName + "," + t7.FirstName,
ChartstatusId = t1.ChartStatusID.Value,
TherapistName = p.LastName + "," + p.FirstName
}).ToList();
Now you can group your list, and concatenate therapist names:
var listAssignmentDetails = flatListAssignmentDetails
.GroupBy(r => r.ReferralId)
.Select(g => new ReferralTempModel {
ReferralId = g.First().ReferralId,
ClientName = g.First().ClientName,
PatientName = g.First().PatientName,
RefferalDate = g.First().RefferalDate,
DisciplineID = g.First().DisciplineID,
ReferralDetailsId = g.First().ReferralDetailsId,
PatientId = g.First().PatientId,
ClientID = g.First().ClientID,
DiscName = g.First().DiscName,
IsRejected = g.First()..IsRejected,
CreatedBy = g.First().CreatedBy,
ChartstatusId = g.First().ChartStatusID,
TherapistNames = string.Join(", ", g.Select(r => r.TherapistName))
}).ToList();
One thing to note here is that all fields that are tied to all referrals in the group are obtained through g.First() construct. The field with "group concatenation" is produced using string.Join method on a projection of TherapistName from the group.

how use multiple join in linq?

var abc1 = from dlist in db.DebtorTransactions.ToList()
join war in db.Warranties on dlist.ProductID equals war.Id
join ag in db.Agents on war.fldAgentID equals ag.pkfAgentID
join sr in db.SalesReps on war.fldSrId equals sr.pkfSrID
where dlist.TransTypeID == 1
select new
{
dlist.Amount,
dlist.TransTypeID,
name = ag.Name,
ag.pkfAgentID,
sr.pkfSrID,
salesnam = sr.Name
} into objabc
group objabc by new
{
objabc.TransTypeID,
objabc.name,
objabc.salesnam,
objabc.Amount
};
var amt1 = abc1.Sum(x => x.Key.Amount);
var abc2 = from dlist in db.DebtorTransactions.ToList()
join cjt in db.CarJackaTrackas on dlist.ProductID equals cjt.pkfCjtID
join ag in db.Agents on cjt.AgentID equals ag.pkfAgentID
join sr in db.SalesReps on cjt.SalesRepId equals sr.pkfSrID
where dlist.TransTypeID == 0
select new
{
dlist.Amount,
dlist.TransTypeID,
name = ag.Name,
ag.pkfAgentID,
sr.pkfSrID,
enter code here` salesnam = sr.Name
} into objabc
group objabc by new
{
objabc.TransTypeID,
objabc.name,
objabc.salesnam,
objabc.Amount
};
var amt2 = abc1.Sum(x => x.Key.Amount);
//var result1=
return View();
i am new to linq, this query is working but i need to get the sum of Amount where dlist.TransTypeID == 0 and where dlist.TransTypeID == 1 by just single query. may anybody help me? thanks in advance
Here's a trimmed down example of how you can do it. You can add the joins if they are necessary, but I'm not clear on why you need some of the extra join values.
var transTypeAmountSums = (from dlist in db.DebtorTransactions
group dlist by dlist.TransTypeId into g
where g.Key == 0 || g.Key == 1
select new
{
TransTypeId = g.Key,
AmountSum = g.Sum(d => d.Amount)
}).ToDictionary(k => k.TransTypeId, v => v.AmountSum);
int transTypeZeroSum = transTypeAmountSums[0];
int transTypeOneSum = transTypeAmountSums[1];
A couple of things to note:
I removed ToList(). Unless you want to bring ALL DebtorTransactions into memory then run a Linq operation on those results, you'll want to leave that out and let SQL take care of the aggregation (it's much better at it than C#).
I grouped by dlist.TransTypeId only. You can still group by more fields if you need that, but it was unclear in the example why they were needed so I just made a simplified example.

Doing 2 Left Joins on the same table with one call

I have the following tables:
Users:
userId, userFirstName, userLastName.
holdBilling:
bEntityID, CarrierOID, PayerOID, holdTYPE, createUserID.
carrier:
carrierOID, carrierName.
payer:
payerOID, payerName.
I want the code to save in a new entity
holdBilling => new
{
FirstName, LastName, CarrierName, PayerName
}
One of these Entities has either a payer or a carrier value (cannot have both). Basically i want to make 2 left joins on the same table with one call. This would be the SQL query that would work for me.
SELECT TOP 1000 [ID]
,[bEntityID]
,c.carrierID
,c.carrierName
,p.payerID
,p.payerName
,[holdType] ( this is "C" for carrier and "P" for payer )
FROM .[dbo].[holdBilling] hb
left join dbo.payer p on hb.payerID = p.payerID
left join dbo.carrier c on hb.carrierID = c.carrierID
where [bEntityID] = 378
The temporary solution I've found is getting a list of all Carriers
var listC = (from hold in holdBilling
join u in Users on hold.createUserID equals u.userID
join c in carrier.DefaultIfEmpty() on hold.carrierID equals c.carrierID
select new
{
Elem = hold,
FName = u.userFirstName,
LName = u.userLastName,
Carrier = c.carrierName,
Payer = ""
}).ToList();
and one for all payers
select new
{
Elem = hold,
FName = u.userFirstName,
LName = u.userLastName,
Carrier = "",
Payer = p.payerName
}).ToList();
and merging the two,I'm confident there has to be a solution for doing both in one query.
Something like this maybe:
var listC = (
from hb in holdBilling
from p in payer.Where(a=>a.payerID==hb.payerID).DefaultIfEmpty()
from c in carrier.Where(a=>a.carrierID=hb.carrierID).DefaultIfEmpty()
where hb.bEntityID==378
select new
{
hb.bEntityID,
c.carrierID,
c.carrierName,
p.payerID,
p.payerName,
holdType=(payer==null?"P":"C")
}
).Take(1000)
.ToList();
You need to use DefaultIfEmpty to do a left join
var listC = (from hold in holdBilling
from u in Users.Where(x => hold.createUserID == x.userID).DefaultIfEmpty()
from c in carrier.Where(x => hold.carrierID == x.carrierID).DefaultIfEmpty()
select new
{
Elem = hold,
FName = u.userFirstName,
LName = u.userLastName,
Carrier = c.carrierName,
Payer = ""
}).ToList();

LINQ 2 lefts joins with Sum

I am trying to do a relatively straight forward SQL query with linq:
SELECT ir.resource_id, r.first_name, r.surname, rt.job_title, SUM(plan_ts_hours), SUM(ts_hours) FROM tbl_initiative_resource ir
INNER JOIN tbl_resource r ON ir.resource_id = r.resource_id
INNER JOIN tbl_resource_type rt ON rt.resource_type_id = r.resource_type_id
LEFT JOIN tbl_plan_timesheet pts on pts.resource_id = ir.resource_id AND pts.initiative_id = ir.initiative_id
LEFT JOIN tbl_timesheet ts on ts.resource_id = ir.resource_id AND ts.initiative_id = ir.initiative_id
WHERE ir.initiative_id = 111
GROUP BY ir.resource_id, r.first_name, r.surname, rt.job_title
After reading this blog: http://smehrozalam.wordpress.com/2010/04/06/linq-how-to-build-complex-queries-utilizing-deferred-execution-and-anonymous-types/
I came up with the following linq:
var query = (from initRes in Context.tbl_initiative_resource
join res in Context.tbl_resource on initRes.resource_id equals res.resource_id
join resType in Context.tbl_resource_type on res.resource_type_id equals resType.resource_type_id
from tsheet in Context.tbl_timesheet.Where(x => x.resource_id == initRes.resource_id).Where(x => x.initiative_id == initRes.initiative_id).DefaultIfEmpty()
from plannedtsheet in Context.tbl_plan_timesheet.Where(x => x.resource_id == initRes.resource_id).Where(x => x.initiative_id == initRes.initiative_id).DefaultIfEmpty()
where initRes.initiative_id == initiativeID
group new { ID = res.resource_id, ResourceType = resType.job_title, Times = tsheet, P = plannedtsheet } by initRes.resource_id into g
select new
{
ResourceID = g.Key,
Hours = g.Sum(x => (decimal?)x.Times.ts_hours),
PlannedHours = g.Sum(x => (decimal?)x.P.plan_ts_hours)
}).ToList();
Any ideas on how I can access the ResourceType when selecting the new anonymous type?
ResourceType is part of the the grouping key, so g.Key.ResourceType should do it.
(Check out the type of ResouceID in the results, as you've assigned it g.Key it will be an instance of the (anonymous) type created in the group clause.

linq projection

I'm building a linq query using join and into statements.
var testmyquery = from uf in TheFolderModel.UserFolders
where (uf.UserID == TheUserID)
join ul in TheFolderModel.UserBooksheets on
uf.FolderID equals ul.FolderID
join ll in TheFolderModel.BooksheetBooks on
ul.BooksheetID equals ll.BooksheetID
join ua in TheFolderModel.BooksAppointments on
ll.BookID equals ua.BookID
group ua.BookID by uf.FolderID into Holdingvar
orderby Holdingvar.Key
select new { TheCount = Holdingvar.Count(), Holdingvar.Key };
I need to add columns from tables in uf and ul to the retuning object but once I've got a projection on Holdingvar, the intellisense doesn't give me access to uf or ul anymore when I'm in the select new{} statement. How do I add columns in the select statement?
Thanks.
I got it:
var testmyquery = from uf in TheFolderModel.UserFolders
where (uf.UserID == TheUserID)
join ul in TheFolderModel.UserBooksheets on
uf.FolderID equals ul.FolderID
join ll in TheFolderModel.BooksheetBooks on
ul.BooksheetID equals ll.BooksheetID
join ua in TheFolderModel.BooksAppointments on
ll.BookID equals ua.BookID
group ua.BookID by uf.FolderID into Holdingvar
orderby Holdingvar.Key
from uf2 in TheFolderModel.UserFolders
where uf2.FolderID == testg.Key
select new { testk = testg.Count(), testg.Key, uf2.FolderName };
I just needed to redefine a variable after I did the projection in the holding variable.
You can't...once you select new {..., the uf and ul are out of scope.
I'm not sure if this would work for you, but if you need the count and key, you could do a let statement:
var testmyquery = from uf in TheFolderModel.UserFolders
where (uf.UserID == TheUserID)
join ul in TheFolderModel.UserBooksheets on
uf.FolderID equals ul.FolderID
join ll in TheFolderModel.BooksheetBooks on
ul.BooksheetID equals ll.BooksheetID
join ua in TheFolderModel.BooksAppointments on
ll.BookID equals ua.BookID
group ua.BookID by uf.FolderID into Holdingvar
orderby Holdingvar.Key
let someproperty = new { TheCount = Holdingvar.Count(), Holdingvar.Key }
select new { uf.UserID, ........ }
I do this all the time. Why is it not working for you? The structure of my query is a bit different. Perhaps you need to reform it?
var q = (from at in Repository.For<AuditTrailEntity>()
from att in Repository.For<AuditTrailTypeEntity>()
join ue in Repository.For<UserEntity>() on at.UserId equals ue.RowId
join up in Repository.For<UserProfileEntity>() on ue.AspnetUserId equals up.UserId
where (at.AffectedEntityName == "AssetEntity" && at.PKId == assetId.ToString() && at.ActionType == att.RowId)
orderby at.CreatedDate descending
select new AssetAuditTrailModel
{
RowId = at.RowId,
AffectedEntityName = at.AffectedEntityName,
PKId = at.PKId,
ActionType = at.ActionType,
ActionData = at.ActionData,
UserId = at.UserId,
CreatedDate = at.CreatedDate,
ActionName = att.FriendlyName,
UserName = up.FirstName + " " + up.LastName,
});

Resources