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).
The problem is that you're using SaveOrUpdateCopy
. What happens here is that existingDocument
is not used to persist to the database.
From http://nhibernate.info/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()
andSaveOrUpdateCopy()
.
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:
The result from SaveOrUpdateCopy
should be the correct Document
. You can change your code to:
existingDocument = (Document)session.SaveOrUpdateCopy(existingDocument);
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;
You can reload the Document
. This means that after the SaveOrUpdateCopy
, you could put:
var existingDocument = FetchDocumentFromDatabase(someDocumentGuidId);
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.