【问题标题】:XML Serializer fails processing large XML dataXML 序列化程序无法处理大型 XML 数据
【发布时间】:2015-06-02 15:55:24
【问题描述】:

我知道这一定是直截了当的,但不知何故我无法理解它。

-- 场景--

我们使用的系统具有 WPF 前端,然后是 DB first 实体框架,然后是 SQL 2008R2 数据库。

我们拥有的大多数实体都是父子关系。没有孩子就不能存在父母,因此我们采用的存储过程

父数据和子数据列表,这里的子数据列表在使用 XML Serializer 序列化整个列表后作为 XML 传递。

在我们必须保存 500,000 条子记录之前,这一切都运行良好。 当我们尝试执行此操作时,XML 序列化程序无法抱怨内存不足。 然后我尝试分批发送 10,000 条子记录,它执行查询并保存数据,但原子性随后丢失,因为它只是一个像这样的 for 循环..

 For Each batchList In groups.Split(10000)
     Dim serializer As New XmlListSerializer(Of someObject)(batchList.ToList(), "Data")
     Dim xml = serializer.ToXmlString
     efContext.AddData(CType(addedParentId.Value, Short?), xml, "/Data/someObject")
 Next

其中添加数据是映射到存储过程的函数导入。

-- 问题--

现在我的问题是,在保持操作的原子性处于活动状态的同时,将多个子记录添加到数据库中的正确方法是什么。

另外,上面提到的方式非常慢,对于半百万记录需要5分钟,无论如何这是不可接受的。

【问题讨论】:

  • 您的问题似乎与 xml 序列化有关,而不是与将数据批处理到 ddbb 相关。事实上,除非您不调用 context.save,否则您不会点击 ddbb,这不是您的代码的一部分
  • 是的,我同意这一点,xmlserializer 已经发布了动态程序集加载,这会导致问题,我想知道其他人在这种情况下会做什么,或者 xml 序列化是处理这种情况的最佳方式?
  • 我认为将 batchList 转换为 Enumerable 类型 (AsEnumerable()) 可能会有所帮助。而且大多数时候我使用 Json 对象而不是 XML,因为它更轻
  • 批处理列表是 IEnumerable,我还没有尝试过 json 反序列化.. 下一个会尝试.. 你有使用它处理大量集合的经验吗?
  • 为什么不将数据存储为常规数据而不是 xml?这是业务要求吗?

标签: c# sql .net database xml-serialization


【解决方案1】:

将多个子记录添加到数据库中的正确方法是什么 同时保持操作的原子性处于活动状态。

您执行此操作的方式是 making a Transaction 在您执行多个步骤之前开始,然后在最后提交或回滚。事实上,实体框架在一次SaveChanges() 调用中执行多次插入时已经在执行此操作。这让我们:

另外,对于 50 万条记录,上述方式非常慢 这需要 5 分钟,这无论如何是不可接受的。

实体框架不是为批量操作而设计的,这 500,000 条记录中的每一条都将生成一个单独的 INSERT INTO 语句。要么使用提供批量插入功能的extension for EF,要么使用带有SqlBulkCopy 的老式ADO.NET 来加载数据。


至于 XML 问题,如果您的孩子列表很大,那么“正确”的做法是不要使用 XML。父子表应该是数据库中的单独表,然后您将拥有第三个“链接表”,即

create table Parent_Child_Links
{
    ParentId int,
    ChildId int
}

当您拥有多对多表关系并让实体框架为您创建表时,它会使用指向这两个表的外键创建第三个链接表。

【讨论】:

  • 当我命令 EF 执行多项操作时,我相信事务会生效,但是当我多次调用 ef.AddSomething 时,我应该如何将它们保存在事务中?
  • AddSomthing (以及您在问题中使用的AddData)不是DbContext 的方法,所以我无法告诉您,您需要做的是在第一次之前打开交易调用SaveChanges(),然后在最后一次调用SaveChanges() 后提交事务。如果ef.AddSomthing 内部调用SaveChanges(),那么是的,您确实需要在调用之前启动事务。
  • 如果AddData 确实调用了SaveChanges,那么您将在For Each 之前执行Using transaction as DbContextTransaction efContext.Database.BeginTransaction(),然后在Next 之后的行之后执行transaction.Commit(),然后执行下一行End Using (我是 C# 人,所以 VB.NET 可能有一两个语法错误)
  • 感谢 Scott,我对 ef 很陌生,我在 efcontext 中看不到任何 .Database .... 是否依赖于版本?
  • Muds 是正确的,我什至没有考虑它,因为 4.0 和 3.5(没有更旧的版本,版本号被编号以匹配它附带的 .NET 框架)是如此旧的版本我认为不会用它来完成新的开发。
猜你喜欢
  • 2011-05-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-12-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多