2

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!

John Pappin
  • 578
  • 3
  • 11
  • 1
    BTW your explicit setting of Columns is redundant no? You could just Map(_ => _.PaymentId).Nullable() - without specifying the column("PaymentId") and it would work just the same? – PandaWood Feb 10 '14 at 01:20
  • @PandaWood - yes that's correct, it's just personal preference that I'm mapping the columns explicitly, but fluent will assume the property and column are named the same if you do not explicitly set it. – John Pappin Nov 10 '14 at 16:20

2 Answers2

2

To use table-per-subclass WITH a discriminator the following works:

public class UnpaidChequeMap : SubclassMap<UnpaidCheque>
{
    public UnpaidChequeMap()
    {
        Schema("Account");
        DiscriminatorValue("U");
        Join("UnpaidCheque", j =>
        {
            j.KeyColumn("OperationId");
            j.Map(_ => _.UnpaidOn).Column("UnpaidOn").Not.Nullable();
            j.Map(_ => _.UnpaidByUserId).Column("UnpaidByUserId").Not.Nullable();
        }
    }
}
AlexDev
  • 4,049
  • 31
  • 36
  • Thanks, @AlexDev. I finally got _discriminator and table per subclass_ to work!!! It's never too late I guess ;) – bounav Dec 04 '18 at 15:58
1

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.

John Pappin
  • 578
  • 3
  • 11
  • 1
    What about section [8.1.3](http://nhibernate.info/doc/nhibernate-reference/inheritance.html) of the nhibernate documentation? It suggests that _table per subclass_ inheritance mapping _with a discriminator_ can be used. I have never been able to work out how to do it with fluent nhibernate though. – bounav May 11 '16 at 11:09
  • 1
    @bounav If you're still interested, see my answer. – AlexDev Jan 27 '17 at 14:23