Правильное отображение полиморфных отношений с NHibernate - PullRequest
6 голосов
/ 18 февраля 2010

Я пытаюсь создать отображение таблицы на иерархию с помощью NHibernate 2.0.1. У меня есть базовый класс со свойствами, которые существуют для каждого подкласса, от которого наследуются другие классы. Все эти объекты сохраняются в одной таблице «Сообщения», которая содержит все возможные поля для каждого класса. Существует SourceID, который является дискриминатором и должен указывать, какой Poco возвращать для каждого подкласса. Вот мое текущее отображение.

<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
                   assembly="NS.Core"
                   namespace="NS.Core.Model">
  <class name="BaseMessage" table="Messages">
    <id name="MessageID" type="Int64">
      <column name="MessageID" />
      <generator class="native" />
    </id>
    <discriminator column="SourceID" type="Int32"/>
    <property name="DateCreated" access="property" column="DateCreated" type="DateTime" not-null="true"/>
    <property name="DatePublished" access="property" column="DatePublished" type="DateTime"/>
    <property name="SourceID" access="property" column="SourceID" type="Int32"/>
    <many-to-one name="User" column="UserID" access="property" cascade="none" lazy="false" fetch="join" outer-join="true" />
    <subclass name="NMessage" discriminator-value="0">
      <property name="Body" access="property" column="Body" type="String"/>
    </subclass>
    <subclass name="BMessage" discriminator-value="1">
      <property name="Title"  access="property" column="Title" type="String"/>
      <property name="Body" access="property" column="Body" type="String"/>
    </subclass>
    <subclass name="CMessage" discriminator-value="2">
      <property name="Url"  access="property" column="Url" type="String"/>
      <property name="Body" access="property" column="Body" type="String"/>
    </subclass>
  </class>
</hibernate-mapping>

Я получаю сообщение об ошибке, в котором говорится, что не удалось отформатировать значение дискриминатора в строке SQL объекта NS.Core.Model.BaseMessage, поэтому я поместил значение дискриминатора в этот класс, хотя он никогда не должен возвращать базовый класс. Это привело меня к некоторым ошибкам.

Я неправильно отношусь к этой проблеме? Я хотел бы запросить таблицу и получить список различных POCO, которые все унаследованы от базового класса. Он никогда не вернет сам базовый класс.

ниже - BaseMessage.cs

using System;
using System.Collections.Generic;
using System.Runtime.Serialization;
using System.Text;

namespace NS.Core.Model
{

    [Serializable]
    [DataContract]
    [KnownType(typeof(User))]
    public class BaseMessage
    {
        #region Private variables
        private long _messageID;
        private DateTime? _dateCreated;
        private DateTime? _datePublished;
        private int _sourceID;
        private User _user = new User();

        #endregion

        #region Properties
        [DataMember]
        public virtual long MessageID
        {
            get { return _messageID; }
            set { this._messageID = value; }
        }
          [DataMember]
        public virtual DateTime? DateCreated
        {
            get { return _dateCreated; }
            set { this._dateCreated = value; }
        }
        [DataMember]
        public virtual DateTime? DatePublished
        {
            get { return _datePublished; }
            set { this._datePublished = value; }
        }
        [DataMember]
        public virtual int SourceID
        {
            get { return _sourceID; }
            set { this._sourceID = value; }
        }
        [DataMember]
        public virtual User User
        {
            get
            {
                if (this._user != null)
                { return this._user; }
                else { return new User(); }
            }
            set { this._user = value; }
        }
        #endregion
    }
}

1 Ответ

9 голосов
/ 19 февраля 2010

Подход - это звук. Я делал именно это несколько раз.

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

Вам также необходимо объявить значение дискриминатора для базового класса.

Я предпочитаю строковые значения для дискриминаторов, поскольку они более понятны при выполнении запросов или отчетов SQL. Кроме того, поскольку экземпляры BaseMessage не должны существовать, в in будет использоваться значение null для значения дискриминатора.

<class name="BaseMessage" table="Messages" discriminator-value="null">
    <id />
    <discriminator column="SourceID" />
    <subclass name="NMessage" discriminator-value="NMessage">
    </subclass>
    <subclass name="BMessage" discriminator-value="BMessage">
    </subclass>
    <subclass name="CMessage" discriminator-value="CMessage">
    </subclass>
</class>

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

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

class BaseMessage
{
    public virtual string MessageType { return null; }
}

class NMessage : BaseMessage
{
    public override string MessageType { return "NMessage"; }
}

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

  • access = "property" - это значение по умолчанию
  • type = "String" - все типы, которые вы используете, могут быть выведены из вашего .NET класса
  • column = "COL" - по умолчанию совпадает с именем
  • аналогично для элемента столбца id

Все ваши подклассы Message имеют свойство Body, поэтому переместите его в отображение базового класса. Если это поле может быть длиннее, чем ваша база данных varchar, оно должно быть текстовым столбцом и иметь type = "StringCLob", который отображается на строку в .NET

<class name="BaseMessage" table="Messages" discriminator-value="null">
    <id name="MessageID">
        <generator class="native" />
    </id>
    <discriminator column="SourceID"/>
    <property name="DateCreated" />
    <property name="DatePublished" />
    <many-to-one name="User" column="UserID" cascade="none" lazy="false" fetch="join" outer-join="true" />
    <property name="Body" type="StringCLob" />
    <subclass name="NMessage" discriminator-value="NMessage">
    </subclass>
    <subclass name="BMessage" discriminator-value="BMessage">
        <property name="Title" />
    </subclass>
    <subclass name="CMessage" discriminator-value="CMessage">
        <property name="Url" />
    </subclass>
</class>
...