Try fast search NHibernate

01 June 2010

Duck Typing with NHibernate Reloaded

Around two years ago you read this post. One year ago you read this other post. Today we have DLR!!!

The mapping

Again the same mapping:
  1. <hibernate-mapping xmlns="urn:nhibernate-mapping-2.2">
  2.     
  3.     <class entity-name="ProductLine">
  4.         <id name="Id" type="int">
  5.             <generator class="hilo"/>
  6.         </id>
  7.         <property name="Description" not-null="true" length="200" type="string"/>
  8.  
  9.         <bag name="Models" cascade="all" inverse="true">
  10.             <key column="productId"/>
  11.             <one-to-many class="Model"/>
  12.         </bag>
  13.  
  14.     </class>
  15.  
  16.     <class entity-name="Model">
  17.         <id name="Id" type="int">
  18.             <generator class="hilo"/>
  19.         </id>
  20.  
  21.         <property name="Name" not-null="true" length="25" type="string"/>
  22.         <property name="Description" not-null="true" length="200" type="string"/>
  23.         <many-to-one name="ProductLine"
  24.                      column="productId"
  25.                      not-null="true"
  26.                      class="ProductLine"/>
  27.     </class>
  28.  
  29. </hibernate-mapping>

Saving Ducks

Instead anonymous objects this time I’ll use a DynamicEntity.
  1. using (ISession s = sessions.OpenSession())
  2. using (ITransaction t = s.BeginTransaction())
  3. {
  4.     dynamic line = new DynamicEntity();
  5.     line.Description = "High quality cars";
  6.     line.Models = new ArrayList();
  7.     line.Pizza = "calda";
  8.  
  9.     dynamic ferrari = new DynamicEntity();
  10.     ferrari.ProductLine = line;
  11.     ferrari.Name = "Dino";
  12.     ferrari.Description = "Ferrari Dino";
  13.     ferrari.Fuel = "Gasoline";
  14.  
  15.     dynamic lamborghini = new DynamicEntity();
  16.     lamborghini.ProductLine = line;
  17.     lamborghini.Name = "Countach";
  18.     lamborghini.Description = "Lamborghini Countach";
  19.     lamborghini.Origin = "Italy";
  20.     line.Models.Add(ferrari);
  21.     line.Models.Add(lamborghini);
  22.  
  23.     savedId = s.Save("ProductLine", (object)line);
  24.     t.Commit();
  25. }

Updating Ducks

  1. dynamic productLine;
  2. using (ISession s = sessions.OpenSession())
  3. using (ITransaction t = s.BeginTransaction())
  4. {
  5.     var entity = s.Get("ProductLine", savedId);
  6.     productLine = new DynamicEntity(entity);
  7.  
  8.     productLine.Description = "Quality cars";
  9.     dynamic newModel = new DynamicEntity();
  10.     newModel.ProductLine = entity;
  11.     newModel.Name = "Locus";
  12.     newModel.Description = "Audi Locus";
  13.  
  14.     productLine.Models.Add(newModel);
  15.  
  16.     t.Commit(); // Persist modification
  17. }
  18.  
  19. // Test over modifications
  20. using (ISession s = sessions.OpenSession())
  21. {
  22.     var entity = s.Get("ProductLine", savedId);
  23.     productLine = new DynamicEntity(entity);
  24.     Assert.AreEqual("Quality cars",productLine.Description);
  25.     Assert.AreEqual(3, productLine.Models.Count);
  26. }

Deleting Ducks

  1. using (ISession s = sessions.OpenSession())
  2. using (ITransaction t = s.BeginTransaction())
  3. {
  4.     // Delete the detached dynamic entity
  5.     s.Delete("ProductLine", productLine);
  6.     t.Commit();
  7. }
  8.  
  9. using (ISession s = sessions.OpenSession())
  10. {
  11.     // check entity deletation with cascade
  12.     s.Get("ProductLine", savedId).Should().Be.Null();
  13.     s.CreateQuery("from Model").List().Count.Should().Be.EqualTo(0);
  14. }

The “Magic”

This time the magic is all in few lines of one class…
  1. public class DynamicEntity : DynamicObject, IDictionary
  2. {
  3.     private readonly Hashtable dictionary = new Hashtable();
  4.  
  5.     public DynamicEntity()
  6.     {
  7.     }
  8.  
  9.     public DynamicEntity(object fromNh)
  10.     {
  11.         dictionary = fromNh as Hashtable;
  12.     }
  13.  
  14.     public override bool TryGetMember(GetMemberBinder binder, out object result)
  15.     {
  16.         string name = binder.Name;
  17.         result = dictionary[name];
  18.         var nestedEntity = result as Hashtable;
  19.         if (nestedEntity != null)
  20.         {
  21.             result = new DynamicEntity(nestedEntity);
  22.         }
  23.         return dictionary.Contains(name);
  24.     }
  25.  
  26.     public override bool TrySetMember(SetMemberBinder binder, object value)
  27.     {
  28.         dictionary[binder.Name] = value;
  29.  
  30.         return true;
  31.     }
  32.  
  33.     #region IDictionary Members
  34.     ...
  35.     #endregion
  36. }
The implementation of the IDictionary is only a forward to the dictionary private field.

Conclusion

If you are working with anemic entities, and especially if you then transforms it to DTO, ask yourself why you should write those classes when you can do the same work with just one implementation.
re-NHibernating… quack quack ;-)

6 comments:

  1. "ask yourself why you should write those classes"

    Well, one of the reasons to write classes is just for the Intellisense. This is BIG reason for me.

    ReplyDelete
  2. so, we can define it as : IDD = IDE Driven Design

    ReplyDelete
  3. Lol IDD. What would you call you example? NDD - Notepad Driven Design.

    ReplyDelete
  4. Static Typing. You can't just assume that duck typing is better. The intellisense is just a hint that you're not just putting random strings in your code that will blow when you try to execute them.

    ReplyDelete
  5. where lamborghini.Origin is saved?

    ReplyDelete
  6. It will be ignored because not present in the schema.

    ReplyDelete