Ссылка на объекты:Смешанный граф сущностей, EntityKeys, Присоединенных и Новых объектов
-
06-07-2019 - |
Вопрос
В моей модели данных у меня есть довольно распространенное разделение между моими объектами / таблицами / данными:
- "Транзакционные" сущности, которые представляют работу, выполняемую системой.Эти объекты создаются системой и важны только в определенных контекстах.Они регулярно создаются "на лету".(В сторону:Есть ли подходящее название для этого типа сущности?)
- Объекты "Словаря данных", которые представляют общие свойства транзакционных объектов.Они определяются нерегулярно (в основном в начале проекта) и имеют гораздо более статичный жизненный цикл.Как правило, они создаются мной.
Так, например, у меня может быть объект User (транзакционный) и объект UserType (словарь данных).
Я хочу (жестко) закодировать ссылки на экземпляры объектов словаря данных в свой код.Это дает мне возможность кодировать бизнес-логику с помощью простого для понимания языка.(Так, например, у меня мог бы быть пользовательский тип "Plain" и пользовательский тип "Admin", а затем правило, которое гласит "разрешать доступ, только если пользовательский тип пользователя равен Admin").
Я использую LINQ-to-Entities в качестве моей технологии доступа к данным / ORM.Чтобы реализовать ссылки на словарь данных, я сохраняю EntityKeys .Насколько я понимаю, они оторваны от контекста объекта и поэтому подходят для этой цели.(Поскольку они не содержат состояния объекта, мне также не нужно беспокоиться о том, что это состояние устареет.)
Однако это вызывает у меня проблемы, когда я пытаюсь добавить новую транзакционную сущность с помощью DD-EntityKey-reference .Продолжая мой пример, я делаю это:
UserEntities userEntities = new UserEntitites()
User user = new User()
user.UserType = new UserType()
user.UserType.EntityKey = adminEntityKey
userEntities.AddToUser(user)
...и это выдает мне следующую ошибку:
Система.Исключение InvalidOperationException :Объект не может быть добавлен в ObjectStateManager, поскольку у него уже есть EntityKey .Используйте ObjectContext.Прикрепить, чтобы прикрепить объект, у которого есть существующий ключ.
Если я попытаюсь вызвать userEntities.Attach(user) вместо AddToUser , я получу это:
Система.Исключение InvalidOperationException :Объект с нулевым значением EntityKey не может быть присоединен к контексту объекта.
Обе эти ошибки имеют смысл, учитывая смешивание новых и ранее существовавших объектов, которое я делаю.В чем я не уверен, так это в том, как обойти эту проблему.Есть ли какой-нибудь способ, которым я могу отсоединить ссылки на DD-объекты и назначить их новым присоединенным объектам, не требуя от меня загрузки всего состояния DD-объекта?
Решение
Я попытаюсь объяснить, почему вы получаете эти исключения:
Система.Исключение InvalidOperationException :Объект не может быть добавлен в ObjectStateManager, поскольку у него уже есть EntityKey .Используйте ObjectContext.Прикрепить, чтобы прикрепить объект, у которого есть существующий ключ.
Создаются новые объекты User и UserType.Ключ EntityKey для UserType был установлен, но когда вы передаете его в userEntities.Add() EF пытается присвоить им статус ДОБАВЛЕННЫХ.Поскольку UserType имеет EntityKey, EF понимает, что что-то не так, и выдает исключение.
Система.Исключение InvalidOperationException :Объект с нулевым значением EntityKey не может быть присоединен к контексту объекта.
В этом исключении с UserType все в порядке, но исключение выдается из-за того, что пользовательская сущность - у нее нет EntityKey и, следовательно, она не может быть присоединена.
Вот как я бы решил эту проблему (если, как вы говорите, жесткое кодирование ссылок на словарь данных в порядке):
Для объекта UserType я бы создал статические ссылки на идентификаторы UserType:
public partial class UserType
{
public const int AdminUserTypeID = 1;
public const int PlainUserTypeID = 2;
}
В пользовательском объекте было бы свойство extender для облегчения доступа к внешнему ключу UserType:
public partial class User
{
public int? UserTypeID
{
set
{
UserTypeReference.EntityKey = new EntityKey("UserEntities.UserType", "UserTypeID", value);
}
get
{
if (UserTypeReference.EntityKey == null)
return null;
if (UserTypeReference.EntityKey.EntityKeyValues.Count() > 0)
return (int)UserTypeReference.EntityKey.EntityKeyValues[0].Value;
else
return null;
}
}
}
Наконец, вариант использования для создания нового пользователя будет выглядеть следующим образом:
using(UserEntities userEntities = new UserEntitites())
{
User user = new User();
user.UserTypeID = UserType.AdminUserTypeID;
userEntities.AddToUser(user);
userEntities.SaveChanges()
}