2010-02-18 8 views
6

Ich versuche, eine Tabelle-pro-Hierarchie-Zuordnung mit NHibernate 2.0.1 zu erstellen. Ich habe eine Basisklasse mit Eigenschaften, die für jede Unterklasse existieren, von der andere Klassen erben. Alle diese Objekte werden in einer Tabelle mit dem Namen Nachrichten beibehalten, die alle möglichen Felder für jede Klasse enthalten. Es gibt eine SourceID, die der Diskriminator ist und angeben sollte, welches Poco für jede Unterklasse zurückgegeben werden soll. Hier ist meine aktuelle Zuordnung.Richtiges Zuordnen einer polymorphen Beziehung mit 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> 

Ich erhalte eine Fehlermeldung, dass die Abfrage kann nicht Diskriminatorwert zu SQL-Zeichenfolge der Einheit NS.Core.Model.BaseMessage formatiert, damit ich auch auf dieser Klasse einen Diskriminatorwert setzen althout es sollte nie die Basisklasse zurück. Das hat mich zu einigen Fehlern geführt.

Werde ich dieses Problem falsch angehen? Ich möchte die Tabelle abfragen und eine Liste von verschiedenen POCOs zurückbekommen, die alle von der Basisklasse erben. Es würde nie die Basisklasse selbst zurückgeben.

unten ist die 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

können Sie den Code für BaseMessage posten? – hackerhasid

+0

aktualisiert.Hoffe es hilft – CountCet

Antwort

9

Der Ansatz ist Klang. Ich habe genau das mehrmals gemacht.

Sie könnten es in Ihrem Code explizit machen, dass der Standardkonstruktor von BaseMessage geschützt ist.

Sie müssen auch einen Diskriminator-Wert für die Basisklasse deklarieren.

Ich bevorzuge String-Werte für Diskriminatoren, wie dies klarer bei der Durchführung von SQL-Abfragen oder Berichten. Da Instanzen von BaseMessage nicht existieren sollten, würde In auch Null für seinen Diskriminatorwert verwenden.

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

Auch ich sehe, dass Sie eine Eigenschaft der Diskriminator-Spalte zugeordnet haben. Sie sollten stattdessen eine Methode verwenden, die der Klasse einen eindeutigen Wert zurückgibt - in diesem Fall den Code.

Beachten Sie, dass Sie die Klasse einer abgebildeten Entität nicht ändern können, nachdem sie gespeichert wurde. Nicht einmal durch den Diskriminator. Wenn Sie es über SQL geändert haben, enthält Ihr Cache der zweiten Ebene immer noch eine Version mit der ursprünglichen Klasse.

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

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

Schließlich ist Ihre Zuordnungsdatei übermäßig ausführlich, da sie Werte enthält, die die Standardeinstellung sind. Die folgenden Attribute und Elemente können entfernt werden:

  • access = „Eigentum“ - dies ist die Standardeinstellung
  • type = „String“ - alle Typen, die Sie von Ihrer .NET-Klasse verwenden kann, abgeleitet werden
  • column = „COL“ - Standard ist der gleiche wie der Name
  • in ähnlicher Weise für die ID-Spalte Element

Alle Ihre Nachricht Subklassen haben Eigenschaft Körper, so dass es auf die Basisklasse Mapping bewegen. Wenn dieses Feld Ihrer Datenbank varchar länger als sein kann, sollte es eine Textspalte und type = „StringCLob“ haben, die in .NET-String Karten

<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

Was ist, wenn die Datenbank keine SourceID für die BaseMessage enthält, die nie in der Datenbank gespeichert wird? – CountCet

+0

Als abgebildete Klasse wird ein Diskriminatorwert benötigt. Dass ein solcher Wert niemals existieren wird, ist die Gültigkeitslogik der Anwendungsebene. –

+0

Dies war ein kurzes Beispiel und der Körper ist nicht in jedem Beispiel, aber Ihre Antwort hat funktioniert. Vielen Dank! – CountCet

Verwandte Themen