Tuesday, September 09, 2008
#
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
Sunday, June 29, 2008
#
NHibernate supports the ability to map a single entity to multiple tables. Now I know this is not encouraged, but it is sometimes necessary. I just started a project in which the existing code base uses the ASP.NET Membership Provider. The existing software uses ActiveRecord and wanted to represent the concept of a user with several fields for the membership database. The user in the membership database is spread over several tables so the ActveRecord model used had lots of formulas which really smelled.
To understand what NHibernate can do, Ayende has a really nice post on this subject.
To avoid the formula clutter, I added ActiveRecord support for this. T
Here's an example of how to mark an ActiveRecord class to join multiple tables
[ActiveRecord("People")
JoinedTable("Addresses", Column = "person_id")]
public class Person : ActiveRecordBase
A new attribute, JoinedTable has been introduced that identifies the additional table to be joined an the column used to join the, Multiple of these attributes can be present.
Here's how you would identify properties, field's, any's or components that come from the alternate table
[Nested("name_", Table = "Addresses")]
public FullName FullName
{
get { return _fullName; }
set { _fullName = value; }
}
[Property(Table = "Addresses")]
public string Address
{
get { return _address; }
set { _address = value; }
}
[Field(Table = "Addresses")]
public string City;
As you can see, the [Property], [Field], [Nested], and [Any] attributes now accept a Table property which links to a Table in a [JoinedTable] attribute
Cheers. Craig
Friday, March 28, 2008
#
My name is Craig Neuwirt and I am principal consultant and partner of Improving Enterprises. I am the proud father of 2 girls and a boy ages 5, 3 and 8 months. My limited or nonexistent blogging is directly related to that. However, my friend Ayende award me, and I quote, "the first hostile blogging award". Ayende was also nice enough to provide me a blog so the least I can do is try and use it. Despite my limited free time, I am an active contributor to Castle and RhinoTools open source projects. I have had several comments that mailing groups are not the best medium to share my information so I will try and utilize this blog to do that. They will not be plentiful nor beautiful, but if I have something to say, i'll see it here.