(my blog)
The entity-name is another powerful feature of NH2.1.
Part of the implementation was introduced in NH2.0 but it was not fully implemented even if it are working "under the cover" (if you read the SVN-log you saw “one more step to entity-name” many and many times).
As usual I start from domain:
namespace EntityNameInAction
{
public abstract class Animal
{
public virtual int Id { get; private set; }
public virtual string Description { get; set; }
}
public class Reptile: Animal
{
public virtual float BodyTemperature { get; set; }
}
public class Human : Animal
{
public virtual string Name { get; set; }
public virtual string NickName { get; set; }
public virtual DateTime Birthdate { get; set; }
}
public class Family<T> where T: Animal
{
public virtual int Id { get; private set; }
public virtual T Father { get; set; }
public virtual T Mother { get; set; }
public virtual ISet<T> Childs { get; set; }
}
}
In the DB representation I want each Animal in a different table so I’m going to have tree different tables. At this point only one table for all “kinds” of Family is not enough basically because I can’t have a ForeignKey pointing to two tables. What I need is a table for each strongly typed Family. The way to do it, using NHibernate, is the new tag: entity-name
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
assembly="EntityNameInAction"
namespace="EntityNameInAction"
default-access="backfield">
<class name="Animal">
<id name="Id">
<generator class="hilo"/>
</id>
<property name="Description"/>
<joined-subclass name="Reptile">
<key column="animalId"/>
<property name="BodyTemperature"/>
</joined-subclass>
<joined-subclass name="Human">
<key column="animalId"/>
<property name="Name"/>
<property name="NickName"/>
<property name="Birthdate" type="Date"/>
</joined-subclass>
</class>
<class name="Family`1[[Reptile]]" table="ReptilesFamilies"
entity-name="ReptilesFamily">
<id name="Id">
<generator class="hilo"/>
</id>
<many-to-one name="Father" class="Reptile" cascade="all"/>
<many-to-one name="Mother" class="Reptile" cascade="all"/>
<set name="Childs" generic="true" cascade="all">
<key column="familyId" />
<one-to-many class="Reptile"/>
</set>
</class>
<class name="Family`1[[Human]]" table="HumanFamilies"
entity-name="HumanFamily">
<id name="Id">
<generator class="hilo"/>
</id>
<many-to-one name="Father" class="Human" cascade="all"/>
<many-to-one name="Mother" class="Human" cascade="all"/>
<set name="Childs" generic="true" cascade="all">
<key column="familyId" />
<one-to-many class="Human"/>
</set>
</class>
</hibernate-mapping>
The new accessor "backfield" is another new feature but it don’t play some special role here.
As you can see I have a class implementation for all “kinds” of families but two different strongly typed persistence mappings.
Well… that’s all… ups… The test to demonstrate that it are working
[Test]
public void EntityNameDemo()
{
using (ISession s = sessions.OpenSession())
using (ITransaction tx = s.BeginTransaction())
{
var rf = new Reptile {Description = "Crocodile"};
var rm = new Reptile {Description = "Crocodile"};
var rc1 = new Reptile {Description = "Crocodile"};
var rc2 = new Reptile {Description = "Crocodile"};
var rfamily = new Family<Reptile>
{
Father = rf,
Mother = rm,
Childs = new HashedSet<Reptile> {rc1, rc2}
};
s.Save("ReptilesFamily", rfamily);
tx.Commit();
}
using (ISession s = sessions.OpenSession())
using (ITransaction tx = s.BeginTransaction())
{
var hf = new Human {Description = "Flinstone", Name = "Fred"};
var hm = new Human {Description = "Flinstone", Name = "Wilma"};
var hc1 = new Human {Description = "Flinstone", Name = "Pebbles"};
var hfamily = new Family<Human>
{
Father = hf,
Mother = hm,
Childs = new HashedSet<Human> {hc1}
};
s.Save("HumanFamily", hfamily);
tx.Commit();
}
using (ISession s = sessions.OpenSession())
using (ITransaction tx = s.BeginTransaction())
{
IList<Family<Human>> hf = s.CreateQuery("from HumanFamily").List<Family<Human>>();
Assert.That(hf.Count, Is.EqualTo(1));
Assert.That(hf[0].Father.Name, Is.EqualTo("Fred"));
Assert.That(hf[0].Mother.Name, Is.EqualTo("Wilma"));
Assert.That(hf[0].Childs.Count, Is.EqualTo(1));
IList<Family<Reptile>> rf = s.CreateQuery("from ReptilesFamily").List<Family<Reptile>>();
Assert.That(rf.Count, Is.EqualTo(1));
Assert.That(rf[0].Childs.Count, Is.EqualTo(2));
tx.Commit();
}
using (ISession s = sessions.OpenSession())
using (ITransaction tx = s.BeginTransaction())
{
s.Delete("from HumanFamily");
s.Delete("from ReptilesFamily");
tx.Commit();
}
}
As in “Less than GoF is hbm” I’m using the overload of session.Save(string, object) method; the first parameter is the entity-name, I had used in the mapping, the second is the instance.
Now that’s all (code available here).