"If you need to represent that relationship in your domain then you should
have a References (many-to-one) relationship there, not the foreign key"
-- not sure we're on the same page.

my Ticket table, by itself, has no knowledge of the instances in the Note
table which have a foreign key column (namely Note.TicketID).

Ticket.Notes is mapped as:
        HasMany<Note>(x => x.Notes)
                                  .KeyColumn("TicketID") <-- that's the
foreign key column on the Note table
    -- TicketMap also maps Id(x => x.TicketID)

Note.Ticket is mapped as:
        References(x => x.Ticket)
                                   .Column("TicketID") <-- that's the
foreign key column on the Note table.
    -- NoteMap also maps Id(x => x.NoteID)


this, alone, does not work.
-Note.TicketID will NEVER be populated in the resulting Note instances on
load.
--- But it makes sense, because nothing maps the Note.TicketID field to the
column "TicketID" on the Note table.
--- This is why I had to Map(x => x.TicketID, "TicketID") in NoteMap.
----- Doing so makes the LOAD work, but causes the save to fail, because
NHibernate tries to add another, extra, parameter to the insert, which the
insert sql does not have a placeholder for.
-------- This is why i had to add Not.Insert() to the References(..)

Now, as for saving .. there are 2 different ways a Note can be saved. Either
it is saved WITH the Ticket (so it can get the TicketID generated in
Oracle), the first time the ticket is saved, or it is being saved (and the
ONLY thing being saved) for a ticket that already exists in the DB (and we
already know the TicketID).

- In scenario 1, the user has created a new ticket, added notes to it, and
clicked save. ALL those entities come back from RIA Services to my
DomainService Insert methods. So, if I create 1 Ticket with 3 Notes,
InsertNote() gets called 3 times, THEN InsertTicket() is called [by RIA
Services]. In these methods, I do Session.Save(instance).
- In scenario 2, the user is 'editing' an existing ticket in order to add a
new Note. The ticket instance already exists in the Ticket table, and so we
already know the TicketID. Adding a Note to the Ticket.Notes collection does
not make the Ticket dirty. When I save the Note, RIA Services ONLY sends the
Note instance back to the server to be inserted. So ONLY InsertNote() will
get called. Note.Ticket is null, but Note.TicketID is not.

After these methods are called, RIA Services then calls the (overridden)
PersistChangeSet() method, inside which I spin up a Session.BeginTransaction
in a using{}, then trans.Commit() to persist it all to the database.

Now, effects of those 2 scenarios with the supposed 'all i need' mapping...
-- scenario 1 (ticket & 3 notes are all saved in the same transaction, but
notes were added to NHib session first), the save fails, because NHib is
passing a null for Note.TicketID (even though Note.Ticket is not, and even
though Note.TicketID is populated)
-- scenario 2 (ticket already exists in database, only new Note is saved in
transaction), the new Note has TicketID populated, but Ticket is null, and
NHib tries to insert a null for TicketID again.

Now .. if I go ahead and Map(x => x.TicketID, "TicketID") .. the LOAD works,
but breaks the inserts:
-- save scenario 1: same problem with the order, but this time the error is
because NHib is supplying an extra parameter to the insert. the error is an
'index outside of bounds' error. the NHib output logging shows clearly the
disparity between the parameter count expectation by the insert sql and the
extra param passed to it.
-- save scenario 2: same problem ... (again, RIA sends back null
Note.Ticket, but Note.TicketID has a value) ... but still the param count
mismatches the insert string.

So, like i was saying, adding Not.Insert to the References() in NoteMap
solves this problem.

HOWEVER, my outstanding issues: (well, the most pressing atm)
1)  the order in which I insert the items into the NHibernate session
determines (appears to?) the order in which those items are persisted to the
database, IN STARK CONTRAST to the relationships defined by the mappings.
2) saving a NOTE, by itself (save scenario #2), causes a non-null constraint
failure in Oracle, even tho Note.TicketID is set to a valid value.
Note.Ticket is null, so it looks like NHib is nulling TIcketID as a result.
But that's at odds with the original problem where TicketID was being nulled
even when Note.Ticket was set (save scenario 1), because Note.TicketID
wasn't mapped in NoteMap.
3) save scenario #1: if I force the 'correct' order of inserts into the NHib
session (ticket first, then notes), the save 'works'. However, the cache
never gets updated. So, before insert, the Note.TicketID == 0, because the
Ticket.TicketID is 0, because it hasn't been inserted yet. After I insert,
Note.TicketID is never updated to the Ticket's generated ID. If I query
again, NHib loads the existing Ticket from the cache, including the 3 Notes,
all of which have 0 for TicketID. Why isn't the Note being updated? This
breaks the RIA association. (Ticket.Notes.Count == 0).  I said 'works'
before, because there are 7 (not including sequence generation) SQL
operations for inserting those 4 items (1 ticket, 3 notes): 1 insert Ticket,
3 Insert Note (with valid FK's, assuming i order it all correctly into the
session), and 3 Update Note (updating values which are already valid and
inserted).


I'm really hoping you can make sense of all that. I made it as clear as it
can be. If you think it would help, I can give you the HBM output as well.


------
Joe Brockhaus
joe.brockh...@gmail.com
------------


On Fri, Feb 4, 2011 at 10:59 AM, James Gregory <jagregory....@gmail.com>wrote:

> The fix is to not put your foreign key ids in your entities. If you need to
> represent that relationship in your domain then you should have a References
> (many-to-one) relationship there, not the foreign key. Putting foreign key
> ids in your entities is a big bad practice.
>
> Your Ticket should have a HasMany(x => x.Notes) and your Note should have a
> References(x => x.Ticket). That's all you need. I assume you'll be saving
> the Ticket rather than the individual notes directly, so you'll probably
> want Inverse on the Notes relationship which'll make the Ticket responsible
> for cascading saves to each of the Notes.
>
> --
> You received this message because you are subscribed to the Google Groups
> "Fluent NHibernate" group.
> To post to this group, send email to fluent-nhibernate@googlegroups.com.
> To unsubscribe from this group, send email to
> fluent-nhibernate+unsubscr...@googlegroups.com<fluent-nhibernate%2bunsubscr...@googlegroups.com>
> .
> For more options, visit this group at
> http://groups.google.com/group/fluent-nhibernate?hl=en.
>

-- 
You received this message because you are subscribed to the Google Groups 
"Fluent NHibernate" group.
To post to this group, send email to fluent-nhibernate@googlegroups.com.
To unsubscribe from this group, send email to 
fluent-nhibernate+unsubscr...@googlegroups.com.
For more options, visit this group at 
http://groups.google.com/group/fluent-nhibernate?hl=en.

Reply via email to