Try fast search NHibernate

08 July 2009

Duck Typing with NHibernate

This is a review of this old post this time implemented using anonymous types and “dynamic type”. The solution is implemented using NH2.1.0 and .NET3.5, where the DLR is not available, so there is some trick to continue working with classes instead strings.

Duck Typing or Dynamic entities will die very soon when we want make it persistent, our 20th century technology (RDBMS) can understand only Table-Column (no object graph, nothing about no scalar types… etc.) and you can imagine how much it can understand something Dynamic. Fortunately our dear persistent layer is here to fill up the differences.

The persistent mapping

First the description of how I want persist something (something = I don’t know if it will be represented with classes, interfaces or whatever).

<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2">

<
class entity-name="ProductLine">
<
id name="Id" type="int">
<
generator class="hilo"/>
</
id>
<
property name="Description" not-null="true" length="200" type="string"/>

<
bag name="Models" cascade="all" inverse="true">
<
key column="productId"/>
<
one-to-many class="Model"/>
</
bag>

</
class>

<
class entity-name="Model">
<
id name="Id" type="int">
<
generator class="hilo"/>
</
id>

<
property name="Name" not-null="true" length="25" type="string"/>
<
property name="Description" not-null="true" length="200" type="string"/>
<
many-to-one name="ProductLine"
column="productId"
not-null="true"
class="ProductLine"/>
</
class>

</
hibernate-mapping>

Even if in the mapping there is the tag “class” it is only to refer to absolutely abstract entity-name and the entity-name is only to reference a mapped-artifact.

The mapping is not so trivial because it represent a parent-child bidirectional relationship.

Saving Ducks

using (ISession s = sessions.OpenSession())
using (ITransaction t = s.BeginTransaction())
{
var line = new
{
Description = "High quality cars",
Models = new ArrayList(),
Pizza = "calda"
};
var ferrari = new
{
ProductLine = line,
Name = "Dino",
Description = "Ferrari Dino",
Fuel = "Gasoline"
};
var lamborghini = new
{
ProductLine = line,
Name = "Countach",
Description = "Lamborghini Countach",
Origin = "Italy"
};
line.Models.Add(ferrari);
line.Models.Add(lamborghini);

savedId = s.SaveDynamic("ProductLine", line);
t.Commit();
}

I’m creating three anonymous objects with some property described in the mapping above and others properties without a persistent representation. In the SaveDymanic I’m using the entity-name to instruct NHibernate in order to use a specific mapping.

Updating Ducks

I’m not interested to know how NHibernate save things but when I need to retrieve a persistent instance to modify it I need a way to access to each property. Waiting DLR I can represent each state with two simple interfaces:

public interface IProductLine
{
int Id { get; set; }
string Description { get; set; }
IList<IModel> Models { get; set; }
}
public interface IModel
{
int Id { get; set; }
string Name { get; set; }
string Description { get; set; }
IProductLine ProductLine { get; set; }
}

To modify the state of the saved entity

using (ISession s = sessions.OpenSession())
using (ITransaction t = s.BeginTransaction())
{
// Reload the saved dynamic entity
var entity = s.Get("ProductLine", savedId);
// Transform it to a know type
productLine = entity.AsDynamic<IProductLine>();

// Modify through know type
productLine.Description = "Quality cars";
productLine.Models.Add(
(new
{
ProductLine = entity,
Name = "Locus",
Description = "Audi Locus"
}).AsDynamic<IModel>());

t.Commit(); // Persist modification
}

and the little test of modifications

using (ISession s = sessions.OpenSession())
{
var entity = s.Get("ProductLine", savedId);
productLine = entity.AsDynamic<IProductLine>();
productLine.Description.Should().Be.EqualTo("Quality cars");
productLine.Models.Count.Should().Be.EqualTo(3);
}

Deleting

using (ISession s = sessions.OpenSession())
using (ITransaction t = s.BeginTransaction())
{
// Delete the detached dynamic entity
s.DeleteDynamic("ProductLine", productLine);
t.Commit();
}

using (ISession s = sessions.OpenSession())
{
// check entity deletation with cascade
s.Get("ProductLine", savedId).Should().Be.Null();
s.CreateQuery("from Model").List().Count.Should().Be.EqualTo(0);
}

Conclusion

NHibernating… quack quack ;-)

7 comments:

  1. Fabio,
    you have a branche somewhere (in Nh / uNh) so we can play with this?

    ReplyDelete
  2. Oh! sure...
    The code is here
    http://code.google.com/p/unhaddins/source/browse/#svn/HunabKu/src/DuckTyping/DuckTyping

    ReplyDelete
  3. I guess the way to go is by doing the same big sites are doing with key-value pair DBs.

    I mean, in one place store the serialized object which of course can be very dynamic as serialization allows; and, in another place store just indexes for finding those objects.

    Simple, fast, dynamic :)

    ReplyDelete
  4. fast.... it depend; try to think to a commercial company that need data mining over sold products.
    key-value DB are faster in some scenario, it is a fact, but not in all (it is another fact).

    ReplyDelete
  5. @mausch
    Good work... as you can see NH is waiting again as we was waiting an OO query language for some years.

    ReplyDelete