Geeks With Blogs

News
www.flickr.com
This is a Flickr badge showing public photos from hammett1. Make your own badge here.

hamilton *hammett* verissimo Another special person... just like everyone else

Castle ActiveRecord is an important piece of Castle's stack, and according to the questions we get on Castle's forum it's being heavily pushed and bended. This is great and I hope the users are having a great experience and being able to deliver more in less time. That's why we created it in first place.

What is it?

If you don't know what I'm talking about then you can check the ActiveRecord pattern described on wikipedia. Quoting:

"Active record is an approach to reading data from a database. A row in a database table or view is wrapped into a class, thus an object instance is tied to a single row in the database. When an object is created, a new row will be added to the database when it's saved. When an object is loaded, it gets its information from the database, when it's updated, the row in the database gets updated. The wrapper class implements getters and setters for each column in the table or view."

I'll add that non-instance (static) methods are used to act on the whole set of records.

That being said, Castle ActiveRecord works by inspecting attributes on the class to infer mapping information and finally generate the necessary NHibernate mappings. But it does more than that, in fact it deliveries a complete different solution.

What makes NHibernate great? IMHO its non-intrusiveness. You can map a class to a database without the need to extend from some NHibernate base class and this is great. But this comes with a cost. With NHibernate you need to develop a layer to deal with the data classes and the NHibernate's session. Usually you'll end up creating a DAO layer.

Castle ActiveRecord on the other hand, provides the basic methods on each class. That means that you must (although we'll see that this is not strictly true) create a class that extends from an ActiveRecord base class.

Is it intrusive then? Yes, but remember that we provide an implementation of the ActiveRecord pattern.

Using the code to demonstrate what I'm talking about:

NHibernate solution:

ISession session = sessionFactory.OpenSession();

Customer customer = (Customer) session.Load( typeof(Customer), 1 );

customer.Name = "a different name";

session.Update(customer);

session.Flush();

session.Dispose();

ActiveRecord equivalent solution:

Customer customer = Customer.Find(1);

customer.Name = "Castle Stronghold";

customer.Update();

Now being fair and using the same session: :-P

using(new SessionScope())
{
    Customer customer = Customer.Find(1);

    customer.Name = "Castle Stronghold";

    customer.Update();
}

The purpose of this article

ActiveRecord has been heavily improved since my first implementation, with Kevin's xml generation class. I rewrite its core and the Castle team took care of the rest. Some of the features and improvements they brought relates to .Net 2 support with generics, lots of refactors and hundreds of bug fixes. I won't dare to name the committers as I'm sure I'll forget one or two names, and that's just not fair. Nevertheless, if you're curious about who's behind Castle, you can check the Committers page.

However, some pieces of AR remains untouched and this concerns me. Why no fixes or improvements on these areas of the core? Did I write a perfect code? Absolutely not. For newbies, how difficult it is to improve AR? "Where should my change go?" So in order to allow more people to hack AR and thus make it better I'm dumping everything on my brain that relates somehow to ActiveRecord.

In a few words, I'd love to see more ActiveRecord hackers ;-)

The foundation

We at Castle Project are very concerned about not reinventing the wheel. So since AR was envisioned it was decided that it would be built on top of a ORM framework. We picked NHibernate for several reasons, but the most strong one was that it was the more mature project at the time (around the end of 2004). I'm convinced it was the best choice, and I'm quite sure NHibernate still the most mature project in its area.

So ActiveRecord uses and exposes NHibernate. You have to keep this in mind all the time when programming with ActiveRecord. If you want to query the database with a custom query, you'll use HQL, just like you would if you were using only NHibernate. Also, NHibernate has some behaviors that you might not predict if you don't know it, especially with sessions that last a little longer than the usual. This is often a source of confusion. Anyway, this article will - hopefully - shed some light on this obscurities.

The value of Castle ActiveRecord

I don't know much about you reading this article, but I need to be completely honest with you. I'm lazy. That being said I need to confess that I didn't want to learn NHibernate's mapping schema. I was excited about NHibernate, I admit, but not excited about all that xml. ActiveRecord is for all those lazy developers like me. :-)

Seriously, it's a matter of DRY principle too. With .net attributes we have a chance to keep things together or at least close without all that hacks necessary when all we had was a JDK 1.4 compiler and qdox.

Let's compare code again. Suppose you have a Blog class. Two fields, one mapping.

NHibernate solution:

public class Blog
{
    private int _id;
    private String _name;
    private String _author;
    private IList _posts;

    public int Id
    {
        get { return _id; }
        set { _id = value; }
    }

    public String Name
    {
        get { return _name; }
        set { _name = value; }
    }

    public String Author
    {
        get { return _author; }
        set { _author = value; }
    }

    public IList Posts
    {
        get { return _posts; }
        set { _posts = value; }
    }
}

And the xml mapping file:

<?xml version="1.0" encoding="utf-16"?>
<hibernate-mapping xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="urn:nhibernate-mapping-2.0">
  <class name="Company.Model.Blog, Company" table="BlogTable" lazy="false">
    <id name="Id" access="property" column="Id" type="Int32" unsaved-value="0">
      <generator class="native"/>
    </id>
    <property name="Name" access="property" column="Name" type="String" />
    <property name="Author" access="property" column="Author" type="String" />
    <bag name="Posts" access="property" table="Posts" lazy="false">
      <key column="blogid" />
      <one-to-many class="Company.Model.Post, Company" />
    </bag>
  </class>
</hibernate-mapping>

The equivalent using ActiveRecord:

[ActiveRecord("BlogTable")]
public class Blog : ActiveRecordBase
{
    private int _id;
    private String _name;
    private String _author;
    private IList _posts;

    [PrimaryKey]
    public int Id
    {
        get { return _id; }
        set { _id = value; }
    }

    [Property]
    public String Name
    {
        get { return _name; }
        set { _name = value; }
    }

    [Property]
    public String Author
    {
        get { return _author; }
        set { _author = value; }
    }

    [HasMany(typeof(Post))]
    public IList Posts
    {
        get { return _posts; }
        set { _posts = value; }
    }
}

By comparing the code you might reach a few conclusions. For example, on the ActiveRecord side you don't need to express everything. See the Posts relation for instance. ActiveRecord will check the other side of the relation to infer the table and primary key column. ActiveRecord also uses lots of defaults. If you don't specify a table name, it will pick the class name. If you don't specify the column name, it will use the property name.

I think that makes ActiveRecord more suitable for agile people, as it's extremelly simple to get something working.

Now suppose your manager, or team leader, or customer wants to rename the Author column to AuthorName. If you're using ActiveRecord, go to the class and change the property attribute to

[Property("AuthorName")]
public String Author

If you're using NHibernate, go to the xml.

<property name="Author" access="property" column="AuthorName" type="String" />

Easy on both. Now if you're changing the property name and the column name, you have to change on two places. If you're changing the type, same thing. If you're adding a new column, or removing an existing one... If you're adding or removing a relation... Well, this pretty much represents all what the DRY principle goes against.

With Castle ActiveRecord, methods like Save, Create, Update and Delete are available as public instance methods on the base class, thus your class will expose them. A bunch of protected methods are there to make common operations type safe. For example, implementing a Find which uses the primary key:

public static Blog Find(int id)
{
    return (Blog) FindByPrimaryKey(typeof(Blog), id);
}

And, unless you want to deal with a NHibernate session directly, you won't see one on regular ActiveRecord projects.

To summarize, we can say that ActiveRecord:

  • keeps things synch'ed (DRY)
  • allow you to be less verbose by infering as much as it can
  • performs checkings to see if your mapping information makes sense
  • hides complexity and avoids repetitive code

On the next article I'll talk about the usage, validation and the Mediator. Feel free to suggest a topic or ask questions.

Cheerio!

Posted on Friday, April 28, 2006 9:44 PM | Back to top


Comments on this post: All you wanted to know about Castle ActiveRecord - Part I

No comments posted yet.
Your comment:
 (will show your gravatar)


Copyright © hamilton verissimo | Powered by: GeeksWithBlogs.net