Отслеживание изменений в Entity Framework 4.0 с использованием динамических прокси POCO в нескольких контекстах данных - PullRequest
11 голосов
/ 15 апреля 2010

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

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

Проблема в том, что когда я вызываю dataContext.SaveChanges в методе Test выше, обновления не применяются. Сущность testStore показывает состояние «Изменено», когда я проверяю его EntityStateTracker, но больше не изменяется, когда я просматриваю его в свойстве Stores нового dataContext. Я бы подумал, что вызов метода Attach для нового dataContext также перенесет состояние «Изменено» объекта, но, похоже, это не так. Есть ли что-то, что мне не хватает? Я определенно работаю с POCO с самоконтролем, используя 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);
}

Ответы [ 4 ]

8 голосов
/ 22 апреля 2010

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

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

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

Проблема в том, что при подключении сущности - измененное состояние от прокси-сервера не передается менеджеру состояния в контексте. Кроме того, если вы используете 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();
}

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

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

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

5 голосов
/ 26 мая 2010

Попробуйте

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

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

2 голосов
/ 21 апреля 2010

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

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

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

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

0 голосов
/ 14 октября 2010

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

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

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

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

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

...