代码之家  ›  专栏  ›  技术社区  ›  chobo2

如何对此进行性能测试并提出更快的建议?

  •  0
  • chobo2  · 技术社区  · 6 年前

    我似乎写了一些非常慢的代码,当我不得不处理efcore时,它会变得更慢。

    var itemDtos = new List<ItemDto>();
    
    
                var inventoryItems = dbContext.InventoryItems.Where(x => x.InventoryCategoryId == categoryId);
    
                var inventorySpecifications = dbContext.InventoryCategorySpecifications.Where(x => x.InventoryCategoryId == categoryId).Select(x => x.InventorySpecification);
    
    
    
                Stopwatch a = new Stopwatch();
                a.Start();
    
                foreach (var item in inventoryItems)
                {
                    var specs = JObject.Parse(item.Attributes);
                    var specDtos = new List<SpecDto>();
    
                    foreach (var inventorySpecification in inventorySpecifications.OrderBy(x => x.DisplayOrder))
                    {
                        if (specs.ContainsKey(inventorySpecification.JsonKey))
                        {
    
                            var value = specs.GetValue(inventorySpecification.JsonKey);
    
                            var newSpecDto = new SpecDto()
                            {
                                Key = inventorySpecification.JsonKey,
                                Value = displaySpec.ToString()
                            };
    
                            specDtos.Add(newSpecDto);
    
                        }
                    }
    
                    var dto = new InventoryItemDto()
                    {
                        // create dto
                    };
    
                    inventoryItemDtos.Add(dto);
                }
    

    现在当我添加更多我需要的信息的列时,它变得非常慢。

         var dto = new InventoryItemDto()
               {
                   // access brand columns
                   // access company columns
                   // access branch columns
                   // access country columns
                   // access state columns
               };
    

    我不明白为什么这么慢,这是我唯一真正做的改变,我一定要加载所有的东西。

    对我来说,这几乎让我觉得急切的加载不起作用,但我不知道如何验证它是否起作用。

       var inventoryItems = dbContext.InventoryItems.Include(x => x.Branch).ThenInclude(x => x.Company)
                                                        .Include(x => x.Branch).ThenInclude(x => x.Country)
                                                        .Include(x => x.Branch).ThenInclude(x => x.State)
                                                        .Include(x => x.Brand)
                                                        .Where(x => x.InventoryCategoryId == categoryId).ToList();
    

    所以我想,因为这样做的速度不会有太大的不同,然后原来的18-30秒。

    3 回复  |  直到 6 年前
        1
  •  2
  •   Leonardo    6 年前

    首先,循环中的循环是一件非常糟糕的事情,你应该重构它,使之成为一个单一的循环。这应该不是问题,因为 inventorySpecifications

    第二,线
    var inventorySpecifications = dbContext.InventoryCategorySpecifications.Where(x => x.InventoryCategoryId == categoryId).Select(x => x.InventorySpecification);
    应该以 ToList() ,因为它的枚举发生在内部foreach中,这意味着查询正在为每个“inventoryItems”运行

    那会节省你很多时间

        2
  •  0
  •   Christopher Lake    6 年前

    inventorySpecifications.OrderBy(x => x.DisplayOrder) . 因为这是在另一个foreach内部调用的,它正在执行 .OrderBy inventoryItems .

    在第一个foreach循环之前,请尝试以下操作: var orderedInventorySpecs = inventorySpecifications.OrderBy(x => x.DisplayOrder); 然后使用 foreach (var inventorySpec in orderedInventorySpecs) 看看有没有什么不同。

        3
  •  0
  •   Henry    6 年前

    为了帮助您更好地理解EF在幕后运行的是什么,添加一些登录以公开正在运行的SQL,这可能有助于您了解查询是如何/在哪里出错的。这对于确定查询是否经常访问数据库非常有帮助。作为一个非常普遍的规则,您希望尽可能少地点击DB,并通过使用.Select()来减少返回的内容,仅检索所需的信息。日志记录的文档有: http://docs.microsoft.com/en-us/ef/core/miscellaneous/logging

    我显然不能测试这个,我有点不确定你的规格去哪里,一旦你有他们,但我想他们成为库存项目的一部分?

    var itemDtos = new List<ItemDto>();
    
    var inventoryItems = dbContext.InventoryItems.Where(x => x.InventoryCategoryId == categoryId).Select(x => new InventoryItemDto() {
        Attributes = x.Attributes,
        //.....
        // access brand columns
       // access company columns
       // access branch columns
       // access country columns
       // access state columns
    }).ToList();
    
    
    var inventorySpecifications = dbContext.InventoryCategorySpecifications
    .Where(x => x.InventoryCategoryId == categoryId)
    .OrderBy(x => x.DisplayOrder)
    .Select(x => x.InventorySpecification).ToList();
    
    
    
    foreach (var item in inventoryItems)
    {
        var specs = JObject.Parse(item.Attributes);
        // Assuming the specs become part of an inventory item?
        item.specs = inventorySpecification.Where(x => specs.ContainsKey(x.JsonKey)).Select(x => new SpecDto() { Key = x.JsonKey, Value = specs.GetValue(x.JsonKey)});
    }
    

    对inventoryItems的DB的第一个调用应该产生一个SQL查询,该查询将一次提取构造InventoryItemDto所需的所有信息,从而只对DB进行一次命中。然后,它提取规范并在实现之前使用OrderBy(),这意味着OrderBy将作为SQL查询的一部分而不是在内存中运行。这两个结果都是通过.ToList()实现的,这将导致EF一次性将结果拉入内存。

    最后,循环遍历构建的inventoryItems,解析Json,然后根据它过滤规范。我不确定您在哪里使用specDtos,所以我假设它是模型的一部分。我建议您检查您正在进行的Json工作的性能,因为这可能会导致您的速度减慢。

    使用Json作为EF模型一部分的一种更为集成的方法可以在以下答案中看到: https://stackoverflow.com/a/51613611/621524