views:

1021

answers:

1

I'm having trouble setting up a role based structure using inheritance and NHibernate.

Basically I would like to have an abstract class called ObjectInRole. This class (and corresponding database table) contains a RoleId, an ObjectId, and an ObjectType (this would be the discriminator column). The discriminator column would be the object type that gains the role.

For simplicity's sake assume that there is a User object that contains a list (bag) of Roles. I would like it so that I can have a UserInRole class that would be a subclass of ObjectInRole.

I've created this bag like this:

<bag name="Roles" table="ObjectInRole" where="ObjectType = 'AthletesCafe.Core.Domain.System.Users.User, AthletesCafe.Core'" generic="true"
     access="field.pascalcase-underscore" lazy="true">
  <key column="ObjectId" />
  <many-to-many class="AthletesCafe.Core.Domain.System.Roles.Role, AthletesCafe.Core" column="RoleId" />
</bag>

I've set up the mapping for the ObjectInRole to be this:

<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" namespace="AthletesCafe.Core.Domain.System.Roles" assembly="AthletesCafe.Core">
  <class name="ObjectInRole" table="ObjectInRole" lazy="true" abstract="true">
<id name="ID" type="Int32" unsaved-value="0">
  <column name="ID" not-null="true" unique="true" index="PK_ObjectInRole"/>
  <generator class="identity" />
</id>
<discriminator column="ObjectType" type="String" insert="true" force="true"/>
<property name="ApplicationName" column="ApplicationName" type="String" not-null="false" length="255" />
<property name="ObjectId" column="ObjectId" type="int" not-null="true" />
<property name="ObjectType" column="ObjectType" type="String" />

<many-to-one name="Role" column="RoleId" class="AthletesCafe.Core.Domain.System.Roles.Role, AthletesCafe.Core" not-null="true" cascade="none" />

<subclass name="AthletesCafe.Core.Domain.System.Roles.UserInRole" 
          discriminator-value="AthletesCafe.Core.Domain.System.Users.User, AthletesCafe.Core">
  <many-to-one name="User" column="ObjectId" class="AthletesCafe.Core.Domain.System.Users.User, AthletesCafe.Core" not-null="false" cascade="none" />
</subclass>

Having this setup and running it I cannot get the ObjectType field to save when I add a new Role to the collection for the User object. It saves a new entry in the ObjectToRole table, but the ObjectType is null. Looking at the bag it wouldn't load this into the collection. I think I've narrowed it down to the subclass structure being setup incorrectly or that the bag is setup incorrectly. I'm leaning towards the bag as I think it may just be treating the collection as a bunch of the abstract class instead of the subclass UserInRole.

Please provide your thoughts on either area. Thanks!

EDIT:

So instead of trying to add to the bag (User.Roles.add(Role)) and having the many-to-many persist the linking object I decided that I would just straight add the object to the table. I'm getting a new error:

null id in AthletesCafe.Core.Domain.System.Roles.UserInRole entry (don't flush the Session after an exception occurs)

If looking at the class above I've set the User object and Role object. The id is an identity field and is specified as such. I am at a complete loss.

A: 

OK! After much playing around with mapping files I have found an undocumented issue (at least I could find no mention of the error I was committing).

When using subclassing the parent class cannot contain a mapping for a property that is the discriminator column. If you look at the second mapping document I posted it shows the super class and the sub class. The super class's discriminator is the ObjectType column. I also map this as a property to the abstract class ObjectInRole. You cannot do this. Once this line was removed everything worked correctly (not saving the bag).

The bag seems to be an issue that is not controllable through anything I have found in documentation. I cannot find another instance of where the abstract class is saved through the mapping. It appears that NHibernate does not know that the linking class is a UserInRole object and not a ObjectInRole object. Researching all the options that are provided for the bag node there was no apparent way to tell NHibernate what type of class it was linking through to make the many-to-many association. From what I can tell it doesn't care, but more or less treats the table as just a table, not a mapped object. If there was a way to tell it append something for the insert then maybe it would be possible, but I have not seen any information regarding this.

Mike G