Проблема с EF STE и самообращающимися таблицами - PullRequest
1 голос
/ 08 июня 2011

Это мой первый пост здесь, поэтому я надеюсь, что все в порядке.

Вот моя проблема: в моей базе данных есть таблица с именем UserTypes .Он имеет:

  1. ID;
  2. IsPrivate;
  3. Parent_ID;

Соответствующими являются первый и третий.У меня есть другая таблица с именем UserTypes_T , которая содержит информацию для различных типов, которая зависит от языка.Поля:

  1. Language_ID;
  2. UserType_ID;
  3. Name;

Я пытаюсь достичь загрузкивсю иерархию из таблицы UserTypes и показать ее в виде дерева (это пока не актуально).Затем, выбрав некоторые типы пользователей, я могу редактировать их в отдельном окне редактирования (имя) и комбинированном окне (родитель).

Все работает нормально, пока я не попытаюсь сохранить изменения в базе данных.EF сгенерировал для меня два класса сущностей для этих таблиц:

Класс для пользовательских типов имеет:

  1. ID;
  2. IsPrivate;
  3. Parent_ID;
  4. Навигационное свойство для самостоятельной ссылки (0..1);
  5. Навигационное свойство для дочерних элементов;
  6. Другое навигационное свойство для UserTypes_Tтаблица (1 .. *);

Класс для переведенной информации имеет:

  1. UserType_ID;
  2. Language_ID;
  3. Имя;
  4. Навигационное свойство к таблице UserTypes (* .. 1);
  5. Навигационное свойство к таблице Языки (* .. 1);

Я получаю необходимые данные, используя:

return context.UserTypes.Include("UserTypes_T").Where(ut => ut.IsPrivate==false).ToList();

в моем веб-сервисе WCF.Я могу добавить новые типы пользователей без проблем, но когда я пытаюсь обновить старые, происходят некоторые странные вещи.

Если я обновляю корневой элемент (Parent_ID == null), все работает!Если я обновляю элемент, где Parent_ID! = Null, я получаю следующую ошибку:

AcceptChanges не может продолжаться, поскольку значения ключа объекта конфликтуют с другим объектом в ObjectStateManager.

Я искал по всему интернету и прочитал сообщение в блоге Diego B Vega (и многие другие), но моя проблема в другом.Когда я меняю родительский тип пользователя, я на самом деле меняю свойство Parent_ID, а не свойство навигации.Я всегда стараюсь работать с идентификаторами, а не сгенерированными навигационными свойствами, чтобы избежать проблем.

Я провел небольшое исследование, попытался увидеть, какой граф объектов я получаю, и увидел, что было многодубликаты сущностей:

Корневой элемент имеет список своих дочерних элементов.Каждый дочерний элемент имел обратную ссылку на корень или его родителя и так далее.Ты можешь представить.Поскольку я не использовал эти навигационные свойства, потому что я использовал идентификаторы для получения / установки необходимых мне данных, я удалил их из модели.Точнее говоря, я удалил точки 4 и 5 из класса сущностей UserTypes .Тогда у меня был граф объектов с каждым элементом только один раз.Я попробовал новое обновление, но у меня была та же проблема:

Корневой элемент был обновлен нормально, но элементы, у которых были некоторые родители, выдали то же исключение.

Я увидел, что у меня былосвойство навигации в классе сущности UserTypes_T , указывающее на тип пользователя, поэтому я тоже удалил его.Затем эта ошибка исчезла.Все элементы в графе объектов были уникальными.Но проблема осталась - я мог обновить свой корневой элемент без проблем, но при попытке обновить дочерние элементы (без исключений) я получил исключение нулевой ссылки в сгенерированном классе Model.Context.Extensions:

if (!context.ObjectStateManager.TryGetObjectStateEntry(entityInSet.Item2, out entry))
{
    context.AddObject(entityInSet.Item1, entityInSet.Item2);//here!
}

Я пытался обновить только имя (в UserTypes_T ), но ошибка та же.

У меня нет идей, и я пытался решить эту проблемув течение 8 часов, поэтому я буду признателен, если кто-то даст мне идеи или поделится своим опытом.

PS:

Единственный способ, которым мне удалось обновить дочерний объект, - использовать следующий код для извлечения данных:

var userTypes = argoContext.UserTypes.Include("UserTypes_T").Where(ut => ut.IsPrivate==false).ToList();
foreach (UserType ut in userTypes)
{
    ut.UserType1 = null;
    ut.UserTypes1 = null;
}
return userTypes;

, где UserType1 - это свойство навигации, указывающее на родительский тип пользователя и UserTypes1 - свойство навигации, содержащее список дочернего элемента.Проблема заключалась в том, что EF «исправляет» объекты и изменяет Parent_ID на null .Если я установлю его снова, EF также установит UserTypes1 ... Может быть, есть способ остановить это поведение?

1 Ответ

1 голос
/ 09 июня 2011

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

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

using (MyEntities context = new MyEntities()) 
{
    string errMsg = MyValidator.ValidateSomething(context.UserTypes,...);
    if (!string.IsNullOrEmpty(errMsg)) throw new FaultException(errMsg);
    //some other code here...
    context.UserTypes.ApplyChanges(_userType);//_userType is the one that is updated
    context.UserTypes.SaveChanges();
}

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

Решение простое - использовать другой контекст для проверки на сервере:

using (MyEntities validationContext = new MyEntities())
{
    //validation goes here...
}
using (MyEntities context = new MyEntities())
{
    //saving changes and other processing...
}

Другим может быть:

using (MyEntities context = new MyEntities())
{
    using (MyEntities validationContext = new MyEntities())
    {
        //validation
    }
    //saving changes and other processing...
}

Это оно!Надеюсь это кому-нибудь пригодится!

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...