Что вызывает .Attach(), чтобы быть медленным в EF4?

У нас есть общий метод обновления в нашем коде, который выполняет

foreach (var entity in entityList)
{
    Context.GetIDbSet<T>().Attach(entity);
    Context.SetState(entity, EntityState.Modified);
}

Я тестирую это, передавая перечисление сущностей и вызывая это один раз для каждого объекта.

Я нахожу, что перечисление 1000 объектов занимает приблизительно 47 секунд для запуска. Это ожидаемое поведение? Или что-то не так с фрагментом кода?

Профилирование показало, что метод Attach() был медленнее, чем метод SetState().

Тест, который я запускал, был на объекте с 50 свойствами и без отношений, если это имеет какое-то влияние.

Ответ 1

Я могу подтвердить это медленное поведение, и я также нашел основную причину. Я сделал небольшой тест со следующей моделью...

public class MyClass
{
    public int Id { get; set; }
    public string P1 { get; set; }
    // ... properties P2 to P49, all of type string
    public string P50 { get; set; }
}

public class MyContext : DbContext
{
    public DbSet<MyClass> MyClassSet { get; set; }
}

... и эта тестовая программа...

using (var context = new MyContext())
{
    var list = new List<MyClass>();
    for (int i = 0; i < 1000; i++)
    {
        var m = new MyClass()
        {
            Id = i+1,
            P1 = "Some text ....................................",
            // ... initialize P2 to P49, all with the same text
            P50 = "Some text ...................................."
        }
        list.Add(m);
    }

    Stopwatch watch = new Stopwatch();
    watch.Start();
    foreach (var entity in list)
    {
        context.Set<MyClass>().Attach(entity);
        context.Entry(entity).State = System.Data.EntityState.Modified;
    }
    watch.Stop();
    long time = watch.ElapsedMilliseconds;
}

Тест 1

Точно код выше:

- > время = 29,2 с


Тест 2

Прокомментируйте строку...

//context.Entry(entity).State = System.Data.EntityState.Modified;

- > время = 15,3 сек


Тест 3

Прокомментируйте строку...

//context.Set<MyClass>().Attach(entity);

- > время = 57,3 сек

Этот результат очень странный, потому что я ожидал, что вызов Attach не требуется, так как изменение состояния все равно добавляется.


Тест 4

Удалите свойства P6-P50 (так что у нас всего 5 строк в сущности), исходный код:

- > время = 3,4 с

Итак, да, очевидно, имеет значение число свойств.


Тест 5

Добавьте следующую строку перед циклом (модель снова со всеми 50 свойствами):

context.Configuration.AutoDetectChangesEnabled = false;

- > время = 1,4 сек


Тест 6

Снова с AutoDetectChangesEnabled = false, но с 5 свойствами:

- > время = 1,3 с

Таким образом, без отслеживания изменений количество свойств больше не имеет значения.


Заключение

На данный момент большую часть времени, по-видимому, тратится на получение моментального снимка свойств прикрепленных объектов механизмом отслеживания изменений. Если вам не нужно отключать отслеживание изменений для фрагмента кода. (Я думаю, что в вашем коде вам действительно не нужно отслеживать изменения, потому что, установив состояние entitiy на Modified, вы в основном помечаете все свойства как измененные в любом случае. Таким образом, все столбцы отправляются в базу данных в инструкции обновления.)

Edit

Время тестирования выше в режиме отладки. Но режим Release не имеет большого значения (например: Test 1 = 28,7 с, тест 5 = 0,9 сек).