Как избежать геометрического замедления с большими транзакциями Linq?
Я написал несколько действительно хороших библиотек для использования в LinqToSql. (Когда-нибудь, когда я успею подумать об этом, я могу сделать его открытым исходным кодом...:))
В любом случае, я не уверен, связано ли это с моими библиотеками или нет, но я обнаружил, что когда у меня есть большое количество измененных объектов в одной транзакции, а затем вызывается DataContext.GetChangeSet()
, все начинает получать reaalllly slooowwwww, Когда я ворвался в код, я обнаружил, что моя программа вращает свои колеса, делая очень много сравнений Equals()
между объектами в наборе изменений. Я не могу гарантировать, что это правда, но я подозреваю, что если в наборе изменений есть n объектов, то вызов GetChangeSet()
вызывает каждый объект для сравнения с любым другим объектом для эквивалентности, т.е. В лучшем случае (n ^ 2-n)/2 вызывает Equals()
...
Да, конечно, я мог бы совершать каждый объект по отдельности, но этот вид побеждает цель транзакций. И в программе, которую я пишу, у меня могло бы быть пакетное задание, содержащее 100 000 отдельных элементов, и все они должны выполняться вместе. Там собрано около 5 миллиардов сравнений.
Итак, вопрос в следующем: (1) правильно ли я оцениваю ситуацию? Получаете ли вы это поведение в чистом учебнике LinqToSql, или это то, что делают мои библиотеки? И (2) существует стандартное/разумное решение, чтобы я мог создать свою партию, не делая программу геометрически медленнее с каждым дополнительным объектом в наборе изменений?
Ответы
Ответ 1
В конце концов я решил переписать партии так, чтобы каждый отдельный элемент сохранялся независимо, все в рамках одной большой транзакции. Другими словами, вместо:
var b = new Batch { ... };
while (addNewItems) {
...
var i = new BatchItem { ... };
b.BatchItems.Add(i);
}
b.Insert(); // that a function in my library that calls SubmitChanges()
.. вам нужно сделать что-то вроде этого:
context.BeginTransaction(); // another one of my library functions
try {
var b = new Batch { ... };
b.Insert(); // save the batch record immediately
while (addNewItems) {
...
var i = new BatchItem { ... };
b.BatchItems.Add(i);
i.Insert(); // send the SQL on each iteration
}
context.CommitTransaction(); // and only commit the transaction when everything is done.
} catch {
context.RollbackTransaction();
throw;
}
Вы можете понять, почему первый кодовый блок является более чистым и более естественным в использовании, и мне жаль, что меня заставили использовать вторую структуру...