如何更快地运行此entity framework示例
我有这个代码用于向数据库添加大约1500条记录:
async void button7_Click(object sender, EventArgs e) { var task = await Task.Run(() => { Random rnd = new Random(); for (int i = 0; i <= 1500; i++) { db.Tbls.Add(new Tbl() { Name = "User" + i + 1, Num = rnd.Next(10, i + 10) / 10 }); progress.Report(i * 100 / 1500); } db.SaveChanges(); return db.Tbls.Count(); }); }
但是完成这个过程花了大约4秒钟,但因为我使用async/await
它不会冻结UI。 现在我的问题是:如何改进此代码以更快地运行。 我如何在这里使用并行编程? 你能告诉我一个如何使用TPL的例子吗?
编辑 :
我按照建议使用了并行循环和AddRange
,但没有效果。 在所有建议的方式,我的过程仍然需要大约4秒。 如果有人能帮助我解决这个问题,我真的很感激。
如果性能是目标,那么你应该使用EF以外的东西,比如dapper (StackOverflow.com使用的东西)或原始ADO.Net 。 除此之外,您可以通过在保存时禁用更改自动检测和validation来提高entity framework性能:
yourDbContext.Configuration.AutoDetectChangesEnabled = false; yourDbContext.Configuration.ValidateOnSaveEnabled = false;
您应该将上面的代码与使用AddRange
@Evk建议结合起来。
另一种可能有助于加速插入的解决方案是使用BulkInsert (从这里拍摄的图像):
并行处理在这里不会有很大的帮助,因为你的循环本身就没有时间。 所有消耗的时间都分为两个方面:
-
当您向Entity Framework上下文添加新项目(并且您在此处使用EF)时,它会对上下文中的许多项目执行某些操作(而不仅仅是您添加的项目),并且您添加的项目越多越慢。
-
Perfoming 1500数据库插入也需要一些时间。
这里最简单的方法是减少上面列表中第1点所花费的时间。 你可以这样做:
async void button7_Click(object sender, EventArgs e) { var task = await Task.Run(() => { Random rnd = new Random(); var tbls = new List(); for (int i = 0; i <= 1500; i++) { tbls.Add(new Tbl() { Name = "User" + i + 1, Num = rnd.Next(10, i + 10) / 10 }); progress.Report(i * 100 / 1500); } db.Tbls.AddRange(tbls); db.SaveChanges(); return db.Tbls.Count(); }); }
通过使用AddRange
并首先收集简单List
所有项目,您将大大减少代码消耗的时间至少低于1秒。
更新。 如果你想使用并行循环,即使这没有帮助,你可以这样做:
int seed = Environment.TickCount; var random = new ThreadLocal(() => new Random(Interlocked.Increment(ref seed))); var tbls = new ConcurrentBag(); Parallel.For(0, 1500, (i) => { tbls.Add(new Tbl() { Name = "User" + i + 1, Num = random.Value.Next(10, i + 10) / 10 }); }); db.Tbls.AddRange(tbls);
注意事项:
-
Random
不是线程安全的,所以我们使用线程局部实例(一个用于并行循环内的每个线程),每个实例具有不同的种子值。 -
List
不是线程安全的 - 我们使用ConcurrentBag
代替。