Preventing a cascading update of a many-to-many relationship with NHibernate/FluentNHibernate,

1.5k Views Asked by At

I have a simple many-to-many relationship between the User class and the Place class. This relationship is represented by the User's "Neighbourhood" property

public class User
{
  public virtual IList<UserPlace> Neighbourhood { get; set; }
}

The UserPlace class is just a component that adds a single piece of information to the relationship between a User and a Place:

public class UserPlace
{
  public virtual User User { get; set; }
  public virtual Place Place { get; set; }
  public virtual bool IsDefault { get; set; }
}

The relationship is mapped as follows:

public class UserMappings : ClassMap<User>
{
  //... other mappings

  HasMany(x => x.NeighbourHood)
            .Component(c =>
                           {
                               c.Map(x => x.IsDefault);
                               c.References(x => x.Place).Cascade.None();
                           }
            ).Not.LazyLoad().Cascade.SaveUpdate();
}

When a user is created a number of Places are loaded from the database and added to the user, with one of these being set to default. When the user is persisted, the associated UserPlace entities are also persisted.

The problem I'm experiencing is that each Place entity associated with a UserPlace is also updated. That's not the desired effect, and I've tried preventing the Cascade to Place from taking place (pun not intended) by setting Cascade to None()... this doesn't change anything, however.

I've only managed to stop this update from taking place by specifying that each property on Place should not be updated ([PropertyName].Not.Updated()). This works, but it's not a solution.

Does anyone have any ideas on how I can prevent this from happening?

Edit: The Place entity mapping has DynamicUpdate() specified as well - so the entity really shouldn't change when a user is saved.

PS! I don't think it makes a difference to this scenario (but I may be wrong!), but consider this also: The Place entities are loaded outside of NHibernate (using ADO.NET and a stored procedure, because of some geospatial querying I couldn't do with NHibernate) and then attached to the session by calling Session.Update() on each entity.

1

There are 1 best solutions below

1
On BEST ANSWER

This is not a cascading problem. NH updates the places because you call session.Update with each place. What you write in the PS is absolutely related to the problem.

When you call update on a detached entity NH can't know if the state of the entity actually changed or not. To avoid querying the data before updating (two database round-trips) it blindly updates the data.

To avoid this:

  • Set select-before-update (SelectBeforeUpdate in fluent)
  • Use session.Merge(place). It also performs a query before updating. Note that it has a return value which needs to be used.
  • Use session.Load(place.Id) to create a proxy of the place in question and assign it (note that NH loads the place again when accessing the proxy's properties)
  • Use session.Lock(place, LockMode.None) to put the objects into the session.
  • Use an NH query to retrieve the Places.