Search code examples
nhibernatefluent-nhibernate

Avoid Delete-and-Reinsert when updating a compnonent mapped with Fluent NHibernate


Consider the following scenario: A user has a collection of spices, and each spice can either be 'in stock' or 'run out' (denoted by a boolean RunOut property).

I have mapped this relationship with FluentNHibernate using a component like this

public UserMappings()
{
   Id(x => x.Id);
   Map(x => x.FirstName);
   Map(x => x.LastName);
   Map(x => x.Email).Length(255).Unique();
   HasOne(x => x.Credentials).Cascade.SaveUpdate().Fetch.Select();
   HasMany(x => x.Spices)
      .Component(c =>
          {
              c.Map(x => x.RunOut);
              c.References(x => x.BaseSpice).Fetch.Join();
          }).Table("User_Spices").Fetch.Join();

}

The "Spices" collection in the above mapping is to UserSpice class (a value-object):

public class UserSpice
{
    public virtual Spice BaseSpice { get; protected set; }
    public virtual bool RunOut { get; protected set; }

    public static UserSpice Create(Spice baseSpice, bool runOut)
    {
        var userSpice = new UserSpice {BaseSpice = baseSpice, RunOut = runOut};
        return userSpice;
    }
}

This works 'fine' - however when I update any of the components (change a spice to RunOut = true, for example), all the user's spices are deleted and re-inserted.

I understand that this is happening because NHibernate has no way of uniquely identifying which user-spice reference it should update.

How can I model this differently to avoid this delete-and-reinsert behaviour?


Solution

  • NHibernate has different types of collections. Some of them are profiting from more complex mapping like <list> or <idbag>. Please read this part from documenation about collections types issues:

    http://nhibernate.info/doc/nh/en/index.html#performance-collections-mostefficientupdate

    short extract

    There is, arguably, one more advantage that indexed collections have over sets for many to many associations or collections of values. Because of the structure of an ISet, NHibernate doesn't ever UPDATE a row when an element is "changed". Changes to an ISet always work via INSERT and DELETE (of individual rows). Once again, this consideration does not apply to one to many associations.

    So, if you will use indexed list (or even idbag) NHibernate can understand which collection item is changed and can even call UDPATE

    And there is another question where you can findout how to map indexed list

    Fluent Nhibernate - Mapping a list results in NullReferenceException?

    short summary:

    HasMany(x => x.MyChildren).AsList(x => x.Column("Ordinal")).KeyColumn("AId").Not.KeyNullable();