0

I'm trying to understand how NHibernate works. To do so I've created a small test, given below. But the test is failing on the marked line and I don't understand why.

What am I misunderstanding?

To briefly explain the code chunk... I create an entity in the DB. Then I call Evict to remove the entity from session cache so that the next call for it would force a DB read. Then I do a DB read, but instead of getting back an entity instance read from DB, I get NULL, on the marked line.

using NHibernate;
using MyCorp.MyProject.Resources.MyEntity;
using MyCorp.MyProjectTests.Common.Fixture;
using Xunit;

namespace MyCorp.MyProjectTests.Common.DB
{
    [Collection("Component")]
    public class NHibernateTest
    {
        private readonly ISessionFactory dbSessionFactory;

        public NHibernateTest(ComponentFixture componentFixture)
        {
            this.dbSessionFactory = componentFixture.DatabaseFixture.DBSessionFactory;
        }

        [Fact]
        [Trait("Category", "Component")]
        public void TestSessionCache()
        {
            const string QUERY = @"DELETE MyEntityModel mg WHERE mg.Id = :id";
            const string TITLE = "NHibernate session test object";

            using (ISession dbSession = this.dbSessionFactory.OpenSession())
            {
                // Create new entity and then remove it from session cache.
                long id = (long) dbSession.Save(new MyEntityModel
                {
                    Title = TITLE
                });
                dbSession.Evict(dbSession.Get<MyEntityModel>(id));

                // Entity loaded from DB and stored into session cache.
                Assert.Equal(TITLE, dbSession.Get<MyEntityModel>(id).Title); // ===== FAILS HERE =====

                // Delete entity from DB, but don't evict from session cache yet.
                dbSession.CreateQuery(QUERY).SetParameter("id", id).ExecuteUpdate();

                // Entity still reachable through session cache.
                Assert.Equal(TITLE, dbSession.Get<MyEntityModel>(id).Title);

                // Evict deleted entity from session cache.
                dbSession.Evict(dbSession.Get<MyEntityModel>(id));

                // Entity not available in neither DB nor session cache.
                Assert.Null(dbSession.Get<MyEntityModel>(id));
            }
        }
    }
}
morgoth84
  • 1,070
  • 2
  • 11
  • 25

1 Answers1

1

Save() is not equal to SQL INSERT.

Save() means: make the session aware of this object and have the session send it to the database at a suitable time. Depending on mappings and configuration, this can be before Save() returns, or not.

So you evict the object from the session before it gets persisted.

If you omit the call to Evict(), your test works because none of the other code actually depends on the item being in the database (the DELETE statement may indicate it found 0 rows to delete, but this is not a problem for the test).

To use automatic flush behaviour, you should always be inside a transaction, not just a session. In fact, for best reliability, you really should always be inside a transaction whenever you are inside a session (other patterns are possible, but tend to be more complicated to get right).

Here is the documentation on when flushing of changes to the database happens: http://nhibernate.info/doc/nhibernate-reference/manipulatingdata.html#manipulatingdata-flushing

Oskar Berggren
  • 5,583
  • 1
  • 19
  • 36