NHibernate на:сопоставление одного столбца из типа "многие к одному" с примитивным типом

StackOverflow https://stackoverflow.com/questions/1617701

Вопрос

У меня есть следующее отображение:

<set name="People" lazy="true" table="ProjectPeople">
  <key column="ProjectId" />
  <composite-element class="PersonRole">
    <many-to-one name="Person" column="PersonId" cascade="save-update" not-null="true" />
    <many-to-one name="Role" column="RoleId" cascade="save-update" not-null="true"  />
  </composite-element>
</set>

Теперь я действительно не хочу иметь отдельный класс для роли в домене, мне нужно только имя роли.Однако в БД Роли по-прежнему должны быть нормализованы для отдельной таблицы Role (Id, Name).

Как мне сопоставить это так, чтобы люди использовали следующий класс PersonRole?

public class PersonRole {
    public virtual Person Person { get; set; }
    public virtual string Role { get; set; }
}

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

Это было полезно?

Решение

Лично я бы создал класс Role, например, Yassir

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

Измените таблицу сопоставления Set, чтобы она указывала на новый вид Затем измените сопоставление ролей, чтобы оно было свойством, а не сопоставлением «многие к одному».

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

Изменить . Чтобы обновить роль, вы можете добавить <sql-insert>,<sql-update> and <sql-delete> в свой файл сопоставления, чтобы все каскад работало

Другие советы

На самом деле вы не получите ответа, на который надеетесь, просто потому, что это невозможно.(N) Hibernate - это платформа объектно-реляционного отображения и поддержки три вида отображения стратегии:

  • таблица для каждой иерархии классов
  • таблица для каждого подкласса
  • таблица для каждого класса бетона

Это также позволяет вам отклониться от этого, используя formula или sql-insert и т.д., но, как вы выяснили, в конечном итоге это только причиняет вам больше боли, не поощряется сообществом Hibernate и плохо сказывается на ремонтопригодности вашего кода.

Решение?

На самом деле, это очень просто.Вы не хотите использовать класс для Role.Я полагаю, вы имеете в виду, что не хотите разоблачать класс типа Role, который вы не хотите вводить prObject.Role.Name все время.Просто prObject.Role, который должен возвращать строку.У вас есть несколько вариантов:

  1. Используйте внутренний класс, скажем, в PersonRole, этот класс может быть внутренним или частным.Добавьте роль свойства, которая устанавливает и обновляет поле участника;
  2. Используйте внутренний класс.Добавьте роль свойства, которая устанавливает и обновляет поле участника;

Давайте рассмотрим вариант 2:

// mapped to table Role, will not be visible to users of your DAL
// class can't be private, it's on namespace level, it can when it's an inner class
internal class Role 
{
    // typical mapping, need not be internal/protected when class is internal
    // cannot be private, because then virtual is not possible
    internal virtual int Id { get; private set; }
    internal virtual string Name { get; set; }
}

// the composite element
public class PersonRole
{
    // mapped properties public
    public virtual Person Person { get; set; }

    // mapped properties hidden
    internal virtual Role dbRole { get; set; }

    // not mapped, but convenience property in your DAL
    // for clarity, it is actually better to rename to something like RoleName
    public string Role     /* need not be virtual, but can be */
    { 
        get
        {
            return this.dbRole.Name;
        }
        set
        {
            this.dbRole.Name = value;    /* this works and triggers the cascade */
        }
    }
}

И отображение может выглядеть так, как ожидалось.Результат:вы не нарушили правило "одна таблица для каждого класса" (Редактировать:аскер говорит, что он явно хочет нарушить это правило, и Hib поддерживает его, что правильно), но вы скрыли объекты от модификации и доступа, используя типичные объектно-ориентированные методы.Все функции NH (каскад и т.д.) По-прежнему работают так, как ожидалось.

(N) Hibernate - это все, что касается такого типа решений:как создать хорошо продуманный и безопасный уровень абстракции в вашей базе данных, не жертвуя ясностью, краткостью или ремонтопригодностью и не нарушая правил OO или ORM.


Обновление (после q.был закрыт)

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

  • Создавайте свои сопоставления обычным образом (т. Е. по одному классу на таблицу, я знаю, что вам это не нравится, но это к лучшему) и используйте методы расширения:

     // trivial general example
     public static string GetFullName(this Person p)
     {
         return String.Format("{0} {1}", p.FirstName, p.LastName);
     }
    
     // gettor / settor for role.name
     public static string GetRoleName(this PersonRole pr)
     {
         return pr.Role == null ? "" : pr.Role.Name;
     }
     public static SetRoleName(this PersonRole pr, string name)
     {
         pr.Role = (pr.Role ?? new Role());
         pr.Role.Name = name;
     }
    
  • Создавайте свои сопоставления обычным образом, но используйте partial classes, которые позволяют вам "украсить" свой класс любым удобным вам способом.Преимущество:если вы используете сгенерированное отображение ваших таблиц, вы можете создавать их повторно так часто, как пожелаете.Конечно, частичные классы должны храниться в отдельных файлах, поэтому, учитывая ваше желание уменьшить "раздувание", это, вероятно, не очень хороший сценарий в настоящее время.

     public partial class PersonRole
     {
         public string Role {...}
     }
    
  • Возможно, самый простой:просто перегрузка ToString() для Role, что делает его пригодным для использования в String.Format и друзья, но, конечно, это не делает его возможным для назначения.По умолчанию каждый класс сущностей или POCO должен иметь ToString() в любом случае, перегрузка.

Хотя это можно сделать напрямую с помощью NHibernate, q.был закрыт до того, как я успел на него взглянуть (никто не виноват, у меня просто не было времени).Я обновлю, если найду время сделать это с помощью Hibernate HBM mapping, даже если я не согласен с таким подходом.нехорошо ломать голову над передовыми концепциями Hib, когда конечный результат менее понятен для других программистов и менее понятен в целом (куда делась эта таблица?почему для этой таблицы нет абстракции IDao?Смотрите также Лучшие практики NHibernate и S#arp).Тем не менее, это упражнение, тем не менее, интересно.

Принимая во внимание комментарии к "лучшим практикам":в типичных ситуациях это должен быть не только "один класс на таблицу", но также один IDaoXXX, один DaoConcreteXXX и один GetDaoXXX для каждой таблицы, где вы используете иерархию классов / интерфейсов, чтобы различать таблицы только для чтения и записи.Это минимум четыре класса / строки кода на таблицу.Обычно это генерируется автоматически, но предоставляет очень четкий уровень доступа (dao) к вашему уровню данных (dal).Уровень данных лучше всего содержать в максимально спартанском порядке.Ничто из этих "рекомендаций" не мешает вам использовать методы расширения или частичные методы для перемещения Role.Name в Role.

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

Я не думаю, что можно сопоставить многие-к-одному примитивному типу, если бы вы были на месте, я бы добавил класс Role в модель

Это самое большое отключение всей путинской вещи. Конечно, цель состоит в том, чтобы иметь работающее приложение. Не чья-то версия идеальной иерархии классов. Так что, если вам нужно кодировать & Quot; prObject.Role.Name & Quot; вместо " prObject.Role " ;. Как это поможет вам создать более надежную программу?

С точки зрения разработчика приложений, то, что вы хотите, просто неправильно. Человек может иметь несколько ролей, роль обычно может быть назначена нескольким людям. Зачем идти на все эти неприятности, чтобы навязывать нереалистичную иерархию классов на одного человека, если в модели данных с задержкой используется много ролей на человека?

Если у вас действительно есть & квота, только одна роль на человека &; Правило, то это должно быть отражено в базовой модели данных.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top