6

i have x which an object of type objectX which has a property ListOfObjectYs thats a

List<objectY>

the nhibernate mapping looks like this:

    public ObjectXMap()
    {
        HasMany(x => x.ListOfObjectYs).AsBag().Inverse(); 
    }

when i go to save it, i change some properties on objectX and then have:

    Session.SaveOrUpdate(x);

now i need to update this property that is a list. I get a new list of objectYs and i want to replace the existing list of objectY with a new list. do i need to do this?

  foreach (ObjectY y in x.ListOfObjectYs)
                {
                   Session.Delete(y);
                    deleted = true;
                }
                if (deleted)
                {
                   _session.Flush();
                }
                x.ListOfObjectYs.Clear();

                foreach (ObjectY y in newObjectYList)
                {
                    x.ListOfObjectYs.Add(y);
                   Session.SaveOrUpdate(y);
                }
                _session.Flush();

my questions are:

  1. Do i have to delete everything and flush before adding new ones.
  2. Do i need to do all of these incremental saves in between

is there a better way of doing this update where i need to update an object (properties) but also update properties that are list where there is a whole new list (meaning that items need to be deleted and added).

leora
  • 188,729
  • 360
  • 878
  • 1,366

2 Answers2

4

The answers to your questions are no and no. If you want to replace the list you should clear it and add new items. If you have cascade set to all-delete-orphan, as in James Kovacs' answer, then the changes to the collection will be persisted when the session is flushed.

It's important to understand what Save, Update, and SaveOrUpdate mean in NHibernate:

  • Save - make a new object persistent
  • Update - make a changed detached object persistent
  • SaveOrUpdate - Save or Update depending on the unsaved-value of the object's identifier

See also Manipulating Persistent Data.

Assuming that all your objects were loaded in the same session then replacing a collection may be as easy as:

x.ListOfObjectYs.Clear();
foreach (ObjectY y in newObjectYList)
{
    x.ListOfObjectYs.Add(y);
}
_session.Flush();
Jamie Ide
  • 48,427
  • 16
  • 81
  • 117
  • how can you do that without actually saving the Y objects. is your point, that cascade.AllDeleteOrphan() takes care of that ?? – leora Dec 10 '10 at 03:32
  • i am now getting an error: A collection with cascade="all-delete-orphan" was no longer referenced by the owning entity instance: ObjectY – leora Dec 10 '10 at 03:47
  • 3
    If the Y objects are new then you have to Save them first to make them persistent. The error message may indicate that you are re-assigning the collection reference somewhere, see http://www.sleberknight.com/blog/sleberkn/entry/20070329 and http://stackoverflow.com/questions/2127016/nhibernate-mapping-a-collection-with-cascadeall-delete-orphan-was-no-longer-r. – Jamie Ide Dec 10 '10 at 04:15
3

You need a cascade on your HasMany(). If the child objects are fully owned by the parent, Cascade.AllDeleteOrphan() is a good choice. This way saves, updates, and deletes on the parent are automatically cascaded to the child objects, which removes the need for your complex foreach block. With this in place, simply update your collection and commit your transaction. NHibernate will take care of the rest.

UPDATE: To modify the list, simply add and remove items from the list as you normally would with a .NET collection. For example:

public void RemoveY(ObjectY someY) {
    ListOfObjectYs.Remove(someY);
}

public void AddY(ObjectY someY) {
    ListOfObjectYs.Add(someY);
}

public void ClearAllY() {
    ListOfObjectYs.Clear();
}

When the transaction is committed, any changes to the ListOfObjectYs collection will be persisted along with the parent ObjectX.

James Kovacs
  • 11,549
  • 40
  • 44
  • to be clear, i add Cascade.AllDeleteOrphan() to my mapping file. Thats simply enough. Can you provide the syntax to do the update of the list as I don't quite follow what you mean, "i can just remove the for each", at some point i have to delete existing entries and add new ones. I dont see how i get out of this. – leora Dec 10 '10 at 03:25
  • - i am now getting an error: A collection with cascade="all-delete-orphan" was no longer referenced by the owning entity instance: ObjectY – leora Dec 10 '10 at 03:52
  • Are you assigning one collection to another? e.g. x1.ListOfYs = x2.ListOfYs? You should be copying items between collections, not duplicating references to collections. If that's not it, you'll have to provide the relevant code. – James Kovacs Dec 10 '10 at 04:37
  • i think @Jamie Ide got it, where i just had to save the new child objects before adding them to the collection. – leora Dec 10 '10 at 05:17