使用 Entity Framework 将大量行插入数据库
Insert huge number of rows into database using Entity Framework
@编辑
我按照 Fastest Way of Inserting in Entity Framework 中的步骤进行操作,结果更差,所以它不是重复的。
我的目标是创建一个种子方法来填充 LocalDb 的一个表。该方法将添加 182500 行(以模拟 500 台设备的年度 activity 数据)以供进一步测试。我可能想 运行 它多几次来改变设备的数量,所以会生成更多的行。这就是为什么我需要尽可能高效地插入行。
protected void SeedReportDataTable(int numberOfTerminals)
{
var rand = new Random();
var tidsList = new List<string>();
// generuj liste losowych numerow tid
for (int i = 0; i < numberOfTerminals; i++)
{
var randomTid = rand.Next(100000, 1000000).ToString(); // generuj 6-cyfrowy numer tid
while (tidsList.Contains(randomTid)) { randomTid = rand.Next(100000, 1000000).ToString(); } // elminuj powtorzenia
tidsList.Add(randomTid);
}
// dla kazdego z numerow tid generuj roczna historie aktywnosci
var recordsList = new BlockingCollection<ReportData>();
int year = Convert.ToInt32(DateTime.Now.Year);
Parallel.ForEach(tidsList, tid =>
{
// dla kazdego miesiaca
for (int month = 1; month <= 12; month++)
{
// dla kazdego dnia
for (int day = 1; day <= DateTime.DaysInMonth(year, month); day++)
{
var record = new ReportData
{
Tid = tid,
Active = Convert.ToBoolean(
rand.Next(0, 11)), // generuj losowy stan aktywnosci z prawdopodbienstwem 1/10 na bycie nieaktywnym
Date = new DateTime(year, month, day)
};
recordsList.Add(record);
}
}
});
// dodaj unikalne klucze glowne rekordom przed dodaniem do kontekstu bazy
var keyValue = 1;
foreach (var record in recordsList)
{
record.Id = keyValue++;
}
// podziel liste na czesci
int chunkSize = 1000;
for (int recordsSkipped = 0; recordsSkipped < recordsList.Count; recordsSkipped += chunkSize)
{
// wymieniaj kontekst
using (var db = new dbEntities())
{
db.Configuration.AutoDetectChangesEnabled = false;
db.Configuration.ValidateOnSaveEnabled = false;
// dodawaj do bazy po kawalku
db.ReportData.AddRange(recordsList.Skip(recordsSkipped).Take(chunkSize));
db.SaveChanges();
}
}
}
运行 此代码需要 30 分钟才能完成。在此之前,我 运行 一个以以下结尾的版本:
using (var db = new dbEntities())
{
db.ReportData.AddRange(recordsList);
db.SaveChanges();
}
用了15分钟,还是比我预想的要慢。
为什么我的 "improvements" 失败了?
我怎样才能让它更快地插入行?
当我将我的播种方法添加到 Configuration.cs
和 运行 update-database
命令时,插入所有行只需不到 5 分钟。
仅调用一次 Context.AddRange()
时效果最佳。
dbContext.Configuration.AutoDetectChangesEnabled = false;
dbContext.Configuration.ValidateOnSaveEnabled = false;
dbContext.ReportData.AddRange(recordsList);
dbContext.SaveChanges();
@编辑 我按照 Fastest Way of Inserting in Entity Framework 中的步骤进行操作,结果更差,所以它不是重复的。
我的目标是创建一个种子方法来填充 LocalDb 的一个表。该方法将添加 182500 行(以模拟 500 台设备的年度 activity 数据)以供进一步测试。我可能想 运行 它多几次来改变设备的数量,所以会生成更多的行。这就是为什么我需要尽可能高效地插入行。
protected void SeedReportDataTable(int numberOfTerminals)
{
var rand = new Random();
var tidsList = new List<string>();
// generuj liste losowych numerow tid
for (int i = 0; i < numberOfTerminals; i++)
{
var randomTid = rand.Next(100000, 1000000).ToString(); // generuj 6-cyfrowy numer tid
while (tidsList.Contains(randomTid)) { randomTid = rand.Next(100000, 1000000).ToString(); } // elminuj powtorzenia
tidsList.Add(randomTid);
}
// dla kazdego z numerow tid generuj roczna historie aktywnosci
var recordsList = new BlockingCollection<ReportData>();
int year = Convert.ToInt32(DateTime.Now.Year);
Parallel.ForEach(tidsList, tid =>
{
// dla kazdego miesiaca
for (int month = 1; month <= 12; month++)
{
// dla kazdego dnia
for (int day = 1; day <= DateTime.DaysInMonth(year, month); day++)
{
var record = new ReportData
{
Tid = tid,
Active = Convert.ToBoolean(
rand.Next(0, 11)), // generuj losowy stan aktywnosci z prawdopodbienstwem 1/10 na bycie nieaktywnym
Date = new DateTime(year, month, day)
};
recordsList.Add(record);
}
}
});
// dodaj unikalne klucze glowne rekordom przed dodaniem do kontekstu bazy
var keyValue = 1;
foreach (var record in recordsList)
{
record.Id = keyValue++;
}
// podziel liste na czesci
int chunkSize = 1000;
for (int recordsSkipped = 0; recordsSkipped < recordsList.Count; recordsSkipped += chunkSize)
{
// wymieniaj kontekst
using (var db = new dbEntities())
{
db.Configuration.AutoDetectChangesEnabled = false;
db.Configuration.ValidateOnSaveEnabled = false;
// dodawaj do bazy po kawalku
db.ReportData.AddRange(recordsList.Skip(recordsSkipped).Take(chunkSize));
db.SaveChanges();
}
}
}
运行 此代码需要 30 分钟才能完成。在此之前,我 运行 一个以以下结尾的版本:
using (var db = new dbEntities())
{
db.ReportData.AddRange(recordsList);
db.SaveChanges();
}
用了15分钟,还是比我预想的要慢。
为什么我的 "improvements" 失败了?
我怎样才能让它更快地插入行?
当我将我的播种方法添加到 Configuration.cs
和 运行 update-database
命令时,插入所有行只需不到 5 分钟。
仅调用一次 Context.AddRange()
时效果最佳。
dbContext.Configuration.AutoDetectChangesEnabled = false;
dbContext.Configuration.ValidateOnSaveEnabled = false;
dbContext.ReportData.AddRange(recordsList);
dbContext.SaveChanges();