2010-11-10 80 views
0

下面的代碼片段做工作以外的實體爲我所需要的。我相信雖然這一定有更好的做法?做這個查詢的更優化的方式?過濾由Max和ID與分組加入到LINQ到實體框架(C#)

需要的是讓那些員工/經理X的直接下屬員工對象的列表。直接報告列在歷史表中,每個員工有多個記錄,因此每個直接報告(員工)只能從該表中返回一個(最近的)記錄,然後應使用Employee表獲取員工對象的員工ID與此過濾結果集中每個歷史記錄的員工ID相同。我可以通過兩個獨立的LINQ to EF查詢來獲得兩半。

一個問題試圖加入來自第一個結果集的employeeHistory對象上時發生。根據MSDN:不支持引用非標量閉包[不支持在查詢中引用非標量閉包,如實體。當這樣的查詢執行,NotSupportedException異常引發異常,同時,指出「無法創建類型‘閉合類型’的恆定值,只有原始類型的消息(‘如的Int32,字符串,和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(); 
} 

謝謝。

回答

2

兩件事我能想到的,以提高查詢:

ToList是非遞延。在您的Queryable集合上調用它會導致大量額外的數據庫訪問。我也相信這個呼籲以及IEnumerable<int>的明確聲明導致了關閉錯誤。

使用EmployeeHistoryEmployee之間的關係,在您的ObjectContex中加入查詢。這將使框架產生更高效的SQL。當您的ToList呼叫對directRpts進行評估時,它應該僅對數據庫進行一次訪問。

讓我知道這是否有幫助。

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(); 
} 
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(); 
    } 
} 
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(); 
    } 
+0

幹得漂亮。我很高興獲得幫助。 – Sorax 2010-11-10 23:37:54