NHibernate: get the incremented value of version property after updating the entity

Let's assume, there is an entity:

class Book
{
    public Guid ID { get; set; }

    public int Version { get; set; }

    public string Author { get; set; }

    public string Title { get; set; }
}

The appropriate Fluent NHibernate-mapping:

class BookMapping : ClassMap<Book>
{
    public BookMapping()
    {
        Id(x => x.ID).GeneratedBy.GuidComb();
        Version(x => x.Version);
        Map(x => x.Author);
        Map(x => x.Title);
    }
}

I would like to get the incremented value of the Version property after IStatelessSession.Update() method call for the Book instance to perform the related update of another entity (see the comment):

using (var session = sessionFactory.OpenStatelessSession())
{
    using (var transaction = session.BeginTransaction())
    {
        session.Update(book);

        // Is it safe to use the value of the book.Version property here?
        //
        // Just for example:
        // bookReference.ID = book.ID;
        // bookReference.Version = book.Version; // I would like to have the latest (incremented) version here.
        // session.Insert(bookReference);

        transaction.Commit();
    }
}

Currently the debugging shows it works as desired. But I have not found the documentation which states that such behavior, i.e. increasing the version value after IStatelessSession.Update() method call, is guaranteed by NHibernate.

  1. Could you please provide the reference to the appropriate official documentation? Is there such guarantee?
  2. Which methods can cause the increment of the Version value before the ITransaction.Commit() method is called?

Upvotes: 0

Views: 974

Answers (1)

Radim K&#246;hler
Radim K&#246;hler

Reputation: 123861

The behavior described above is not random.

More than doc (not sure about it) could be to observe the code. The update of any entity is at the end delegated to the:

EntityUpdateAction.cs

Its method Execute() is below (just essential parts)

public override void Execute()
{
    ... // some importan stuff

    // here the UPDATE is executed
    if (!veto)
    {
        persister.Update(id, state, dirtyFields, hasDirtyCollection, 
                         previousState, previousVersion, instance, null, session);
    }

    EntityEntry entry = Session.PersistenceContext.GetEntry(instance);
    if (entry == null)
    {
        throw new AssertionFailure("Possible nonthreadsafe access to session");
    }

    // HERE we can see that NHibernate is going for GENERATED properties
    // imeditally 
    if (entry.Status == Status.Loaded || persister.IsVersionPropertyGenerated)
    {
        // get the updated snapshot of the entity state by cloning current state;
        // it is safe to copy in place, since by this time no-one else (should have)
        // has a reference  to the array
        TypeHelper.DeepCopy(state, persister.PropertyTypes,
                            persister.PropertyCheckability, state, Session);
        if (persister.HasUpdateGeneratedProperties)
        {
            // this entity defines property generation, so process those generated
            // values...
            persister.ProcessUpdateGeneratedProperties(id, instance, state, Session);

And this is the IPersister method ProcessUpdateGeneratedProperties() description:

/// <summary>
/// Perform a select to retrieve the values of any generated properties
/// back from the database, injecting these generated values into the
/// given entity as well as writing this state to the persistence context.
/// </summary>
/// <remarks>
/// Note, that because we update the persistence context here, callers
/// need to take care that they have already written the initial snapshot
/// to the persistence context before calling this method. 
/// </remarks>
/// <param name="id">The entity's id value.</param>
/// <param name="entity">The entity for which to get the state.</param>
/// <param name="state">The entity state (at the time of Save).</param>
/// <param name="session">The session.</param>
void ProcessUpdateGeneratedProperties(object id, object entity, 
         object[] state, ISessionImplementor session);

So, for sure, the behavior you experience is not random. It is driven by fact, that there are generated properties - generated by DB. NHibernate does always reload these, after every update...

To touch the second part, the only place where this is called is: session.Flush(). That could be part of the commit, but is in fact dependent on SessionFlush Mode (which could be even Never and we have to / can do it manually). Anyhow, once real DB UPDATE is triggered, it goes in the above bundle, and we can be sure, that DB generated properties are up to date

Upvotes: 2

Related Questions