Trying to get my head around this one. To me, it doesn't seem possible.
My server has reported the following error occurring once or twice a day on a busy server:
PlaylistItem.create System.Data.SqlClient.SqlException
Violation of PRIMARY KEY constraint 'PK__Videos__3214EC075812160E'. Cannot insert duplicate key in object 'dbo.Videos'. The statement has been terminated.
A PlaylistItem contains a reference to a Video object. The ID of a video is pre-determined and not handled by NHibernate. Here are the mappings for my PlaylistItem and Video entities:
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="Streamus" namespace="Streamus.Domain">
<class name="PlaylistItem" table="[PlaylistItems]" lazy="false" >
<id name="Id" unsaved-value="00000000-0000-0000-0000-000000000000">
<generator class="guid.comb" />
</id>
<property name="Title" not-null="true" />
<property name="Sequence" not-null="true" />
<many-to-one name="Playlist" column="PlaylistId" />
<many-to-one name="Video" column="VideoId" not-null="true" cascade="save-update" />
</class>
</hibernate-mapping>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="Streamus" namespace="Streamus.Domain">
<class name="Video" table="[Videos]" lazy="false" mutable="false">
<id name="Id" length="11" type="String">
<generator class="assigned"></generator>
</id>
<property name="Title" not-null="true" />
<property name="Duration" not-null="true" />
<property name="Author" not-null="true" />
</class>
</hibernate-mapping>
And here is the Create/Save method in question:
/// <summary>
/// This is the work for saving a PlaylistItem without the Transaction wrapper.
/// </summary>
private void DoSave(PlaylistItem playlistItem)
{
// This is a bit of a hack, but NHibernate pays attention to the "dirtyness" of immutable entities.
// As such, if two PlaylistItems reference the same Video object -- NonUniqueObjectException is thrown even though no changes
// can be persisted to the database.
playlistItem.Video = VideoDao.Merge(playlistItem.Video);
playlistItem.ValidateAndThrow();
playlistItem.Video.ValidateAndThrow();
PlaylistItemDao.Save(playlistItem);
}
I don't understand how my Create method could ever throw a PK violation if I call Merge on Video before saving.
Maybe it's a race condition? Or something else? Any advice appreciated.