I am trying to create an outer join statement in LINQ and am not having much luck. I know that Performing an outer join requires two steps:
(1) Convert the join into a group join with into
(2) Use DefaultIfEmpty() on the group to generate the null value you expect if the joined result set is empty.
I have been using this code as an example:
var query = (from p in dc.GetTable<Person>()
join pa in dc.GetTable<PersonAddress>() on p.Id equals pa.PersonId into tempAddresses
from addresses in tempAddresses.DefaultIfEmpty()
select new { p.FirstName, p.LastName, addresses.State });
So I tried to do this:
var outerJoin =
from h in resultHours
join u in results on h.Key equals u.Key into outer
from dictionary in outer.DefaultIfEmpty()
select new {
The problem is that intellisense doesn't recognize anything in the select new {} statement. I've tried u. and h., even dictionary.
The problem I may be running into is that I'm trying to outer join two dictionaries. It doesn't seem to like that, although this is what I was told I need to do. I am obviously doing something wrong or not understanding something.
I need to join the dictionary results.Unit with the dictionary resultHours.Hours because my output is missing unitId fields under certain conditions. Doing an outer join is supposed to clear this up.
Here is the code for results:
var results =
(from v in VDimUnit
join vf in VFactEnergyAllocation on v.UnitKey equals vf.UnitKey
join vd in VDimGadsEvent on vf.GadsEventKey equals vd.GadsEventKey
join vt in VDimTime on vf.TimeKey equals vt.TimeKey
where typeCodes.Contains(vd.GadsEventTypeCode)
&& vt.YearNum >= (year - 3) && vt.YearNum <= year
group vf by new {v.PlantId, v.PhysicalUnitId, v.NetDependableCapacity, v.NetMaximumCapacity,
vt.MonthNum} into groupItem
select new {groupItem.Key.PlantId, groupItem.Key.PhysicalUnitId, groupItem.Key.NetMaximumCapacity,
groupItem.Key.MonthNum, PO_HRS = groupItem.Sum(
x=> (float)x.AllocatedEnergyMwh / groupItem.Key.NetDependableCapacity),
UO_HRS = groupItem.Sum(x=> (float)x.AllocatedEnergyMwh / groupItem.Key.NetDependableCapacity),
Unit = groupItem.Count(), groupItem.Key}).ToDictionary(x=> x.Key, x=> x);
Here is the code for resultHours:
var resultHours =
(from vt in VDimTime
join vf in VFactEnergyAllocation on vt.TimeKey equals vf.TimeKey
join vd in VDimGadsEvent on vf.GadsEventKey equals vd.GadsEventKey
join v in VDimUnit on vf.UnitKey equals v.UnitKey
group vt by new {v.PlantId, v.PhysicalUnitId, v.NetDependableCapacity, v.NetMaximumCapacity,
vt.MonthNum} into groupItem
select new {groupItem.Key.PlantId, groupItem.Key.PhysicalUnitId, groupItem.Key.NetMaximumCapacity,
Hours = groupItem.Count(), groupItem.Key}).ToDictionary(x=> x.Key.ToString(), x=> x.Hours);
This is presently how I have my output. It will change after I figure out how to do the outer join.
var finalResults =
(from r in results
orderby r.Key.MonthNum, r.Key.PlantId, r.Key.PhysicalUnitId
select new {Site = r.Key.PlantId, Unit = r.Key.PhysicalUnitId, r.Key.MonthNum, Numerator = r.Value.PO_HRS, Denominator =
resultHours[r.Key.ToString()], Weight = r.Key.NetMaximumCapacity, Data_Indicator = DATA_INDICATOR,
Budgeted = budgetedPlannedOutageHrs, Industry_Benchmark = INDUSTRY_BENCHMARK, Comments = comments,
Executive_Comments = executiveComments, Fleet_Exec_Comments = fleetExecComments});
I'm at a loss. The LINQ outer join examples I have found apaprently do not apply when joining dictionaries.
Related
I'm trying to convert my SQL statement to a Linq statement and I'm not sure how to add the second COUNT to it. This is my SQL statement
SELECT l.Campus_Name, Labs = COUNT(*), LabsWithSubnets = COUNT(s.Lab_Space_Id)
FROM vw_Lab_Space l
LEFT JOIN vw_Subnet s on l.Lab_Space_Id = s.Lab_Space_Id
GROUP BY l.Campus_Name
ORDER BY 1
and this is my LINQ statement so far:
from l in Vw_Lab_Space
from s in Vw_Subnet
.Where(s => s.Lab_Space_Id == l.Lab_Space_Id)
.DefaultIfEmpty() // <=- triggers the LEFT JOIN
group l by new { l.Campus_Name } into g
orderby g.Key.Campus_Name
select new {
Campus_Name = g.Key.Campus_Name,
Labs = g.Count()
}
So I have everything but the LabsWithSubnets part in there. I'm just not sure how to add that in as I can't just do an s.Lab_Space_id.Count() in the select statement.
If you need table structure and sample data please see Need help creating an OUTER JOIN to count spaces.
Using your query as a basis, you need the groups to include s so you can count when non-null (I also removed the unnecessary anonymous object around the grouping key):
from l in Vw_Lab_Space
from s in Vw_Subnet
.Where(s => s.Lab_Space_Id == l.Lab_Space_Id)
.DefaultIfEmpty() // <=- triggers the LEFT JOIN
group new { l, s } by l.Campus_Name into g
orderby g.Key
select new {
Campus_Name = g.Key,
Labs = g.Count(),
LabsWithSubnets = g.Count(ls => ls.s != null)
}
However, rather than translate the SQL, I would probably take advantage of LINQ's group join to handle the query slightly differently:
var ans = from l in Vw_Lab_Space
join s in Vw_Subnet on l.Lab_Space_Id equals s.Lab_Space_Id into sj
group new { l, sj } by ls.Campus_Name into lsjg
select new {
Campus_Name = lsjg.Key,
NumLabs = lsjg.Count(),
LabsWithSubnets = lsjg.Sum(lsj => lsj.sj.Count())
};
PS Even in your query, I would use join...from...DefaultIfEmpty rather than from...from...where but depending on your database engine, may not matter.
I'm having trouble getting my Linq statemnt to work when doing an outer join and a group by. Here's a SQL version of what I'm trying to accomplish:
select p.PRIMARY_KEY, min(p.EFFECTIVE_DATE), sum(IsNull(c.PAID_INDEMNITY, 0))
from PRMPOLCY p
left outer join CLMMAST c on p.PRIMARY_KEY = c.POLICY_NO
where p.UNDERWRITER_UID = 93
GROUP BY p.PRIMARY_KEY
Here's what I have in Linq (which doesn't work):
var result = from p in context.PRMPOLCies
join c in context.CLMMASTs on p.PRIMARY_KEY equals c.POLICY_NO into polClm
where (p.UNDERWRITER_UID == underwriter)
from grp in polClm.DefaultIfEmpty()
group grp by p.PRIMARY_KEY into g
select new PolicySummation()
{
PolicyNo = g.Key,
Incurred = g.Sum(grp => grp.PAID_INDEMNITY ),
EffDate = g.Min(grp => grp.PRMPOLCY.EFFECTIVE_DATE
};
Beating my head against the wall trying to figurwe this out!
Assuming you have a navigation property set up between PRMPOLCY and CLMMAST, you shouldn't need to specify the join explicitly. It's much easier to express most queries in linq without explicit joins, but rather treating your structures as a hierarchy. I don't know the specifics of your model property names, but I'd take a guess that something like this would work.
var result =
from p in context.PRMPOLCies
where (p.UNDERWRITER_UID == underwriter)
select new PolicySummation {
PolicyNo = p.PRIMARY_KEY,
Incurred = p.CLMASTs.Select(c => c.PAID_INDEMNITY).DefaultIfEmpty().Sum(),
EffDate = p.EFFECTIVE_DATE,
};
You need to include both your tables in the group clause like this:
group new { p, grp } by p.PRIMARY_KEY into g
Then in your Sum / Min
g.Sum(grp => grp.grp == null ? 0 : grp.grp.PAID_INDEMNITY )
g.Min(grp => grp.p.PRMPOLCY.EFFECTIVE_DATE)
Hi I am trying to join multiple entities in CRM 2011 but I get and error saying: {"'xrmsm_sessionEnrollments' entity doesn't contain attribute with Name = 'xrmsm_termsid'."}. That is correct but I am joining the entity that have that attribute.
My Linq Query:
var query2 = from e in svsContext.xrmsm_sessionEnrollmentsSet
join s in svsContext.xrmsm_sessionsSet on e.xrmsm_SessionLookup.Id equals s.xrmsm_sessionsId
join ic in svsContext.xrmsm_institutionCoursesSet on s.xrmsm_institutionCourseLookup.Id equals ic.xrmsm_institutionCoursesId
join ts in svsContext.xrmsm_term_sessionsSet on e.xrmsm_termSessionLookup.Id equals ts.xrmsm_term_sessionsId
join t in svsContext.xrmsm_termsSet on ts.xrmsm_TermLookup.Id equals t.xrmsm_termsId
where (e.xrmsm_StudentLookup.Equals(studentlookup)
&& e.xrmsm_YearLookup.Equals(entity.GetAttributeValue("xrmsm_studentlookup"))
&& ic.xrmsm_institutionCoursesId == institutionCourseGuid
&& t.xrmsm_termsId == termGuid)
select new { sessionName = s.xrmsm_sessionsName, StudentName = e.xrmsm_studentsName, StudentId = e.xrmsm_StudentLookup.Name };
My original SQL query that works on SQL Server:
SELECT en.xrmsm_currentsessionenrollmentsname
,en.xrmsm_isreadonlyname
,en.xrmsm_sessionenrollmentsaverage
,en.xrmsm_sessionenrollmentsgrade
,en.xrmsm_sessionenrollmentsid
,en.xrmsm_sessionenrollments_id
,en.xrmsm_sessionlookup as sessionid
,en.xrmsm_sessionlookupname
,en.xrmsm_sessionsname
,en.xrmsm_studentlookup AS studentid
,en.xrmsm_studentlookupname
,en.xrmsm_studentsname
,en.xrmsm_termsessionlookup
,en.xrmsm_termsessionlookupname
,en.xrmsm_withdrawal
,en.xrmsm_yearaverage
,en.xrmsm_yeargrade
,en.xrmsm_yearlookup
,en.xrmsm_yearlookupname
FROM CoseyTest_MSCRM.dbo.Filteredxrmsm_sessionEnrollments as en INNER JOIN
CoseyTest_MSCRM.dbo.Filteredxrmsm_sessions crmsessions ON
(en.xrmsm_sessionlookup = crmsessions.xrmsm_sessionsid AND en.xrmsm_yearlookup = crmsessions.xrmsm_yearlookup)
INNER JOIN Filteredxrmsm_institutionCourses institutionCourses
on crmsessions.xrmsm_institutioncourselookup = institutionCourses.xrmsm_institutioncoursesid
Inner Join Filteredxrmsm_term_sessions as termsession
on en.xrmsm_termsessionlookup = termsession.xrmsm_term_sessionsid
Inner Join Filteredxrmsm_terms as terms
on termsession.xrmsm_termlookup = terms.xrmsm_termsid
where en.xrmsm_yearlookup = '4BA07BED-3F51-E211-8359-00155D004403'
and en.xrmsm_studentlookup = 'C844AF65-5B51-E211-8359-00155D004403'
and terms.xrmsm_termsid = 'D1D107B7-4551-E211-8359-00155D004403'
and institutionCourses.xrmsm_institutioncoursesid = '2121914E-4551-E211-8359-00155D004403'
Linq provider for CRM has some limitations. You can fount it here. Check if your query violates limitations.
Try having a look at the actual query that it is generating and run that in SQL, it may help understand what is going on.
It could be that something your doing in LINQ is not available in CRM as paramosh mentioned but I can't see anything that jumps out at me.
Thanks for all your responses. I did the following workaround.
Make a query that retrieve a list of all the Student enrollments
Use the Find method of the List<T> class to find if a record exist with the conditions I am searching for.
var query2 = (from e in svsContext.xrmsm_sessionEnrollmentsSet
join s in svsContext.xrmsm_sessionsSet on e.xrmsm_SessionLookup.Id equals s.xrmsm_sessionsId
join ic in svsContext.xrmsm_institutionCoursesSet on s.xrmsm_institutionCourseLookup.Id equals ic.xrmsm_institutionCoursesId
join ts in svsContext.xrmsm_term_sessionsSet on e.xrmsm_termSessionLookup.Id equals ts.xrmsm_term_sessionsId
join t in svsContext.xrmsm_termsSet on ts.xrmsm_TermLookup.Id equals t.xrmsm_termsId
where (e.xrmsm_StudentLookup.Equals(studentlookup))
select new
{
EnrollmentId = e.xrmsm_sessionEnrollments_id,
SessionId = s.xrmsm_sessions_id,
EnrollmentYear = e.xrmsm_YearLookup,
InstitutionCourseId = (Guid)ic.xrmsm_institutionCoursesId,
TermId = (Guid)t.xrmsm_termsId,
Student = e.xrmsm_StudentLookup
}).ToList();
var q = query2.Find(r => r.EnrollmentYear.Id == entity.GetAttributeValue<EntityReference>("xrmsm_yearlookup").Id
&& r.InstitutionCourseId == institutionCourseGuid
&& r.TermId == termGuid);
I've come up with the following LINQ, but I can't help thinking that I should be able to do this in one query instead of two. Can anyone help?
The idea is to inner join three tables and group by one.
var q1 = from er in ExportRules
join per in PlaceExportRules on er.ExportRulesID equals per.ExportRulesID
select new
{
PlaceID = per.PlaceID,
Description = er.Description,
Tag = er.TagName,
ExportName = per.ExportName,
ExportAddress = per.ExportAddress
};
var q2 = from p in Places
join rules in q1 on p.PlaceID equals rules.PlaceID into joined2
where joined2.Any()
orderby p.PlaceName
select new {Place = new {p.PlaceID, p.PlaceName}, joined2};
Well, you can just bracket things:
var query = from p in Places
join rules from
(from er in ExportRules
join per in PlaceExportRules
on er.ExportRulesID equals per.ExportRulesID
...)
on p.PlaceId equals rules.PlaceId into joined2
where joined2.Any()
orderby p.PlaceName
select new {Place = new {p.PlaceID, p.PlaceName}, joined2};
However, I'd personally probably leave it as two queries. It's clearer, and won't affect the performance - it's not like the first statement in your code would actually execute the query.
I am trying to do a left outer join on three tables. I know there is a way to do it in one variable but I haven't been able to figure it out. Instead I have performed two separate left outer joins and then did a left out join on those. Here is my code for that:
var outerJoin1 =
(from h in resultHours
join u in results on new {h.PhysicalUnitId, h.MonthNum} equals new {u.PhysicalUnitId, u.MonthNum} into outer
from grouping in outer.DefaultIfEmpty()
select new {timeKey = h, Key = grouping});
var outerJoin2 =
(from h in resultHours
join s in serviceHrsResults on new {h.PhysicalUnitId, h.MonthNum} equals new {s.PhysicalUnitId, s.MonthNum} into outer2
from grouping in outer2.DefaultIfEmpty()
select new {timeKey = h, Key = grouping});
var outerJoin =
(from a in outerJoin1
join b in outerJoin2 on new {a.timeKey.PlantId, a.timeKey.MonthNum} equals new {b.timeKey.PlantId, b.timeKey.MonthNum} into outer
from grouping in outer.DefaultIfEmpty()
select new{timeKey = a, Key = grouping}).Distinct();
I have tried putting the above together in one variable I can't get it to work. Here is what I have tried:
var outerjoin =
from h in resultHours
join u in results on new {h.PhysicalUnitId, h.MonthNum} equals new {u.PhysicalUnitId, u.MonthNum} into outer
from grouping in outer.DefaultIfEmpty()
from hr in resultHours
join s in serviceHrsResults on new {hr.PhysicalUnitId, hr.MonthNum} equals new {s.PhysicalUnitId, s.MonthNum} into outer2
from grouping in outer2.DefaultIfEmpty()
select new {timeKey = h && timeKey = hr, Key = grouping};
The problem with this is the two groupings conflict. i'm pretty sure I just need a single grouping right before the select but cannot figure out how to use the "grouping in" and include both outer.DefaultIfEmpty() and outer2.DefaultIfEmpty().
I would appreciate it if someone could enlighten me.
You are using duplicate range variable ids - you've got 2x grouping.
- Which grouping is it that you want in the result set ?
- What is this line expected to achieve?
timeKey = h && timeKey = hr - how do you want to combine those in the projection ?
I would try the query below, but I've changed the select part of it as your combined version doesn't make sense to me.
Let me know if it at all works for you as without data it's not easy to test this, so I can't be sure that the new version is fine.
var outerjoin =
from h in resultHours
join u in results on new {h.PhysicalUnitId, h.MonthNum} equals new {u.PhysicalUnitId, u.MonthNum} into outer
from grouping1 in outer.DefaultIfEmpty()
//from hr in resultHours
join s in serviceHrsResults on new {grouping1.PhysicalUnitId, grouping1.MonthNum} equals new {s.PhysicalUnitId, s.MonthNum} into outer2
from grouping2 in outer2.DefaultIfEmpty()
select new {timeKey = h, Key1 = grouping1, Key2 = grouping2};