Coding out of procrastination

A developer’s rants and light bulb moments!

NHibernate mapping with dynamic-component

without comments

One of the more obscure features of NHibernate 2.0 I’ve found, that is somewhat lacking in documentation and certainly in the fame it deserves, is dynamic-component, it has just a one-liner in the official docs and few sparse mentions elsewhere. In it’s simplest forms, thinking in terms of constructing your NHibernate mapping, when you define mapping elements inside a dynamic-component element it adds each of them to an IDictionary object rather than binding them to CLR properties, i.e. the name attribute refers to the key on the dictionary entry instead.

This provides one way for you to define entities that contain property bags mapped to explicit columns on the database, either in the same table or in a different table using your standard join or joined-subclass mapping elements.

Jumping straight into a primitive example, whilst this may be a little trivial and its applicability questionable, the real magic follows later when combined with other tools at your disposal.

   1: using System.Collections;
   2: using System.Collections.Generic;
   3:  
   4: namespace Blog.Example.Domain
   5: {
   6:     public class Person
   7:     {
   8:         public virtual int Id { get; protected set; }
   9:  
  10:         protected Person() {}
  11:  
  12:         public Person(string name)
  13:         {
  14:             Name = name;
  15:             Details = new Dictionary<string, object>();
  16:         }
  17:  
  18:         public virtual string Name { get; protected set; }
  19:         public virtual IDictionary Details { get; protected set; }
  20:  
  21:       
  22:     }

And define a mapping file, pretty standard conventions, but now including a dynamic-component:

   1: <?xml version="1.0" encoding="utf-8" ?> 
   2: <hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
   3:                    assembly="Blog.Example.Domain"
   4:                    namespace="Blog.Example.Domain"
   5:                    >
   6:   <class name="Person" table="Person"  
   7:          dynamic-insert="true"
   8:          dynamic-update="true">
   9:     <id name="Id" type="Int32" column="PersonId">
  10:       <generator class="identity" />
  11:     </id>
  12:     <property name="Name" />
  13:     <dynamic-component name="Details">
  14:       <property name="SocialNumber" type="String"/>
  15:       <property name="DVLANumber" type="String"/>
  16:     </dynamic-component>
  17:   </class>
  18: </hibernate-mapping>

And a test wrapper to ensure everything works (this isn’t a great example of test design, this is purely for illustration):

   1: using System;
   2: using System.Diagnostics;
   3: using Blog.Example.Domain;
   4: using NUnit.Framework;
   5: using NUnit.Framework.SyntaxHelpers;
   6:  
   7: namespace Blog.Example.Host.IntTests
   8: {
   9:     [TestFixture]
  10:     public class PersonDatabaseTests
  11:     {
  12:  
  13:         [Test]
  14:         public void can_save_new_instance_of_person_and_retrieve()
  15:         {
  16:             Person person = new Person("Matt");
  17:             person.Details.Add("SocialNumber","A730399/0");
  18:             person.Details.Add("DVLANumber","100-153-200");
  19:  
  20:             var session = new SessionProvider().GetSession();
  21:             session.SaveOrUpdate(person);
  22:  
  23:             Assert.That(person.Id, Is.Not.EqualTo(0));
  24:  
  25:             int personId = person.Id;
  26:  
  27:             session.Clear();
  28:  
  29:             Person fetchedPerson = session.Load<Person>(personId);
  30:             Assert.That(fetchedPerson.Details.Contains("SocialNumber"),Is.True);
  31:             Assert.That(fetchedPerson.Details.Contains("DVLANumber"),Is.True);
  32:             
  33:             OutputPersonDetails(fetchedPerson);
  34:  
  35:         }
  36:  
  37:         static void OutputPersonDetails(Person subject)
  38:         {
  39:             foreach (string detailKey in subject.Details.Keys)
  40:             {
  41:                 Trace.WriteLine(String.Format("{0} - {1}", detailKey, subject.Details[detailKey]));
  42:  
  43:             }
  44:         }
  45:  
  46:     }
  47: }

As you can probably gather this inserts the values  in the dictionary in correct columns as defined by the mapping file, and also verifies the retrieval too. Hypothetically, in cases where your domain object design must remain static providing an extension data property bag provides some interim flexibility between major releases, of course you lose some of type safety afforded by properties but when you are integrating at a database level (I don’t condone this) and other applications are evolving the database schema perhaps this could be quite handy in moving some of the extra new data through your application.

My real interest in the dynamic-property I’ll demonstrate with more depth in  the next article when we look more a dynamic scenario and a less isolated example. 

Share/Save/Bookmark

Written by matt-csharp

January 2nd, 2009 at 11:06 pm

Posted in nhibernate

Tagged with ,

Leave a Reply

Security Code: