Try-Catch-FAIL

Failure is inevitable.

The pleasure of ActiveRecord-based data access code

clock July 24, 2009 07:46 by author Matt

At the beginning of the year, I described the horrible experience I had while removing *TWO* columns from the database of a legacy system here at the day job.  For those too lazy to go back and read, I basically spent nearly an entire day working on this “trivial” task and ended up with a 15,563 line SVN diff spread across 40 files.

Yesterday I was tasked with making major changes to the object model we’re persisting.  The change consisted of *nine* new entities and one modified entity.  Compare this to the simple deletion of two columns/properties, and you might think that this single task would take me the rest of the year.  And you would probably be right, except something is different this time.  This time, I’m working on a project that uses Castle ActiveRecord for its data access.

Again, for those who are too lazy to read, what does that really mean compared to the hand-rolled DAL I described 6 months ago?  The hand-rolled DAL consisted of several layers: a generic (massive) interface, two huge, concrete implementations of that interface (one for unit testing and one that actually talked to SQL Server), stored procedures, and the actual tables.  Oh, and a web service layer on top, just for good measure.

On the ActiveRecord (AR) side, I have my AR objects, which inherit from generic AR base classes, and the database… and that’s it.  All CRUD and querying capabilities are inherited from the generic base classes.  LINQ is supported through the generic base classes.  Unit tests are seamlessly handled by AR via SQLite. I have a simple ad-hoc method that I can run with TestDriven.NET that generates the database schema from my object definitions, and the application itself currently has the capability to upgrade its own schema as the objects change (such functionality is dangerous, but this app is still in the rapid prototype/evaluation stages). 

Clearly, the AR approach sounds a lot simpler with no stored procedures, no massive DAL objects, and automatic schema maintenance, but how much time did it *really* save?

I did the math.  I added things up, and there were over 50 database additions as part of this update.  Looking at my old timesheets, it took me 8 hours to complete the removal of *two* columns.  So, it should have taken me about 200 hours to implement these changes. :D  How long did it really take? 

Less than a day.  That includes time spent designing the new objects on paper, creating the classes, and figuring out how to handle one tricky mapping that involved an interface and a one-to-one relationship (which cost me about 2 hours).  I don’t even want to think about how long this would have taken if I’d done it by hand.

I think ORMs have advanced from “cool tool” to “required knowledge” for software developers.  If you consider yourself a developer but don’t know how to use at least one ORM, now would be a great time to acquaint yourself with one. My (biased) opinion is that Castle ActiveRecord is a great place to start. There are also others checking out, such as NHibernate (which ActiveRecord is built on) and Nvigorate.  At this point, I can’t imagine I will ever build another data-driven application that doesn’t use an ORM.  The alternative is just too painful.

Share or Bookmark this post…
  • del.icio.us
  • DotNetKicks
  • Digg
  • msdn Social
  • Reddit
  • StumbleUpon

Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5


ActiveRecord 2.0 Beta 1 Ships!

clock July 15, 2009 01:31 by author Matt

It’s very nice to see new releases coming out of the Castle Project finally.  Windsor has been updated, and now ActiveRecord is getting some love.  Highlights include built-in support for in-memory unit tests, LINQ support, and simplified integration with Lucene via NHSearch. You can read more about it over at Morts Like Us.

Share or Bookmark this post…
  • del.icio.us
  • DotNetKicks
  • Digg
  • msdn Social
  • Reddit
  • StumbleUpon

Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5


Castle ActiveRecord: Quite Possibly the Greatest Thing Ever

clock October 20, 2008 09:22 by author Matt

Well, maybe not, but it has quickly become my favorite tool for writing .NET applications.  I've been using it to write the persistence layer for a brand-new enterprise application we're building, and so far, it's elegantly supported every single thing I've wanted to do.  LINQ?  Got it.  Part-of relationships?  Easy.  Many-to-many?  No sweat.  Multiple data sources?  Simple.  Validation?  Available

Some may argue that using something like ActiveRecord is bad because it requires you to use inheritance, and while that may be true some of the time, I don't think it is generally true.  To me, architectural purity is only important if it makes things easier to build and maintain.  If it doesn't, what's the point?  I would gladly take on a bit of "impurity" in order to gain tremendous flexibility.  No more shall I fear a change in requirements that will lead to database changes!

Share or Bookmark this post…
  • del.icio.us
  • DotNetKicks
  • Digg
  • msdn Social
  • Reddit
  • StumbleUpon

Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5


Easy testing via ActiveRecord and SQLite

clock August 20, 2008 07:50 by author Matt

As I've mentioned, I'm a big fan of ActiveRecord.  I like having all of my data-access related code stored in exactly one place.  There's no separate mapping file to maintain, and if I'm really lazy, I can even let ActiveRecord generate the schema for me (yeah, that doesn't really work once you have to worry about migrating databases between schema versions, but there's probably a way around that). 

Testing code that depends on my ActiveRecord object model can be complicated.  The tests require that the underlying database be in a known, consistent state, and that means that I have to use test setup/tear down methods to initialize the database with the schema and/or wipe out data created by other tests.  Worse, it means I have to actually have a database accessible for use in the tests!  Since this behavior tends to be the same across my ActiveRecord-dependent test fixtures, I have created a base test fixture and a simple utility class that keeps my real NUnit test fixtures very simple and free of ActiveRecord initialize and cleanup code, and it uses an in-memory database so there's no dependency on an external database.  First, the utility class:

   1: /// <summary>
   2: /// Contains various utilty methods and properties
   3: /// to aide in the configuration and management
   4: /// of ActiveRecord.  All objects that are to be persisted
   5: /// by ActiveRecord are defined here.
   6: /// </summary>
   7: public static class ActiveRecordHelper
   8: {
   9:     #region Private Static Fields
  10:  
  11:     /// <summary>
  12:     /// This field holds the types that are to be persisted
  13:     /// by ActiveRecord.  These should be listed bottom-up in order
  14:     /// by dependency, so use C, B, A if C depends on B, and B depends on A.
  15:     /// </summary>
  16:     static readonly Type[] mModelTypes = new Type[]
  17:                                              {
  18:                                                  typeof(Widget)
  19:                                              };
  20:  
  21:     #endregion
  22:  
  23:     #region Public Properties
  24:  
  25:     /// <summary>
  26:     /// Gets all the models that are available for persistence
  27:     /// in ActiveRecord.
  28:     /// </summary>
  29:     public static Type[] ModelTypes
  30:     {
  31:         get
  32:         {
  33:             return mModelTypes;
  34:         }
  35:     }
  36:  
  37:     #endregion
  38: }

Note how the types used by ActiveRecord are listed in order of dependency.  This is important because the test fixture will clean up the database by running DeleteAll commands on the types in that order.

Next is our base test fixture class:

   1: /// <summary>
   2: /// Provides a base class that database-dependent unit tests can utilize.  It 
   3: /// handles managing the ActiveRecord data layer setup/init, clean up, etc.
   4: /// </summary>
   5: public abstract class DatabaseDependentTestFixture
   6: {
   7:     #region Protected Methods
   8:  
   9:     /// <summary>
  10:     /// Deletes all data in the database.
  11:     /// </summary>
  12:     protected void DeleteAllData()
  13:     {
  14:         Type[] types = ActiveRecordHelper.ModelTypes;
  15:  
  16:         foreach (Type t in types)
  17:         {
  18:             if (typeof(ActiveRecordBase).IsAssignableFrom(t) &&
  19:                 t.GetMethod("DeleteAll", BindingFlags.Public | BindingFlags.Static | BindingFlags.FlattenHierarchy, null, new Type[0], null) != null)
  20:             {
  21:                 t.InvokeMember("DeleteAll", BindingFlags.Public | BindingFlags.Static | BindingFlags.FlattenHierarchy | BindingFlags.InvokeMethod, null, null, null);
  22:             }
  23:         }
  24:     }
  25:  
  26:     /// <summary>
  27:     /// Initializes ActiveRecord to work with an in-memory temporary database
  28:     /// for the RBCMS objects.
  29:     /// </summary>
  30:     protected void SetupActiveRecord()
  31:     {
  32:         Dictionary<string, string> settings = new Dictionary<string, string>();
  33:         settings.Add("connection.driver_class", "NHibernate.Driver.SQLite20Driver");
  34:         settings.Add("dialect", "NHibernate.Dialect.SQLiteDialect");
  35:         settings.Add("connection.provider", "NHibernate.Connection.DriverConnectionProvider");
  36:         settings.Add("connection.connection_string", "Data Source=ActiveRecord.dat;Version=3;");
  37:  
  38:         InPlaceConfigurationSource config = new InPlaceConfigurationSource();
  39:         config.PluralizeTableNames = true;
  40:         config.Add(typeof(ActiveRecordBase), settings);
  41:  
  42:         if (ActiveRecordStarter.IsInitialized)
  43:         {
  44:             ActiveRecordStarter.ResetInitializationFlag();
  45:         }
  46:  
  47:         ActiveRecordStarter.Initialize(config, ActiveRecordHelper.ModelTypes);
  48:     }
  49:  
  50:     /// <summary>
  51:     /// Drops and recreates the RBCMS database schema.
  52:     /// </summary>
  53:     protected void RecreateSchema()
  54:     {
  55:         ActiveRecordStarter.CreateSchema();
  56:     }
  57:  
  58:     #endregion
  59:  
  60:     #region Public Methods
  61:  
  62:     /// <summary>
  63:     /// Initializes the data layer for first use.  
  64:     /// </summary>
  65:     [TestFixtureSetUp]
  66:     public virtual void TestFixtureSetup()
  67:     {
  68:         SetupActiveRecord();
  69:         RecreateSchema();
  70:     }
  71:  
  72:     /// <summary>
  73:     /// Wipes out all existing data in the database, allowing
  74:     /// unit tests to start with a blank slate each time (which
  75:     /// is what they should be doing anyway).
  76:     /// </summary>
  77:     [SetUp]
  78:     public virtual void TestSetup()
  79:     {
  80:         DeleteAllData();
  81:     }
  82:  
  83:     #endregion
  84: }

There's quite a bit going on here, so let's walk through it.  When inherited by a derived class, the TestFixtureSetup and TestSetup mehods will be called by NUnit automatically.  The first method will initialize ActiveRecord, configuring it to use a temporary SQLite database, then populating the database with the schema generated by ActiveRecord.  Using SQLite means that the database resides entirely in memory, so I don't have to worry about configuring a separate MS SQL database or something like that.  The second method is called by NUnit before each test runs, and it uses the ActiveRecordBase<T>.DeleteAll method to clean out any existing data.  Reflection is used because DeleteAll is a static, generic method that must be called on the derived type, not the base type, and there's not a way to do that without reflection unless you couple the code to your models.

The setup methods are declared as virtual, so derived test fixtures can override their behavior if needed.  Most of the time though, you can just passively take advantage of the functionality like so:

   1: /// <summary>
   2: /// Test fixture for <see cref="ActiveRecordLinqContext"/>.
   3: /// </summary>
   4: [TestFixture]
   5: public class ActiveRecordLinqContextTests : DatabaseDependentTestFixture
   6: {
   7:     /// <summary>
   8:     /// Verifies that LINQ works as expected.
   9:     /// </summary>
  10:     [Test]
  11:     public void LinqTest1()
  12:     {
  13:         //First, insert a few widgets.
  14:         for (int i = 0; i < 10; i++)
  15:         {
  16:             Widget model = new Widget {Name = "Test" + i, Description = "Test Description " + i};
  17:             model.SaveAndFlush();
  18:         }
  19:  
  20:         using (new SessionScope())
  21:         {
  22:             ActiveRecordLinqContext context = new ActiveRecordLinqContext();
  23:  
  24:             string[] names = (from cm in context.Session.Linq<CostModel>()
  25:                          select cm.Name).ToArray();
  26:  
  27:             Assert.AreEqual(10, names.Length);
  28:  
  29:             names = (from cm in context.Session.Linq<CostModel>()
  30:                      where cm.Id > 5
  31:                      select cm.Name).ToArray();
  32:  
  33:             Assert.AreEqual(5, names.Length);
  34:         }
  35:     }
  36: }
This is the test fixture from yesterday's post, but all its setup logic is now handled by the base class, freeing you up to write simple, clean tests.
Share or Bookmark this post…
  • del.icio.us
  • DotNetKicks
  • Digg
  • msdn Social
  • Reddit
  • StumbleUpon

Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5


Using LINQ with ActiveRecord

clock August 19, 2008 09:42 by author Matt

One of the new projects at my day job is using ActiveRecord for data access.  I'm a huge fan of ActiveRecord (and of all things Castle), but I like the fact that LINQ makes it very easy to do ad-hoc queries with a compile-time safety net.  Unfortunately, ActiveRecord does not support LINQ out of the box.  Luckily though, ActiveRecord is built on top of NHibernate, and LINQ support is available via NHibernate Contrib, at least if you aren't afraid to venture into the source.  If you follow the steps below, you can build your own LINQ provider for ActiveRecord and get the syntactical sugar of language-integrated query with the simplicity of ActiveRecord.  Note that you will need to know the basics of using Subversion in order to follow these steps (I highly recommend TortoiseSVN), and you must have NAnt installed and in working order.

  1. Checkout NHibernate.LINQ from https://nhcontrib.svn.sourceforge.net/svnroot/nhcontrib/trunk/src/NHibernate.Linq.
  2. Checkout Castle from http://svn.castleproject.org:8080/svn/castle/trunk.
  3. Copy NHibernate.dll from "NHibernate.Linq\lib" to "Castle\SharedLibs\net\2.0".  This is necessary because the latest release build of NHibernate doesn't work with NHibernate.LINQ.  You could try to build NHibernate from its trunk and copy the DLL to both Castle and NHibernate.LINQ, but since the projects occasionally end up out-of-sync, I prefer the copy-and-paste method.
  4. Build Castle using NAnt by running this command in the root of your Castle checkout: nant rebuild -D:common.testrunner.enabled=false. Do *NOT* try to build using the solution; the NAnt scripts create some of the required AssemblyInfo files, and you can't build without those.
  5. Copy the following DLLs from the Castle build output to "NHibernate.Linq\lib": Castle.Core.dll, Castle.DynamicProxy2.dll, Iesi.Collections.dll.
  6. Build NHibernate.Linq using MSBuild, Visual Studio, or whatever you want.
  7. That's it (sort-of).
  8. (Recommended) I like to copy the required DLLs to a common folder that I keep under version control with my project.  This insures that nothing gets lost since I'm lazy and really don't want to jump through these hoops more often than absolutely necessary.  If you go that route, copy the following DLLs from the various build output directories to a common folder: Castle.ActiveRecord.dll, Castle.Components.Validator.dll, Castle.Core.dll, Castle.DynamicProxy.dll, Iesi.Collections.dll, NHibernate.dll, and NHibernate.Linq.dll. 

At this point, you should now have Castle, NHibernate, and NHibernate.LINQ all synced up where they can play together.  All that remains is to create our ActiveRecord LINQ context.  So, let's begin!

  1. Create a new class library in Visual Studio.
  2. Add references to the DLL's listed in the optional step 8 above.  If you didn't copy things to a central folder, you probably will do so now. :) 
  3. Create a new class named ActiveRecordLinqContext.  Go ahead and create a corresponding test fixture class now as well (you *do* practice unit testing, right??)

A quick note: I'm using SQLite for my tests.  If you go that route, be sure you place the SQLite DLL where NHibernate can find it.  Alternatively, you can test with any other database that NHibernate supports.  A future post will detail how to use SQLite for clean and simple ActiveRecord testing.

That's it for the boring stuff, on to the glorious code!

   1: /// <summary>
   2: /// Provides a LINQ-enabled data context that works with ActiveRecord.
   3: /// </summary>
   4: public class ActiveRecordLinqContext : NHibernateContext
   5: {
   6:     #region Public Constructors
   7:  
   8:     /// <summary>
   9:     /// Creates and configures the context.
  10:     /// </summary>
  11:     public ActiveRecordLinqContext() : base(GetSession())
  12:     {
  13:     }
  14:  
  15:     #endregion
  16:  
  17:     #region Private Static Methods
  18:  
  19:     /// <summary>
  20:     /// Gets a correctly-initialized session.
  21:     /// </summary>
  22:     /// <returns></returns>
  23:     private static ISession GetSession()
  24:     {
  25:         ISessionScope scope = SessionScope.Current;
  26:  
  27:         if (scope == null)
  28:         {
  29:             throw new InvalidOperationException("You must have an active SessionScope object to use this class.");
  30:         }
  31:  
  32:         ISessionFactoryHolder holder = ActiveRecordMediator.GetSessionFactoryHolder();
  33:  
  34:         return holder.CreateSession(typeof (ActiveRecordBase));
  35:     }

That's pretty simple, but how do we use it?  Here's the test fixture and a test class:

   1: /// <summary>
   2: /// A simple widget!
   3: /// </summary>
   4: [ActiveRecord]
   5: public class Widget : ActiveRecordBase<Widget>
   6: {
   7:     /// <summary>
   8:     /// The ID.
   9:     /// </summary>
  10:     [PrimaryKey]
  11:     public int Id { get; set; }
  12:  
  13:     /// <summary>
  14:     /// Its name.
  15:     /// </summary>
  16:     [Castle.ActiveRecord.Property]
  17:     public string Name { get; set; }
  18:  
  19:     /// <summary>
  20:     /// Its description.
  21:     /// </summary>
  22:     [Castle.ActiveRecord.Property]
  23:     public string Description { get; set; }
  24: }
  25:  
  26: /// <summary>
  27: /// Test fixture for <see cref="ActiveRecordLinqContext"/>.
  28: /// </summary>
  29: /// <author>MBH</author>
  30: /// <dateAuthored>7/22/08</dateAuthored>
  31: [TestFixture]
  32: public class SimpleLinqTests
  33: {
  34:     #region Private Helpers
  35:  
  36:     /// <summary>
  37:     /// Initializes ActiveRecord to work with an in-memory temporary database
  38:     /// via SQLite.
  39:     /// </summary>
  40:     private static void SetupActiveRecord()
  41:     {
  42:         Dictionary<string, string> settings = new Dictionary<string, string>();
  43:         settings.Add("connection.driver_class", "NHibernate.Driver.SQLite20Driver");
  44:         settings.Add("dialect", "NHibernate.Dialect.SQLiteDialect");
  45:         settings.Add("connection.provider", "NHibernate.Connection.DriverConnectionProvider");
  46:         settings.Add("connection.connection_string", "Data Source=LinqTest.dat;Version=3;");
  47:  
  48:         InPlaceConfigurationSource config = new InPlaceConfigurationSource();
  49:         config.PluralizeTableNames = true;
  50:         config.Add(typeof(ActiveRecordBase), settings);
  51:  
  52:         if (ActiveRecordStarter.IsInitialized)
  53:         {
  54:             ActiveRecordStarter.ResetInitializationFlag();
  55:         }
  56:  
  57:         ActiveRecordStarter.Initialize(config, typeof (Widget));
  58:     }
  59:  
  60:     /// <summary>
  61:     /// Drops and recreates the database schema.
  62:     /// </summary>
  63:     private static void RecreateSchema()
  64:     {
  65:         ActiveRecordStarter.CreateSchema();
  66:     }
  67:  
  68:     #endregion
  69:  
  70:     /// <summary>
  71:     /// Initializes the data layer for first use.  
  72:     /// </summary>
  73:     [TestFixtureSetUp]
  74:     public virtual void TestFixtureSetup()
  75:     {
  76:         SetupActiveRecord();
  77:         RecreateSchema();
  78:     }
  79:  
  80:     /// <summary>
  81:     /// Verifies that LINQ works as expected.
  82:     /// </summary>
  83:     [Test]
  84:     public void LinqTest1()
  85:     {
  86:         //First, insert a few test widgets models.
  87:         for (int i = 0; i < 10; i++)
  88:         {
  89:             Widget widget = new Widget { Name = "Test" + i, Description = "Test Description " + i };
  90:             widget.SaveAndFlush();
  91:         }
  92:  
  93:         using (new SessionScope())
  94:         {
  95:             ActiveRecordLinqContext context = new ActiveRecordLinqContext();
  96:  
  97:             //The real beauty of this is not testable: it is actually only
  98:             //querying the database for the names, not the full Widget
  99:             //objects!  You can turn on log4net to verify this.
 100:             string[] names = (from w in context.Session.Linq<Widget>()
 101:                               select w.Name).ToArray();
 102:  
 103:             Assert.AreEqual(10, names.Length);
 104:  
 105:             //This is all translated into SQL and executes in the DB, not in
 106:             //memory!
 107:             names = (from w in context.Session.Linq<Widget>()
 108:                      where w.Id > 5
 109:                      select w.Name).ToArray();
 110:  
 111:             Assert.AreEqual(5, names.Length);
 112:         }
 113:     }
 114: }

Run the tests, and you should see green.  You can turn on logging (by calling log4net.Config.BasicConfigurator.Configure()) if you want to see the raw SQL being sent to the database to confirm that LINQ is working.

Big, big props go out to Ken Egozi; his post got me on the right track to get all this working.

Share or Bookmark this post…
  • del.icio.us
  • DotNetKicks
  • Digg
  • msdn Social
  • Reddit
  • StumbleUpon

Currently rated 5.0 by 1 people

  • Currently 5/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5


About Matt

I am an overworked (and apparently overpaid) software developer with aspirations of acquiring a PhD in Computer Science. I started off coding in C over a decade ago.  Since then, I've migrated from C to C++ and branched out to C#, PHP, VB.NET, JavaScript, and worked with a wide assortment of other languages that I hope to never deal with again (I'm looking at you, COBOL). Oh, and yes, I've written some Java.  Does that make me a bad person?

Disclaimer

The opinions expressed herein are my own personal opinions and do not represent my employer's view in  anyway.

© Copyright 2009

Sign in