我编写了一个使用Entity Framework批量更新/删除/插入数据库表的方法。我已粘贴下面的代码。 DBTable有23列,DBTableRow是一个具有映射到DBTable每列的属性的类。该方法的输入参数是IEnumerables,在其上使用自定义相等比较器进行一些比较,以得到需要添加,删除或修改的行列表。通常,可枚举的大小可以达到50000-60000。
我面临的问题是方法的缓慢。对于200行的净额(在所有操作中 - 添加,删除和更新),需要30分钟。对于2000行的净额,它已经花了将近6个小时并且还没有完成。
专家能否指出代码中的性能瓶颈?非常感谢...
private void InsertIntoDB(DbContext dbContext, IEnumerable<DBTableRow> fromLatestB, IEnumerable<DBTableRow> olderB,
IEnumerable<DBTableRow> toBeAddedB, IEnumerable<DBTableRow> toBeDeletedB,
IEnumerable<DBTableRow> toBeModifiedB, IQueryable<int> listMultiple)
{
dbContext.Configuration.AutoDetectChangesEnabled = false;
dbContext.Configuration.ValidateOnSaveEnabled = false;
int TypeId = 30;
if (toBeAddedB != null && toBeAddedB.Any())
toBeAddedB.ToList().ForEach(s => dbContext.DBTable.Add(s));
if (toBeDeletedB != null && toBeDeletedB.Any())
{
toBeDeletedB.ToList().ForEach(s =>
{
if (s.Type == TypeId)
{
var rlRows = dbContext.DBTable.Where(x => x.Type == TypeId && x.Url.Equals(s.Url, StringComparison.OrdinalIgnoreCase));
if (rlRows != null && rlRows.Any())
{
rlRows.ToList().ForEach(y =>
{
if (dbContext.Entry(y).State == EntityState.Detached)
dbContext.DBTable.Attach(y);
dbContext.DBTable.Remove(y);
});
}
}
else
{
dbContext.DBTable.Attach(s);
dbContext.DBTable.Remove(s);
}
});
}
if (toBeModifiedB != null && toBeModifiedB.Any())
{
var eqComp = new CustomEqualityComparer(listMultiple);
var toBeModifiedNew = fromLatestB.Intersect(olderB, new CustomEqualityComparer(true, listMultiple));
toBeModifiedB.ToList().ForEach(x =>
{
var rowNew = ReturnRowFromModifiedNewList();
if (rowNew != null)
{
x.Type = rowNew.Type;
x.Url = rowNew.Url;
x.Data = rowNew.Data;
x.LastModified = DateTime.UtcNow;
dbContext.Entry(x).State = EntityState.Modified;
}
});
}
dbContext.SaveChanges();
dbContext.Configuration.AutoDetectChangesEnabled = true;
dbContext.Configuration.ValidateOnSaveEnabled = true;
}
答案 0 :(得分:2)
<强>任何强>
Any方法看起来很棒,因为你检查枚举是否包含实体,但由于你可能枚举不止一次,因此通常在枚举上非常糟糕。
例如,在删除部分中,需要两次数据库往返。
示例:
if (toBeDeletedB != null && toBeDeletedB.Any())
{
toBeDeletedB.ToList().ForEach(s =>
因此在调用Any方法之前执行ToList
if (toBeDeletedB != null)
{
var toBeDeletedBList = toBeDeletedB.ToList();
toBeDeletedBList.ForEach(s => ...
在使用Any方法的任何地方都会发生同样的错误。
<强> toBeAdded 强>
这里的一切看起来都很完美。
因为您将AutoDetectChangesEnabled设置为false,所以Add&amp;&amp; AddRange将提供大致相同的性能。
<强> toBeDeleted 强>
对于您删除的每个实体,您都会进行数据库往返(自使用Any以来两次)
此行是性能问题:
var rlRows = dbContext.DBTable.Where(x => x.Type == TypeId && x.Url.Equals(s.Url, StringComparison.OrdinalIgnoreCase));
你应该改为:
实施例
var toBeDeletedBList = toBeDeletedB.ToList();
var listA = toBeDeletedBList.Where(x => x.Type == TypeId);
var listB = toBeDeletedBList.Where(x => x.Type != TypeId);
var rlRows = dbContext.DBTable.Where(x => x.Type == typeId && listA.Contains(s.Url);
listB.ForEach(s => {
dbContext.DBTable.Attach(s);
dbContext.DBTable.Remove(s);
});
<强> toBeModified 强>
我不确定CustomEqualityComparer方法究竟做了什么,但同样,您可能在listMultiple IQueryable上执行多个查询时遇到问题。
<强>的SaveChanges 强>
对于您需要插入,更新或删除的每个实体,都会执行数据库往返。
因此,如果您需要对50000行执行操作,则使用 INSANE 执行50000数据库往返
免责声明:我是Entity Framework Extensions
的所有者此库允许您执行批量操作并提高性能。
例如,BulkSaveChanges与SaveChanges完全相同,但通过大幅减少所需的数据库往返速度更快。
实施例
// Easy to use
context.BulkSaveChanges();
// Easy to customize
context.BulkSaveChanges(bulk => bulk.BatchSize = 100);
// Perform Bulk Operations
context.BulkDelete(endItems);
context.BulkInsert(endItems);
context.BulkUpdate(endItems);
// Customize Primary Key
context.BulkMerge(endItems, operation => {
operation.ColumnPrimaryKeyExpression =
endItem => endItem.Code;
});
答案 1 :(得分:1)
您是否看过使用AddRange()&amp; RemoveRange()方法? EF6中的新功能我相信。