Optional condition in join clause - Linq - c#

I have below linq query
var resultGuardian = from s in _db.Students
join sg in _db.StudentGuardians on s.StudentID equals sg.StudentID
join g in _db.Guardians on sg.GuardianId equals g.GuardianId
join lr in _db.luRelationTypes on sg.RelationTypeID equals lr.RelationTypeID
join ga in _db.GuardianAddresses on g.GuardianId equals ga.GuardianId
join ad in _db.Addresses on ga.AddressID equals ad.AddressID
join lt in _db.luAddressTypes on ad.AddressTypeID equals lt.AddressTypeID
join lg in _db.luGenders on g.GenderID equals (int?)lg.GenderID into ssts
from gdnr in ssts.DefaultIfEmpty()
where
s.TenantID == tenantid && sg.TenantID == tenantid && g.TenantID == tenantid &&
s.StatusId == (int?)Extension.StatusType.Active //1
&& g.StatusID == (int?)Extension.StatusType.Active &&
lr.StatusID == (int?)Extension.StatusType.Active && lr.TenantID == tenantid &&
s.StudentID == sid
select new
{
g.FirstName,
g.LastName,
IsPrimary = sg.IsPrimaryGuardian,
g.Education,
g.Email,
g.Phone,
lr.RelationCD,
ga.IsStudentAddress,
gdnr.GenderCD,
lt.AddressName,
ad.Address1,
ad.Address2,
ad.City,
ad.State,
ad.Zipcode
};
In above query when ad.AddressTypeID is null, it is not returning any result.
I have requirement if ad.AddressTypeID is null,than from LuAddressTypes fetch default record where AddressTypeCd=1. If I try this way
join lt in LuAddressTypes on ad.AddressTypeID equals lt.AddressTypeID into v1
from v2 in v1.DefaultIfEmpty()
select new
{
v2.AddressName,
g.Phone,..
});
in result v2.AddressName always returning null. I am unable to specify AddressTypeCd=1 where condition as well. AddressTypeCd=1 is not ad.AddressTypeID.
I need v2.AddressName where AddressTypeCd=1. How can I do that?
Find related entities all related entities

You can't use the standard LINQ join, but in LINQ to Entities you could use the alternative join syntax based on correlated Where - EF is smart enough to translate it to JOIN.
In your case, instead of
join lt in _db.luAddressTypes on ad.AddressTypeID equals lt.AddressTypeID
you could use
from lt in _db.luAddressTypes.Where(lt => ad.AddressTypeID == lt.AddressTypeID
|| (ad.AddressTypeID == null && lt.AddressTypeCd == 1))
which is translated to something like this
INNER JOIN [dbo].[LuAddressTypes] AS [Extent3]
ON ([Extent2].[AddressTypeID] = [Extent3].[AddressTypeID])
OR (([Extent2].[AddressTypeID] IS NULL) AND (1 = [Extent3].[AddressTypeCd]))
If you want LEFT OUTER JOIN, simply add .DefaultIfEmpty() at the end of the above line.

Related

Checking a null value in a join query after 2 joins and a where or condition

I have a linq query that is checking to see if a value exists, given criteria. It joins Permits, AccessControlTypes, and EntanceEnhancementHistories, all on their Guid FKs. It checks Permits of Type "Full" or "Limited" to see if they have any associated EntranceEnhancementHistories.
bool LimitedorFullEntranceEnhancementValue = (
from p in context.Permits
join a in context.AccessControlTypes on p.EntranceAccessControlTypeGUID equals a.GUID
join e in context.EntranceEnhancementHistories on p.GUID equals e.PermitGUID
where p.GUID.Equals(PermitGuid)
&& ((a.Description.Equals("Full") || a.Description.Equals("Limited"))
&& (e.GUID == null))
select e).Any();
return LimitedorFullEntranceEnhancementValue;
This query is failing. It's returning false in the case that is does find description of full or limited, but does not find an entrancehancementvalue (it should be null).
It works properly without the check for a null entranceenhancementvalue, and I'm able to get it working with 2 queries, but I don't think this should be necessary.
You're using an "inner join" on p.GUID equals e.PermitGUID, which means you'll only see results where an e.PermitGUID exists. I think you want something more like this:
(from p in context.Permits
join a in context.AccessControlTypes on p.EntranceAccessControlTypeGUID equals a.GUID
where p.GUID.Equals(PermitGuid)
&& ((a.Description.Equals("Full") || a.Description.Equals("Limited"))
&& !context.EntranceEnhancementHistories.Any(e => p.GUID == e.PermitGUID)
select p).Any();
You need to use a LEFT OUTER JOIN if you expect to see rows where the Guid key is null:
(from p in context.Permits
join a in context.AccessControlTypes on p.EntranceAccessControlTypeGUID equals a.GUID
join t in context.EntranceEnhancementHistories on p.GUID equals e.PermitGUID into leftJoin
join e in leftJoin on p.DefaultIfEmpty()
where p.GUID.Equals(PermitGuid)
&& ((a.Description.Equals("Full") || a.Description.Equals("Limited"))
&& (e.GUID == null))
select e).Any();
return LimitedorFullEntranceEnhancementValue;

Converting SQL to LINQ using MAX

I have two tables
1) T_EJV_CREDIT_DS_INDEX
2) T_EJV_CREDIT_DS_INDEX_CONTRACT
I would like a SQL query like below as a LINQ expression
SELECT MAX(INDEX_FAMILY_VERSION) FROM T_EJV_CREDIT_DS_INDEX cdi
INNER JOIN T_EJV_CREDIT_DS_INDEX_CONTRACT cdic
ON cdic.INDEX_ID = cdi.INDEX_ID
WHERE cdi.INDEX_SHORT_NAME LIKE '%#VARIABLE1%'
AND cdic.TENOR = #VARIABLE2
This is what I have attempted so far
var maxFamilyVersion = (from ic in dsIndexContract
join i in dsIndex on i.INDEX_ID equals ic.INDEX_ID
where i.INDEX_SHORT_NAME.CONTAINS(strindex) && ic.TENOR equals d.TERM
select new
{
ic.INDEX_FAMILY_VERSION.Max()
}).Take(1).ToList();
But the above mentioned starts showing compile issues with the syantax as shown below
Checking for equality in your where condition can be done with ==. The keyword equals is only used in a join condition.
var result = (from ic in dsIndexContract
join i in dsIndex on i.INDEX_ID equals ic.INDEX_ID
where i.INDEX_SHORT_NAME.CONTAINS(strindex) && ic.TENOR == d.TERM
select new
{
ic.INDEX_FAMILY_VERSION.Max()
}).FirstOrDefault();
And instead of .Take(1).ToList(), you can use .FirstOrDefault() to retrieve the first item.
Or a more efficient way is to use .Max() directly instead of .FirstOrDefault():
var result = (from ic in dsIndexContract
join i in dsIndex on i.INDEX_ID equals ic.INDEX_ID
where i.INDEX_SHORT_NAME.CONTAINS(strindex) && ic.TENOR == d.TERM
select ic.INDEX_FAMILY_VERSION).Max();
This should do it:
var maxFamilyVersion =
(from ic in dsIndexContract
join i in dsIndex on ic.INDEX_ID equals i.INDEX_ID
where i.INDEX_SHORT_NAME.CONTAINS(strindex) && ic.TENOR == d.TERM
select ic.INDEX_FAMILY_VERSION).Max();

LINQ Left Join 3 tables with OR operator

I have this SQL query:
SELECT * FROM [CMS_MVC_PREPROD].[dbo].[T_MEMBER] m
left outer join [CMS_MVC_PREPROD].[dbo].[T_COMPANY] c
ON m.Company_Id = c.Id
left outer join [CMS_MVC_PREPROD].[dbo].[T_ADRESSE] a
ON m.Id = a.Member_Id OR a.Company_Id = c.Id
But i could not translate it to Linq
I have some trouble with this line in particular:
ON m.Id = a.Member_Id OR a.Company_Id = c.Id
EDIT: I try this query from Ehsan Sajjad
from m in db.Member
join c in db.T_Company on m.Company_Id equals c.Id into a
from c in a.DefaultIfEmpty()
from ta in db.T_Address
where m.Id == ta.Member_Id || ta.Company_Id == c.Id
But it only returns members who have an address and i want all members. Maybe it will work with a full join
Thanks in advance
LINQ only supports equi-joins directly. You'll have to use a different query pattern:
from m in db.MEMBER
//add join to "c" here
from a in (
from a db.ADRESSE
where m.Id == a.Member_Id || a.Company_Id == c.Id
select a).DefaultIfEmpty()
select new { m, a }
Just a sketch. The trick is using DefaultIfEmpty on a subquery. Both L2S and EF can translate this to an OUTER APPLY which is equivalent to a LEFT JOIN.
You can write this way to make it work:
from m in db.Member
join c in db.T_Company on m.Company_Id equals c.Id into a
from c in a.DefaultIfEmpty()
from ta in db.T_Address
where m.Id == ta.Member_Id || ta.Company_Id == c.Id

Can anyone reduce these 3 LINQ to SQL statements into one?

Ok so I am trying to get all the Companies assigned to BOTH courses that exist in a course mapping table.
The course mapping table has 2 FK CourseIDs, that point to two different courses in the same table.
Each course has a bundle, and the companies are assigned to bundles.
I am trying to select all the companies that are assigned to both bundles from both courses.
I have been able to do this (Edit: apparently not, because of the OR, can anyone fix this too?) using 3 different LINQ queries, but I am hoping there is a way to reduce it into one for both brevity and performance:
Bundle vegasBundle = (from cm in db.VegasToPegasusCourseMaps
join c in db.Courses on cm.VegasCourseID equals c.CourseID
join b in db.Bundles on c.BundleID equals b.BundleID
where cm.VPCMapID == CourseMapID
select b).FirstOrDefault();
Bundle pegasusBundle = (from cm in db.VegasToPegasusCourseMaps
join c in db.Courses on cm.PegasusCourseID equals c.CourseID
join b in db.Bundles on c.BundleID equals b.BundleID
where cm.VPCMapID == CourseMapID
select b).FirstOrDefault();
IQueryable<Company> companyAssigned = from cb in db.CompanyBundles
join c in db.Companies on cb.CompanyID equals c.CompanyID
where cb.BundleID == vegasBundle.BundleID || cb.BundleID == pegasusBundle.BundleID
select c;
return companyAssigned.ToList();
Here's your simplified query:
return (
from cm in db.VegasToPegasusCourseMaps
join cv in db.Courses on cm.VegasCourseID equals cv.CourseID
join bv in db.Bundles on cv.BundleID equals bv.BundleID // vegasBundle
join cp in db.Courses on cm.PegasusCourseID equals cp.CourseID
join bp in db.Bundles on cp.BundleID equals bp.BundleID // pegasusBundle
from cb in db.CompanyBundles // OR-Join must be in the where clause
join c in db.Companies on cb.CompanyID equals c.CompanyID
where cm.VPCMapID == CourseMapID
&& (cb.BundleID == bv.BundleID || cb.BundleID == bp.BundleID)
select c
).ToList();
[Update]:
Here's the query that matches your requirements. It will only match companies that match both courses.
return (
from cm in db.VegasToPegasusCourseMaps
join cv in db.Courses on cm.VegasCourseID equals cv.CourseID
join bv in db.Bundles on cv.BundleID equals bv.BundleID // vegasBundle
join cbv in db.CompanyBundles on bv.BundleId equals cbv.BundleId
join cv in db.Companies on cbv.CompanyID equals cv.CompanyID
join cp in db.Courses on cm.PegasusCourseID equals cp.CourseID
join bp in db.Bundles on cp.BundleID equals bp.BundleID // pegasusBundle
join cbp in db.CompanyBundles on bp.BundleId equals cbp.BundleId
join cp in db.Companies on cbp.CompanyID equals cp.CompanyID
where cm.VPCMapID == CourseMapID
&& cv.CompanyID == cp.CompanyID
select cv
).ToList();
Another thing: since you have the following relationship: Courses.BundleId => Bundles.BundleId => CompanyBundles.BundleId, you can actually join Courses to CompanyBundles and skip the Bundles join. But SQL probably does this anyway.
Here's a modification for your last query to ensure that you get companies that are enrolled in both bundles:
IQueryable<Company> companyAssigned =
from c in db.Companies
join vcb in db.CompanyBundles on c.CompanyID equals vcb.CompanyID
join pcb in db.CompanyBundles on c.CompanyID equals pcb.CompanyID
where vcb.BundleID == vegasBundle.BundleID && pcb.BundleID == pegasusBundle.BundleID
select c;
For combining the queries, you can look at Scott Rippey's answer.

LINQ join with OR

I want to do a JOIN with LINQ using an OR statement.
Here is the SQL query I'm starting with:
SELECT t.id
FROM Teams t
INNER JOIN Games g
ON (g.homeTeamId = t.id OR g.awayTeamId = t.id)
AND g.winningTeamId != 0
AND g.year = #year
GROUP BY t.id
I'm having trouble converting that ON clause to LINQ. This is where I'm at:
var y = from t in db.Teams
join g in db.Games on t.ID equals g.AwayTeamID //missing HomeTeamID join
where g.WinningTeamID != 0
&& g.Year == year
group t by t.ID into grouping
select grouping;
I think I could use:
join g in db.Games on 1 equals 1
where (t.ID == g.HomeTeamID || t.ID == g.AwayTeamID)
and this works but seems kind of seems hacky. Is there a better way?
I struggled with this as well until I found the following solution, which worked well for my situation:
var y = from t in db.Teams
from g in db.Games
where
(
t.ID == g.AwayTeamID
|| t.ID == g.HomeTeamID
)
&& g.WinningTeamID != 0
&& g.Year == year
group t by t.ID into grouping
select grouping;
Under the covers, your solution probably works very close to this one. However, I bet this one is just a bit faster if you benchmark it since it is not JOINING every item in the first dataset with every item in the second dataset, which could be a disaster if either (or both) dataset were really big.
The where clause applies a boolean condition, so using "||" is the way to go. You can chain multiple where clauses but I believe that will give you a "and" operation, rather than an "or".
I think you can do like this:
from t1 in db.Table1
// inner join with OR condition
from t2 in db.Table2 where t1.col1 == t2.col1 || t1.col2 == t2.col2
// normal inner join
join t3 in db.Table3 on t1.col1 equals t3.col1
// inner join with complex condition
join t4 in db.Table4 on t2.col4 equals t4.col4 where t2.col5.Contains(t4.col5)
// left join with OR condition
from t5 in db.Table5.Where(x => x.col5 == t1.col5 || x.col6 == t1.col6).DefaultIfEmpty()
select new {
x = 1 // select whatever you want here
}
The underlying SQL query probably won't use native sql joins but the above is just a way to make your code look pretty and organized.

Categories

Resources