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 ;-)