I have setup my mappings like this:
public ParentMap()
{
...
HasMany( x => x.Children ).Cascade.AllDeleteOrphan().Inverse()
}
public ChildMap()
{
...
References ( x => x.Parent ).Not.Nullable();
}
I have written a test that uses the PersistenceSpecification class
like this:
Action<Parent, Child> listItemSetter = ( p, c ) => p.AddChild( c );
new PersistenceSpecification<Parent>( session, new EqualityComparer
() )
.CheckList( c=> c.Children, children, listItemSetter )
.VerifyTheMappings();
Note: I'm using FluentNH 1.0.0.602
When I run this, I get an error that says:
"NHibernate.PropertyValueException: not-null property references a
null or transient value"
This is problem #1. It seems that it is not executing the Action I
provided.
To continue trying to see what was going on, I removed
the .Not.Nullable() from the child's mapping and tried it again with
ShowSQL on.
What I get is:
1. Insert Child
2. Insert Parent
3. Update Child
This is problem #2, it should Insert Parent then Insert Child.
To verify this I wrote another test that does the same thing but
doesn't use the PersistenceSpecification. This test looks like this:
Parent p = new Parent();
p.AddChild( c );
session.Save( p );
session.Flush();
What I get is:
1. Insert Parent
2. Insert Child
This is the correct result. Note that I haven't changed the mappings
at all.
If I then add the .Not.Nullable back in the Child's mappings, this
second test still works.
Why is the PersistenceSpecification saving differently than regular
NH?
And am I not setting up my Action correctly (or something), causing
PersistenceSpecification to not use my listItemSetter?
Thanks,
Kevin Berridge
I now understand problem #2 as well. The reason why
PersistenceSpecification is saving the child before the parent is
because that's how it's written to work!
Specifically, CheckList calls RegisterCheckedProperty passing in the
ReferenceList. That calls HasRegistered, which loops over every item
in the list and calls TransactionalSave. That starts a transaction,
calls Save on the session, and commits the transaction.
Thus, PersistenceSpecification is attempting to save all the children
before saving the parent and also before using the listItemSetter.
This doesn't work when the child has a Not.Nullable() reference to its
parent. In my specific case, Save shouldn't be called for the child.
Using the listItemSetter is enough because it will set the parent
pointer on the child and add the child to the parent's collection,
which has Cascade.AllDeleteOrphan, so NH will automatically insert the
child after inserting the parent.
So that's what's happening, but I'm assuming the code is written the
way it is for a good reason. Any idea how it would need to be updated
to support both scenarios?
1. Where Save cannot be called on the Child
2. Where Save must be called on the Child
Thanks,
Kevin Berridge
On Dec 30 2009, 11:23 am, kberridge <kevin.w.berri...@gmail.com>
wrote:
Thanks,
Kevin