以下代码段可以满足我的需求。我相信虽然必须有更好的做法?执行此查询的更佳方式是什么?
需要获取员工对象列表,这些员工对象是employee / mgr x的直接报告。直接报告列在历史记录表中,每个员工都有多条记录,因此每个直接报告(员工)只应从该表返回一条(最新的)记录,然后应使用Employee表获取员工对象,其中员工ID等于此筛选结果集中每个历史记录的员工ID。我可以通过两个单独的LINQ到EF查询来获得两个部分。
尝试从第一个结果集加入employeeHistory对象时出现问题。根据MSDN:不支持引用非标量闭包[不支持在查询中引用非标量闭包,例如实体。执行此类查询时,将抛出NotSupportedException异常,并显示一条消息,指出“无法创建类型'闭包类型'的常量值。在此上下文中仅支持基本类型(如Int32,String和Guid') 。“]
所以我运行两个查询,并使第一个类型为int而不是复杂对象的列表。这确实有效,但似乎做作了。关于更好的方式的任何建议(我想做一个查询)。
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();
}
谢谢。
答案 0 :(得分:2)
我可以考虑改善您的查询的两件事:
ToList
没有被拒绝。在Queryable
集合上调用它会导致大量额外的数据库访问。我也相信这个调用以及IEnumerable<int>
的明确声明导致了关闭错误。
使用EmployeeHistory
中Employee
和ObjectContex
之间的关系加入查询。这将使框架产生更高效的SQL
。在directRpts
来电评估ToList
时,它应该只进行一次数据库访问。
如果有帮助,请告诉我。
private List<BO.Employee> ListDirectReports(int mgrId)
{
using(var ctx = new Entities())
{
var directRptIDList =
from employeeHistory in ctx.EmployeeHistory
.Where(h => h.DirectManagerEmployeeID == mgrId)
group employeeHistory by employeeHistory.EmployeeID into grp
let maxDt = grp.Max(g => g.DateLastUpdated) from history in grp
where history.DateLastUpdated == maxDt
select history;
var directRpts =
from emp in ctx.Employee
join directRptHist in directRptIDList
on emp equals directRptHist.Employee
select emp;
}
return directRpts.ToList();
}
答案 1 :(得分:0)
这里有很多问题,其中最重要的是通过在之前执行Where
获得最新的历史记录项目,你得到的记录是否定的更长的有效。我就是这样做的:
private List<BO.Employee> ListDirectReports(int mgrId)
{
using(var ctx = new Entities())
{
// First make sure we're only looking at the current employee information
var currentEntries =
from eh in ctx.EmployeeHistory
group employeeHistory by employeeHistory.EmployeeID into grp
select grp.OrderBy(eh => eh.DateLastUpdated).FirstOrDefault();
// Now filter by the manager's ID
var directRpts = currentEntries
.Where(eh => eh.DirectManagerEmployeeID == mgrId);
// This would be ideal, assuming your entity associations are set up right
var employees = directRpts.Select(eh => eh.Employee).Distinct();
// If the above won't work, this is the next-best thing
var employees2 = ctx.Employee.Where(
emp => directRpts.Any(
eh => eh.EmployeeId == emp.EmployeeId));
return employees.ToList();
}
}
答案 2 :(得分:0)
谢谢Sorax。我发布的代码没有错误,并且确实给了我需要的结果,但正如您所指出的,在包含ToList()方法时合并两个查询会出错。使用您的提示我成功合并(测试它)并在下面发布了改进的单一查询方法。 StriplingWarrior我也试过你的,也许我可以按摩它。功能评估超过了第一个查询,因此我现在将坚持使用Sorax的建议。我很感激帮助,并会重新审视。
private static List<BO.Employee> ListDirectReports(int mgrId)
{
IQueryable<BO.Employee> directRpts;
using(var ctx = new Entities())
{
directRpts =
from emp in ctx.Employee
join directRptHist in
(from employeeHistory in ctx.EmployeeHistory
.Where(h => h.DirectManagerEmployeeID == mgrId)
group employeeHistory by employeeHistory.EmployeeID into grp
let maxDt = grp.Max(g => g.DateLastUpdated) from history in grp
where history.DateLastUpdated == maxDt
select history)
on emp equals directRptHist.Employee
select emp;
}
return directRpts.ToList();
//IQueryable<BO.Employee> employees;
//using(var ctx = new Entities())
//{
// //function evaluation times out on this qry:
// // First make sure we're only looking at the current employee information
// IQueryable<BO.EmployeeHistory> currentEntries =
// from eh in ctx.EmployeeHistory
// group eh by eh.EmployeeID into grp
// select grp.OrderBy(eh => eh.DateLastUpdated).FirstOrDefault();
// // Now filter by the manager's ID
// var dirRpts = currentEntries
// .Where(eh => eh.DirectManagerEmployeeID == mgrId);
// // This would be ideal, assuming your entity associations are set up right
// employees = dirRpts.Select(eh => eh.Employee).Distinct();
// //// If the above won't work, this is the next-best thing
// //var employees2 = ctx.Employee.Where(
// // emp => directRpts.Any(
// // eh => eh.EmployeeId == emp.EmployeeId));
//}
//return employees.ToList();
}