Search code examples
c#nhibernatefluent-nhibernatefluent-nhibernate-mapping

Fluent NHibernate - how to create table-per-subclass mapping using a discriminator?


I've looked at a lot of questions on SO and google regarding sub-class mappings in nhibernate / fluent-nhibernate and not managed to find anyone with the same problem as me. I've followed the basic instructions from the fluent-nhibernate wiki (http://wiki.fluentnhibernate.org/Fluent_mapping#Subclasses) but this hasn't helped.

I have a base entity called Operation - which has a base table associated with it, I then have sub-tables UnpaidCheque, Refund etc - the primary key for each of these sub-tables is a foreign-key, the OperationId (PK) from the Operation table.

When I create a persistence specification and try to verify my mappings it is trying to save all the columns to the Operation table, rather than saving to the Operation table and then saving the specific columns of Unpaid Cheque to the UnpaidCheque table.

The error is:

could not insert: [UnpaidCheque][SQL: INSERT INTO Account.Operation (PaymentId, Amount, UnpaidOn, UnpaidByUserId, OperationType) VALUES (?, ?, ?, ?, 'U'); select SCOPE_IDENTITY()]
  ----> System.Data.SqlClient.SqlException : Invalid column name 'UnpaidOn'.
Invalid column name 'UnpaidByUserId'.

As you can see, it's trying to save values in UnpaidByUserId and UnpaidOn columns which are members of the sub-table/class and not the base.

On the plus side, the fact it's trying to insert 'U' into the operationtype column indicates to me that it seems to be setting the right descriminator value for the class type. The only place I have specified the operationtype is in the DiscriminatorValue() call in the class map, I am not explicitly setting it anywhere else.

The class hierarchy is as follows:

public class Operation
    {
        public virtual long OperationId { get; set; }
        public virtual string OperationType { get; set; }
        public virtual long? PaymentId { get; set; }
        public virtual decimal Amount { get; set; }
    }

public class UnpaidCheque : Operation
    {
        public virtual DateTime UnpaidOn { get; set; }
        public virtual long UnpaidByUserId { get; set; }
    }

The class mappings are:

public class OperationMap : ClassMap<Operation>
    {
        public OperationMap()
        {
            Schema("Account");
            Table("Operation");
            LazyLoad();
            Id(_ => _.OperationId).Column("OperationId").GeneratedBy.Identity();
            Map(_ => _.PaymentId).Column("PaymentId").Nullable();
            Map(_ => _.Amount).Column("Amount").Not.Nullable();

            DiscriminateSubClassesOnColumn("OperationType");
        }
    }
public class UnpaidChequeMap : SubclassMap<UnpaidCheque>
{
    public UnpaidChequeMap()
        {
            Schema("Account");
            Table("UnpaidCheque");
            LazyLoad();
            DiscriminatorValue("U");
            KeyColumn("OperationId");
            Map(_ => _.UnpaidOn).Column("UnpaidOn").Not.Nullable();
            Map(_ => _.UnpaidByUserId).Column("UnpaidByUserId").Not.Nullable();
        }
}

I can't see anything I've done differently from the example other than the addition of the KeyColumn() in the subclass map however I get the same error message without that as well. Can anyone shed any light on what I have missed or if what I am trying to achieve is supported by nhibernate? As far as I can tell it should be.

Thanks in advance!


Solution

  • We found the solution:

    Descriminators are only for mappings where all the sub-classes are stored in the same table in the database.

    Removing the lines:

    DiscriminateSubClassesOnColumn("OperationType");
    

    from the parent mapping and

    DiscriminatorValue("U");
    

    from the child mapping, and subsequently removing the column from the database solved the issue.