Problema con NHibernate e Table for Hierarchy

Tempo fa, insieme a Marco, mi sono imbattuto in un problema relativo al fetching di collection polimorfiche tramite NHibernate.

In pratica, quando si hanno collection il cui tipo contenuto eredita da un’altra entity persistita tramite Table for Hierarchy (maggiori info qui) , si possono riscontrare dei problemi in fase di fetching quando la collection è contenuta in una entity parent.
Per capire meglio il significato di quanto appena detto si osservi il diagramma di classe seguente:

Diagramma

Per la situazione sopra descritta si avrà un mapping come il seguente:

<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping
    schema="dbo"
    xmlns="urn:nhibernate-mapping-2.2"
    default-access="property"
    namespace="ConsoleApplication1.ObjectModel"
    assembly="ConsoleApplication1">

    <class name="Post" table="Posts" lazy="true" dynamic-update="true">
        <id name="ID" column="ID" unsaved-value="0">
            <generator class="identity"/>
        </id>
        <property name="Title">
            <column name="Title" sql-type="nvarchar(100)" not-null="true" />
        </property>
    <bag name="PostTags" cascade="all-delete-orphan" generic="true">
            <key column="PostID" />
            <one-to-many class="PostTag" />
        </bag>
    <bag name="ImageTags" cascade="all-delete-orphan" generic="true">
      <key column="PostID" />
      <one-to-many class="ImageTag" />
    </bag>
    </class>
    
</hibernate-mapping>

 

<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping
    schema="dbo"
    xmlns="urn:nhibernate-mapping-2.2"
    default-access="property"
    namespace="ConsoleApplication1.ObjectModel"
    assembly="ConsoleApplication1">

    <class name="TagBase" table="Tags" lazy="true" dynamic-update="true">
        <id name="ID" column="ID" unsaved-value="0">
            <generator class="identity"/>
        </id>
    <discriminator force="true">
      <column name="Discriminator" not-null="true"  sql-type="int" />
    </discriminator>
        <property name="Name">
            <column name="Name" sql-type="nvarchar(100)" not-null="true" />
        </property>
    </class>
    
</hibernate-mapping>

 

<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping
    schema="dbo"
    xmlns="urn:nhibernate-mapping-2.2"
    default-access="property"
    namespace="ConsoleApplication1.ObjectModel"
    assembly="ConsoleApplication1">

    <subclass name="ImageTag" extends="TagBase" discriminator-value="1" >
    </subclass>
    
</hibernate-mapping>

 

<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping
    schema="dbo"
    xmlns="urn:nhibernate-mapping-2.2"
    default-access="property"
    namespace="ConsoleApplication1.ObjectModel"
    assembly="ConsoleApplication1">

    <subclass name="PostTag" extends="TagBase" discriminator-value="0">
    </subclass>
    
</hibernate-mapping>

Se si tenta di recuperare una collection di PostTag non si riscontrano problemi; al contrario, se si tenta di recuperare un’istanza della classe Post e, allo stesso tempo, la collection PostTag ad esso associata, si andrà incontro ad un’eccezione.
Per ovviare al problema basta inserire nel mapping della collection la where con il discriminator, come mostrato di seguito:

<bag name="PostTags" cascade="all-delete-orphan" generic="true" where="Discriminator = 0">
    <key column="PostID" />
    <one-to-many class="PostTag" />
</bag>
<bag name="ImageTags" cascade="all-delete-orphan" generic="true" where="Discriminator = 1">
    <key column="PostID" />
    <one-to-many class="ImageTag" />
</bag>

Questo comportamento, pur essendo documentato dal team di Nhibernate qui, resta comunque strano.
Un’altra soluzione, sicuramente più comoda e mantenibile, consiste nell’impostare l’attributo force del discriminator a true, dicendo così ad Nhibernate di specificare sempre la where del discriminator nelle proprie query, come mostrato dal seguente mapping:

<discriminator force="true">
  <column name="Discriminator" not-null="true"  sql-type="int" />
</discriminator>

In allegato potete trovare un esempio che riproduce il problema.
Maggiori informazioni ed aggiornamenti potete trovarli qui.

Download Esempio qui


Comments