views:

33

answers:

2

Hi,

The following unit test fails, I make changes to an object that I've retrieved and then in the same session I make a query which appears to take into account the uncommited changes I've made to the object.

How can I get NHibernate to ignore any uncommited changes and only query the database?

[Test]
public void Test()
{
    // Arrange
    Area area1 = new Area { Name = "Area 1" };
    Area area2 = new Area { Name = "Area 2" };
    using (ISession session = SessionFactory.OpenSession())
    {
        using (var transaction = session.BeginTransaction())
        {
            session.Save(area1);
            session.Save(area2);
            transaction.Commit();
        }
    }
    int resultCount;

    // Act
    using (ISession session = SessionFactory.OpenSession())
    {
        Area existingArea;
        using (var transaction = session.BeginTransaction())
        {
            existingArea = session.Get<Area>(area1.ID);
        }

        existingArea.Name = area2.Name;

        using (var transaction = session.BeginTransaction())
        {
            resultCount = session.CreateCriteria<Area>().Add(Restrictions.Eq("Name", existingArea.Name)).List<Area>().Count;
        }
    }

    // Assert
    Assert.AreEqual(1, resultCount);
}
+1  A: 

All changes you make within the session are persistent across the session. So inside a session, there is no way to know what data was changed earlier in the session and what data matches the database schema.

You could try to workaround this by having two different sessions open. In one of them don't make any changes, and then you'll always know with what you started out with.

...Or, if there are specific spots in the application where you need history tracking, I would create some kind of history table where you save every change to certain items that you want to track. I think this solution would be the best, but then again, it all depends on what your specific needs are.

gillyb
Thanks for your response, I'm using this in an ASP.NET application and I open one session per request. Referring to the code sample, I fetch the existingArea, and then read values from form elements into it. I then perform a query to make sure that I'm not changing the value to anything that would conflict with any other Areas (Name must be unique). Multiple sessions is not an option and there is no necessity for history tracking (at least not in this context). I don't particularly need to know what has changed in the session, I just need my query to go straight to the database.
Matt
I'm not sure if i understood correctly - but maybe you can just Clone the Area object to a new one, and then when making the changes on it, you can compare the changed one with the cloned one from before the changes. If you implement IClonable and IComparable on the Area object, then this will be really easy for you to do throughout your code.
gillyb
+3  A: 

I've found what I need:

session.FlushMode = FlushMode.Commit;

The problem was caused because the default FlushMode (FlushMode.Auto) will sometimes flush changes to the database before queries to prevent stale reads. By changing to FlushMode.Commit it only writes changes to the database when commit is called which suits my needs.

Thanks for your help gillyb.

Matt
(+1) Let me say thanks to you! I didn't know that either, and i'm sure it will help me knowing that in the future.
gillyb
btw - Where did you read about it ?
gillyb
I typed 'session.' in Visual Studio and then scrolled through the auto complete list looking for things that stood out. Reading the help text tooltip for FlushMode.Auto gave it away.
Matt