views:

70

answers:

3

I have the following code:

public class A
{
    private ISessionFactory _sf;
    A(ISessionFactory sf)
    {
        _sf = sf;
    }

    public void SomeFunc()
    {
        using (var session = _sf.OpenSession())
        using (var transaction = session.BeginTransaction())
        {
            // query for a object
            // change its properties
            // save the object

            transaction.commit();
        }
    }
}

Its used as follows in a unit test

_session.CreateCriteria ... // some setting up values for this test

var objectA = new A(_sessionFactory);
objectA.SomeFunc();

// _session.Clear();

var someVal = _session.CreateCriteria ... // retrieve value from db to 
                                   //check if it was set to the 
                                   //proper value
                                   //it uses a restriction on a property
                                   //and a uniqueresult to get the object.
                                   //it doesnt use get or load.

Assert.That(someVal, Is.EqualTo(someOtherValue)); // this is false as long 
                                   //as the _session.Clear() is commented. 
                                   //If uncommented, the test passes

I am testing against a sqlite file database. In my tests I make some changes to the db to setup it up properly. I then call SomeFunc(). It makes the required modifications. Once I am back in my test, the session however doesnt get the updated values. It still returns the value as was before calling SomeFunc(). I have to execute _session.Clear() to have the changes reflect in my assertion in the test.

Why is this needed?

Edit: cache.use_second_level_cache and cache.use_query_cache are both set to false

Edit2: Read the following statements in the NH Documentation.

From time to time the ISession will execute the SQL statements needed to synchronize the ADO.NET connection's state with the state of objects held in memory. This process, flush, occurs by default at the following points

* from some invocations of Find() or Enumerable()
* from NHibernate.ITransaction.Commit()
* from ISession.Flush()

And in section 10.1 it says,

Ensure you understand the semantics of Flush(). Flushing synchronizes the persistent store with in-memory changes but not vice-versa.

So, how do I get the in memory objects to get updated? I understand that objects are cached per session. But executing a UniqueResult() or a List() should sync with the db and invalidate the cache, right?

What I cannot understand is why is the session reporting stale data?

A: 

It depens on what king of operations do you make. NHibernate has first level cache by default. It uses cache to get entities by ID and so on.

Sly
updated the question with some more info.
Amith George
A: 

The in memory view of objects (the level 1 cache) is per session.

A takes an ISessionFactory and opens its own session with its own transaction scope.

Even if the contents of the ISession used in SomeFunc are flushed to the database, _session will not see those changes until its level 1 cache is cleared.

mattk
I understand what you are saying, but the statements in the documentation are conflicting. On one hand it says that a session will never return stale data. On the other it says that flushing is a one way process: in-memory changes go to database. So, when exactly does the session return proper data? Do I have to keep clearing the cache before I query? In that case whats the point of the cache? I am sure I am missing some finer aspect here...
Amith George
As I understand it there are situations where the session will return stale data, such as this one where the db is changed in another session and also when the database is changed by another process. NHibernate will query the database only if it cannot load from L1 (or L2) cache.If you need to observe changes made since the data was first loaded, you can call ISession.Evict(object) to purge an instance from the cache, ISession.Clear() to empty the cache completely, or ISession.Refresh(object) to update the in-memory instance to the current database state.
mattk
A: 

You have two sessions. One is in A.SomeFunc, and the other is in your unit test. Each session has it's own instance of the entities in the session-cache (1st level cache). The sessions do not communicate or coordinate with each other. When one session writes its changes, the other session isn't notified. It still has it's own outdated instance in its session cache.

When you call _session.Clear(), you make the session "forget" everything by clearing the session cache. When you re-query, you are reading fresh data from the database, which includes the changes from the other session.

Jason Dentler