In NHibernate parlance, bidirectional associations have a notion of ownership. Ownership defines how the foreign key that underpins the bidirectional association is updated when new items are added to an association in memory. You can declare any end of the association to be the owner. Your choice of ownership affects the SQL that is generated in order to synchronize the association correctly to database. Let's elaborate this with an example.
Following diagram depicts the bidirectional relationship between the Employee
and Benefit
class:
The Employee to Benefit association is a bidirectional association which can also be seen as parent-child relation. Employee acts as parent and has multiple child benefits associated to it. From purely technical point of view, this is a one-to-many association. In one-to-many association, if ownership is not defined, then "one" side is considered as owner by default. So in our example, employee
is owner of the association. What that tells NHibernate is that employee is responsible for setting this association up in the database correctly. So, for every benefit record that gets inserted into the database, employee would make sure that foreign key column Employee_Id
on the Benefit
table is updated with its own id
. This is clear from the SQL that is generated by the following piece of code:
using (var tx = Session.BeginTransaction()) { var employee = new Employee(); employee.AddBenefit(new SeasonTicketLoan { Amount = 1200, MonthlyInstalment = 100, StartDate = new DateTime(2014, 5, 12), EndDate = new DateTime(2015, 4, 12) }); id = Session.Save(employee); tx.Commit(); }
The preceding code sends the following SQL to the database (stripped down to essential part for brevity):
If you remember, we have seen this example earlier when we talked about setting both ends of bidirectional association. As we had noted back then, after inserting the Employee
and Benefit
record, an additional update
statement is issued to update the Employee_Id
column on the Benefit
table. Think of it like this - because employee is responsible for making sure that foreign key column is updated correctly, employee would issue an additional update statement for every benefit item in its Benefits
collection. This additional update is actually unnecessary and at times may impact performance. If we turn the tables around and make the Benefit
entity the owner of the association, then this extra update
statement goes away. So how do you make Benefit
the owner of association? This is where the Inverse
property of one-to-many association mapping comes in. Inverse
is a Boolean property that you can declare on the one-to-many mapping, as shown next:
Set(e => e.Benefits,mapper =>
{
mapper.Key(k => k.Column("Employee_Id"));
mapper.Cascade(Cascade.All.Include(Cascade.DeleteOrphans));
mapper.Inverse(true);
},
relation => relation.OneToMany(mapping => mapping.Class(typeof(Benefit))));
The preceding code listing is part of employee mapping. Note the code in bold where Inverse
is set to true
. This is telling NHibernate that other end of this association (Benefit
in this case) is owner of the association and should be responsible for updating the foreign key in the database. After this change, if we run the preceding code again, we would notice that the extraneous update
statement is no more generated.
So to summarize – in one-to-many association, if "one" side is declared as owner then you get an unnecessary update statement during insertion of new records. On the other hand, if "many" side is made the owner, the unnecessary update
statement is not generated.
In the preceding code, we passed employee
instance to the session.Save()
method. You could have passed the benefit
instance as well here and got the same outcome. NHibernate does not care in which order you added entities to session or which entity is passed into the Save
method. NHibernate uses cascade settings and navigates as deep into the association tree of the entity as possible to determine which entities to save
or update
. In case of one-to-many associations, it is easier to pass in the "one" side entity. If you choose to pass "many" side entity into the Save
method then you would have to pass every entity in the collection. Imagine how your code would look when you have large number of entities on "many" side.
As we know, many-to-many association is just a combination of two one-to-many associations. So ownership concepts that apply to one-to-many association also apply to many-to-many association. There are two differences though that you need to be aware of. To understand these differences, let's use many-to-many association between Employee
and Community
in our domain as example.
Employee
and Community
class mappings. So you can, in principle, set Inverse
to true
on both of these mappings. Setting Inverse
to true
on the Communities
mapping of the Employee
class tells NHibernate that the Employee
class is not the owner of this association and hence not responsible for updating foreign key. Similar setting on the Members
mapping of the Community
class tells NHibernate that the Community
class is not the owner of this association. Do you see where this is going? We end up telling NHibernate that none of the two sides own the association. The end result is that no records are inserted into the intermediate table holding the foreign keys. Though NHibernate would report that all entities in the association are saved successfully, in reality, the association would not be saved. So, always ensure that you set Inverse
only on one end of the association.
Employee
is declared as the owner (mapping of the Members
collection on the Community
class has Inverse
set to true
) then the Communities
collection on Employee
must be initialized with the community instances that we want to persist. This can be confusing to beginners and experts alike, hence setting both ends of associations would save you from frustration when something goes wrong.The preceding points really boil down to two simple guidelines. One - always set only one end as inverse. Two - always use convenience methods to add items to collection instead of directly accessing collections, so that both ends of the association are set correctly. Convenience methods for many-to-many collection should not be very different from those for one-to-many collection. I will leave it to readers as an exercise to come up with these methods.
Inverse
is probably not the best name to describe the behavior it offers. Also, the way this property works in not intuitive. Instead of being able to say that "this end is owner", you need to configure to say "the other end is owner". The negative nature of the property makes it even difficult for the beginners to understand how inverse works. There is not much we can do about it. Take your time and let the idea sink into your brain and then inverse would be second nature to you.
In beginning, I used to get confused by cascade
and
inverse
. I always thought that they have some relation and one impacts the other. But in reality, they are totally independent and have nothing to do with each other. Cascade
determines whether the current action (save
, update
, delete
and so on.) should be propagated down the association or not. On the other hand, inverse
determines who is responsible for setting correct value in the foreign key column. Cascade
settings can be applied to any type of associations whereas inverse
can be specified only on the singular end of one-to-many association.