Как я могу добавить наследование к объекту, определенному в EntityDataModel (EF 4)?

У меня есть простое наследование двух объектов, определенных в модели EF, Person ‹- User.

Человек - это базовый объект / класс, он не является абстрактным и содержит такие поля, как имя, фамилия, адрес электронной почты.

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

В базе данных используется схема «таблица на тип», поэтому есть одна таблица для человека и еще одна таблица для пользователя. Оба используют одно и то же поле первичного ключа, PersonID. PersonID - это столбец IDENTITY, но в EDM для StoreGeneratedPattern для PersonID установлено значение None.

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

У меня есть тестовый пример для этого сценария, когда система находит человека, которого она хочет превратить в пользователя. Я пробовал установить поле PersonID в новом объекте User, добавить его в ObjectContext и сохранить изменения. Что происходит, база данных создает новую строку Person вместе с новой строкой User, игнорируя значение, которое я установил для PersonID.

Как я могу это обойти? Нужно ли мне создавать sprocs для обработки грубых операций EDM?


person danludwig    schedule 24.09.2010    source источник


Ответы (2)


Вот твоя проблема:

У меня есть тестовый пример для этого сценария, где система находит человека, которого она хочет превратить в пользователя.

Объекты не могут изменять типы в C # или любом другом OOPL на основе классов, о котором я знаю. EF этого не меняет.

Вам нужно изменить свой дизайн. Это не ОО. Я писал об этом в прошлом году:

Один из ментальных барьеров, который вы должны преодолеть при разработке хорошего объектно-реляционного сопоставления, - это тенденция мыслить в первую очередь объектно-ориентированными терминами или терминами отношений, в зависимости от того, что подходит вашей личности. Однако хорошее объектно-реляционное сопоставление включает в себя как хорошую объектную модель, так и хорошую реляционную модель. Например, предположим, что у вас есть база данных с таблицей для людей и связанными таблицами для сотрудников и клиентов. У одного человека может быть запись во всех трех таблицах. Теперь, с чисто реляционной точки зрения, вы можете создать представление базы данных для сотрудников и еще одно для клиентов, причем оба они включают информацию из таблицы People. При использовании того или иного ВИДА вы можете временно думать об отдельном человеке как о «просто» сотруднике или «просто» о клиенте, даже если вы знаете, что они оба. Таким образом, у кого-то из этого мировоззрения может возникнуть соблазн сделать объектно-ориентированное отображение, в котором Employee и Customer являются (прямыми) подклассами Person. Но это не работает с имеющимися у нас данными; поскольку у одного человека есть записи и о сотруднике, и о клиенте (и поскольку ни один экземпляр Person не может одновременно относиться к конкретному подтипу Employee и Customer), объектно-ориентированные отношения между Person и Employee должны быть композиция, а не наследование, и то же самое для Person и Customer.

person Craig Stuntz    schedule 24.09.2010
comment
Я бы проголосовал за это, если бы у меня была какая-то репутация. Мое мышление было в контексте «Пользователь - это человек», а не «Человек - это пользователь». Я вспоминаю, как типы данных Person с непересекающимся наследованием проходили экзамен EER 5 лет назад и думали, как это отстойно пытаться нормализовать. Я до сих пор не понимаю, почему правильнее использовать композицию. В моей модели Человек может существовать отдельно или как Пользователь, Студент, Преподаватель, Персонал, Выпускник и т. Д. Мне кажется неправильным изменять базовый класс (добавляя композицию) каждый раз, когда создается новый специализированный Человек. Я хочу иметь возможность подключаться к Person, не меняя его. - person danludwig; 25.09.2010

Мне удалось решить эту проблему, но я не совсем доволен решением. Я закончил тем, что написал хранимые процедуры для сопоставления операций вставки, обновления и удаления как для человека, так и для пользователя (поскольку они находятся в одном наборе сущностей, оба должны быть сопоставлены со всеми тремя sprocs). Спрок вставки для User принимает параметр PersonID и использует его, чтобы решить, создавать ли новую строку Person или присоединяться к существующей строке Person.

Загвоздка в том, что PersonID должен отображаться как входной параметр sproc и как привязка столбца результатов. Когда база данных должна сгенерировать новый PersonID, мы должны вернуть его, используя привязку результата, чтобы другие таблицы получили правильное значение для столбцов внешнего ключа, ссылающихся на PersonID. Кроме .... вы не можете сопоставить User.PersonID одновременно как входной параметр и привязку столбца результата. Это вызывает исключение времени выполнения «Невозможно определить допустимый порядок для зависимых операций. Зависимости могут существовать из-за ограничений внешнего ключа, требований модели или сохранения сгенерированных значений».

В итоге я добавил новый столбец в таблицу User (и в соответствующую концептуальную модель), который является обнуляемым int (я назвал его BasePK). В EDM я сделал геттеры и сеттеры для этого свойства защищенными, чтобы скрыть их от общедоступного интерфейса. Затем я сопоставил BasePK как входной параметр sproc и сопоставил PersonID как привязку столбца результата, чтобы получить значение идентификатора, если оно было сгенерировано. Одна перегрузка для конструктора User выглядит следующим образом:

общедоступный пользователь (int personID): base () {this.BasePK = personID; }

Итак, теперь, когда я хочу превратить существующего человека в пользователя, мне просто нужно создать пользователя, используя эту перегрузку.

int personID = [какой-то метод поиска человека и возврата его / ее PersonID]; var User = новый пользователь (personID);

Когда ObjectContext вставляет сущность, он передает PersonID в sproc через свойство BasePK. Когда sproc завершен, он возвращает PersonID фактическому свойству первичного ключа.

Мне не понравилась идея добавления столбца и свойства для этого, но она действительно сочетается с моим комментарием к ответу Крейга: я хочу иметь возможность подключаться к Person с новыми специализациями без необходимости изменять / перекомпоновывать Person сущность каждый раз. Таким образом, по крайней мере, я могу сохранить все изменения в типах производных сущностей, даже если это означает, что эти изменения включают добавление бессмысленных столбцов, которые всегда будут иметь значение NULL в физическом хранилище.

person danludwig    schedule 25.09.2010