Членство в группе моделирования с «выбранным» членом в базе данных - PullRequest
2 голосов
/ 30 октября 2011

В моей модели данных у меня есть группа сущностей и другая сущность GroupMember.Один Group состоит из одного или нескольких GroupMembers, но один GroupMember может находиться только в одном Group одновременно.Пока проблем нет, в базе данных GroupMember есть внешний ключ для идентификатора Group.Однако теперь я хочу, чтобы один из членов был «выбранным по умолчанию» или «выбранным».Всегда должен быть ровно один выбранный член, не больше и не меньше.

Я пытался смоделировать это в Entity Framework, имея одну ассоциацию 1- * для моделирования членства в группе и одну (0..1) -1 связь ссохранить экземпляр выбранного GroupMember внутри Group.

Однако теперь у меня, очевидно, возникает проблема, заключающаяся в том, что при вставке экземпляров Group и GroupMember я получаю ошибку, которую не может определить структура сущностейв каком порядке для вставки элементов, поскольку для Group требуется действительный GroupMember в качестве элемента по умолчанию, но GroupMember не может быть вставлен, если только он не ссылается на существующую сущность Group.Так сказать, проблема с куриным яйцом ...

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

Другим способом было бы смоделировать выбранный элемент как логическое свойство "IsSelected"в группе член.Однако я не уверен, как обеспечить одновременное использование только одного выбранного члена, используя только конструктор структуры сущностей (я хочу попытаться избежать непосредственной работы с базой данных).

Можете ли вы предложить любойруководство, что было бы предпочтительным способом справиться с этим?Спасибо!

Ответы [ 2 ]

2 голосов
/ 30 октября 2011

правильный способ смоделировать это с помощью таблицы ассоциации :

+-------+              +--------+                 +--------+
| Group |--------------| Member |-----------------| Person |
+-------+ 1          * +--------+ 1             1 +--------+
    | 1                                               | 1
    |                                                 |
    |                                                 |
    | 0..1                                            |
+--------+                                            |
| Leader |--------------------------------------------+
+--------+ 0..1

Я притворяюсь, что "лидер" - точное описание того, кто«особенный» в группе.Вы должны попытаться использовать более описательное имя, чем «selected».

Схема выглядит следующим образом:

CREATE TABLE Group
(
    Id int NOT NULL PRIMARY KEY,
    ...
)

CREATE TABLE Person
(
    Id int NOT NULL PRIMARY KEY,
    ...
)

CREATE TABLE Member
(
    PersonId int NOT NULL PRIMARY KEY
        CONSTRAINT FK_Member_Person FOREIGN KEY REFERENCES Person (Id)
            ON UPDATE CASCADE ON DELETE CASCADE,
    GroupId int NOT NULL
        CONSTRAINT FK_Member_Group FOREIGN KEY REFERENCES Group (Id)
            ON UPDATE CASCADE ON DELETE CASCADE
)
CREATE INDEX IX_Member_Group ON Member (GroupId)

CREATE TABLE Leader
(
    PersonId int NOT NULL PRIMARY KEY
        CONSTRAINT FK_Leader_Person FOREIGN KEY REFERENCES Person (Id)
            ON UPDATE CASCADE ON DELETE CASCADE,
    GroupId int NOT NULL
        CONSTRAINT FK_Leader_Group FOREIGN KEY REFERENCES Group (Id)
            ON UPDATE CASCADE ON DELETE CASCADE,
    CONSTRAINT U_Member_Group UNIQUE (GroupId)
)

Она выражает следующую информацию об отношениях:

  • Группа существует, период .Это может иметь или не иметь членов.Если у него нет членов, то по определению у него также нет лидера.Он все еще существует, потому что новые члены могут быть добавлены позже.

  • Человек существует, период .Человек не перестанет существовать просто потому, что его группа существует.

  • Человек может быть членом одной и только одной группы.

  • Человек также может быть лидером группы.Группа может иметь только одного лидера одновременно.Лидер группы может или не считаться участником .

Вы можете подумать, что ограничения, налагаемые этим реляционным дизайном,значительно слабее, чем те, о которых спрашивают в вашем вопросе.И ты был бы прав.Это потому, что ваш вопрос связывает модель данных с требованиями бизнеса / домена.

В дополнение к этой модели у вас также должно быть несколько бизнес-правил , которые применяются вашим приложение , например:

  • Если в группе нет участников, она удаляется / деактивируется / скрывается.

  • Если деактивированная / скрытая группа получает участников, она реактивируется / отображается.

  • Человек должен быть членом какой-либо группы.Эта информация должна быть предоставлена ​​при добавлении нового лица (это не обязательно должна быть существующая группа, это может быть новая группа).Если членская группа человека удалена, это должно вызвать процесс исключения;альтернативно, не разрешайте удалять группу, если в ней все еще есть участники.

  • Группа, в которой есть участники, должна иметь лидера.Если новый человек добавляется в пустую группу, он становится лидером.Если лидер (человек) удален, то новый лидер должен быть автоматически выбран на основе некоторых критериев или должен быть запущен процесс исключения.

Почему это "Правильный «дизайн»

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

Что еще более важно, потому что индексирование и ограничения намного чище:

  • Быстро опрашивать членов группы.
  • Быстро опрашивать членов группы.
  • Быстро опрашивать лидера группы.
  • Опрос лиц, которые также являются лидерами, выполняется быстро.
  • Удаление группы автоматически удаляет все членства / лидеров группы.
  • Удаление человека автоматически удаляет все членство / лидерство в группе.
  • Изменение членства - это все еще один оператор UPDATE.
  • Смена руководства - это все еще один оператор UPDATE.
  • SQL Server не будет жаловаться на множественные каскадные пути.
  • Каждая таблица имеет не более 2 индексов для столбцов, которые вы ожидаете индексировать.
  • Вы можете легко расширить этот дизайн, т.е.mmodate различные типы членства.
  • Изменения в членстве / руководстве никогда не будут мешать простым запросам (таким как поиск человека по имени).
  • Каждый ORM может справиться с этимбез проблем на всех.Обычно вы относитесь к нему как ко многим, но вы можете реализовать его как nullable-one-to-one.

Все других решений имеют серьезный, фатальный недостаток:

  • Помещение GroupId в Person и LeaderId в Group приводит к циклу, который не может быть разрешен, за исключением случаев, когда хотя бы один из столбцов обнуляется. Вы также не сможете CASCADE одно из отношений.

  • Помещение GroupId в Person и дополнительное IsLeader в Person не позволяет вам применять верхнюю границу (1 лидер на группу) без триггера. На самом деле, технически вы можете сделать это с отфильтрованным индексом (только для SQL '08), но он все еще ошибочен, поскольку бит IsLeader фактически не обозначает отношение, и если вы случайно обновите GroupId, но забудете о IsLeader тогда вы вдруг сделали этого человека лидером совершенно другой группы и, вероятно, нарушили ограничение не более одного.

  • Некоторые люди решат добавить GroupId к Person, но при этом сохранят таблицу ассоциации Leader. Концептуально это лучший дизайн, но, поскольку у вас, скорее всего, будет CASCADE от группы к человеку, вы не сможете также поставить двустороннюю CASCADE на лидера (вы получите "каскадные пути" ошибка при попытке).

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

0 голосов
/ 30 октября 2011

Самый простой способ сделать это следующим образом:

  • Объявите логическое свойство IsSelected для объекта GroupMember.
  • Добавить частичное объявление класса в класс GroupMember (все классы сущностей EF объявлены частичными, поэтому их легко расширить с помощью пользовательского кода).
  • Подпишитесь на событие BeforeValueChanging свойства IsSelected (я не могу вспомнить точное название события из головы, но вы можете быть уверены, что EF предоставляет нечто подобное.) 1012 *
  • В вашем обработчике событий вы можете реализовать желаемую логику. Нет необходимости напрямую заботиться о базе данных ...

НТН!

...