views:

584

answers:

2

I said Fluent NHibernate in the subject, but I think this is an NHibernate question. However, I didn't want to confuse things by leaving out details.

I'm using Fluent NHibernate 1.0.0.593 with NHibernate 2.1.0.4000.

First a little background...

I have a base class with three implementation classes that I'm trying to map using the DiscriminateSubClassesOnColumn/SubclassMap.DiscriminatorValue Fluent NHibernate syntax. The sub classes represent responses I can get from three different third parties data providers. The information is the generally same, but the relationships are different (one response type can have multiple addresses, another can only have one, etc.).

I had Fluent NHibernate extract the NHibernate configuration xml it was generating and based on my understanding of what the discriminator and subclass blocks should look like, the configuration appears to be generating correctly (which is why I'm thinking this is an NHibernate question, not a Fluent NHibernate one).

The "problem" that I'm seeing is that for the initial query from the database, NHibernate appears to be scanning the subclasses and doing all the one-to-one relationships as joins in the initial query. Since some of the sub class have the same child type as a one-to-many relationship, this is causing an error when I try to retrieve one of these other types.

I'll post the generated NHibernate configuration as well for reference.

Anyone have any ideas on how to control this or maybe what I'm doing wrong?

Thanks.

A: 
    <hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" default-access="property" auto-import="true" default-cascade="none" default-lazy="true">
  <class xmlns="urn:nhibernate-mapping-2.2" discriminator-value="Unknown" name="PersonValidationResponse, Domain, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" table="PersonValidationResponse">
    <id name="ResponseId" type="System.Int32, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
      <column name="pk_ResponseId" />
      <generator class="identity" />
    </id>
    <discriminator type="PersonValidationResponseType, Domain, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null">
      <column name="ResponseType" />
    </discriminator>
    <property name="CreatedBy" type="System.String, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
      <column name="CreatedBy" />
    </property>
    <property name="CreatedDate" type="System.DateTime, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
      <column name="CreatedDate" />
    </property>
    <many-to-one class="PersonValidationRequest, Domain, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" name="Request">
      <column name="fk_RequestId" />
    </many-to-one>
    <subclass name="ResponseTypeAResponse, Domain, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" discriminator-value="ResponseTypeA">
      <one-to-one cascade="all" class="PersonValidationAddress, Domain, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" foreign-key="fk_ResponseId" lazy="false" name="Address" property-ref="Response" />
      <one-to-one cascade="all" class="PersonValidationBiographic, Domain, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" foreign-key="fk_ResponseId" lazy="false" name="Biographic" property-ref="Response" />
      <one-to-one cascade="all" class="PersonValidationPhone, Domain, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" foreign-key="fk_ResponseId" lazy="false" name="Phone" property-ref="Response" />
      <property name="ResponseStatus" type="ResponseStatus, Domain, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null">
        <column name="ResponseStatus" />
      </property>
      <property name="RawResponse" type="System.String, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
        <column name="RawResponse" />
      </property>
      <bag cascade="all-delete-orphan" lazy="false" name="Alerts" table="PersonValidationResponseAlert">
        <key>
          <column name="fk_ResponseId" />
        </key>
        <many-to-many class="PersonValidationAlert, Domain, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null">
          <column name="fk_AlertId" />
        </many-to-many>
      </bag>
      <bag cascade="all-delete-orphan" inverse="true" lazy="false" name="Scores" table="PersonValidationResponseScore">
        <key>
          <column name="fk_ResponseId" />
        </key>
        <one-to-many class="PersonValidationScore, Domain, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
      </bag>
      <bag cascade="all-delete-orphan" inverse="true" lazy="false" name="Exceptions" table="PersonValidationResponseException">
        <key>
          <column name="fk_ResponseId" />
        </key>
        <one-to-many class="PersonValidationException, Domain, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
      </bag>
    </subclass>
    <subclass name="ResponseTypeBResponse, Domain, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" discriminator-value="ResponseTypeB">
      <one-to-one cascade="all" class="PersonValidationWatchList, Domain, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" foreign-key="fk_ResponseId" lazy="false" name="WatchListHit" property-ref="Response" />
      <property name="ResponseStatus" type="ResponseStatus, Domain, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null">
        <column name="ResponseStatus" />
      </property>
      <property name="RawResponse" type="System.String, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
        <column name="RawResponse" />
      </property>
      <bag cascade="all-delete-orphan" inverse="true" lazy="false" name="Addresses" table="PersonValidationAddress">
        <key>
          <column name="fk_ResponseId" />
        </key>
        <one-to-many class="PersonValidationAddress, Domain, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
      </bag>
      <bag cascade="all-delete-orphan" inverse="true" lazy="false" name="Biographics" table="PersonValidationBiographic">
        <key>
          <column name="fk_ResponseId" />
        </key>
        <one-to-many class="PersonValidationBiographic, Domain, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
      </bag>
      <bag cascade="all-delete-orphan" inverse="true" lazy="false" name="Phones" table="PersonValidationPhone">
        <key>
          <column name="fk_ResponseId" />
        </key>
        <one-to-many class="PersonValidationPhone, Domain, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
      </bag>
      <bag cascade="all-delete-orphan" lazy="false" name="Alerts" table="PersonValidationResponseAlert">
        <key>
          <column name="fk_ResponseId" />
        </key>
        <many-to-many class="PersonValidationAlert, Domain, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null">
          <column name="fk_AlertId" />
        </many-to-many>
      </bag>
      <bag cascade="all-delete-orphan" inverse="true" lazy="false" name="Scores" table="PersonValidationResponseScore">
        <key>
          <column name="fk_ResponseId" />
        </key>
        <one-to-many class="PersonValidationScore, Domain, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
      </bag>
      <bag cascade="all-delete-orphan" inverse="true" lazy="false" name="Exceptions" table="PersonValidationResponseException">
        <key>
          <column name="fk_ResponseId" />
        </key>
        <one-to-many class="PersonValidationException, Domain, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
      </bag>
    </subclass>
    <subclass name="ResponseTypeCResponse, Domain, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" discriminator-value="ResponseTypeC">
      <property name="ResponseStatus" type="ResponseStatus, Domain, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null">
        <column name="ResponseStatus" />
      </property>
      <property name="RawResponse" type="System.String, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
        <column name="RawResponse" />
      </property>
      <bag cascade="all-delete-orphan" inverse="true" lazy="false" name="Addresses" table="PersonValidationAddress">
        <key>
          <column name="fk_ResponseId" />
        </key>
        <one-to-many class="PersonValidationAddress, Domain, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
      </bag>
      <bag cascade="all-delete-orphan" inverse="true" lazy="false" name="Biographics" table="PersonValidationBiographic">
        <key>
          <column name="fk_ResponseId" />
        </key>
        <one-to-many class="PersonValidationBiographic, Domain, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
      </bag>
      <bag cascade="all-delete-orphan" inverse="true" lazy="false" name="CreditAccounts" table="PersonValidationCreditAccount">
        <key>
          <column name="fk_ResponseId" />
        </key>
        <one-to-many class="PersonValidationCreditAccount, Domain, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
      </bag>
      <bag cascade="all-delete-orphan" inverse="true" lazy="false" name="Phones" table="PersonValidationPhone">
        <key>
          <column name="fk_ResponseId" />
        </key>
        <one-to-many class="PersonValidationPhone, Domain, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
      </bag>
      <bag cascade="all-delete-orphan" inverse="true" lazy="false" name="WatchListHits" table="PersonValidationWatchList">
        <key>
          <column name="fk_ResponseId" />
        </key>
        <one-to-many class="PersonValidationWatchList, Domain, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
      </bag>
      <bag cascade="all-delete-orphan" lazy="false" name="Alerts" table="PersonValidationResponseAlert">
        <key>
          <column name="fk_ResponseId" />
        </key>
        <many-to-many class="PersonValidationAlert, Domain, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null">
          <column name="fk_AlertId" />
        </many-to-many>
      </bag>
      <bag cascade="all-delete-orphan" inverse="true" lazy="false" name="Scores" table="PersonValidationResponseScore">
        <key>
          <column name="fk_ResponseId" />
        </key>
        <one-to-many class="PersonValidationScore, Domain, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
      </bag>
      <bag cascade="all-delete-orphan" inverse="true" lazy="false" name="Exceptions" table="PersonValidationResponseException">
        <key>
          <column name="fk_ResponseId" />
        </key>
        <one-to-many class="PersonValidationException, Domain, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
      </bag>
    </subclass>
    <subclass name="UnknownResponse, Domain, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" discriminator-value="Unknown" />
  </class>
</hibernate-mapping>
NFrank
A: 

Not sure it's the best answer, so I'm still interested in hearing other thoughts, but I found a fix.

Adding .Fetch.Select to any HasOne relationships on the subclasses, seems to force the queries for those child types to always be in a separate SELECT. It ends up being less efficient in those cases where a join would be better, but it makes the exception go away.

NFrank