The following snippet does work for what I need. I believe though that there must be a better practice? A more optimal way of doing this query?
What is needed is to get a list of employee objects that are the direct reports for employee/mgr x. The direct reports are listed in a history table that has multiple records for each employee, and so only one (the most recent) record should be returned from that table per each direct report (employee) and then the Employee table should be used to get the employee object where employee id is equal to employee id from each history record in this filtered resultset. I can get both halves with two separate LINQ to EF queries.
A problem occurs when trying to join on the employeeHistory object from the first result set. According to MSDN: Referencing Non-Scalar Closures is Not Supported [Referencing a non-scalar closure, such as an entity, in a query is not supported. When such a query executes, a NotSupportedException exception is thrown with a message that states “Unable to create a constant value of type ‘Closure type’. Only primitive types (‘such as Int32, String, and Guid’) are supported in this context.”]
So I run two queries and make the first a list of type int rather than a complex object. This does work, but seems contrived. Any suggestions as to a better way (I would like to do one query).
private List<BO.Employee> ListDirectReports(int mgrId)
{
IQueryable<BO.Employee> directRpts;
using(var ctx = new Entities())
{
//to get a list of direct rpts we perform two separate queries. linq to ef with linq to objects
//first one gets a list of emp ids for a direct mgr emp id from the history table
//this first qry uses grouping and a filter by empid and a filter by max(date)
//the second qry joins to the resultset from the first and goes to the employee table
//to get whole employee objects for everyone in the int emp id list from qry #1
//qry #1: just a list of integers (emp ids for those reporting to emp id of mgrId)
IEnumerable<int> directRptIDList =
from employeeHistory in ctx.EmployeeHistory
.Where(h => h.DirectManagerEmployeeID == mgrId).ToList()
group employeeHistory by employeeHistory.EmployeeID into grp
let maxDt = grp.Max(g => g.DateLastUpdated) from history in grp
where history.DateLastUpdated == maxDt
select history.EmployeeID;
//qry #2: a list of Employee objects from the Employee entity. filtered by results from qry #1:
directRpts = from emp in ctx.Employee
join directRptHist in directRptIDList.ToList()
on emp.EmployeeID equals directRptHist
select emp;
}
return directRpts.ToList();
}
Thank you.
2 things I can think of to improve your queries:
ToListis non-deffered. Calling it on yourQueryablecollections is causing lots of extra trips to the DB. I also believe this call, along with the explicit declaration ofIEnumerable<int>, was causing the closure error.Use the relation between
EmployeeHistoryandEmployee, in yourObjectContex, to join the queries. This will let the Framework produce more efficientSQL. And whendirectRptsis evaluated on yourToListcall, it should only make 1 trip to the DB.Let me know if this helps.