К вашему замечанию в комментариях к вашему вопросу:
"... SavingChanges ( для каждого
запись ) ... "
Это худшее, что вы можете сделать! Вызов SaveChanges()
для каждой записи значительно замедляет массовые вставки. Я бы сделал несколько простых тестов, которые, скорее всего, повысят производительность:
- Позвоните
SaveChanges()
один раз после ВСЕХ записей.
- Позвоните
SaveChanges()
после, например, 100 записей.
- Позвоните
SaveChanges()
после, например, 100 записей и утилизируйте контекст и создайте новый.
- Отключить обнаружение изменений
Для объемных вставок я работаю и экспериментирую с таким шаблоном:
using (TransactionScope scope = new TransactionScope())
{
MyDbContext context = null;
try
{
context = new MyDbContext();
context.Configuration.AutoDetectChangesEnabled = false;
int count = 0;
foreach (var entityToInsert in someCollectionOfEntitiesToInsert)
{
++count;
context = AddToContext(context, entityToInsert, count, 100, true);
}
context.SaveChanges();
}
finally
{
if (context != null)
context.Dispose();
}
scope.Complete();
}
private MyDbContext AddToContext(MyDbContext context,
Entity entity, int count, int commitCount, bool recreateContext)
{
context.Set<Entity>().Add(entity);
if (count % commitCount == 0)
{
context.SaveChanges();
if (recreateContext)
{
context.Dispose();
context = new MyDbContext();
context.Configuration.AutoDetectChangesEnabled = false;
}
}
return context;
}
У меня есть тестовая программа, которая вставляет 560 000 сущностей (9 скалярных свойств, без свойств навигации) в БД. С этим кодом он работает менее чем за 3 минуты.
Для исполнения важно позвонить SaveChanges()
после «много» записей («много» около 100 или 1000). Это также повышает производительность для удаления контекста после SaveChanges и создания нового. Это очищает контекст от всех объектов, SaveChanges
этого не делает, объекты все еще привязаны к контексту в состоянии Unchanged
. Это растущий размер присоединяемых объектов в контексте, который замедляет вставку шаг за шагом. Поэтому через некоторое время полезно очистить его.
Вот несколько измерений для моих 560.000 сущностей:
- commitCount = 1, пересоздатьContext = false: много часов (это ваша текущая процедура)
- commitCount = 100, пересоздатьContext = false: более 20 минут
- commitCount = 1000, пересоздатьContext = false: 242 сек
- commitCount = 10000, пересоздатьContext = false: 202 сек
- commitCount = 100000, пересоздатьContext = false: 199 с
- commitCount = 1000000, пересоздатьContext = false: исключение нехватки памяти
- commitCount = 1, пересоздатьContext = true: более 10 минут
- commitCount = 10, пересоздатьContext = true: 241 сек
- commitCount = 100, пересоздатьContext = true: 164 сек
- commitCount = 1000, пересоздатьContext = true: 191 сек
Поведение в первом тесте, приведенном выше, заключается в том, что производительность очень нелинейная и сильно снижается со временем. («Много часов» является оценкой, я никогда не заканчивал этот тест, я остановился на 50 000 объектов через 20 минут.) Это нелинейное поведение не так важно во всех других тестах.