Почему в dbset.Добавить работу так медленно?

та же тема обсуждалась здесь 8 месяцев назад: Как ускорить DbSet.Add ()?. Не было предложено никакого решения, кроме использования SqlBulkCopy, что неприемлемо для нас. Я решил снова поднять его, надеясь, что вокруг этой проблемы могут быть новые мысли и идеи, и предлагаются другие обходные пути. По крайней мере, мне просто интересно, почему эта операция занимает так много времени.

Ну, проблема в том, что я должен обновить объекты 30K в базу данных (EF 4.1, POCO). Тип сущности довольно прост, содержащий integer Id + другие 4 целочисленных свойства без отношения к другим типам. 2 случая:

  • все они являются новыми записями. Запуск контекста.Сущности.Добавление (entity) по одному для каждого объекта занимает 90 секунд с Cntx.Конфигурация.AutoDetectChangesEnabled=false (значение true заставляет его работать вечно). Затем SaveChanges занимает всего секунду. Другой подход: прикрепление его к контексту, как это, занимает те же 90 sec:

    Cntx.Entities.Attach(entity);
    Cntx.Entry(entity).State = EntityState.Added;
    
  • все они являются существующими записями с некоторыми изменениями. В случае, если требуется всего несколько миллисекунд, чтобы прикрепить его к существующему контексту данных следующим образом:

    Cntx.Entities.Attach(entity);
    Cntx.Entry(entity).State = EntityState.Modified;
    

    видите разницу?

что находится за сценой метода Add, который делает его работу настолько невероятно медленной?

1 ответов


У меня есть интересные результаты тестирования производительности, и я нашел виновника. Я не видел такой информации ни в одном источнике EF, который я когда-либо читал.

он оказывается равным переопределенным в базовом классе. Базовый класс должен содержать свойство Id, совместно используемое всеми типами конкретных объектов. Этот подход рекомендован многими книгами EF и довольно хорошо известен. Вы можете найти его здесь, например: как лучше всего реализовать Equals для пользовательских типы?

точнее, производительность убивается операцией распаковки (преобразование объекта в конкретный тип), что сделало его работу настолько медленной. Как я прокомментировал эту строку кода, потребовалось 3 сек, чтобы запустить противостояние 90 сек раньше!

public override bool Equals ( object obj )
{
    // This line of code made the code so slow 
    var entityBase = obj as EntityBase;
    ...
}

когда я нашел его, я начал думать о том, что может быть альтернативой этому равно. Первой идеей было реализовать IEquatable для EntityBase, но оказалось, что он вообще не запускается. Так что я решил, наконец, сделать, чтобы реализовать IEquatable для каждого конкретного класса сущности в моей модели. У меня их всего несколько, так что это незначительное обновление для меня. Вы можете поместить всю функциональность операции Equal (обычно это сравнение идентификаторов объектов 2) в метод расширения для совместного использования между конкретными классами сущностей и запустить его следующим образом: Equal (((EntityBase)ConcreteEntityClass). Самое интересное, что это IEquatable ускоряет EntitySet.Добавить 6 раз!

поэтому у меня больше нет проблем с производительностью, тот же код запускается для меня менее чем за секунду. я получил 180 раз прирост производительности! Потрясающе!

вывод:

  1. самый быстрый способ запуска EntitySet.Add должен иметь IEquatable для конкретного объекта (0.5 sec)
  2. отсутствует IEquatable заставляет его работать 3 сек.
  3. наличие равных (object obj), которые рекомендуют большинство источников, заставляет его работать 90 сек