nhibernatetransactionssecond-level-cache

The Nhibernate transaction has been successfully committed, but the result of the query is still the unmodified value


Execute the following server code, and then check the promotion table and task table in the database. The related fields have been updated correctly, which indicates that the transaction has been successfully committed.

using (ITransaction tx = session.BeginTransaction())
{
    try
    {
        Promotion p = session.Get<Promotion>(request.PromotionId);
        p.Status = PromotionStatus.Canceled;
        foreach (Task task in p.Tasks)
        {
            if (task.AnnounceStatus == TaskAnnounceStatus.New)
            {
                task.AnnounceStatus = TaskAnnounceStatus.PromotionCanceled;
                task.CancelTime = DateTime.Now;
                //session.Update(task);
            }
        }
        tx.Commit();
    }   
    catch
    {
        tx.Rollback();
        throw;
    }
}

Then execute the following query(Query A), the data obtained is also the updated value. It looks like everything is very good.

tasks = session.Query<Task>().Where(p => p.AnnounceStatus == Model.TaskAnnounceStatus.New && p.ProcessStatus == Model.TaskProcessStatus.New).ToList();

However, if I execute a query on the task using the following code before committing the transaction, the result of the above query(Query A) will get the old unmodified value. At the same time, what you see in the database is still the correctly updated value.

Task task = session.Get<Task>(taskId);

So I modified the first piece of code and explicitly called the update method (see the code at the comment), and everything worked fine this time.

My guess is that Nhibernate's cache is causing the above problem. I use syscache2 to manage the second-level cache, the cache was set to ReadWrite, and use sessionFacotry.getCurrentSession to manage Nhibernate's session.

Hope someone can help me explain how this works.


Solution

  • You execute query session.Get<Task>(taskId); first. This loads the entity in first level cache.

    Then in your transaction, you Get the Promotion entity. The Task is the IEnumerable property of it. As lazy load may be, your foreach loop iterate through Task entity with ID taskID - Modifies it - Updates it - Transaction is successful. As all this is happening inside the transaction, your initial entity returned by session.Get<Task>(taskId); is not updated. It still hold the old values.

    Then, you again session.Query<Task>() outside the transaction. This time, NHibernate see that the entity with same identifier is already loaded in session cache (with session.Get<Task>(taskId); query), it does not load that entity again, it simply returns the entity already in session cache. As that entity hold the old values, you see the problem.

    To confirm this, put all these queries inside the transaction block and check the result.
    Alternatively, manage so scope of session properly.

    Understand that your ISession is your Unit Of Work; scope it carefully.