Отслеживание изменений в Entity Framework 4.0 с использованием динамических прокси POCO в нескольких контекстах данных

Я начал возиться с EF 4.0, потому что мне интересно узнать о возможностях POCO... Я хотел смоделировать отключенную веб-среду и написал следующий код, чтобы имитировать это:

  • Сохранить тестовый объект в базе данных.
  • Получить тестовый объект
  • Утилизируйте DataContext, связанный с тестовым объектом, который я использовал для его получения.
  • Обновить тестовый объект
  • Создайте новый контекст данных и сохраните изменения на тестовом объекте, которые автоматически отслеживаются в DynamicProxy, сгенерированном против моего объекта POCO.

Проблема заключается в том, что когда я вызываю dataContext.SaveChanges в методе теста выше, обновления не применяются. Объект testStore показывает статус "Модифицированный", когда я проверяю его EntityStateTracker, но он больше не изменяется, когда я просматриваю его в новом свойстве dataContext Stores. Я бы подумал, что вызов метода Attach в новом dataContext также приведет к состоянию объекта "Модифицированное", но это, похоже, не так. Есть что-то, чего я не хватает? Я определенно работаю с самоконтролем POCOs с помощью DynamicProxies.

private static void SaveTestStore(string storeName = "TestStore")
{
  using (var context = new DataContext())
  {
    Store newStore = context.Stores.CreateObject();
    newStore.Name = storeName;
    context.Stores.AddObject(newStore);
    context.SaveChanges();
  }
}

private static Store GetStore(string storeName = "TestStore")
{
  using (var context = new DataContext())
  {
    return (from store in context.Stores
            where store.Name == storeName
            select store).SingleOrDefault();
  }
}

[Test]
public void Test_Store_Update_Using_Different_DataContext()
{
  SaveTestStore();
  Store testStore = GetStore();
  testStore.Name = "Updated";      

  using (var dataContext = new DataContext())
  {
    dataContext.Stores.Attach(testStore);
    dataContext.SaveChanges(SaveOptions.DetectChangesBeforeSave);        
  }

  Store updatedStore = GetStore("Updated");
  Assert.IsNotNull(updatedStore);
}

Ответ 1

Как вы сказали позже, вы использовали генератор POCO, а не генератор самопроверки.

Я тоже пробовал, и стал очень озадачен. Кажется, что прокси-классы работают не так, как ожидалось, и может быть ошибка. Затем снова. ни один из примеров в MSDN не попробовал что-то подобное, и когда они ссылаются на обновления в разных уровнях приложения (что-то вроде того, что мы делаем здесь), они используют объекты самоконтроля, а не прокси-серверы POCO.

Я не уверен, как работают эти прокси, но они, похоже, сохраняют какое-то состояние (мне удалось найти "измененное" состояние внутри частных свойств). Но, похоже, это свойство ПОЛНОСТЬЮ игнорируется. Когда вы присоединяете свойство к контексту, контекст добавляет запись в ObjectStateManager и хранит там дополнительные обновления состояния. На этом этапе, если вы внесете изменения, он будет зарегистрирован и применен.

Проблема заключается в том, что когда вы .Attach сущность - измененное состояние из прокси-сервера не передается диспетчеру состояния внутри контекста. Кроме того, если вы используете context.Refresh(), обновления переопределяются и забываются! Даже если вы передадите в него RefreshMode.ClientWins. Я попытался изменить свойство состояния состояния объекта, но он все равно был переопределен, и исходные настройки были восстановлены.

Кажется, что ошибки в EF нет, и единственный способ сделать это - использовать что-то вроде этого:

using (var db = new Entities())
{
    var newUser = (from u in db.Users
                    where u.Id == user.Id
                    select u).SingleOrDefault();
    db.Users.ApplyCurrentValues(user);
    db.SaveChanges();
}

Еще одна вещь здесь

Framework Entityity: отслеживание изменений в SOA с помощью подхода POCO

Кажется, что POCO просто не поддерживает подход, который вы ищете, и поскольку я ожидал, что объекты самоконтроля были созданы для решения ситуации, которую вы тестировали, в то время как прокси-серверы POCO отслеживают изменения только в контексте, который они создали.. Или так кажется...

Ответ 2

Попробуйте

        db.ObjectStateManager.ChangeObjectState(user, System.Data.EntityState.Modified);

Перед вызовом SaveChanges

Ответ 3

Поиграв с объектами самоконтроля, я понял, в чем была ваша ошибка. Вместо того, чтобы пытаться привязать объект к контексту данных, вы должны указать, что хотите, чтобы контекст данных применял новые изменения, внесенные вами в базу данных.

В этом случае измените код сохранения:

using (var dataContext = new DataContext())
{
    dataContext.Stores.ApplyChanges(testStore);
    dataContext.SaveChanges();        
}

По крайней мере, я протестировал его на своей локальной машине, и он работал после этого обновления:)
Надеюсь, это поможет!

Ответ 4

Я думаю, что корень вашей проблемы - это управление объектом Context.

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

есть небольшое сообщение об этом, которое вы можете прочитать здесь: http://social.msdn.microsoft.com/forums/en-US/adodotnetentityframework/thread/5ee5db93-f8f3-44ef-8615-5002949bea71/

Если вы переключитесь на самостоятельное отслеживание, я думаю, вы обнаружите, что ваши сущности работают так, как вам хочется.

другой вариант заключается в добавлении свойства в частичный класс вашего poco для отслеживания изменений вручную после отсоединения POCO из контекста, который вы использовали для его загрузки.