Messing around with Castle ActiveRecord

This post is part of a small series on .NET ORM tools. You can find the rest of them here.

After getting a surprise kick, I got a suggestion from Josh to take a look at the Castle ActiveRecord project. So here goes, using Castle 1.0 RC3.

Scene refresher

I have a table of suppliers, and a table of states (or provinces, territories, prefectures etc.). Both suppliers and states have names, which are stored as strings/varchars, and IDs, which are stored as Guids/uniqueidentifiers. Each supplier can service many states. So we have a simple many-to-many relationship between the two main entities. It looks a bit like this:

I am using Aussie states for my tests, so I have populated the State table with the following names: NSW, VIC, QLD, TAS, SA, WA, ACT, NT.

Setting up Castle ActiveRecord

I created a new C# class library project, then added references to:

  • Castle.ActiveRecord.dll
  • Castle.Core.dll
  • Castle.Components.Validator.dll
  • Castle.DynamicProxy.dll
  • NHibernate.dll

I also decided to store the ActiveRecord configuration information in App.Config (there are several configuration methods mentioned in the documentation). My App.config looked like this:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <configSections>
    <section name="activerecord" type="Castle.ActiveRecord.Framework.Config.ActiveRecordSectionHandler, Castle.ActiveRecord" />
  </configSections>
  <activerecord>
    <config>
      <add key="hibernate.connection.provider" value="NHibernate.Connection.DriverConnectionProvider" />
      <add key="hibernate.dialect" value="NHibernate.Dialect.MsSql2005Dialect" />
      <add key="hibernate.connection.driver_class" value="NHibernate.Driver.SqlClientDriver" />
      <add key="hibernate.connection.connection_string" value="Data Source=127.0.0.1\SQLEXPRESS;Initial Catalog=SubSonicWorkshop;Integrated Security=True" />
    </config>
  </activerecord>
</configuration>

The next step was to create my entities, State and Subject:

[ActiveRecord]
public class State : ActiveRecordBase<State> {
  [PrimaryKey(PrimaryKeyType.GuidComb)]
  public Guid StateId { get; set; }
  
  [Property]
  public String Name { get; set; }
}

[ActiveRecord]
public class Supplier : ActiveRecordBase<Supplier> {
  [PrimaryKey(PrimaryKeyType.GuidComb)]
  public Guid SupplierId { get; set; }
  
  [Property]
  public String Name { get; set; }

  [HasAndBelongsToMany(
    typeof(State), RelationType.Bag,
    Table="Supplier_StatesServiced", ColumnKey="SupplierId", ColumnRef="StateId",
    Lazy=true)
  ]
  public IList<State> StatesServiced { get; set; }
}

ActiveRecord uses attributes to specify the mapping between entities and the database. This saves some of the work I went through producing mapping files for the NHibernate example, although as ActiveRecord is built on NHibernate, I am really just specifying the same information in a different form and letting ActiveRecord fill in the blanks.

I just needed to specify a [PrimaryKey] for each entity, add an [ActiveRecord] attribute on the class and a [Property] attribute on persistent properties, and then map the Supplier.StatesServicedcollection to the many-to-many relationship in the database using [HasAndBelongsToMany]. Besides the attributes, the entities need to inherit from ActiveRecordBase<T>, which gives the entity all the ActiveRecord-smarts, but also ties it to this persistence mechanism.

Populating the database

As per my previous posts, I’ll use a test fixture to run the remainder of the code. Before we start ActiveRecording, we need to initialise ActiveRecord so it can load up our entity mappings. I did this via a static constructor in the fixture. By calling ActiveRecordStarter.Initialize() without arguments, ActiveRecord will examine the current configuration (we used App.config for this earlier) and assembly to load the appropriate mappings. I assume this is synonomous with the NHibernate SessionFactory initialisation.

[TestFixture]
public class CastleFixture {
  static CastleFixture() {
    ActiveRecordStarter.Initialize();
  }
  ...

We are now ready to populate the database. The method to create a single Supplier is really nice using ActiveRecord:

private static void createSupplier(String name, String[] statesServiced) {
  Supplier supplier = new Supplier();
  supplier.Name = name;
  supplier.StatesServiced = State.FindAll(Expression.In("Name", statesServiced));
  supplier.Create();
}

As usual, we’ll run the code below to populate the database with our test data:

createSupplier("Dave^2 Quality Tea", new string[] { "NSW", "VIC" });
createSupplier("ORMs'R'Us", new string[] { "NSW" });
createSupplier("Lousy Example", new string[] { "TAS", "VIC" });
createSupplier("Bridge Sellers", new string[] { "QLD" });

Querying the data

As ActiveRecord is built on NHibernate, you can use most (all?) of the standard NHibernate features for querying your entities, such as HQL (Hibernate Query Language). In addition to that power, ActiveRecord provides a simpler query layer on top of NHibernate via the ActiveRecordBase<State> base class your entities use.

Here’s the two simple queries I’ve been using in the previous posts, first getting all suppliers and then suppliers that have an "s" in their name:

[Test]
public void Should_be_able_to_get_all_suppliers() {
  IList<Supplier> suppliers = Supplier.FindAll();
  Assert.That(suppliers.Count, Is.EqualTo(4));
}

[Test]
public void Should_be_able_to_get_all_suppliers_with_s_in_their_name() {
  IList<Supplier> suppliers = 
    Supplier.FindAll(Expression.Like("Name", "%s%"));
  Assert.That(suppliers.Count, Is.EqualTo(3));
}

And here’s the code to get data over the supplier-state relationship, retrieving all suppliers that service NSW:

[Test]
public void Should_be_able_get_all_suppliers_that_service_NSW() {
  IList<Supplier> suppliers = Supplier.FindAll(
    DetachedCriteria.For<Supplier>()
      .CreateCriteria("StatesServiced")
        .Add(Expression.Eq("Name", "NSW"))
    );
  Assert.That(suppliers.Count, Is.EqualTo(2));
}

To be honest I had trouble finding good examples on querying using ActiveRecord in the documentation. I ended up reproducing the NHibernate query. I’m not sure if there is a neater way of doing this – it wouldn’t surprise me if I’ve missed something obvious here. Still the query works, and I think that, for the first two queries in particular, you aren’t going to find a simpler, more concise or easier to understand syntax.

Vague semblance of a conclusion

There is some great information around about configuring ActiveRecord, and it was trivially easy to setup. The only real downside I found during this simple exercise was finding ActiveRecord-specific documentation on querying. I guess the required approach is to use the basic Find/FindAll methods where possible and drop back into NHibernate queries whenever you need more power.

Another consideration when using Castle ActiveRecord is the underlying design pattern itself. If you can’t or won’t use the ActiveRecord design pattern (for example, want to keep entities and persistence logic seperate), then it isn’t going to help you much.

And finally, Castle ActiveRecord also integrates with Monorail, which may be a plus for you if you are developing MVC web applications.

Comments