I recently introduced the NHibernate.Caches.SysCache into my application to cache some very common queries. I created a unit test like the following to ensure it was properly working.
[Test]
public void DoesNotLoadEntityFromSysCache()
{
Foo foo = new Foo();
foo.Name = "Craig";
using (ISession s = OpenSession())
{
s.Save(foo);
}
using (ISession s = OpenSession())
{
Foo result = (Foo)s.CreateCriteria(typeof(Foo))
.Add(Property.ForName("Name").Eq("Craig"))
.SetCacheable(true)
.UniqueResult();
Assert.IsNotNull(result);
}
using (ISession s = OpenSession())
{
using (IDbCommand command = s.Connection.CreateCommand())
{
command.CommandText = "DELETE FROM Foos";
command.ExecuteNonQuery();
}
}
using (ISession s = OpenSession())
{
Foo result = (Foo)s.CreateCriteria(typeof(Foo))
.Add(Property.ForName("Name").Eq("Craig"))
.SetCacheable(true)
.UniqueResult();
Assert.IsNotNull(result);
}
using (ISession s = OpenSession())
{
s.Delete("from Foo");
s.Flush();
}
}
Too my surprise, it didn't work :-(
In a nutshell, I was using NHibernate to Save the Foo entity. The first error I made, was to not put this in a transaction. NHibernate requires it to be in a transaction to propertly work. Without the transaction, the Foo entities in the Second Level Cache were polluted and not candidates for query cache. However, applying the Save in a transaction made the unit test pass, but this prevented testing what I actually wanted to do since the committing of the transaction put my Foo in the second level cache and thus put the cache in a populated state before my query ran. The simple solution was to insert the Foo explicitly and then run the queries. Here is the correct test
[Test]
public void CanQueryFromSecondLevelCache()
{
using (ISession s = OpenSession())
{
using (IDbCommand command = s.Connection.CreateCommand())
{
command.CommandText = "INSERT INTO Foos VALUES('Craig')";
command.ExecuteNonQuery();
}
}
using (ISession s = OpenSession())
{
Foo result = (Foo)s.CreateCriteria(typeof(Foo))
.Add(Property.ForName("Name").Eq("Craig"))
.SetCacheable(true)
.UniqueResult();
Assert.IsNotNull(result);
}
using (ISession s = OpenSession())
{
using (IDbCommand command = s.Connection.CreateCommand())
{
command.CommandText = "DELETE FROM Foos";
command.ExecuteNonQuery();
}
}
using (ISession s = OpenSession())
{
Foo result = (Foo)s.CreateCriteria(typeof(Foo))
.Add(Property.ForName("Name").Eq("Craig"))
.SetCacheable(true)
.UniqueResult();
Assert.IsNotNull(result);
}
}
Much thanks to James Kovacs and Ayende for setting me straight on the often mysterious Second Level Cache