Sebastian Edelmeier
Sebastian Edelmeier

Reputation: 4157

Fluent NHibernate Child collection persistence issues

I have the following mapping classes (only the relevant part copied):

public class CardTemplateMapping : ClassMap<CardTemplate>
{
    public CardTemplateMapping()
    {
        Table("cardtemplate");

        Id(x => x.Id)
            .Column("id")
            .GeneratedBy.Native();

        HasMany(x => x.CostStructures)
            .KeyColumn("cardtemplate_id")
            .Cascade.All();

    }
}
public class CostStructureMapping : ClassMap<CostStructure>
{
    public CostStructureMapping()
    {
        Table("coststructure");

        Id(x => x.Id)
            .Column("id")
            .GeneratedBy.Native();

        References(x => x.CardTemplate)
            .Column("cardtemplate_id");

        HasMany(x => x.CostComponents)
            .KeyColumn("coststructure_id")
            .Cascade.AllDeleteOrphan();

    }
}
public class CostStructureComponentMapping : ClassMap<CostStructureComponent>
{
    public CostStructureComponentMapping()
    {
        Table("CostStructureComponent");

        Id(x => x.Id)
            .Column("id")
            .GeneratedBy.Native();

        References(x => x.CostStructure)
            .Column("coststructure_id");

        References(x => x.ResourceType)
            .Column("resourcetype_id");
    }
}

Nhibernate loads the relations as I intended. But two parts of the Mapping seem a bit weird (either that or my expectations are weird).

First:

 CostStructure structure = new CostStructure() { CardTemplate = template };
 template.CostStructures.Add(structure);
 Session.Save(template);
 Session.Flush();

This does not save the new CostStructure instance. Why?

Second one: Assuming I have loaded a CardTemplate having a CostStructure containing three CostStructureComponent entities. Now I want to remove one of those CostStructureComponents from the CostStructure.
I tried the following :

costStructure.CostComponents.Remove(component);
component.CostStructure = null;
session.Save(template)

Now, I know that explicitly deleting explicitly works, but shouldn't the DeleteOrphan part also assert that the component, now without CostStructure to reference, is deleted? Instead, NHibernate tries to perform the following update:

UPDATE CostStructureComponent 
SET amount = @p0, 
     coststructure_id = @p1, 
     resourcetype_id = @p2
WHERE id = @p3;

@p0 = 1 [Type: Int32 (0)], 
@p1 = NULL [Type: Int64 (0)], 
@p2 = 5 [Type: Int64 (0)], 
@p3 = 13 [Type: Int64 (0)]

Could it be Equals / GetHashCode have been implemented the wrong way?

Sorry, it's late, been a long day and so forth...If I'm talking gibberish, please let me know...

Upvotes: 1

Views: 1173

Answers (1)

Radim K&#246;hler
Radim K&#246;hler

Reputation: 123861

All the answers are hidden in one setting .Inverse()

public CardTemplateMapping()
{
    HasMany(x => x.CostStructures)
        .KeyColumn("cardtemplate_id")
        .Cascade.All()
        .Inverse(); // HERE this setting

And here:

public CostStructureMapping()
{
    ..
    HasMany(x => x.CostComponents)
        .KeyColumn("coststructure_id")
        .Cascade.AllDeleteOrphan()
        .Inverse();  // and HERE and everywhere on HasMany()

Try to find some articles about this setting, but in general: If mapped as inverse, NHibernate is ready to do much better SQL Statements, because it is working with other end of relation...

Some sources:

19.5.2. Lists, maps, idbags and sets are the most efficient collections to update cite:

However, in well-designed NHibernate domain models, we usually see that most collections are in fact one-to-many (comment: HasMany in Fluent) associations with inverse="true". For these associations, the update is handled by the many-to-one (comment: References in Fluent) end of the association, and so considerations of collection update performance simply do not apply.

6.2. Mapping a Collection

inverse (optional - defaults to false) mark this collection as the "inverse" end of a bidirectional association

Upvotes: 1

Related Questions