views:

1073

answers:

3

I am trying to implement my object hierarchy in one table with the "table per class hierarchy" strategy in NHibernate. I'm getting an error with my NHibernate mapping that can be easily reproduced with a simple example. The error is:

System.NotSupportedException: Attempting to parse a null value into an sql string (column:activity0_.Type).
at NHibernate.SqlCommand.InFragment.ToFragmentString() in InFragment.cs: line 109
at NHibernate.Persister.Entity.SingleTableEntityPersister.DiscriminatorFilterFragment(String alias) in SingleTableEntityPersister.cs: line 551

I can reproduce this with the following domain classes:

public interface IActivity
{
    Guid Id { get; set; }
}

public abstract class Activity : IActivity
{
    public DateTime StartTime { get; set; }
    public Guid Id { get; set; }
}

public class Running : Activity
{
    public string Where { get; set; }
}

public class Talking : Activity
{
    public string ToWhom { get; set; }
}

And the following XML mapping:

<?xml version="1.0" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
                   assembly="NHibernateTesting"
                   namespace="NHibernateTesting" >
  <class name="IActivity"
         lazy="false"
         table="Activity"
         discriminator-value="0"
         abstract="true">
    <id name="Id">
      <generator class="guid" />
    </id>
    <discriminator column="Type" type="Int16" />
    <subclass name="Activity"
              discriminator-value="1"
              abstract="true"
              lazy="false">
      <property name="StartTime" />
    </subclass>
    <subclass name="Running"
              discriminator-value="2"
              lazy="false"
              extends="Activity">
      <property name="Where" />
    </subclass>
    <subclass name="Talking"
              discriminator-value="3"
              lazy="false"
              extends="Activity">
     <property name="ToWhom" />
    </subclass>
  </class>
</hibernate-mapping>

Does anyone have an idea of what I am doing wrong?

+1  A: 

afaik Table Per Class mean that you get a Table in the DB per class, with 1-1 FK from Parent table to Inheriting tables. No need for discriminators then. You also do not need to map the IActivity (unless you intend your sessions to work directly against it; then there's some tweaking needed).

Anyhow, I strongly suggest you'd take a peek at FluentNHibernate, which makes the mapping super simple, and have very useful defaults for mapping the 80% with almost no code at all

Ken Egozi
I'm referring to the table per class *hierarchy*, which has one table for the entire class hierarchy.I actually did take a look at Fluent NHibernate but I'm learning it so I find the XML (with the associated manual) much easier. I also have a complicated object model where I have to deal with stuff that isn't normal.
Michael Hedgpeth
I'd say that your best option is to consult with NH users group (nhusers @ google groups) if you hadn't done so yet. They are super helpful there on NH usage tips
Ken Egozi
I wouldn't switch to Fluent, because they are not an official part of NH, it's not even a contribution package. When NH is developing, Fluent will be out-dated.
Stefan Steinegger
@Stefan - I beg to differa. Looking at Hibernate's history, the mapping API does not change drastically over night. b. FluentNH is being developed by heavy NH users. It's safe to assume they'll keep it in-line with any NH developmentc. Even if not, it's OSS, so you can always pick it up and still use should NH introduce breaking changesd. Even if you don't want to, and even if the mapping API changes drastically, you could always export your current fluent mapping to hbm (it's a one liner) and continue with the xml route.Loosing the super powers given to you by Fluent is a mistake IMO
Ken Egozi
+2  A: 

I'm not that familiar with c# and NHibernate but are you sure Activity should be set as abstract="true" in the mapping? It looks like the Activity class isn't abstract.

Shouldn't Running and Talking reside inside of Activity else the StartTime won't be saved. Right?

<subclass name="Activity"
...
  <subclass name="Running"
  ...
  </subclass>
  <subclass name="Talking"
  ...
  </subclass>
...
</subclass>

Anyhow the errormessage suggest that NHibernate is trying to create some query with an in-statement. But there are no values for it. Basically it tries to bild something like

...
WHERE foo IN (null)

If I parsed the code correctly. What are you trying to do when hit throws this error?

Did you already try to enable these in the hibernate-configuration to check what is going on?

<property name="show_sql">true</property>
<property name="hibernate.format_sql">true</property>
<property name="hibernate.use_sql_comments">true</property>
jitter
I think, this is the correct answer: you need to nest the subclass elements. The inherits attribute is intended to be used if the subclasses are moved to a separate mapping file.
Stefan Steinegger
The problem wasn't in the class not being declared as abstract (although good catch), but with the subclass not being nested.Thanks for your help. This one stumped me for a few days.
Michael Hedgpeth
A: 

Thank you for the hint on not using DiscriminateSubtypesOn("fieldName") when using Fluent NH. This saved the day for me.

Daudi
That should be a comment, not an answer
Michael Hedgpeth