Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Fluent-NHibernate many-to-many cascade does not populate the link table

OK, no matter how I define these mappings, my many-to-many mapping does not want to work with cascade insert. I have tried various combination of Cascade() with Reverse() and removing all unnecessary properties just to understand if they had anything to do with this not working, but no lock.

It is really Simple stuff: I have a Message (like an email) which is sent from a user (I have called the entity BasicUser) to a number of users (through property To). User and Message in terms of recipients have a many-to-many relationship but FromUser has one-to-many. FromUser works fine and it is updated alright but my problem is with many-to-many. I even removed FromUser and relationship just to check if this was the problem, but did not help.

So here is the table design (Have removed the relationship from FromUser to BasicUser for simplicity)

enter image description here

And here are the mappings:

public class MessageMap : ClassMap<Message>
{

    public MessageMap()
    {
        Id(x => x.Id).Column("MessageId");
        Map(x => x.Subject);
        Map(x => x.SentAt);
        Map(x => x.Body);
        References(x => x.From).Column("FromUser");
        HasManyToMany(x => x.To).Table("BasicUserMessage").ChildKeyColumn("BasicUserId")
            .ParentKeyColumn("MessageId").Cascade().All();
    }
}

public class BasicUserMap : ClassMap<BasicUser>
{
    public BasicUserMap()
    {
        Id(x => x.Id).Column("BasicUserId");
        Map(x => x.DisplayName);
        Map(x => x.Username);
        HasManyToMany(x => x.Messages).Table("BasicUserMessage").ChildKeyColumn("MessageId")
            .ParentKeyColumn("BasicUserId").Inverse();
    }
}

And I call this and it does not work (table BasicUserMessage does not get populated): (Note Users with Id 1, 2 and 3 do exist - I also tried getting them from database and then add to list still did not work)

ISessionFactory factory = GetSessionFactory();
ISession session = factory.OpenSession();
Message m = new Message()
                {
                    Body = "Please note 2",
                    Subject = "Secret 2",
                    From = new BasicUser(){Id = 2},
                    SentAt = DateTime.Now,
                };
m.To.Add(new BasicUser(){Id = 1});
m.To.Add(new BasicUser(){Id=3});
session.SaveOrUpdate(m);
session.Close();
like image 394
Aliostad Avatar asked Mar 17 '11 13:03

Aliostad


1 Answers

The answer about transactions is correct-by-incidental-occurrence. Also correct-by-incidental occurrence is that this is manifesting itself as such because you are using something like an IDENTITY generator that requires a database trip on save to obtain the identity.

Here is what NHibernate does when you set a save-update cascade (or any cascade which implies that) on a many-to-many association with such:

Save the parent entity. This goes to the database immediately because of the identity strategy. The collection is "modified" (because it's new), so let's look at it's members. This step only occurs if inverse is not set on the parent's mapping of the relationship. Create an entry in the link table for each of them.

But wait, some of these entries are transient. Cascades are set properly, so that's okay - but in order to create the link table entry in the session, we need the id of those children, so let's save them immediately.

Now all relevant entities are persistent, and the session has a pending insert for all of the entries in the link table. Flushing the session will issue the commands and create those entries.

When you wrap your code in a transaction, committing the transaction flushes the session, so that is created when you commit. If you use an identity generator that doesn't require a DB round-trip, then the link table entries and the entities are all inserted at the same time, so you won't see the "disconnect" that you're seeing - if the session is never flushed, nothing is ever inserted, and when it is flushed, everything is inserted. If you have neither of these things, flushing your session explicitly will create the link table entries and all will be well.

like image 142
Matt Enright Avatar answered Oct 01 '22 16:10

Matt Enright