【问题标题】:JSON Serialization takes a very long timeJSON序列化需要很长时间
【发布时间】:2017-10-06 15:50:03
【问题描述】:

我有两个 ViewModel(产品和零件):

public class ProductViewModel
{
    public int Id { get; set; }

    public string Name { get; set; }

    public string Description { get; set; }       

    public bool IsActive { get; set; }

    public IEnumerable<PartViewModel> Parts { get; set; }
}

public class PartViewModel
{
    public int Id { get; set; }

    public string Name { get; set; }

    public bool IsActive { get; set; }
}

我运行了一个 EF Core 查询,该查询返回 1727 个产品,每个产品至少有 1 个部件。返回其中 1 个的示例将序列化为 JSON,如下所示:

[  
   {  
      "Id":8761,
      "Name":"Product Name 1",
      "Description":"This is a product",
      "IsActive":true,
      "Parts":[  
         {  
            "Id":103767,
            "Name":"Name 1"
            "IsActive":true
         },
         {  
            "Id":156436,
            "Name":"Name 2",
            "IsActive":true
         },
         {  
            "Id":109436,
            "Name":"Name 3",
            "IsActive":true
         }
      ]
   }
]

现在,这适用于我 .Take(10) 的查询,虽然看起来很慢,但是当我尝试序列化 1727 条记录时,它陷入困境,五分钟的等待甚至没有完成序列化过程。

我尝试过这样使用 Json.Net:

var ret = JsonConvert.SerializeObject(products, new JsonSerializerSettings { Formatting = Formatting.Indented });

我只决定尝试使用 Json.Net 中的 JsonConvert,因为在我的控制器操作中,当尝试返回 JsonResult 时,将我的对象转换为 JSON 的以下代码存在同样的效率问题:

return Json(products);

我通过 EF Core 获得产品:

var products = _context.Products.OrderBy(o => o.Name).Where(w => w.IsActive  //all products are active
            && (w.Parts.Count(c => c.IsActive) > 0)) //remove parts that are
            .Select(pr => new ProductViewModel
            {
                Id = pr.Id,
                Name = pr.Name,
                Description = pr.Description,
                IsActive = pr.IsActive,
                Parts = pr.Parts.OrderBy(o => o.Name).Where(w => w.IsActive)  //all parts are active
                .Select(prt => new PartViewModel
                {
                    Id = prt.Id,
                    Name = prt.Name,
                    IsActive = prt.IsActive,
                })
            }).ToList();

我能做什么?

【问题讨论】:

  • 阅读How to Ask,看看你是如何获得products的。
  • 你确定是序列化过程花了这么长时间吗?此外,您可能希望在服务器上实现某种分页以避免内存不足。
  • 我添加了查询,@CodeCaster
  • 既然你说需要很长时间,那么指标是什么? 有多慢
  • similar problem 查询可能不是最优的,但反序列化耗时最长

标签: .net json asp.net-web-api serialization json.net


【解决方案1】:

序列化不是什么大问题,现在很容易注意到您已经添加了 LINQ 查询,问题是糟糕的 SQL Entity Framework 将从它生成。

首先,您应该使用 eager loading 将您的产品表与部件表连接起来。您只需添加一个Include 方法调用即可做到这一点。

_context.Products.Include(p => p.Parts)

如果不这样做,查询实际上是在执行 N + 1 个查询。您应该使用一个简单的技巧来观察您的查询正在执行的实际 SQL 查询,将此代码添加到您的 DbContext。 (仅当您使用 EF6 时才执行此操作,EF Core 会为您查询日志记录。)

public YourDBContext()
{
   #if DEBUG   
   this.Database.Log = msg =>
   {
       Debugger.Log(1, "ALL", "EF DB SQL: " + msg + Environment.NewLine);
   };
   #endif
}

另一个执行查询需要很长时间的条件是.Where(w =&gt; w.IsActive &amp;&amp; (w.Parts.Count(c =&gt; c.IsActive) &gt; 0))。我猜实体框架正在生成一个 HAVING 子句,但如果您发布生成的 SQL 以优化查询,它会有所帮助。

最后,在您的 Select 方法中进行微优化,将 Parts 属性获取表达式更改为。

// other properties ...
Parts = pr.Parts.Where(w => w.IsActive).OrderBy(o => o.Name),
// other properties ...

这将阻止您的数据库获取和排序非活动部分。

【讨论】:

    猜你喜欢
    • 2017-12-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-02-27
    • 1970-01-01
    • 2013-09-07
    • 2020-08-26
    • 2014-10-09
    相关资源
    最近更新 更多