2010-02-18 10 views
6

Sto tentando di creare un mapping tabella per gerarchia utilizzando NHibernate 2.0.1. Ho una classe base con proprietà che esistono per ogni sottoclasse ereditata da altre classi. Tutti questi oggetti vengono mantenuti in una tabella denominata Messaggi che contiene tutti i campi possibili per ogni classe. C'è un SourceID che è il discriminatore e dovrebbe indicare quale Poco ritornare per ogni sottoclasse. Ecco la mia attuale mappatura.Mappatura corretta di una relazione polimorfica con NHibernate

<?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> 

ottengo un'interrogazione errore che dice Impossibile formattare il valore discriminante di stringa SQL di entità NS.Core.Model.BaseMessage così ho messo un valore discriminante in questa classe anche althout non dovrebbe mai restituire la classe di base. Questo mi ha portato ad alcuni errori antlr.

Sto prendendo l'approccio sbagliato a questo problema? Vorrei interrogare la tabella e recuperare un elenco di POCO diversi che ereditano tutti dalla classe base. Non restituirebbe mai la classe base stessa.

sotto è i 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 
    } 
} 
+0

puoi pubblicare il codice per BaseMessage? – hackerhasid

+0

aggiornato.Spero che aiuti – CountCet

risposta

9

L'approccio è suono. Ho fatto esattamente questo diverse volte.

È possibile rendere esplicito nel proprio codice il fatto che il costruttore predefinito di BaseMessage sia protetto.

È necessario dichiarare un valore discriminatore anche sulla classe base.

Preferisco i valori stringa per i discriminatori, in quanto più chiari quando si eseguono query o rapporti SQL. Inoltre, poiché non esistono istanze di BaseMessage shoudl, in usa null per il suo valore di discriminatore.

<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> 

Inoltre, vedo che hai mappato una proprietà alla colonna discriminatore. Dovresti invece avere un metodo che restituisca qualcosa di unico per la classe, in questo caso il codice.

Si noti che non è possibile modificare la classe di un'entità mappata dopo che è stata salvata. Neanche cambiando il discriminatore. Se lo hai modificato tramite SQL, la cache di secondo livello conserverà ancora una versione con la classe originale.

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

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

Infine, il file di mapping è eccessivamente verbose quanto comprende valori che sono il default. I seguenti attributi e gli elementi possono essere rimossi:

  • accesso = "proprietà" - questo è il default
  • type = "String" - tutti i tipi che si usa può essere dedotta dalla classe .NET
  • column = "COL" - di default è lo stesso del nome
  • allo stesso modo per l'elemento colonna id

Tutte le vostre sottoclassi Messaggio avere proprietà Body, quindi spostarlo alla mappatura classe di base. Se questo campo può essere più lungo di quanto il tuo varchar base di dati, dovrebbe essere una colonna di testo e hanno type = "StringCLob" che associa a stringa nel .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> 
+0

Cosa succede se il database non contiene un SourceID per BaseMessage, che non viene mai memorizzato nel database? – CountCet

+0

Come classe mappata richiede un valore discriminatore. Che tale valore non esisterà mai è la logica di convalida del livello di applicazione. –

+0

Questo era un esempio succinto e il corpo non è in ogni esempio, ma la tua risposta ha funzionato. Grazie! – CountCet

Problemi correlati