2016-07-05 160 views
-2

我有以下函數返回Model。需要時間說是否有2000 employees,需要3-4 minutes to return data。我其實想要optimize this function。我已經完成了下面的代碼中包含的一些東西,但仍然需要很多時間。C#優化代碼

using (var ctx = new ApplicationDbContext(schemaName)) 
{ 
       List<Employee> list = new List<Employee>(); 
       Employee mod = new Employee(); 

       var data = ctx.Employee.Where(a => a.Company == comp && a.Status == Live) 
        .Select(a => new 
        { 
         Id = a.Id, 
         Code = a.Code, 
         FName = a.FName, 
         DateOfJoining = a.DateOfJoining, 
         Category = a.Category, 
         Department = a.Department, 
         Designation = a.Designation; 
        }) 
        .ToList(); 

       var loadValues = ctx.CValue.Where(c => c.Company == comp).ToList(); 

       foreach (var item in data) 
       { 
        mod = new Employee(); 

        mod.Id = item.Id; 
        mod.Code = item.Code; 
        mod.FName = item.FName; 
        mod.DateOfJoining = item.DateOfJoining; 
        mod.Category = item.Category; 
        mod.Designation = item.Designation; 
        mod.Department = item.Department; 

        int designation = (item.Designation == null) ? 0 : item.Designation; 
        int department = (item.Department == null) ? 0 : item.Department; 

        if (designation != 0) 
         mod.DesignationString = loadValues.Where(c => c.CompanyId == comp && c.Id == designation).Select(c => c.ComboValue).FirstOrDefault(); 
        if (department != 0) 
         mod.DepartmentString = loadValues.Where(c => c.Company == comp && c.Id == department).Select(c => c.ComboValue).FirstOrDefault(); 

        list.Add(mod); 
       } 

       return list; 
      } 
} 

我認爲這是需要時間的foreach循環。任何解決方法?如何優化上述代碼?

+0

如何填充ctx?您提供的代碼不應該花費很長時間 –

+0

如何使用秒錶逐行測量時間? – 2016-07-05 10:59:42

+0

_「我認爲這是需要時間的'foreach'循環」_找出肯定的答案。有這樣的事情的好工具,例如(隨機選擇):[ANTS](http://www.red-gate.com/products/dotnet-development/ants-performance-profiler/)。在開始優化之前,您需要知道哪個位比較慢。 –

回答

0

第一件事是第一件事。

您正在使用實體框架和數據庫。我會冒險,它不是foreach循環,讓你頭痛,但數據訪問。

使用秒錶和登錄到控制檯來查看代碼的每一部分所消耗的時間:

using (var ctx = new ApplicationDbContext(schemaName)) 
{ 
       System.Diagnostics.Stopwatch sw = System.Diagnostics.Stopwatch.StartNew(); 
       List<Employee> list = new List<Employee>(); 
       Employee mod = new Employee(); 

       var data = ctx.Employee.Where(a => a.Company == comp && a.Status == Live) 
        .Select(a => new 
        { 
         Id = a.Id, 
         Code = a.Code, 
         FName = a.FName, 
         DateOfJoining = a.DateOfJoining, 
         Category = a.Category, 
         Department = a.Department, 
         Designation = a.Designation; 
        }) 
        .ToList(); 
       Console.Write(sw.ElapsedMilliseconds); 
       var loadValues = ctx.CValue.Where(c => c.Company == comp).ToList(); 

       Console.Write(sw.ElapsedMilliseconds); 

       foreach (var item in data) 
       { 
        mod = new Employee(); 

        mod.Id = item.Id; 
        mod.Code = item.Code; 
        mod.FName = item.FName; 
        mod.DateOfJoining = item.DateOfJoining; 
        mod.Category = item.Category; 
        mod.Designation = item.Designation; 
        mod.Department = item.Department; 

        int designation = (item.Designation == null) ? 0 : item.Designation; 
        int department = (item.Department == null) ? 0 : item.Department; 

        if (designation != 0) 
         mod.DesignationString = loadValues.Where(c => c.CompanyId == comp && c.Id == designation).Select(c => c.ComboValue).FirstOrDefault(); 
        if (department != 0) 
         mod.DepartmentString = loadValues.Where(c => c.Company == comp && c.Id == department).Select(c => c.ComboValue).FirstOrDefault(); 

        list.Add(mod); 
       } 
       Console.Write(sw.ElapsedMilliseconds); 
       return list; 
      } 
} 

然後,如果確實是數據訪問是問題,你應該看看你如何使用索引加快你正在做的db查詢。

讓我知道你是否需要一隻手。

2

您可以通過使用快速查找數據結構中,進而優化嵌套循環(LINQ的Whereforeach內):

var loadValues = (from c in ctx.CValues 
        where c.Company == comp 
        select c).ToLookup(x => Tuple.Create(x.CompanyId, x.ID), 
             x => x.ComboValue); 

foreach (var item in data) 
{ 
    // your same code goes here 
    // then 

    if (designation != 0) 
     mod.DesignationString = loadValues[Tuple.Create(comp, designation)].FirstOrDefault(); 
    if (department != 0) 
     mod.DepartmentString = loadValues[Tuple.Create(comp, department)].FirstOrDefault(); 

    list.Add(mod); 
} 

我看不到能夠優化任何其他部分。

值得注意的是,comp上的過濾器在if塊中是多餘的,因爲它已經是您初始查詢的一部分。

2

我已經將代碼優化爲一個linq查詢。我認爲這將是更快

using (var ctx = new ApplicationDbContext(schemaName)) 
{ 
    var loadValues = ctx.CValue.Where(c => c.Company == comp).ToList(); 
    return ctx 
     .Employee 
     .Where(a => a.Company == comp && a.Status == Live) 
     .Select(item => new Employee 
     { 
      Id = item.Id, 
      Code = item.Code, 
      FName = item.FName, 
      DateOfJoining = item.DateOfJoining, 
      Category = item.Category, 
      Designation = item.Designation, 
      Department = item.Department, 
      DesignationString = loadValues.Where(c => c.CompanyId == comp && c.Id == item.Designation ?? 0).FirstOrDefault(c => c.ComboValue); 
      DepartmentString = loadValues.Where(c => c.Company == comp && c.Id == item.Department ?? 0).FirstOrDefault(c => c.ComboValue); 
     }); 
} 
+0

這與序列列表中的子查詢幾乎完全相同,所以我認爲沒有太大的區別。它*可能會稍微快一點,但沒有實質性收益。 – derpirscher

+0

我收到錯誤實體或複雜類型'Models.Employee'不能在LINQ to Entities查詢中構建。' – Anup

2

以下剪斷可能需要相當長的(做性能測試,可以肯定的)

var loadValues = ctx.CValue.Where(c => c.Company == comp).ToList(); 

.... 

if (designation != 0) mod.DesignationString = loadValues.Where(c => c.CompanyId == comp && c.Id == designation).Select(c => c.ComboValue).FirstOrDefault(); 
if (department != 0) mod.DepartmentString = loadValues.Where(c => c.Company == comp && c.Id == department).Select(c => c.ComboValue).FirstOrDefault(); 

因爲loadValuesList它總是順序搜索。因此,根據loadValues的大小,此列表中可能會有很多搜索。此外,您不需要比較CompanyId,因爲您已在loadValues的定義中通過CompanyId進行過濾。

要加快這裏的速度,您可以使用Lookup

var loadValues = ctx.CValue.Where(c => c.Company == comp).ToLookup(x=> x.Id); 

if (designation != 0 && loadValues.Contains(designation)) mod.DesigationString = loadValues[designation].Select(c => c.ComboValue).FirstOrDefault(); 
if (department != 0 && loadValues.Contains(department)) mod.DepartmentString = loadValues[department].Select(c => c.ComboValue).FirstOrDefault(); 

或者,當你被Id搜索應該是獨一無二的,你也可以做一個簡單的Dictionary<int, string>

var loadValues = ctx.CValue.Where(c=>c.Company == comp).ToDictionary(x=> x.Id, y=> y.ComboValue); 

if (designation != 0) mod.DesigationString = loadValues.ContainsKey(designation) ? loadValues[designation] : String.Empty; 
if (department != 0) mod.DepartmentString = loadValues.ContainsKey(department) ? loadValues[department] : String.Empty; 
2

我會寫這樣的說法:

using (var ctx = new ApplicationDbContext(schemaName)) 
{ 
    var company = ctx.Companies.FirstOrDefault(e => e.ID == 42); 
    if(company == null) 
     throw new Exception(); 

    var empl = company.Employees.Where(e=> e.Status == Live).Select(e=> 
     new EmployeeInfo{ 
      ID = e.ID, 
      FName = e.FName, 
      //TODO 
      DesignationString = e.Designation != null ? e.Designation.ComboValue : string.Empty, 
      //TODO 

     }); 

    return empl.ToList(); 
} 

但我假設你有DB結構,適當外鍵指定是列引用表CValue。沒有嵌套循環,沒有加倍的內存分配(正如我在我的評論中提到的那樣),並且所有內容都將使用加入,這比更快,其中 clausule。

編輯您需要返回與映射實體不同的模型列表。看看我的例子中的第8行。如果您嘗試運行.Select(e => new Employee()),則會在評論中提到您提到的錯誤。 (更多信息在這裏:The entity cannot be constructed in a LINQ to Entities query

但如果返回List<EmployeeInfo>,你可以通過這個清單,你的應用程序的上層在任何地方工作,你可以加入更多的元素(它們可以是其他方法比DB查詢結果,例如XML導入),並且您將獨立於實際的DB映射類。

你可以在這裏找到更多關於應用程序構成的信息:https://softwareengineering.stackexchange.com/questions/240704/confused-about-layered-application-development

+0

我得到錯誤實體或複雜類型'Models.Employee'不能在LINQ to Entities中構建查詢。 – Anup

+0

@Anup我已經擴展了我的答案。我希望它有幫助。 –