Как отобразить этот класс в NHibernate (не FluentNHibernate)? - PullRequest
3 голосов
/ 04 марта 2010

Предположим, у меня есть такая база данных:

alt text

Это настроено для предоставления прав доступа к меню по ролям.

Обратите внимание, что таблица User не имеет прямой связи с таблицей Permission.

Тогда как мне сопоставить этот класс с таблицами базы данных?

class User
    {
        public int ID { get; set; }
        public string Name { get; set; }
        public string Username { get; set; }
        public string Password { get; set; }
        public bool? IsActive { get; set; }

        public IList<Role> RoleItems { get; set; }
        public IList<Permission> PermissionItems { get; set; }
        public IList<string> MenuItemKeys { get; set; }
    }

Это значит,

(1) У каждого пользователя есть Role с.

(2) У каждого пользователя есть Permission с (в зависимости от Role с).

(3) Каждый пользователь имеет несколько разрешенных MenuItemKey с (согласно Permission с).

Как должен выглядеть мой User.hbm.xml?

Ответы [ 3 ]

3 голосов
/ 08 марта 2010

Роли и разрешения, вероятно, будут доступны в приложении.Скорее всего, они находятся в кэше второго уровня, а это означает, что мы можем ожидать эффективной итерации User.RoleItems и Role.Permissions.

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

Вы можете отобразить классы следующим образом.

Свойства User.PermissionItems и User.MenuItemKeys являются производными от постоянных сущностей и, следовательно, не отображаются в сопоставлениях.

<class name="User" table="user">
  <id name="ID">
    <generator class="native"/>
  </id>
  <property name="Name"/> 
  <property name="Username"/>
  <property name="Password"/>
  <property name="IsActive"/>

  <bag name="RoleItems" table="userrole" lazy="true">
    <key column="userid" />
    <many-to-many class="Role" column="roleid"/>
  </bag>
</class>

<class name="Role" table="role">
  <id name="ID">
    <generator class="native"/>
  </id>
  <property name="RoleName"/>
  <property name="IsActive"/>

  <bag name="Permissions" table="permission">
    <key column="roleid" />
    <one-to-many class="Permission"/>
  </bag>
</class>

<class name="Permission" table="permission">
  <id name="ID">
    <generator class="native"/>
  </id>
  <property name="MenuItemKey"/>
</class>

Я бы включил 2 дополнительных списка, которые вы имели в User, в производные перечисления.Если они были списками, то в них нет однозначного способа вставить их, поскольку вы не можете знать, к какой роли относится значение.Кроме того, роль не принадлежит пользователю.

Обновление: теперь используется улучшенная версия этих свойств Диего.

class User
{
    public virtual IEnumerable<Permission> PermissionItems
    {
        get {
            return RoleItems.SelectMany(role => role.PermissionItems);
        }
    }

    public virtual IEnumerable<string> MenuItemKeys
    {
        get {
            return RoleItems.SelectMany(role => role.PermissionItems,
                        (role, permission) => permission.MenuItemKey);
        }
    }
}
1 голос
/ 10 марта 2010

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

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

public IEnumerable<Permission> PermissionItems
{
    get
    {
        return RoleItems.SelectMany(role => role.PermissionItems);
    }
}

public IEnumerable<string> MenuItemKeys
{
    get
    {
        return RoleItems.SelectMany(role => role.PermissionItems,
                                   (role, permission) => permission.MenuItemKey);
    }
}
1 голос
/ 04 марта 2010

Вот ссылка: Глава 6. Отображение коллекции Вот еще одна полезная ссылка: Глава 7. Сопоставления ассоциаций

EDIT

Пройдя весь вечер, я пришел к следующему выводу:

  1. Учитывая Наилучшие практики NHibernate , то, что вы хотите сделать, не годится;

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

  1. Как философия программирования, я предпочитаю быть проще, чем писать умный код, в котором даже я больше не пойму, что я написал через некоторое время;
  2. Кроме того, я даже подумал об использовании элемента подзапроса сопоставления связей, который сработал бы, если бы я нашел способ его параметризации, если это выполнимо, но, похоже, он не позволит мне параметризовать запрос с пользователем значение свойства экземпляра экземпляра;
  3. В оптике хорошо спроектированной ОО-модели ребенок, осведомленный о свойствах своего родителя, в порядке, но родитель, получающий доступ к свойству ребенка, не имеет смысла - запаха дизайна;
  4. Как я понимаю, учитывая, что контекст раскрывает преимущества наличия прав доступа или значений MenuItemKey, доступных непосредственно от пользователя, я предлагаю следующее решение:

    • Создайте себе определенный пользователем вид данных, который будет содержать значения, относящиеся к атрибуту Permission MenuItemKey, полученному через роли, участником которых является пользователь, например:

CREATE VIEW udvUsersPermissions AS
ВЫБЕРИТЕ UR.UserID, P.ID как N'ID ', P.MenuItemKey
ОТ пользователей U
ВНУТРЕННЕЕ СОЕДИНЕНИЕ UsersRoles UR ON UR.UserID = U.ID
ВНУТРЕННИЕ СОЕДИНЕНИЯ Роли R ON R.ID = UR.RoleID
ВНУТРЕННЕЕ СОЕДИНЕНИЕ Разрешения P ON P.RoleID = R.ID
GO

Затем сопоставьте его в вашем файле User.hbm.xml:

<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2">
  <class name="User" table="Users">
    <id name="Id" column="ID">
      <generator class="identity"/>
    </id>
    <property name="Name" length="100"/>
    <property name="UserName" length="10" not-null="true"/>
    <property name="Password" length="10" not-null="true"/>
    <property name="IsActive" not-null="true"/>

    <list name="Roles" table="UsersRoles" access="private-property" lazy="true">
      <key column="UserID" foreign-key="FK_UR_U"/>
      <list-index column="UserID"/>
      <many-to-many class="Role" column="RoleID" />
    </list>

    <!-- Here mapping Permissions granted to User. -->
    <list name="Permissions" table="udvUsersPermissions" lazy="true">
      <key column="UserID"/>
      <list-index column="MenuItemKey"/>
      <many-to-many column="ID" class="Permission"/>
    </list>

  </class>
</hibernate-mapping>

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

<list name="Permissions" lazy="true">
  <subselect> <!-- see section 7.6, Chapter 7 - Association mappings -->
    select U.ID, P.ID, P.MenuItemKey
    from Users U
    inner join UsersRoles UR ON UR.UserID = U.ID
    inner join Roles R ON R.ID = UR.RoleID
    inner join Permissions P ON P.RoleID = R.ID
    group by U.ID, P.ID, P.MenuItemKey
    order by P.MenuItemKey
  </subselect>
  <key column="U.ID"/>
  <list-index column="P.MenuItemKey"/>
  <many-to-many class="Permission" column="P.ID"/>
</list>

Теперь, я надеюсь, что я привел достаточно подробностей, чтобы они помогли вам достичь того, что вы хотите сделать, или либо идти в нужное русло. =) * +1058 *

...