2016-05-31 446 views
1

我有一个控制台线应用程序,通过LINQ将大约150,000行保存到数据库。这工作正常(我通常会期望它停止工作)。这是一个沼泽标准保存从CSV文件中读取数据后进行更改来电: -LINQ SaveChanges()异常缓慢

List<Invoice> oldInvoices = db.Invoices.Where(x => !x.IsVisible).ToList(); 
List<int> oldInvoiceIDs = oldInvoices.Select(s => s.InvoiceID).ToList(); 

List<InvoiceProduct> allInvoiceProducts = db.InvoiceProducts.ToList(); 
List<InvoiceProduct> oldInvoiceProducts = allInvoiceProducts.Where(x => oldInvoiceIDs.Contains(x.InvoiceID)).ToList(); 

db.InvoiceProducts.RemoveRange(oldInvoiceProducts); 
db.Invoices.RemoveRange(oldInvoices); 

UpdateConsole.WriteLine("Switching over invoices completed. Please wait...", ConsoleColor.Black, ConsoleColor.Magenta); 

表是用的产品对每张发票子链接表的发票清单。每次我们获取新数据时,我们都会写入新数据,在数据库中将其标记为不可见,然后将当前可见数据切换为不可见,并将当前不可见数据切换为可见数据,从而可以立即切换从一个数据集到下一个数据集。刚刚被标记为不可见的数据集然后通过LINQ被删除。

这需要时间来删除,但不是不合理的时间量。由于此数据来自CSV数据文件,因此我们记录了行数以及文件读取的开始和结束日期和时间。这是存储在另一个数据库表和代码保存为: -

importLog.SuccessfullyImportedRows = successfulRows; 
importLog.FailedImportedRows = failedRows; 
importLog.EndTime = DateTime.Now; 

db.SaveChanges(); 

这保存,超过40分钟需要和我不知道为什么。我能想到的唯一的事情就是它使用了在Visual Studio中生成EDMX时可用的相同的DBEntities类。

有没有人有这个?它给应用程序挂的外观,但它确实持续40分钟打完...

+0

可能重复[如何更快地运行此任务](http://stackoverflow.com/questions/37374480/how-can-i-run-this-task-faster) – Veverke

+0

可能会帮助您http:// stackoverflow.com/questions/37096509/why-getting-data-with-entity-framwork-is-slow – mohsen

+0

感谢您的链接。后者是关于循环内部的SaveChanges(),而我并不是。还值得注意的是,当我保存177,000条记录时,我保存到本地列表中,然后执行AddRange()和SaveChanges()。我只调用SaveChanges()一次保存列表,第二次保存日志。没有任何循环的SaveChanges(),相同的问题仍然存在吗? –

回答

1

您的办法多的性能问题:

  1. 从数据库中拖动不必要的数据。
  2. 批量插入巨大的记录。
  3. 从Hude Records删除批量。

没有必要将所有的发票从数据库然后在内存中,在那里你可以直接查询这些数据库和retreive只有你想要的清单本地过滤。

您需要更换此:

List<InvoiceProduct> allInvoiceProducts = db.InvoiceProducts.ToList(); 
List<InvoiceProduct> oldInvoiceProducts = allInvoiceProducts.Where(x => oldInvoiceIDs.Contains(x.InvoiceID)).ToList(); 

有:

List<InvoiceProduct> oldInvoiceProducts = db.InvoiceProducts.Where(x => oldInvoiceIDs.Contains(x.InvoiceID)).ToList(); 

对于批量删除更快的方法:

String commaDelimitedIds = String.Join(",", oldInvoiceIDs); 
String query = "DELETE FROM Invoice WHERE InvoiceID IN (" + commaDelimitedIds + ")"; 
db.ExecuteQuery(query); 

通过Linq To SQL插入150,000 recod是不是一个好主意,这将产生150,000Insert声明(否提及关系对象)。

看看这个例子:SQLBulkCopy这是非常适合巨大的插入。

一般来说,ORMs 对于批量操作不是一个好主意。

+0

嗨,谢谢你的回复。你说我从数据库中拖出不必要的数据是正确的。我之前遇到的问题是,“with:”之后的第三行代码检索到错误,因为.Contains()在列表中有太多整数。因此,我选择了一个额外的行中的本地列表,然后做了我上面的方法。非常感谢您的帮助!我会放弃它。 –

+1

@MikeUpjohn您可以使用for循环将每个2000 ID拆分在一起,最后将Concat列表一起分割。这是SQL参数限制的解决方法。 – user3185569

1

首先,在您的查询中,我看到使用.toList()时出现问题。 toList意味着你强制该查询立即运行并将其存储到内存中。对于小数据来说速度更快,但对于超过150,000行来说,你肯定会遇到性能问题,并且会因为内存不足而出现问题。您可以使用AsQueryable()而不是。

AsQueryable只是创建一个查询,获取 列表所需的说明。您可以在稍后对查询进行进一步更改,例如将新的Where子句添加到 级别的 级别。

对于EF 6或更高版本,RemoveRange的性能非常快。所以我不认为RemoveRange是这里的根本原因。但是,如果您想提高性能,请尝试使用此扩展名。这太好了。 https://efbulkinsert.codeplex.com/

0

好吧,我找到的解决方案(虽然不知道推理)。如果我不走实体项目纳入日志记录功能,我从EDMX产生实体的新实例e.g.:-

using(DBEntities db = new DBEntities()) { 
    importLog.SuccessfullyImportedRows = successfulRows; 
    importLog.FailedImportedRows = failedRows; 
    importLog.EndTime = DateTime.Now; 

    db.SaveChanges(); 
} 

这个工作在不到一秒钟。在插入如此多行的DBEntities的原始实例中缓存了某些内容?