У меня есть сценарий в системе, который я попытался максимально упростить. У нас есть таблица (назовем их) артефактов, артефакты могут быть доступны любому количеству ролей безопасности, а роли безопасности могут получить доступ к любому количеству артефактов. Таким образом, у нас есть 3 таблицы в базе данных: одна описывает артефакты, другая описывает роли и таблица ассоциаций «многие ко многим», связывающая идентификатор артефакта с идентификатором роли.
Что касается домена, у нас есть два класса - один для роли и один для артефакта. класс артефакта имеет свойство IList, которое возвращает список ролей, которые могут получить к нему доступ. (Однако роли не предлагают свойства для получения артефактов, к которым можно получить доступ).
Таким образом, отображение nhibernate для артефакта содержит следующее;
<bag name = "AccessRoles" table = "ArtefactAccess" order-by = "RoleID"
lazy = "true" access = "field.camelcase-underscore" optimistic-lock = "false">
<key column = "ArtefactID"/>
<many-to-many class = "Role" column = "RoleID"/>
</bag>
Все это работает нормально, и если я удалю артефакт, таблица ассоциаций будет очищена соответствующим образом, и все ссылки между удаленным артефактом и ролями будут удалены (хотя роль не удаляется правильно - поскольку мы не хотим удалять сирот).
Проблема в том, как удалить роль и сделать так, чтобы она автоматически очищала таблицу ассоциаций. Если я сейчас попытаюсь удалить роль, я получаю ссылочное ограничение, так как в таблице ассоциаций все еще есть записи для этой роли. Единственный способ успешно удалить роль - это запросить все артефакты, которые связаны с этой ролью, удалить роль из коллекции ролей артефакта, обновить артефакты, а затем удалить роль - не очень эффективно или приятно, особенно когда в не- упрощенная система, роли могут быть связаны с любым количеством других таблиц / объектов.
Мне нужно иметь возможность намекнуть NHibernate, что я хочу, чтобы эта ассоциативная таблица очищалась всякий раз, когда я удаляю роль - возможно ли это, и если да, то как мне это сделать?
Спасибо за любую помощь.





Вы можете создать сопоставление для таблицы ассоциаций, а затем вызвать delete для этой таблицы, где Role_id - это значение, которое вы собираетесь удалить, а затем выполнить удаление самой роли. Это должно быть довольно просто.
Хотя я считаю, что NHibernate должен предоставлять способ сделать это, не имея коллекции в классе C# ролей, вы всегда можете установить это поведение в SQL. Выберите каскадное удаление для FK в базе данных, и оно должно быть автоматическим, просто следите за кешем NHib.
Но я настоятельно рекомендую вам использовать это как последний ресурс.
Вам необходимо создать сопоставление роли с Artifact.
Вы можете сделать его ленивой загрузкой и сопоставить его с защищенным виртуальным членом, чтобы к нему никогда не было доступа, но вам нужно это сопоставление там, чтобы NHibernate знал, что он должен удалить роли из таблицы ArtefactAccess.
Дэвид, добавив сопоставление роли -> артефакта, не удалит данные ассоциации автоматически, поскольку данные, обратные этому сопоставлению, принадлежат, то есть: удаление роли из артефакта или удаление артефакта очистит таблицу ассоциаций, а не другую наоборот. Итак, добавив сопоставление, мне пришлось бы снова найти все артефакты, у которых есть роль, удалить роль и повторно сохранить артефакты (очистить таблицу ассоциаций), а затем удалить роль - это просто не кажется мне эффективным.
Поскольку я искал этот ответ и нашел эту ветку в Google (без ответа), я решил, что опубликую свое решение для этого. С тремя таблицами: Role, RolesToAccess (ManyToMany), Access.
Создайте следующие сопоставления: Доступ:
<bag name = "Roles" table = "RolesToAccess" cascade = "none" lazy = "false">
<key column = "AccessId" />
<many-to-many column = "AccessId" class = "Domain.Compound,Domain" />
</bag>
<bag name = "RolesToAccess" cascade = "save-update" inverse = "true" lazy = "false">
<key column = "AccessId" on-delete = "cascade" />
<one-to-many class = "Domain.RolesToAccess,Domain" />
</bag>
Роли:
<bag name = "Accesses" table = "RolesToAccess" cascade = "none" lazy = "false">
<key column = "RoleId" />
<many-to-many column = "RoleId" class = "Domain.Compound,Domain" />
</bag>
<bag name = "RolesToAccess" cascade = "save-update" inverse = "true" lazy = "false">
<key column = "RoleId" on-delete = "cascade" />
<one-to-many class = "Domain.RolesToAccess,Domain" />
</bag>
Как упоминалось выше, вы можете защитить свойства RolesToAccess, чтобы они не загрязняли вашу модель.
Здравствуйте, в вашем решении нужно создать три класса Role, RolesToAccess и Access? Вы знаете решение только с двумя классами Role и Access?
Что вы здесь говорите:
The only way to successfully delete a role is to query for all artefacts that link to that role, remove the role from the artefact's role collection, update the artefacts and then delete the role - not very efficient or nice, especially when in the un-simplified system, roles can be associated with any number of other tables/objects.
Не обязательно. Предположим, вы не хотите отображать ассоциативную таблицу (сделать ее объектом домена), вы все равно можете выполнять удаление на обоих концах с минимальным кодом.
Допустим, есть 3 таблицы: Role, Artifact и ArtifactAccess (таблица ссылок). В вашем отображении у вас есть только объекты домена для роли и артефакта. У обоих есть сумка для ассоциации "многие-многие".
Роль:
<bag name = "Artifacts" table = "[ArtifactAccess]" schema = "[Dbo]" lazy = "true"
inverse = "false" cascade = "none" generic = "true">
<key column = "[ArtifactID]"/>
<many-to-many column = "[RoleID]" class = "Role" />
</bag>
Артефакт:
<bag name = "Roles" table = "[ArtifactAccess]" schema = "[Dbo]" lazy = "true"
inverse = "false" cascade = "none" generic = "true">
<key column = "[RoleID]"/>
<many-to-many column = "[ArtifactID]" class = "Role" />
</bag>
Как видите, на обоих концах указано inverse = false. Документация NHibernate рекомендует вам выбрать один конец вашей ассоциации в качестве «обратного» конца, но ничто не мешает вам использовать оба в качестве «контрольного конца». При выполнении обновлений или вставок это работает в обоих направлениях без проблем. При выполнении удаления одного из концов вы получаете ошибку нарушения FK, потому что таблица ассоциаций не обновляется, истина. Но вы можете решить эту проблему, просто очистив коллекцию на другом конце перед выполнением удаления, что намного менее сложно, чем то, что вы делаете, которое просматривается на «другом» конце ассоциации, если есть использование 'this ' конец. Если это немного сбивает с толку, вот пример кода. Если у вас есть только один конец в управлении, для вашего сложного удаления вам необходимо сделать:
foreach(var artifact in role.Artifacts)
foreach(var role in artifact.Roles)
if (role == roleToDelete)
artifact.Roles.Remove(role)
artifact.Save();
roleToDelete.Delete();
Что я делаю при удалении роли, это что-то вроде
roleToDelete.Artifacts.Clear(); //removes the association record
roleToDelete.Delete(); // removes the artifact record
Это одна лишняя строка кода, но в этом случае вам не нужно принимать решение о том, какой конец ассоциации является обратным концом. Вам также не нужно отображать ассоциативную таблицу для полного контроля.
При использовании обоих в качестве управляющего конца я получаю, что NHibernate пытается дважды вставить отношение, по одному для каждого конца. Я решил эту проблему, изменив контрольный конец на другую сторону.
Я бы не хотел, чтобы таблица ассоциаций была представлена в модели домена, и это был бы единственный способ вручную удалить таблицу ассоциаций в коде с помощью NHibernate.