C#优化代码

时间:2016-07-05 10:57:23

标签: c# linq optimization

我有以下函数返回Model。如果有2000 employees则需要时间,需要3-4 minutes to return data。我其实想要optimize this function。我做了一些包含在下面的代码中的东西,但是它仍然需要很多时间。

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循环。对此有何解决方法?如何优化上述代码?

4 个答案:

答案 0 :(得分:2)

您可以使用快速查找数据结构优化嵌套循环(Linq&#39; Where内的foreach):

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块中是多余的,因为它已经是初始查询的一部分。

答案 1 :(得分: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);
        });
}

答案 2 :(得分: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,因为您已在CompanyId的定义中按loadValues进行了过滤。

为了加快这里的速度,您可以使用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;

答案 3 :(得分: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();
}

但是我假设您的数据库结构包含正确的外键指定是列引用表 CValue 。没有嵌套循环,没有双倍的内存分配(正如我在评论中提到的),所有内容都将使用加入获得,这比 Where clausule更快。< / p>

编辑您需要返回与映射实体不同的模型列表。看看我的例子中的第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