2010-02-18 6 views
6

J'essaie de créer un mappage de table par hiérarchie à l'aide de NHibernate 2.0.1. J'ai une classe de base avec des propriétés qui existent pour chaque sous-classe dont les autres classes héritent. Tous ces objets sont conservés dans une table appelée Messages qui contient tous les champs possibles pour chaque classe. Il y a un SourceID qui est le discriminateur et devrait indiquer quel Poco retourner pour chaque sous-classe. Voici ma cartographie actuelle.Mappage correct d'une relation polymorphe avec 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> 

je reçois une erreur disant interrogation N'a pas pu formater valeur discriminante à chaîne SQL d'entité NS.Core.Model.BaseMessage donc je mets une valeur discriminante sur cette classe trop althout il ne devrait jamais retourner la classe de base. Cela m'a conduit à quelques erreurs antlr. Est-ce que je prends la mauvaise approche à ce problème? Je voudrais interroger la table et récupérer une liste des différents POCO qui héritent tous de la classe de base. Il ne retournerait jamais la classe de base elle-même.

ci-dessous est le 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

pouvez-vous envoyer le code pour BaseMessage? – hackerhasid

+0

mis à jour.Espérons que cela aide – CountCet

Répondre

9

L'approche est saine. J'ai fait exactement cela plusieurs fois.

Vous pourriez indiquer explicitement dans votre code que le constructeur par défaut de BaseMessage soit protégé.

Vous devez également déclarer une valeur discriminante sur la classe de base. Je privilégie les valeurs de chaîne pour les discriminateurs, car elles sont plus claires lors de l'exécution de requêtes ou de rapports SQL. De même, puisque les instances de BaseMessage n'existent pas, in utilisera null pour sa valeur de discriminateur.

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

En outre, je vois que vous avez mappé une propriété à la colonne de discriminateur. Vous devriez plutôt avoir une méthode qui retourne quelque chose d'unique à la classe - dans ce cas le code.

Notez que vous ne pouvez pas modifier la classe d'une entité mappée après qu'elle a été enregistrée. Pas même en changeant le discriminateur. Si vous l'avez modifié via SQL, votre cache de 2ème niveau contiendra toujours une version avec la classe d'origine. Enfin, votre fichier de mappage est trop détaillé car il contient des valeurs qui sont les valeurs par défaut. peuvent être enlevés Les attributs et les éléments suivants:

  • accès = « propriété » - c'est la valeur par défaut
  • type = « String » - tous les types que vous utilisez peut être déduit de votre classe .NET
  • colonne = « COL » - par défaut est le même que le nom
  • de même pour l'élément de colonne id

Toutes vos sous-classes de message ont la propriété du corps, donc le déplacer à la mise en correspondance de classe de base. Si ce champ peut être plus long que votre base de données varchar, il devrait être une colonne de texte et avoir le type = « StringCLob » qui associe à chaîne dans .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

Que faire si la base de données ne contient pas un SourceID pour le BaseMessage, qui n'est jamais stocké dans la base de données? – CountCet

+0

En tant que classe mappée, elle nécessite une valeur discriminante. Qu'une telle valeur n'existera jamais est une logique de validation au niveau de l'application. –

+0

C'était un exemple succinct et le corps n'est pas dans tous les cas mais votre réponse a fonctionné. Merci! – CountCet