绕过Entity Framework导入/更新大量数据

时间:2013-09-05 12:32:07

标签: c# .net entity-framework

我有一个基于实体框架的完全正常工作的生产站点,现在我需要每周将大量数据导入数据库。 数据以文本文件的形式出现,我逐行检查,检查数据库是否存在,是否确实更新了任何已更改的内容,如果没有则更新。 我遇到的问题是,运行完整的导入过程需要大约32个小时,并且必须手动将一些文件拆分成较小的块,以避免看似由实体框架引起的内存问题。我已经设法减慢内存增加,但上次我没有拆分它运行文件,它运行了大约12个小时,然后在超过1.5GB的内存耗尽内存。 所以有人可以向我建议导入这些数据的最佳方式,我听说过sqlbulkcopy但不确定它是否正确使用。谁能提供任何例子?或建议更合适的东西。例如,我应该使用标准.net sql命令创建实体的副本,并可能使用存储过程

2 个答案:

答案 0 :(得分:2)

虽然SqlBulkCopy在托管代码中很方便,但我认为最快的方法是使用“纯”sql - 假设SqlBulkCopy不容易执行upsert,那么无论如何都需要执行下面的MERGE部分

假设您的文本文件是csv格式,并且它在SQL Server上以“C:\ Data \ TheFile.txt”存在,并且该行结尾标准化为CR-LF(\ r \ n)

我们假设数据是ID,Value1,Value2

此SQL命令将插入到临时表TheFile_Staging中,其中包含具有兼容数据类型的ID,Value,Value2列,然后更新“真实”表TheFile_Table(注意:以下代码未经过测试!)

  truncate table TheFile_Staging
    BULK INSERT TheFile_Staging FROM'C:\Data\TheFile.txt'
 WITH (fieldterminator=',', rowTerminator='\r\n',FirstRow=2)
  //FirstRow=2 means skip Row#1 - use this when 1st row is a header.

MERGE TheFile_Table as target
USING (SELECT ID,Value1,Value2 from TheFile_Staging) as source
on target.ID = source.ID
WHEN MATCHED THEN
  UPDATE SET target.Value1=source.Value1, target.Value2=source.target2
WHEN NOT MATCHED THEN 
  INSERT (id,Value1,Value2) VALUES (source.Id,source.Value1,source.Value2);

你可以创建一个存储过程并将其设置为从代码等运行或调用。这种方法的唯一问题是错误处理批量插入有点乱 - 但只要你的数据输入就好了那么它的速度非常快。

通常我会在WHERE子句中添加某种验证检查,我们USING()选择MERGE只能获取在数据方面有效的行。

也许值得指出的是,登台表的定义应该省略任何非空,主键和身份约束,以便可以在没有错误的情况下读入数据。如果您的源数据中存在空字段;我通常也喜欢将日期/时间数据作为普通nvarchar引入 - 这样可以避免错误格式化的日期导致导入错误,并且MERGE语句可以根据需要执行CAST或CONVERT,同时忽略和/或记录到错误表它遇到的任何无效数据。

答案 1 :(得分:0)

可悲的是,你需要在这种情况下摆脱实体框架;开箱即用EF只能逐行插入。你可以做一些有趣的事情,比如this,或者你可以完全忽略EF,并使用ADO.Net(SqlBulkCopy)手动编写将进行批量插入的类。

编辑:如果性能可以接受,您还可以使用当前方法,但是您需要定期重新创建上下文,而不是对所有记录使用相同的上下文。我怀疑这是令人难以忍受的记忆消耗的原因。