Introduction
The Castle ActiveRecord project is an implementation of the ActiveRecord pattern for .NET. The ActiveRecord pattern consists on instance properties representing a record in the database, instance methods acting on that specific record and static methods acting on all records.
Castle ActiveRecord is built on top of NHibernate, but its attribute-based mapping free the developer of writing XML for database-to-object mapping, which is needed when using NHibernate directly.
You can find the home page of the Castle project here.
How will we use it?
My intent is not to implement the ActiveRecord pattern but rather use ActiveRecord
- as an alternative way to define the mapping between my domain objects and the relational database.
- to simplify the initialization of NHibernate (I'm not so sure about this...)
In my previous posts (here and here) I have shown you how to define the mapping between domain objects and database tables by writing XML documents. Active Record allows me to decorate my domain objects with attributes which define the mapping instead of (hand-) coding XML documents.
Downloading and compiling the Castle Active Record Trunk
If you have never downloaded and compiled OSS software from a SVN repository before please refer to my previous post about preparing your system for NHibernate which you can find here.
I assume you have TortoiseSVN installed as your SVN client.
Make a new sub-folder called 'Castle' in your OSS folder (e.g. m:\dev\OSS\Castle). Right click on the folder an select 'SVN Checkout...'. Enter the URL 'http://svn.castleproject.org:8080/svn/castle/trunk/' in the Checkout dialog and press OK. The Castle stack will be downloaded. Depending on the bandwidth of your Internet connection this may take a while. The whole Castle stack contains more software than we need at the moment but let's just download all and ignore the parts we don't need. The whole download is around 21 MB.
When the download is finished you can compile the Castle stack. This is an automated process.
Unit Tests during the build process
During the build a lot of unit tests will be executed. For this we either need NUnit or MbUnit on our system. If you don't have it you can e.g. download NUnit from here. Download just the zip file containing the binaries for .NET 2.0 (at the time of this writing it's the file 'NUnit-2.4.7-net-2.0.zip'). Extract the binaries into a sub-folder of your OSS directory (e.g. m:\dev\OSS\nunit).
Building the Castle Stack
Open a command console and navigate to the directory where you have downloaded Castle (e.g. m\dev\OSS\Castle). Assuming that NAnt is also installed in the OSS directory enter the following command
..\NAnt\bin\nant.exe -D:nunit-console=<path to nunit-console.exe>
where at the place of <path to nunit-console.exe> you enter the path where you have unzipped/installed NUnit. In our case this is 'm:\dev\OSS\NUnit\bin\NUnit-Console.exe'.
Now the whole Castle stack is compiled and all tests are executed. This can take a while. On my machine it took about 2.5 minutes. The overall build may fail but still the assemblies are created (the reason of the overall failure might be that some of the Unit test fail. Remember that we are on the Trunk and this is under constant development. Sometimes some of the tests may thus fail.). The result on my machine was as follows
After building the Castle stack you should find a new sub-folder 'build' in the Castle folder.
Using Active Record
External dependencies of the Solution
For an introduction on how to setup a new solution please refer to this post. You can now copy all the necessary external assemblies on which the solution depends into the SharedLibs folder in your solution tree. You should copy all files (including NHibernate) from the build folder of Castle which was created during the build process described above. For this solution you need at least the following files
The sql*.dll are in the folder since we are using SQL Server Compact edition as a database. If you have read the previous posts about using NHibernate (e.g. here and here) the only new files are Castle.ActiveRecord.dll and Castle.Components.Validator.dll.
The Domain Model and the Mapping
To have a good comparison let's take the same domain model as in this post. Below is the class diagram of the domain.
Now instead of writing a domain class and then a XML mapping document we now only write the domain class and decorate it and its members with attributes. During initialization ActiveRecord will automatically generate the XML mapping documents for us which NHibernate requires for the object-relational mapping.
When using attributes the Order class will look like this
using System;
using Castle.ActiveRecord;
using Iesi.Collections.Generic;
using NHibernateAndActiveRecord.Domain;
namespace NHibernateAndActiveRecord.Domain
{
[ActiveRecord(Table = "Orders")]
public class Order
{
public Order()
{
OrderLines = new HashedSet<OrderLine>();
}
[PrimaryKey(Generator = PrimaryKeyType.Guid)]
public virtual Guid Id { get; set; }
[BelongsTo(NotNull = true)]
public virtual Customer Customer { get; set; }
[Property]
public virtual string OrderNumber { get; set; }
[Property]
public virtual DateTime OrderDate { get; set; }
[HasMany(Table = "OrderLine",
ColumnKey = "OrderId",
Cascade = ManyRelationCascadeEnum.AllDeleteOrphan)]
public virtual ISet<OrderLine> OrderLines { get; set; }
}
}
compared to a typical XML mapping document this is much more wrist friendly, isn't it. Some people also argue that attributes help to document the code, since I can immediately see e.g. which is the primary key and which property values should never be null, etc. I do not have to consult another file but can just look at the domain class to have the "full" picture. On the other hand the attributes also "clutter" my nice code and I partially violate the SoC pattern since mapping is not part of the domain in a DDD world.
Now let's look at the OrderLine class
using System;
using Castle.ActiveRecord;
namespace NHibernateAndActiveRecord.Domain
{
[ActiveRecord]
public class OrderLine
{
[PrimaryKey(Generator = PrimaryKeyType.Guid)]
public virtual Guid Id { get; set; }
[Property(NotNull = true)]
public virtual int Amount { get; set; }
[Property(NotNull = true, Length = 50)]
public virtual string ProductName { get; set; }
}
}
Here I have defined that Amount and ProductName cannot be null (this will be used for the generation of the database schema - there will be not null constraints on the corresponding table column). I also have defined that the maximal length of the ProductName cannot exceed 50 characters (also used for schema generation).
Finally the Customer class
using System;
using Castle.ActiveRecord;
namespace NHibernateAndActiveRecord.Domain
{
[ActiveRecord]
public class Customer
{
[PrimaryKey(Generator = PrimaryKeyType.Guid)]
public virtual Guid Id { get; set; }
[Property(NotNull = true, Length = 50)]
public virtual string CompanyName { get; set; }
}
}
That's it. Our domain and mapping is complete. We can now write our tests similar to the once we used in this post.
Unit Tests
Add an xml document to your test project and call it ActiveRecord.cfg.xml (similar to what you do when you ONLY use NHibernate). Add the following content to the file
<?xml version="1.0" encoding="utf-8" ?>
<activerecord>
<config>
<add key="connection.provider"
value="NHibernate.Connection.DriverConnectionProvider" />
<add key="dialect"
value="NHibernate.Dialect.MsSqlCeDialect" />
<add key="connection.driver_class"
value="NHibernate.Driver.SqlServerCeDriver" />
<add key="connection.connection_string"
value="Data Source=SampleDb.sdf" />
<add key="show_sql"
value="true" />
</config>
</activerecord>
Set the Copy to Output Directory to Copy always.
Note: we are using SQL server compact edition in this case (the second, third and fourth entry in the XML are specific for this database).
Add an new item of type LocalDatabase to the test project and call it SampleDb.sdf.
Still in the test project add a reference to the System.Data.SqlServerCe.dll in the SharedLibs folder. This assembly contains the ado.net driver for SQL server compact edition.
Open the properties page of the test project and add the following command in the "Pre-build event command line" text box
copy $(ProjectDir)..\..\SharedLibs\sqlce*.dll $(ProjectDir)$(OutDir)
This command copies all the SQL server CE files from the SharedLibs folder to the target folder of the test project. Note: this is important, otherwise the test will not run. Also note that this is only needed if you use SQL server CE.
The first unit test we write will test whether we can successfully create the database schema or not. This is always a good test if your new to ActiveRecord. Add a new class CreateSchema_Fixture.cs to the test project and add the code below
using Castle.ActiveRecord;
using Castle.ActiveRecord.Framework.Config;
using NHibernateAndActiveRecord.Domain;
using NUnit.Framework;
namespace NibernateAndActiveRecord.Tests
{
[TestFixture]
public class CreateSchema_Fixture
{
[Test]
public void Can_initialize_and_create_schema()
{
var configurationSource = new XmlConfigurationSource("ActiveRecord.cfg.xml");
ActiveRecordStarter.Initialize(typeof(Customer).Assembly, configurationSource);
ActiveRecordStarter.CreateSchema();
}
}
}
In the first line of the test we provide ActiveRecord with the necessary configuration info. In the second line we tell ActiveRecord to initialize itself (and NHibernate) with the given configuration info as well as the assembly in which the domain classes are implemented (and which also contains the mapping info since we use attributes). Then in the third line we create the schema. If there is already a schema in the db this will be deleted first.
If you have done every thing right, then the test should succeed.
Now we can continue to test our domain model. Let's first implement a base fixture class to free us from repeating code again and again. I suggest a class as follows
using Castle.ActiveRecord;
using Castle.ActiveRecord.Framework.Config;
using NHibernateAndActiveRecord.Domain;
using NUnit.Framework;
namespace NibernateAndActiveRecord.Tests
{
[TestFixture]
public class TestFixtureBase
{
[TestFixtureSetUp]
public void TestFixtureSetUp()
{
var configurationSource = new XmlConfigurationSource("ActiveRecord.cfg.xml");
ActiveRecordStarter.Initialize(typeof(Customer).Assembly, configurationSource);
}
[SetUp]
public void SetupContext()
{
ActiveRecordStarter.CreateSchema();
Before_each_test();
}
[TearDown]
public void TearDownContext()
{
After_each_test();
}
protected virtual void Before_each_test()
{ }
protected virtual void After_each_test()
{ }
}
}
This class is equivalent to the class presented in this post on NHibernate. Once for each test session I configure ActiveRecord (and implicitly NHibernate). Before each single test I recreate the database schema to avoid any side effects leaking from one test to another. I also provide virtual methods which can be overridden in the child classes to execute specific code before or after each test.
Summary
I have introduced Castle ActiveRecord as an alternative way to use NHibernate. It's a layer on top of NHibernate and simplifies in many cases the usage of NHibernate. I have shown you how you can get the latest code from the source repository and compile it. I have then setup a simple domain model and defined a test fixture base class.
I'll continue to implement the tests in part 2 of this series. So keep ready...
Enjoy
.