views:

1862

answers:

3

I'm using latest Fluent NHibernate lib (0.1.0.452) and I have a problem with saving child entitites.

I think this is rather common scenario... I've got a parent with mapping:

HasMany<Packet>(x => x.Packets)
            .Cascade.All()
            .KeyColumnNames.Add("OrderId");

and a simple Packet class that (in a domain model and FNH mapping) doesn't have any reference to the parent. What gets generated is a correct Packets table that contains a column named OrderId. What doesn't work is the saving. Whenever I try to save parent object, the children are also saved, but the FK stays untouched. I checked the SQL and in INSERT statement the OrderId doesn't even appear!

INSERT INTO KolporterOrders (CargoDescription, SendDate, [more cols omitted] ) VALUES ('order no. 49', '2009-04-22  00:57:44', [more values omitted])
SELECT LAST_INSERT_ID()
INSERT INTO Packets (Weight, Width, Height, Depth) VALUES ('To5Kg', 1, 1, 1)
SELECT LAST_INSERT_ID()

As you see the OrderId is completely missing in the last INSERT.

I also checked the generated NH mapping and it seems it's ok:

<bag name="Packets" cascade="all">
    <key column="OrderId" />
    <one-to-many class="Company.Product.Core.Packet, Core, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
</bag>

I tried setting Cascade to different values. I even added References to the PacketMap (FNH mapping class).

Any ideas why the OrderId is not being inserted?

Edit: forgot to mention: I'm using MySQL5 if it matters.

Edit2: The above FNH mapping generates hbm with bag (not a set) - I edited it. The C# code used for saving:

var order = new Order(); 
NHSession.Current.SaveOrUpdate(order); //yes, order.Packets.Count == 1 here

///Order.cs, Order ctor
public Order()
    {
        CreateDate = DateTime.Now;
        OrderState = KolporterOrderState.New;
        Packets = new List<Packet>();
        Packets.Add(new Packet()
        {
            Depth = 1,
            Height = 1,
            Width = 1,
            Weight = PacketWeight.To5Kg
        });
    }

the session gets flushed and closed at EndRequest.

Edit3: Ok, my fault. I was testing it in ApplicationStart of global.asax, so the Request hadn't been created so the session wasn't flushed. I realised it when I tested it on a simple ConsoleApp project when I saw that flushing actualy causes the FK col update. Anyway: thanks for help :)

A: 

In a "vanilla" parent-children object model, you must update the child's object's reference to the parent in order to cause NHibernate to update the child record's reference to the parent.

In an "inverted" parent-children object model, you must modify the parent's collection of children objects in order to cause NHibernate to update the child records' references to the parent.

It seems you may want to be using an "inverted" parent-children object model.

In the XML mapping, you need

<set name="Packets" cascade="all" inverse="true">
    <key column="OrderId" />
    <one-to-many class="Company.Product.Core.Packet, Core,
        Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
</set>

In the Fluent mapping, you need

HasMany<Packet>(x => x.Packets)
    .Cascade.All()
    .Inverse()
    .KeyColumnNames.Add("OrderId")
;
Justice
That was actually the first change I tried. It didn't solve the problem. [F]NH somehow forgets about OrderId.
inverse=true lets NHibernate ignore this relation. So this would be the way to produce the error if you don't have it :-)
Stefan Steinegger
A: 

This is really strange. You should check subsequent Updates, NHibernate sometimes updates foreign keys afterwards, and then it doesn't appear in the insert.

Make sure that OrderId does not have several meanings on the Packets table. To check this, change the name of OrderId to something else.

Cascading has nothing to do with it. It only controls if you need to save the child explicitly.

Stefan Steinegger
A: 

Problem resolved, see Edit3 at the end of my question.