views:

177

answers:

2

Continuing my explorations in NHibernate with my podcast application, I've come across something odd: NHibernate is UPDATEing when I would expect an INSERT. The situation is a simple one-to-many relationship between a podcast and its items. Here's my NHibernate mapping:

<hibernate-mapping assembly="App.DataModel">
  <class name="Feed" table="Feeds">
    <!-- snip -->
    <bag name="FeedItems" table="FeedItems" cascade="all">
      <key column="FeedId" />
      <one-to-many class="FeedItem" />
    </bag>
  </class>
</hibernate-mapping>

The situation is: I've created a new Podcast object, updated it from the feed (so the Podcast.FeedItems collection property contains a number of FeedItems) and am now saving it to the database via ISession.Save(). Judging by the NHibernate log file, the Podcast object is correctly inserted into the database, but the FeedItems collection is being UPDATEd instead. Here's the SQL generated for the Podcast:

INSERT INTO Feeds (
    Uri, IsPublic, Title, Description, Link, Language, Copyright, 
    LastBuildDate, PublishDate, Docs, Webmaster, Author, Subtitle, 
    Summary, OwnerName, OwnerEmail, IsExplicit, ImageUri, Id 
) VALUES (
    @p0, @p1, @p2, @p3, @p4, @p5, @p6, @p7, @p8, @p9, @p10, @p11, 
    @p12, @p13, @p14, @p15, @p16, @p17, @p18
);

And here's the SQL generated for the FeedItems:

UPDATE FeedItems SET PublishDate = @p0, Author = @p1, 
    IsExplicit = @p2, Subtitle = @p3, Summary = @p4, 
    Duration = @p5 WHERE Id = @p6;

I don't get why an UPDATE is happening when the FeedItems don't necessarily even exist yet. I'm chasing this down, because the test that exercises this code is failing with the exception

NHibernate.StaleStateException: Unexpected row count: 0; expected: 1

Update with more logging: Following some more tweaks, I spotted the following line in the NHibernate log:

Collection found: [Podcast.FeedItems#GUID], was: [<unreferenced>] (initialized)

+2  A: 

I've seen this happen when the unsaved-value attribute on my element didn't match up against the corresponding property value for a new entity. Here's an example:

<class name="Foo">
  <id name="ID" unsaved-value="-1" type="Int32">
    <generator class="native"/>
  </id>
</class>

and

public class Foo 
{
  private int _id = 0;
  public int ID {
    get { return _id; }
    set { _id = value; }
  }
}

In this case, saving a new Foo will result in NHib generating an UPDATE since it thinks that the object already has an identity.

John Rayner
Does this only apply to IDs? I'm using GUIDs for IDs, but all my mappings include an unsaved-value attribute specifying the zero GUID on the <id /> element.
alastairs
I would tend to use a `System.Guid?` (`Nullable<System.Guid>`), and have `null` be the `unsaved-value`.
Justice
Doesn't the non-null requirement on ids prevent this?
alastairs
+2  A: 

The key to this issue was the exception message

NHibernate.StaleStateException: Unexpected row count: 0; expected: 1

Thanks to this question I realised that my IDs were being assigned by my own code and not by NHibernate, which caused NHibernate to think that the entities had already been saved. Once I removed the code auto-generating new IDs, the problem went away.

alastairs