Saturday 21 April 2012

RavenDB Magic – Modelling Polymorphic Collections

I'm starting to port my main application over to RavenDB.  A driving design principle for RavenDB is to reduce friction for the developer and the main reason I’m choosing RavenDB is to make my life easier.  Here's one example of that...

Let’s say that you’ve got a model with a marker interface and some types implementing that interface.  Something like this:
   public interface ILookup { }

   public class IntegerLookup : ILookup
   {
      public int Number { get; set; }
   }
   
   public class StringLookup : ILookup
   {
      public string Text { get; set; }
   }
Then you have a type that holds a collection of the interface:
   public class Container
   {
      public ILookup[] Lookups { get; set; }
   }
All good so far, and now you want to store this in a database.  Not quite so simple!  If you’re using a relational database, whether through an ORM or another means, you will have to do quite a bit more work to get your objects into the database and back.

However, if you’re using RavenDB then your work is done!  Well, nearly - all you need to do is to add an Id property to your container to tell RavenDB to store it as a root:
   public class Container
   {
      public string Id { get; set; }
      public ILookup[] Lookups { get; set; }
   }

That’s it – the rest is pure Raven magic!  Here’s a test showing how Raven it handles it:
   [Test]
   public void can_store_polymorphic_collection()
   {
      using (var store = new EmbeddableDocumentStore
      {
         DataDirectory = "Data",
         RunInMemory = true
      })

      {
         store.Initialize();

         var holder = new Container
               {
                  Lookups = new ILookup[]
                              {
                                 new StringLookup {Text = "Hello"},
                                 new IntegerLookup {Number = 123}
                              }
               };
         using (var session = store.OpenSession())
         {
            session.Store(holder);
            session.SaveChanges();
         }

         using (var session = store.OpenSession())
         {
            var h = session.Load<Container>(holder.Id);

            Assert.That(h.Lookups
               .OfType<StringLookup>().Single().Text == "Hello");
            Assert.That(h.Lookups
               .OfType<IntegerLookup>().Single().Number == 123);
         }
      }
   }
You just new up an instance of Container, add in a couple of ILookup instances, then save it.  When I load it back up, it’s all there just as you need it to be.  Now, that is seriously impressive!