Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

HasMany().Not.KeyNullable() uni-directional mapping generates NULLable FK DDL #414

Open
ghost opened this issue Jun 25, 2018 · 10 comments
Open

Comments

@ghost
Copy link

ghost commented Jun 25, 2018

Hello,

It seems like HasMany().Not.KeyNullable() not quite properly generates DDL with SchemaExport. Assuming it should output NOT NULL FK column on the child side. However what I get is NULLable FK column.

HasMany(x => x.Items)
    .Not.Inverse()
    .Not.KeyNullable()
    .Not.KeyUpdate()
    .Cascade.AllDeleteOrphan();

The background of such modelling is the DDD related motivations where the AggregateRoot is managing its child entities, but children are not holding back reference to the AggregateRoot.

Am I doing something wrong or is this not working as I expect it to?

Cheers

@oleh-zheleznyak
Copy link

I have exactly the same need, and exactly the same problem.
In my project, we also have an AggregateRoot that is managing a collection of child entities, also using composition, so deleting orphans is also a must.

It is not a problem to alter the database script by hand, since we do not rely on automatic schema updates, and instead roll out hand-written migration scripts.

However, it would be nice to see that HNIbernate respects the mapping configuration, and generates proper schema. Otherwise, how can we be sure that it won't try to do insert null and then subsequent update to a value during session flush at runtime?

@rgomez90
Copy link

What's the state of this?

@hazzik
Copy link
Member

hazzik commented Sep 18, 2020

@rgomez90 as far as I know, no one work on this. You are more than welcome to submit a PR. See contributing.

@rgomez90
Copy link

@hazzik thank you.
Will take a look at it in the weekend.

@TraGicCode
Copy link

@rgomez90 ,

Did u attempt to fix this bug yet or did you blocked?

@TraGicCode
Copy link

Hey,

looking at the generated mapping xml fluent nhibernate generated i see this

<bag access="nosetter.camelcase" cascade="all-delete-orphan" name="XXX">
      <key not-null="true" update="false">
        <column name="LineItem_id" />
      </key>
      <one-to-many class="XXX.Domain.XXX, XXX.Domain, Version=67.2017.9.1, Culture=neutral, PublicKeyToken=null" />
    </bag>

In the documentation here ( https://nhibernate.info/doc/nhibernate-reference/collections.html#collections-example) it show what the xml mapping should look like if you want the FK to be not nullable

 <set name="Children">
            <key column="parent_id" not-null="true"/>
            <one-to-many class="Child"/>
        </set>

Notice the different in the column attribute vs the column node. Are the both the same and valid mappings?

@hazzik
Copy link
Member

hazzik commented Jun 14, 2021

Notice the different in the column attribute vs the column node. Are the both the same and valid mappings?

yes

@oskarb
Copy link
Member

oskarb commented Aug 30, 2024

Also seeing this. It seems at least the schema generator is not picking up the not-null value from the parent key element when the column is a child, not an attribute.

@oskarb
Copy link
Member

oskarb commented Aug 30, 2024

In NHibernate, the column name as a child element versus as an attribute produces different while the HbmXxx XML schema classes are interpreted into the Table mapping objects.

CollectionBinder.BindKey() will get the HbmKey object, then call ValuePropertyBinder.BindSimpleValue(). The third parameter of that method is called isNullable and it gets the value model.IsOneToMany (i.e. true). This becomes the default nullability for the columns.

Inside of that, it will iterate the columns (as HbmColumn). If the not-null attribute as been specified on the column element, it is respected. Otherwise the default is used (which comes from the expression model.IsOneToMany).

None of the above directly looks at the not-null attribute on the key element. The only time where that seems to be used is when the column element has not been used. In that case, when getting the list of columns from HbmKey, it will generate a "fake" HbmColumn instance based on the attributes found directly on the key element.

So, in summary, the following two alternatives are not treated exactly the same by NHibernate:

<set name="Children">
    <key column="parent_id" not-null="true"/>
    <one-to-many class="Child"/>
</set>
<set name="Children">
    <key not-null="true">
        <column name="parent_id"/>
    </key>
    <one-to-many class="Child"/>
</set>

@oskarb
Copy link
Member

oskarb commented Aug 30, 2024

There is a workaround:

HasMany(sg => sg.Items).AsSet()
                       .Not.KeyNullable()
                       .KeyColumns.Add("parent_id", part => part.Not.Nullable())

That works, as opposed to using the .KeyColumn("...") shorthand. The .Not.KeyNullable() is still required to make NHibernate treat the column correctly for inserts.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants