Easy Testing Of NHibernate.Spatial Code

May 26, 2010

2 comments

In my current project we’re using NHibernate.Spatial against an SQL Server 2008 DB. This replaces our need to work with the cumbersome ArcSDE when writing and reading data in our applications (while still allowing ArcMap users to view the maps it holds by registering the tables in SDE). This made our life so much lovelier. We now have real domain objects to work with, and absolutely no COM is required to do stuff!

Anyway, we’re using the awesome Fluent NHibernate to map all our entities, and in order to test them, we have a base class (something like this) that all our tests use. The problem is, we want our tests to run against the in-memory SQLite database, but it doesn’t support the spatial stuff, and there is no NHibernate.Spatial dialect for it. A problem indeed – most of our code has nothing to do with geography, and it would be a damn shame if we couldn’t use an in-memory database to test it easily.

Well, we ended up using a cool feature of Fluent NHibernate, that allows us to specify conventions for our class maps. For instance, lets say we have a Country entity, which looks like this:

   1: public class Country

   2: {

   3:     public virtual int Id {get; set;}

   4:     public virtual string Name {get; set;}

   5:     public virtual IGeometry Location {get; set;}

   6: }

And the mapping for this class looks like this:

   1: public class CountryMap : ClassMap<Country>

   2:    {

   3:        public CountryMap()

   4:        {

   5:            Id(c => c.Id);

   6:            Map(c => c.Name);

   7:            Map(c => c.Location).CustomType(typeof(GeometryType));

   8:        }

   9:    }

This mapping class is problematic with SQLite, as this database doesn’t support using the custom GeometryType (which implements NHibernate’s IUserType) that comes with NHibernate.Spatial. If only we could do without specifying the custom type in our mapping, and use a different one in our tests files… Oh! Fluent NHibernate conventions let us do just that. We can use two different conventions, one for SQL 2008, which we will use in our production code, and one for SQLite, which will be used in most tests. Here’s how our convention for SQLite looks like (the one for SQL 2008 is exactly the same, only uses the above GeometryType custom type):

   1: public class SQLiteGeometryTypeConvention: IPropertyConvention

   2:    {

   3:        public void Apply(IPropertyInstance instance)

   4:        {

   5:            if (instance.Property.PropertyType.Equals(typeof(IGeometry)))

   6:            {

   7:                instance.CustomType(typeof (SQLiteGeometryType));

   8:            }

   9:        }

  10:    }

You can see that we’re using SQLiteGeometryType. If you don’t know what that is, it’s OK. It’s our own simple implementation, as defined below:

   1: public class SQLiteGeometryType: GeometryTypeBase<string>

   2:     {

   3:         public SQLiteGeometryType() : base(NHibernateUtil.String)

   4:         {

   5:         }

   6:  

   7:         protected override string FromGeometry(object value)

   8:         {

   9:             return value != null ? ((IGeometry) value).AsText() : null;

  10:         }

  11:  

  12:         protected override IGeometry ToGeometry(object value)

  13:         {

  14:             return value != null ? new WKTReader().Read((string) value) : null;

  15:         }

  16:     }

All we do here is tell NHibernate that when it sees an IGeometry property that it should persist, it should convert it to a string and save it as varchar. It also goes the other way around. GeometryTypeBase is a helper base class that exists in NHibernate.Spatial.

Now all that is left for us is to configure NHibernate to use our convention:

   1: Fluently.Configure()

   2:                .Database(SQLiteConfiguration.Standard.InMemory)

   3:                .Mappings(m => m.FluentMappings.AddFromAssemblyOf<Country>()

   4:                                   .Conventions.Add(new SQLiteGeometryTypeConvention()));

And now we’re done. We can persist our spatially-enabled entities to SQLite and write tests for them. Granted, we can’t use more advanced features like spatial queries that run in the database, but most of our code doesn’t do that anyway. Having an in-memory database is perfect for testing the OR mappings and domain service operations.

Add comment
facebook linkedin twitter email

Leave a Reply

Your email address will not be published.

*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

2 comments

  1. Pedro SousaNovember 23, 2010 ב 13:07

    Great work. You really helped me out with this one :)

    Reply
  2. seo资源October 24, 2012 ב 0:03

    Wonderful update towards the infographic, David! Your homework is actually significantly treasured and gives us all SEMs the preventing opportunity against the inappropriate data throughout the website that will becomes drew in to Google places (formarly local business center).

    Reply