Ответ 1
Во-первых, несколько пояснений:
EF не значительно медленнее, чем другие методы для пакетных операций. в моих тестах вы можете получить 50% -ное улучшение с помощью исходной команды SQL и, возможно, до 10 раз быстрее с копией SQL Bulk, но EF не намного медленнее, чем сравнительные методы, как правило (хотя часто воспринимается как очень медленное). Для большинства приложений EF даст подходящие номера производительности даже в пакетных сценариях, учитывая правильную настройку. (см. мою статью здесь: http://blog.staticvoid.co.nz/2012/3/24/entity_framework_comparative_performance и http://blog.staticvoid.co.nz/2012/8/17/mssql_and_large_insert_statements)
Из-за того, как EF меняет отслеживание, его потенциал может значительно превысить производительность большинства людей, которые напишут заявления на вставку на основе SqlCommand (есть много иконов для планирования запросов, поездок в оба конца и транзакций, которые делают его довольно трудно написать оптимальное выполнение операторов объемной вставки). Я предложил эти дополнения к EF здесь (http://entityframework.codeplex.com/discussions/377636), но havent реализовал их еще.
Вы совершенно правы в своем решении отключить автоматическое обнаружение изменений, каждое .Add или .Attach действие с обнаружением изменений перечисляет граф отслеживания, поэтому, если вы добавляете дополнения 17k в том же контексте, вам нужно будет перечислить график 17000 раз в общей сложности 17000 + 16999 +... + 2 + 1 = 144 500 000 объектов, неудивительно, что он так долго остается? (см. мою статью здесь: http://blog.staticvoid.co.nz/2012/5/7/entityframework_performance_and_autodetectchanges)
Сохранение изменений всегда необходимо перечислить граф отслеживания (он вызывает обнаружение изменений внутри), поэтому ваш первый способ будет медленным, так как на самом деле он будет выполнять такое же количество вызовов отслеживания, как указано выше.
Второй способ намного лучше, но у него все еще есть довольно серьезный недостаток, который, как мне кажется, двоякий, во-первых, граф действительно большой, когда вы идете сохранять изменения (большие графики имеют экспоненциально более высокие времена отслеживания), а во-вторых, занимают много памяти, чтобы сохранить весь график сразу, особенно учитывая, что EF хранит две копии каждого из ваших объектов.
Лучший способ - сохранить свой график в кусках. некоторые
//With Auto detect changes off.
foreach(var batch in batches)//keep batch size below 1000 items, play around with the numbers a little
{
using(var ctx = new MyContext())//make sure you create a new context per batch.
{
foreach(var entity in batch){
ctx.Entities.Add(entity);
}
ctx.SaveChanges();
}
}
Я бы ожидал, что вам нужно ориентироваться в 17-30 секунд, чтобы сделать все строки размером 17 тыс..
делая это с помощью сырых SQL-команд, вы можете получить это примерно до 12-20 с;
с повторной реализацией с массовой копией, вы могли бы получить это до 2-5 с