Using Castle ActiveRecord with multiple databases

I <3 Castle ActiveRecord, but today I ran into something that I couldn't quite figure out.  The solution from the documentation was seemingly incorrect.  After much trial-and-error though, I have it figured out, and it turns out the docs were right, and I was wrong.  I'm documenting exactly what I did here in hopes that it might save someone else some pain someday.

The problem was that I needed to create ActiveRecord types that were backed by two different databases.  I'm not talking about a single type that pulls from both databases (not sure how that would work), I'm talking about a Foo coming from FooDatabase, and Bar coming from BarDatabase.  Checking the docs, it appears like all you need to do is create abstract base classes for each database, then derive your concrete types from them instead of from ActiveRecordBase.  So I tried that, setup my ActiveRecord.config file like it suggested, loaded the page, and EPIC FAIL.  Here's what I had:

   1: [ActiveRecord]
   2: public abstract class FooPersistable<T> : ActiveRecordBase<T>
   3: {
   4: }
   5:  
   6: ...
   7:  
   8: [ActiveRecord]
   9: public class BarPersistable<T> : ActiveRecordBase<T>
  10: {
  11: }

And here's what my config looked like:

   1: <activerecord isWeb="true" pluralizeTableNames="true">
   2:     <config>
   3:         <add key="connection.driver_class" value="NHibernate.Driver.SqlClientDriver" />
   4:         <add key="dialect" value="NHibernate.Dialect.MsSql2005Dialect" />
   5:         <add key="connection.provider" value="NHibernate.Connection.DriverConnectionProvider" />
   6:         <add key="connection.connection_string" value="Data Source=localhost; Initial Catalog=FooDatabase; Integrated Security=SSPI" />
   7:     </config>
   8:     <config type="Objects.BarPersistable`1, Objects">
   9:         <add key="connection.driver_class" value="NHibernate.Driver.SqlClientDriver" />
  10:         <add key="dialect" value="NHibernate.Dialect.MsSql2005Dialect" />
  11:         <add key="connection.provider" value="NHibernate.Connection.DriverConnectionProvider" />
  12:         <add key="connection.connection_string" value="Data Source=localhost; Initial Catalog=BarDatabase; Integrated Security=SSPI" />
  13:     </config>
  14: </activerecord>

Note that since BarPersistable is a generic type, it is specified using the class name suffixed with "`1".  That's very important.  The rest of the "type" attribute just tells ActiveRecord which assembly to look in for BarPersistable<>.  Also notice that the first <config> element doesn't specify a type; it's the "default" configuration.  Trying to specify a type there will cause ActiveRecord to become angry, so don't do it.

And for completeness, here's what my initialization code looked like:

   1: string configFile = HttpContext.Current.Server.MapPath("~/ActiveRecord.config");
   2:  
   3: ActiveRecordStarter.Initialize(
   4:     new XmlConfigurationSource(configFile), 
   5:     //Note the use of a generic type without a type parameter!
   6:     typeof(Foo),typeof(Bar),typeof(BarPersistable<>));

At this point, it maybe immediately obvious what I've done wrong, but I wrestled with it for about an hour before I saw it.  I forgot to mark BarPersistable as abstract.  That's it.  All I had to do was add that one minor keyword, and everything began to work beautifully, even with ASP.NET and the session-per-request model.  Very frustrating, but I have no one to blame but myself.  ActiveRecord even told me what to do in the error message, but I just assumed that there was no way I could have done something silly like left a keyword out.  Oops.

posted @ 2009-11-24 21:24  獨翏淚  阅读(388)  评论(1编辑  收藏  举报