Adding a new item to a collection using Fluent NHibernate doesn't INSERT the new row in the DB

728 views
Skip to first unread message

Mike Christensen

unread,
Jan 20, 2014, 7:46:50 PM1/20/14
to nhusers
(StackOverflow question in case anyone wants upvotes)

I have a model called `ShoppingLists` and each `ShoppingLists` has a collection of `ShoppingListItems` called `Items`.  What I would *like* to be able to do is add a new item to my list as such:

    dbList.Items.Add(new ShoppingListItems(Guid.NewGuid(), identity.UserId, source.Raw));

I would expect the `ShoppingListItems` to automatically be *linked* to its parent `ShoppingLists` class, and for NHibernate to create the appropriate SQL `INSERT` statement when the transaction is committed.  However, instead I get the exception:

    NHibernate.StaleStateException was unhandled
      HResult=-2146232832
      Message=Unexpected row count: 0; expected: 1
      Source=NHibernate

What I have to do instead is create the object, save it, then add it to the collection:

    var newItem = new ShoppingListItems(Guid.NewGuid(), identity.UserId, source.Raw);
    newItem.ShoppingList = dbList;
    session.Save(newItem);
    dbList.Items.Add(newItem);

I'd like to eliminate the need to do this.  My mappings for `ShoppingLists` is as such:

    Id(x => x.ShoppingListId);
    
    Map(x => x.UserId).Not.Nullable();
    Map(x => x.Title).Not.Nullable();
    
    HasMany(x => x.Items)
       .KeyColumn("ShoppingListId")
       .Cascade.Delete(); // If Shopping List is deleted, delete all the Items that reference this list

And my mappings for `ShoppingListItems` is:

    Id(x => x.ItemId);
    
    Map(x => x.Raw).Length(50);
    Map(x => x.Qty);
    Map(x => x.Unit);
    Map(x => x.UserId).Not.Nullable();
    Map(x => x.CrossedOut).Not.Nullable();
    
    References(x => x.Recipe).Column("RecipeId");
    References(x => x.Ingredient).Column("IngredientId");
    References(x => x.ShoppingList).Column("ShoppingListId");

I've tried playing around with `Cascade.All()` on each, to no avoid.  Any ideas?

Pete Appleton

unread,
Jan 21, 2014, 4:32:15 AM1/21/14
to nhu...@googlegroups.com

A few things that cross my mind:

 

1.  You'll definitely need an appropriate Cascade style for the "HasMany" mapping on ShoppingLists.Items, e.g. SaveUpdate - not sure which mapping system you're using, so don't know the exact syntax

2.  99% of the time, this mapping should be 'Inverse'

3.  It looks as you're manually assiging a new ID to the new 'ShoppingListItems' object, which I suspect is causing NH to believe that the new object is persistent.

 

My general pattern for this sort of thing is as follows (pseudo-code for interesting bits only):

 

class ShoppingList {

   Guid Id { get; protected set; }

   ISet<ShoppingListItem> Items { get; protected set; }

 

  public ShoppingList() {

    this.Items = new …

  }

}

 

class ShoppingListLine {

   Guid Id { get; protected set; }

    public ShoppingList List { get; protected set; }

 

  ShoppingListLine(ShoppingList list) {

    this.List = list;

    this.List.Items.Add(this);

  }

 

  void Delete() {

    if(this.List != null) this.List.Items.Remove(this);

    this.List = null;

  }

}

 

var dbList = ….;

var newItem = new ShoppingListLine(dbList);                                     // ctor handles association maintenance

 

 

 

/Pete

--
You received this message because you are subscribed to the Google Groups "nhusers" group.
To unsubscribe from this group and stop receiving emails from it, send an email to nhusers+u...@googlegroups.com.
To post to this group, send email to nhu...@googlegroups.com.
Visit this group at http://groups.google.com/group/nhusers.
For more options, visit https://groups.google.com/groups/opt_out.

Mike Christensen

unread,
Jan 21, 2014, 2:38:26 PM1/21/14
to nhusers
Looks like this almost works..  Setting the initial ID to Guid.Empty definitely makes the problem go away..  I also set the mapping as:

HasMany(x => x.Items)
   .KeyColumn("ShoppingListId")
   .Inverse()
   .Cascade.All();

Now I can add the item as such:

dbList.Items.Add(new ShoppingListItems
{
   Raw = source.Raw,
   UserId = identity.UserId
});

And it will create the new item.  However, one small problem: The new item does NOT get linked to dbList.  Its ShoppingListId property is left as null.

Shouldn't adding a new item to dbList.Items make the ShoppingListId get set to the primary key of dbList?  Or, do I have to manually specify this?  Thanks!

Mike

Fran Knebels

unread,
Jan 21, 2014, 3:01:34 PM1/21/14
to nhu...@googlegroups.com
that's because you haven't made a link from the child to the parent.

when adding records to child lists in nhibernate it is common to add both add/remove functions that manage the lists.  Here's an example using a child collection of ContactInformation.

        public virtual void AddContactInformation(ContactInformation contactInformation)
        {
            if (ContactInformation.Contains(contactInformation))
                return;

            contactInformation.Contact = this;
            ContactInformation.Add(contactInformation);
        }

        public virtual void RemoveContactInformation(ContactInformation contactInformation)
        {
            if (!ContactInformation.Contains(contactInformation))
                return;

            ContactInformation.Remove(contactInformation);
        }


Mike Christensen

unread,
Jan 21, 2014, 3:05:52 PM1/21/14
to nhusers
I was under the impression that this was taken care of automatically by NHibernate.  In other words:

dbList.Items.Add(newItem);

Would automatically set:

newItem.ShoppingList = dbList;

Since I added it to dbList's Items collection.

So, what you're saying is this has to be done by me (or through a add function that I create)?  If so, that's fine - I just want to make sure this is by design and not due to improper mappings.  Thanks!

Mike

Fran Knebels

unread,
Jan 21, 2014, 3:14:29 PM1/21/14
to nhu...@googlegroups.com
yes.  or expose it in your constructor for the line item.

Mike Christensen

unread,
Jan 21, 2014, 3:18:40 PM1/21/14
to nhusers
Excellent!  I think I have everything working now..  Thanks so much for your help, and Pete as well..

Mike
Reply all
Reply to author
Forward
0 new messages