Unidirectional One To Many Without Nullable Foreign Key
Description
Environment
is related to
Activity
Fabio Maulo July 30, 2011 at 2:11 PM
Closed after final release of NH3.2.0GA
Tim Scott October 28, 2008 at 9:30 AM
I was not thinking of adding a mapping in Foo. When I think a little deeper on it, I'm not sure mapping needs to change. Let's say Bar has this:
[HasMany(Access = PropertyAccess.FieldCamelcase, Lazy = true, Inverse = true,
Table = "Foo", ColumnKey = "BarId", Cascade = ManyRelationCascadeEnum.AllDeleteOrphan,
RelationType = RelationType.Set)]
public virtual ICollection<Foo> Foos
Assume Foo has no property Bar, and also that Foo.BarId is not nullable.
var bar = new Bar();
bar.AddFoo(new Foo());
barRepository.Save(bar);
Currently, in this scenario we get a SQL exception because NH will insert the new Foo without a BarId. But why? Before cascading, NH has inserted Bar and gotten the new Id. I have told NH that Foo is related to a Bar via Foo.BarId. It knows enough to insert any new foos with Foo.BarId populated, no?
This all seems pretty obvious to me, which means I'm probably missing something fundamental...
On the other hand, if I do this:
session.Save(new Foo())
...I will get SQL exception. This is the desired outcome. In my domain have expressed that Foo is strictly a member of the Bar aggregate. I have no business saving it but in the context of its aggregate.
The original idea of this post was to support making the FK not-nullable. There is another benefit. When NH inserts child entities for which it knows the identity of the parent already, why the two step process (INSERT without FK then UPDATE)? Seems needlessly inefficient.
OrenE October 28, 2008 at 4:21 AM
Hm,
Just to be clear, I am asking to see the code that you writes in this scenario.
OrenE October 28, 2008 at 4:18 AM
Let us say that we add this:
<many-to-one name="Bar" column="BarId" not-null="true" access="nhibernate-internal-storage"/>// implies no property
Now, show me the code that you think will make this happen.
Tim Scott October 27, 2008 at 7:53 AM
Certainly we must tell NH that Foo is related to Bar in the database via Foo.BarId column. But is Foo.Bar property the only way to express this? Is it the best way to express it for a domain driven design? As Colin originally suggested, why not extend the mapping to handle it? Is there some insurmountable reason that Bar.Foos mapping cannot express this to NH?
Let's say we could. When a Bar is saved and cascades happen, NH knows what to put in Foo.BarId. But let's say I save Foo directly. There is no Foo.Bar so BarId will not be in the insert, hence null. Let's say that nullable=false on Foo.BarId. A SQL exception occurs. This is the correct result. It is what I want.
Especially with a legacy database we need to work with the database design that we have, with a view to future redesign. NHibernate seems to miss two features that would make this work really well:
1) Mapping inheritance at the <component> level would be a big advantage
2) A way to handle unidirectional many-one, with the foreign key in the one end causes us to make that key column nullable which is troublesome.
Its the second issue that I wanted to raise. We get into situations where the ID of the parent (one) is in the child (many) either because our database designers want that approach or because thats the way the database was originally designed. There seem to be two solutions:
1) Use a bidirectional relationship - We don't want to design our associations just so that we can use NHibernate.
2) Make the foreign key nullable.
It would be nice if we could handle this without making the foreign key nullable, even if we had to bake into the mapping more knowledge of our domain model (such as that the child is never saved except by the parent). So if we had an Order with Items in it then if we could say that Items are only saved from the Order (the Order being the aggregate root) then I would have thought you could avoid making the foreign key in Item nullable?
This link covers the sort of issue I'm discussing:
http://forum.hibernate.org/viewtopic.php?t=948277&view=next&sid=ec0773380349d434719a525332b9c962