读取4000个文件后抛出System.OutOfMemoryException

时间:2016-06-02 10:29:43

标签: c# entity-framework sql-server-2012

我们要将52000个文件(.pdf.xls.doc ...等)导入SQL Server 2012数据库。

我有一个包含文件名称的Dossier_fichier.txt文件。我将这些名称加载到一个集合中,然后循环遍历此集合,我尝试在direcotry PiecesJointes中找到这些文件并将它们转换为字节并使用以下代码将它们插入数据库:

var dossierFichiers = addOrUpdateHelper.ReadEntities<DossierFichier, DossierFichierMap>("dossier_fichier.txt").ToArray();
// (2) Parcourir toutes les instances de DossierFichier chargées + Lire le fichier référencé + Le charger dans la propriété Fichier
var dirPath = System.IO.Path.Combine(Environment.CurrentDirectory, "piecesJointes");
var nbfichier = 0;

foreach (var df in dossierFichiers) {
    try {
        var path = System.IO.Path.Combine(dirPath, string.Concat( df.Code,"_", df.Nom));
        df.Fichier = File.ReadAllBytes(path);
        context.DossierFichier.Add(df);
        context.SaveChanges();

        Logger.Info("Le fichier {0} a été inséré", df.Nom);

        nbfichier++;
    } catch (FileNotFoundException ex) {
        Logger.Error("Fichier {0} : {1}", df.Nom, ex.Message);
    } catch (Exception ex) {
        Logger.Error("Fichier {0} : {1}",df.Nom, ex.Message);
    }
}

插入4000个文件后我得到OutOFMemoryException,花了很多时间(60个小时)。请问你能帮助我插入所有这些文件而不是OutOfmemoryException,以及如何更快地完成它?

2 个答案:

答案 0 :(得分:5)

看起来您的代码具有O(n 2 )性能,因为它将所有文件内容保存在内存中。

  • 循环的第一次迭代会保存您添加的第一个对象
  • 第二次迭代保存第二个对象并刷新第一个对象
  • 第三次迭代保存第三个对象并刷新前两个对象
  • ......等等
  • 第N次迭代插入第N个对象,并刷新先前插入的N-1个对象

您应该能够通过将更新“批处理”来解决这个问题:

  • 添加目前已插入的对象数量
  • 请勿立即致电context.SaveChanges();
  • 当插入的对象数量达到100时,请调用context.SaveChanges();,然后将context替换为新实例。

这将确保所有对象只保存一次,数据库往返次数受到控制,并且内存中永远不会有超过100个对象。

此外,您可以通过将文件内容粘贴到df.Fichier来保留文件内容。这可能会使您的系统内存不足,因此您应该复制df

var path = System.IO.Path.Combine(dirPath, string.Concat( df.Code,"_", df.Nom));
var dfCopy = new DossierFichier(df); // Copy df's fields
dfCopy.Fichier = File.ReadAllBytes(path);
context.DossierFichier.Add(dfCopy);

答案 1 :(得分:1)

对于集合中的每个项目,将整个文件内容(字节数组)添加到df.Fichier。这是内存使用量增加的原因之一。

您可以使用临时变量解决此特定问题。