Try fast search NHibernate

15 October 2008

Less than GoF is hbm

In Less than “Few” is GoF I show how have a single implementation for the whole domain entities using Tuplizers.

In this post I will show another NH2.1 feature : EntityMode.Map

Suppose you want prototype the persistence of your application; you don’t want write any entity-interface nor any entity-implementation.

Suppose you want test the ORM, in the strictly meaning of the anachronism, to a legacy data base; you don’t want write any entity-interface nor any entity-implementation.

Suppose a company send you some NH-mappings-files, to optimize data access, but they don’t want send you their implementation; you don’t have the implementation.

To prototype a entirely system there are various tools; one of these is AjGenesis and it’s IDE AjGenesisStudio. AjGenesis is a CodeGenerator that use an XML source (at the end, who are using only the designer for WebForm or WPF ? ). In practices AjGenesis start from a conceptual model written in XML, and NOT from a DB model, to generate your system.

As I can do with AjGenesis (explain how it work is not a target of this post), I’m going to prototype, a little system, using an XML:

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

Do you are seeing something familiar ?

Yes! it is only a NHibernate2.1 mapping file.

How I can write a persistence-test without write a single class ? well… with my hands, VisualStudio, NUnit and… of course our dear NHibernate.

The configuration:

[TestFixtureSetUp]
public void TestFixtureSetUp()
{
cfg = new Configuration();
cfg.Configure();
cfg.AddResource("LessThanGoF.Prototype.ProductLine.hbm.xml", typeof (PrototypeSystemFixture).Assembly);
new SchemaExport(cfg).Create(false, true);

cfg.SetProperty("default_entity_mode", EntityModeHelper.ToString(EntityMode.Map));

sessions = (ISessionFactoryImplementor) cfg.BuildSessionFactory();
}
The line to pay attention is the set of "default_entity_mode". If you paid attention in the mappings of the previous post you sure noted entity-mode="poco". The EntityMode.Poco is the EntityMode you are using until today. The EntityMode.Map is not something new; if you are using dynamic-component, even in NH2.0, you are using part of the implementation of EntityMode.Map.

The EntityMode work together with Tuplizers. In practice the tuplizer defines the way on how transform a Property-Value to it’s persistent representation, and viceversa a Column-Value to it’s in-memory representation, and the EntityMode defines which tuplizer is in use (I have simplified the “definition” of tuplizer).

In NH2.1 each entity may have tree representations:
  • POCO
  • Dynamic-Map (aka Dictionary<PropertyName, PropertyValue>)
  • Xml (Not implemented yet)
And now the test
[Test]
public void DynamicClasses()
{
IDictionary cars;
IList models;
using (ISession s = sessions.OpenSession())
{
using (ITransaction t = s.BeginTransaction())
{
cars = new Hashtable();
cars["Description"] = "Cars";

IDictionary ferrari = new Hashtable();
ferrari["ProductLine"] = cars;
ferrari["Name"] = "Dino";
ferrari["Description"] = "Ferrari Dino.";

IDictionary lamborghini = new Hashtable();
lamborghini["ProductLine"] = cars;
lamborghini["Name"] = "Countach";
lamborghini["Description"] = "Lamborghini Countach";

models = new List<IDictionary> {ferrari, lamborghini};

cars["Models"] = models;

s.Save("ProductLine", cars);
t.Commit();
}
}

using (ISession s = sessions.OpenSession())
{
using (ITransaction t = s.BeginTransaction())
{
cars = (IDictionary) s.CreateQuery("from ProductLine pl order by pl.Description").UniqueResult();
models = (IList) cars["Models"];

Assert.That(models.Count == 2);

s.Clear();

IList list = s.CreateQuery("from Model m").List();
var model = (IDictionary) list[0];

Assert.That(((IList) ((IDictionary) model["ProductLine"])["Models"]).Contains(model));

s.Clear();
t.Commit();
}
}

using (ISession s = sessions.OpenSession())
{
using (ITransaction t = s.BeginTransaction())
{
cars = (IDictionary) s.CreateQuery("from ProductLine pl order by pl.Description").UniqueResult();
s.Delete(cars);
t.Commit();
}
}
}
Take a look to s.Save("ProductLine", cars); the first parameter is the entity-name I had used in the mapping, the second is the instance.

As you can see, I have wrote a complete persistence test without write a single entity-interface nor entity-implementation. All NH-session-API are working as expected, lazy-loading are working as expected and if you activate NHibernate.SQL logging you can see that all are working in the same way than when you have entities implemented.

End of the story!

…… wait… wait…

let me wire some thoughts…

  • The entity-name is a conceptual representation of an entity definition, I can use the entity-name to work with NH without take care if the entity is represented by an interface, a class or a class with some generic type definition…
  • With NHibernate an entity have three possible representations…
  • In this post I have a Dictionary<PropertyName, PropertyValue> to represent the state of an entity… but in the previous post I have the same representation in the DataProxy
  • Dictionary<K, V> is serializable and if I join it with a string, to hold the entityName, I may have a generic DataContract to send my entity state…

Material for some others posts.

To be continued…

1 comment:

  1. That's very nice!

    it gave me a lot of ideas of how this can be used to make dynamic application, where properties can be defines on the fly and UI dragged and dropped accordingly.

    ReplyDelete