You're not crazy for seeing the inconsistency. Unfortunately casual conversation like this is dogged with this kind of inconsistency.
First with regards to having to load the entire object graph before before adding a single note, nhibernate provides a Session.Load(id) method which doesn't actually issue any queries as opposed to Session.Get(id). Session'Load just creates a proxy which can be used for creating the associations required. var parent = Session.Load<Parent>(parentId); var child = new Child(); child.Parent = parent; The example I showed just exposed the internals of what would be going on to establish a bi-directional relationship. parent.AddChild(child); child.Parent = parent; You should probably encapsulate this two step activity in a single helper method on one of the objects that establishes both associations. You also mentioned that you had seen in most examples that child.Parent must be set. Yes this is true but it is a side effect of using the inverse=true attribute on the collection mapping in the parent. As you know in Nhibernate one of the ends of a bidirectional association must be chosen as the owner/manager of the relationship. If not nhibernate will do two updates of the entity (I think it will do one insert & one update) for each entity you add to the collection. So one end opts out of managing the association by indicating it is the inverse. The inverse is usually on the collection mapping (bag, etc) As a result adding the child to the parent is not required for nhibernate to correctly insert the newly created object into the db. All that is required is just child.Parent = parent. but then you'll have to manually save the newly created child with session.Save(child); Next time you load the parent from the database the new child will appear in its children collection. Parent.AddChild(child) is done for two reasons 1. to ensure in-memory consistency. 2. so you don't have to manually do session.Save(child). Instead you set cascade=save...etc and nhibernate will follow all cascaded associations of any persistent entity and ensure any newly attached or updated object it finds is saved as well. cascade=delete-orphan will also ensure that objects that are removed from a persistent objects collection when it was fetched will be deleted. I think this also addresses the other issue you raised about loading the entire collection of a parent before you can add an item to it. Nhibernate collections can be mapped as Set, List, Bag, Idbag etc. Nhibernate will load entire collections mapped with Set or List as soon as they are accessed so it can maintain their invariants of uniqueness for set & order for list. Bag on the other hand which allows duplicate items does not require the loading the entire collection before you can add to it. I'm not sure if this is standard or you have to use lazy="extra" or some other thing but it does exist. So you can probably do something like parent = Session.Load<ParentClass>(parentid); parent.children.add(child); without triggering a load of the entire parents children collection. As you can see I'm basically just running through the issues you've raised and writing out what I can remember about it. This kind of rambling behaviour inevitably leads to the inconsistency you earlier observed. Let me add that although your questions seem very simple they are very fundamental questions that everyone learning to use nhibernate encounters from time to time. Usually what I do then is study the options and decide on some "compromise" strategy for handing the issue or standardise on some conventions that avoid some of these difficult areas (like not trying to do one-to-one associations), then I happily forget the reason I arrived at the strategy. That is, until you come along asking questions and I'm left scratching my head trying to remember why I just use bag mapping and not bother with other exotic mapping until absolutely necessary. Once again I'm sure I left a lot out but I just hope it helps. Jide Ogundipe On Feb 9, 4:56 pm, Joe Brockhaus <[email protected]> wrote: > Thanks for your reply. > > A lot of the frustration has been because of the direction of the advice. > i.e. from a database-to-purist Object model POV. that was further > complicated by perhaps my failure to elaborate on the specific requirements > that RIA brought to the table (which i knew were not that big of a deal, but > something i just had to work around). only after repeated attempts did i > become terse. I had constantly affirmed my understanding that i _didn't > need_ the FK, and was understandably frustrated by the response of no > instead of why not. > > anyway .. > > In short, cascade settings mean that I MUST *always* act (CUD) on items in > collections through the parent? > -- if my client-side code is optimized NOT to send the entire entity graph > back over the wire to add a single, new Note, in order to save that note to > the database, i MUST re-build that entity graph from either the session or > the database, then save the parent? > > in your example: > > parent.addChild(child); // or > parent.children.add(child); > child.Parent = parent; // for bidirectionality > > was it your intention to imply that the method addChild() MIGHT NOT be > setting child.Parent, and might only be adding the child to the collection? > In my experience, and based on what everyone here has already said about > object references not key associations, child.Parent is required to be set. > Or is the rule more like: it either has to be set, or be in a collection of > a parent, and NHib will set it for me? Your 'for bidirectionality' leads me > to believe the rule is not like that (it's optional) -- so why would I get > errors if i DON'T set child.Parent? > > ----> just FYI :: Here's a very good example of a great clearly-explained > response not being consistent, even though it's very clear. I'm not crazy > for seeing the inconsistency, am I? Without the above being clarified, it's > very hard to move forward from this foundation of understanding. Er, it's > hard for 2 people to move forward toward common understanding with such > uncertainty looming in the shadows. > > Nonetheless, I'll keep going. I understand where the disconnection is, I > just need answers to clear them up. :D > > -- Perhaps now would be a good time to note that what I suggested > originally, did actually work... That's not to say that I'm out of the > woods, and it's only because i'm doing explicit stuff that I'd like to not > have to do, like build up the entire entity graph just to add a single Note. > > in case you're all wondering ... the below mapping works as desired: > public class TicketMap : ClassMap<Ticket> > { public TicketMap() { > Not.LazyLoad() > Table("Tickets") > Id(x=>x.TicketID).GeneratedBy.Sequence("SEQ_TICKET_ID") > > HasMany<Note>(x=> x.Notes) > .KeyColumn("TicketID") > .Cascade.SaveUpdate()}} > > public class NoteMap : ClassMap<Note> > { public NoteMap() { > Not.LazyLoad() > Table("Notes") > Id(x=>x.NoteID).GeneratedBy.Sequence("SEQ_NOTE_ID") > > HasMany<Note>(x=> x.Notes) > .KeyColumn("TicketID") > .Cascade.SaveUpdate() > > Map(x => x.TicketID) > .Not.Insert() > .Not.Update() > .Generated.Always() > > }} > > In order to map the FK column (TicketID) AND the instance (Ticket), AND ALSO > make it work for both read and write, I was doing it backwards. That is, I > was misunderstanding the *magic* NHib was doing to populate TicketID in my > domain, but then was frustrated that it couldn't save it to the database > (the logs indicated TicketID was resolved, but then oracle was saying > couldn't insert null). I was trying to map Ticket as not-insert/update, and > have NHib map TicketID. I was also originally just trying to save a single > Note by itself with > Session.Save(newNote_WithEverythingTheDatabaseTableNeedsButNoTicketInstance). > > -- Perhaps this is the question I should ask more directly: I would like to > be able to save an individual note without populating Ticket or without > building out the entire graph of the parent, BUT ALSO be able to save the > entire entity graph like Session.Save(newTicket_WithNewNotesAlso), because > there are scenarios where I will have that as well. Is that possible? > > It should now be clear why I need to understand if setting the note.Ticket > instance is a requirement, and if so, when? > > BTW, after all hell broke loose and assuming I ever got this resolved, I was > going to offer $10 to whomever decided to actually help. Of course I > couldn't say that before, but JideO and John Davidson will get it, if that's > good enough , for actually trying to help. Sometimes it pays to not be a > parrot on repeat =) > > Thanks > > ------ > Joe Brockhaus > [email protected] > ------------ > > > > On Mon, Feb 7, 2011 at 7:08 PM, JideO <[email protected]> wrote: > > I think a little empathy for Joe's frustration is in order. Joe I > > think you should appologise to people you would have offended by > > taking out your frustration on them. > > > I have also been on the receiving end of what initially seem to be > > unhelpful answers but later on when I understood the issues better, I > > find that one of the replies I had been frustrated with already > > contained the answer to my question. > > > Even at this, there are still some questions I have on nhibernate and > > on other forums where I have given up on getting a direct answer > > saying "this is so and so" or "this was not designed to be used like > > so try using it like so" or even "you idiot, what were you thinking, > > that's not what that means but this is the correct meaning". This > > happens even on paid support forums so its not just the OS community. > > Bottom line is this Joe, when you've written a response in frustration > > take a few deep breaths to think it over before pressing the send > > button. > > > I'm also learning nhibernate so I may be able to answer your questions > > at a level you'll understand. Sometimes the more experienced guys > > communicate in very condensed language, and unless you already > > understand the solution you may find it difficult understanding their > > explanation. > > > With regards to the cascade option, from my (limited) experience when > > cascade is used on a class property it specifies what should happen to > > a transient instance of objects referred to by the entity when the > > session is eventually flushed or a transaction committed. > > > eg > > > if you have a parent object which has a collection of child objects > > and the child collection is defined with a cascade=save-update. If the > > parent object was retrieved from the db/session and you add a child > > object to the parents collection, when the session is flushed or (the) > > transaction is committed the child object will be saved without having > > to do a session.save on the child object. > > > void dothis() > > { > > > ISession session = ..... > > > ITransaction t = session.BeginTransaction(); > > var parent = session.Get<Parent>(10); > > child = new child(); > > parent.addChild(child); // or > > parent.children.add(child); > > child.Parent = parent; // for bidirectionality > > > t.Commit(); // or session.flush(); > > // the child object > > will be inserted although you did not > > // explicitly save it > > } > > > If the parent object is also new, you have to call session.save on the > > parent object and all its children reachable by cascaded properties > > will be saved as well. > > > void saveNewParentAndChildren() > > { > > var parent = new Parent(); > > var child = new Child(); > > parent.addChild(child); > > child.Parent = parent; > > > var session = .... get your session > > session.Save(parent); > > session.Flush(); // this will insert > > cascaded child objects as well > > } > > > Another aspect which you seem to be missing is that you are referring > > to TicketID in your object model. This is not what is intended. You > > should not have to deal with the TicketID in your note class instead > > you would be referring to the Ticket object instead. > > > Instead of having > > > class Note > > { > > long TicketID; > > string Text; > > } > > > what you should have is > > > class Note > > { > > Ticket ticket; > > string text; > > } > > > Your mapping is where you would specify the column to be used in the > > Note table to store the ticket's id. > > > The third thing you seem to have trouble with is this issue of > > managing of the references yourself. If you are connecting object > > together nhibernate does not help you manage the object references on > > newly created connections. i.e. if you have a parent object and want > > to add a child object to it you will need to in YOUR code do > > parent.addChild(child) and child.Parent = parent, if you want a > > bidirectional association. > > > Does this help. > > > Jide > > > On Feb 4, 9:42 pm, Joe Brockhaus <[email protected]> wrote: > > > no one is willing to address this, eh? > > > > must be too simple of a problem. but alas, it is not. > > > the behavior I'm seeing is both inconsistent, and routinely unexpected > > given > > > common sense. > > > > for more context, please see: > >http://groups.google.com/group/fluent-nhibernate/browse_thread/thread... > > > > yes, i'm using Fluent for my mappings. > > > yes, I can get you the HBM if you absolutely need it. > > > yes, i'm confident that my problems are both a consequence of not mapping > > > Fluent correctly, and also correct mappings resulting in unexpected NHib > > > behavior. > > > > one such issue that does not seem to differ regardless of how I map my > > > entities: > > > -- Does NHib require me to add entities to the Session in the order in > > which > > > they need to be saved to correctly reference a one-to-many/many-to-one > > > association? For instance, if I have a Ticket instance which has mapped > > an > > > IList<Note> ... I'm seeing that if I add 3 Note instances, which have > > valid > > > Ticket instances references and valid Note.TicketID values, to the > > Session > > > BEFORE adding the Note.Ticket instance, then BeginTransaction().Commit .. > > > NHib fails on > > ... > > read more »- Hide quoted text - > > - Show quoted text - -- You received this message because you are subscribed to the Google Groups "nhusers" group. To post to this group, send email to [email protected]. To unsubscribe from this group, send email to [email protected]. For more options, visit this group at http://groups.google.com/group/nhusers?hl=en.
