views:

119

answers:

1

I'm sure this problem has been addressed before but I can't find a 'duplicate'. So the question is:

I have a bidirectional one-to-many association: document and comments. The comments table has a foreign key (not nullable) pointing to documents table. The classes are:

public class Document {
   public virtual Guid Id {get; set;}
   public virtual string Title {get; set;} 
   public virtual string Body {get; set;}
   public virtual IList<Comment> Comments {get; set;}
}

public class Comment {
   public virtual Guid Id {get; set;}
   public virtual Document Document {get; set;}
   public virtual string Body {get; set;}
}

//Mappings

public class DocumentMap : ClassMap<Document> {
    public DocumentMap(){
       Id(x => x.Id).GeneratedBy.GuidComb();
       Map(x=>x.Title);
       Map(x=>x.Body);
       HasMany(x=>x.Comments).Inverse().Cascade.All().AsBag();
    }
}

public class CommentMap : ClassMap<Comment> {
    public CommentMap(){
       Id(x => x.Id).GeneratedBy.GuidComb();
       Map(x=>Body);
       References(x=>x.Document).Column("DocumentId").Not.Nullable();
    }
}

//The problem

var existingDocument = FetchDocumentFromDatabase(someDocumentGuidId);
Console.WriteLine(existingDocument.Comments.Count()); //0
existingDocument.Comments.Add(new Comment { Document = existingDocument, Body = "Test"});
session.SaveOrUpdateCopy(existingDocument);//all OK
//commiting transaction etc
Console.WriteLine(existingDocument.Comments[0].Id); 
// 00000000-0000-0000-0000-000000000000

But I need my new comment Id here!

The comment is added OK to DB but the object Id is still an empty Guid.

How do I automatically fill child object Ids after they were added as part of parent object update?

I'm using NHibernate 3.0.0 Alpha 3 (seems to be very stable) and Fluent NHibernate v.1.1.

Please advice.

UPDATE 1: all changes propage to DB just fine (document properties are updated as well as new comments are added).

+2  A: 

The problem is that you're using SaveOrUpdateCopy. What happens here is that existingDocument is not used to persist to the database.

From http://nhforge.org/doc/nh/en/index.html#manipulatingdata-updating-detached:

The last case can be avoided by using SaveOrUpdateCopy(Object o). This method copies the state of the given object onto the persistent object with the same identifier. If there is no persistent instance currently associated with the session, it will be loaded. The method returns the persistent instance. If the given instance is unsaved or does not exist in the database, NHibernate will save it and return it as a newly persistent instance. Otherwise, the given instance does not become associated with the session. In most applications with detached objects, you need both methods, SaveOrUpdate() and SaveOrUpdateCopy().

What this means is that existingDocument is left alone and NHibernate uses entirely different instances to actually perform its actions on.

So what can we do. There are a few options:

  1. The result from SaveOrUpdateCopy should be the correct Document. You can change your code to:

    existingDocument = (Document)session.SaveOrUpdateCopy(existingDocument);
    
  2. You can switch to SaveOrUpdate. Now, I guess there is a very specific reason this may not be possible for you, because you specifically chose SaveOrUpdateCopy. Anyway, SaveOrUpdate is what you'd normally use;

  3. You can reload the Document. This means that after the SaveOrUpdateCopy, you could put:

    var existingDocument = FetchDocumentFromDatabase(someDocumentGuidId);
    
  4. If you are using SaveOrUpdateCopy because existingDocument came from a different ISession, you can also choose to reattach the existingDocument to the current ISession using Lock(). After you've 'locked' in the existingDocument into the new ISession, you can use SaveOrUpdate instead of SaveOrUpdateCopy and that instance, including the Comment will then be used and the Guid will be assigned.

Pieter
I'm curious which of the options you eventually chose.
Pieter