Tenek
Tenek

Reputation: 437

nhibernate one-to-one mapping and not-null="false"?

How to create one-to-one relation using NHibernate where other side could be a NULL? For example, I have a Banner entity that has a relation to Image entity like this:

<class name="Banner" table="Banner">
    <id name="Id" type="Int64" unsaved-value="0">
        <generator class="native" />
    </id>

    <many-to-one name="Image" unique="true" column="ImageId" not-null="false"/>

    <!-- check: In the example this property was without inverse attribute -->
    <set name ="BannerSlideSet" fetch="subselect" inverse="true">
        <key column="Banner_ID" foreign-key="FK_Banner_BannerSlide" not-null="false" />
        <one-to-many class="BannerSlide"/>
    </set>
</class>

So, Image can be null for a Banner entity. I create one banner entity with no image without any issues. In DB I have

--------------
ID  | ImageId
--------------
 1  | NULL

After that, I'm trying to create second Banner instance with no Image as well and get the following error:

NHibernate.Exceptions.GenericADOException: could not insert: [Domain.Entities.Banner][SQL: INSERT INTO Banner (ImageId) VALUES (?); select SCOPE_IDENTITY()] ---> System.Data.SqlClient.SqlException: Violation of UNIQUE KEY constraint 'UQ_Banner_7516F70DE6141471'. Cannot insert duplicate key in object 'dbo.Banner'. The duplicate key value is ().

I guess, it happens because I have unique constraint on one-to-many relation between banner and image entities and several Banner instances can't have several NULL values in ImageId field. Question: how I would achieve one-to-nullableone relation with in NHinerbate?

Thanks!

Upvotes: 3

Views: 3537

Answers (1)

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

Reputation: 123861

The solution is to move the ImageId from the Banner table. And place it in the Image table.

This way, whenever we have real (not null) image, we can let:

  1. reference the Banner (BannerId) by Image (many-to-one from the Image side, and collection one-to-many of images from the Banner side) or
  2. use the real one-to-one relationship: the ImageId is the unqiue constrained, generated by the owning Banner's BannerId.

This is the example form the documenation 5.1.11. one-to-one, adjusted to Banner and Image

Now we must ensure that the primary keys of related rows in the BANNER and IMAGE tables are equal. We use a special NHibernate identifier generation strategy called foreign:

The Image mapping:

<class name="Image" table="[Image]">
    <id name="Id" column="[ImageId]">
        <generator class="foreign">
            <param name="property">Banner</param>
        </generator>
    </id>
    ...
    <one-to-one name="Banner" class="Banner" constrained="true"/>
</class>

The Banner:

 <one-to-one name="Image" class="Image"/>

Upvotes: 3

Related Questions