NHibernate Forge
The official new home for the NHibernate for .NET community

entity-name in action: Entity Abstraction

Another time I start from :

“Program to an interface and not to an implementation”

I want have the same approach, I’m using for DAOs, Models, Presenters, Validation, and so on, for my domain.

The domain:

public interface IEntity<TIdentity>: IEquatable<IEntity<TIdentity>>
{
TIdentity Id { get; }
}

public interface IAnimal : IEntity<int>
{
string Description { get; set; }
}

public interface IReptile : IAnimal
{
float BodyTemperature { get; set; }
}

public interface IHuman : IAnimal
{
string Name { get; set; }
string NickName { get; set; }
DateTime Birthdate { get; set; }
}

public interface IFamily<T> : IEntity<int> where T : IAnimal
{
T Father { get; set; }
T Mother { get; set; }
ISet<T> Childs { get; set; }
}

Because I’m going to work with interfaces I will need a sort of factory to have transient-instances of my entities. For this example I’m going to use something simple and more “general purpose”; a class resolver:

public interface IClassResolver : IDisposable
{
T Resolve<T>() where T : class;
T Resolve<T>(string service) where T : class;
}

The responsibility of the IClassResolver implementor is return an instance for a given Type where the Type is an interface (well… in general is an interface). The concrete implementation of a IClassResolver will be injected using some IoC framework but for this post I will use a simple static exposer:

public class DI
{
private static IClassResolver resolver;
private DI() {}

public static IClassResolver Resolver
{
get
{
if (resolver == null)
{
throw new InvalidOperationException("Resolver was not initialized. Use StackResolver.");
}

return resolver;
}
}

public static void StackResolver(IClassResolver dependencyResolver)
{
resolver = dependencyResolver;
}
}

As you can see nothing so complicated.

Now I have all needed to write a test for my domain:

[Test]
public void DomainAbstraction()
{
using (ISession s = sessions.OpenSession())
using (ITransaction tx = s.BeginTransaction())
{
var rf = DI.Resolver.Resolve<IReptile>();
rf.Description = "Crocodile";

var rm = DI.Resolver.Resolve<IReptile>();
rm.Description = "Crocodile";

var rc1 = DI.Resolver.Resolve<IReptile>();
rc1.Description = "Crocodile";

var rc2 = DI.Resolver.Resolve<IReptile>();
rc2.Description = "Crocodile";

var rfamily = DI.Resolver.Resolve<IFamily<IReptile>>();
rfamily.Father = rf;
rfamily.Mother = rm;
rfamily.Childs = new HashedSet<IReptile> { rc1, rc2 };

s.Save(rfamily);
tx.Commit();
}

using (ISession s = sessions.OpenSession())
using (ITransaction tx = s.BeginTransaction())
{
var hf = DI.Resolver.Resolve<IHuman>();
hf.Description = "Flinstone";
hf.Name = "Fred";

var hm = DI.Resolver.Resolve<IHuman>();
hm.Description = "Flinstone";
hm.Name = "Wilma";

var hc1 = DI.Resolver.Resolve<IHuman>();
hc1.Description = "Flinstone";
hc1.Name = "Pebbles";

var hfamily = DI.Resolver.Resolve<IFamily<IHuman>>();
hfamily.Father = hf;
hfamily.Mother = hm;
hfamily.Childs = new HashedSet<IHuman> { hc1 };

s.Save(hfamily);
tx.Commit();
}

using (ISession s = sessions.OpenSession())
using (ITransaction tx = s.BeginTransaction())
{
var hf = s.CreateQuery("from HumanFamily").List<IFamily<IHuman>>();

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

var rf = s.CreateQuery("from ReptilesFamily").List<IFamily<IReptile>>();

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

Note: s.Save(hfamily) <<=== there isn’t a string for the entity-name; now NH are supporting it.

As you can see the users of my domain (the test in this case), are working only using interfaces; there isn’t a reference to a concrete implementation of my domain. The concrete implementation of the domain is trivial and you can see it downloading the code. The main thing you will notice, in the implementation, is the absence of the virtual modifier.

Wiring…

To wire the interface, with its concrete implementation, I want use NHibernate. The mapping is similar to the previous:

<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
assembly="EntityNameInAction.Abstraction.Entities.Impl"
namespace="EntityNameInAction.Abstraction.Entities.Impl.Naturalness"
default-access="backfield">

<
class name="MyAnimal" abstract="true"
proxy="EntityNameInAction.Abstraction.Entities.Naturalness.IAnimal, EntityNameInAction.Abstraction.Entities"
entity-name="Animal">
<
id name="id" access="field">
<
generator class="hilo"/>
</
id>
<
discriminator column="kind"/>
<
property name="Description"/>
</
class>

<
subclass name="MyHuman"
proxy="EntityNameInAction.Abstraction.Entities.Naturalness.IHuman, EntityNameInAction.Abstraction.Entities"
extends="Animal"
entity-name="Human">
<
property name="Name"/>
<
property name="NickName"/>
<
property name="Birthdate" type="Date"/>
</
subclass>

<
subclass name="MyReptile"
proxy="EntityNameInAction.Abstraction.Entities.Naturalness.IReptile, EntityNameInAction.Abstraction.Entities"
extends="Animal"
entity-name="Reptile">
<
property name="BodyTemperature"/>
</
subclass>

<
class name="MyFamily`1[[EntityNameInAction.Abstraction.Entities.Naturalness.IReptile, EntityNameInAction.Abstraction.Entities]]"
proxy="EntityNameInAction.Abstraction.Entities.Naturalness.IFamily`1[[EntityNameInAction.Abstraction.Entities.Naturalness.IReptile, EntityNameInAction.Abstraction.Entities]], EntityNameInAction.Abstraction.Entities"
table="Families" discriminator-value="Reptile" where="familyKind = 'Reptile'"
entity-name="ReptilesFamily">
<
id name="id" access="field">
<
generator class="hilo"/>
</
id>
<
discriminator column="familyKind"/>
<
many-to-one name="Father" cascade="all" entity-name="Reptile"/>
<
many-to-one name="Mother" cascade="all" entity-name="Reptile"/>
<
set name="Childs" generic="true" cascade="all">
<
key column="familyId" />
<
one-to-many entity-name="Reptile"/>
</
set>
</
class>

<
class name="MyFamily`1[[EntityNameInAction.Abstraction.Entities.Naturalness.IHuman, EntityNameInAction.Abstraction.Entities]]"
proxy="EntityNameInAction.Abstraction.Entities.Naturalness.IFamily`1[[EntityNameInAction.Abstraction.Entities.Naturalness.IHuman, EntityNameInAction.Abstraction.Entities]], EntityNameInAction.Abstraction.Entities"
table="Families" discriminator-value="Human" where="familyKind = 'Human'"
entity-name="HumanFamily">
<
id name="id" access="field">
<
generator class="hilo"/>
</
id>
<
discriminator column="familyKind"/>
<
many-to-one name="Father" cascade="all" entity-name="Human"/>
<
many-to-one name="Mother" cascade="all" entity-name="Human"/>
<
set name="Childs" generic="true" cascade="all">
<
key column="familyId" />
<
one-to-many entity-name="Human"/>
</
set>
</
class>
</
hibernate-mapping>

Mapping highlight


  • class/subclass name: are my concrete classes (implementors of domain)
  • proxy : is the interface (the domain); using it as proxy I can avoid virtual methods in the implementation because the underlining Dynamic-Proxy will inherit from the interface. Using interface I have many others vantages but is to long explain each (only one for example: I can cast a proxy-instance to an interface)
  • entity-name :  is the name I will use for persistence and represent another abstraction-level. For persistence stuff I can use a “conceptual-name” of the entity without take care about its representation in C#. As you can see the entity-name are playing on each association/aggregation/extends; not the concrete class nor the interface.
  • As in this post the domain is represented in two tables.

Class Resolver

In the implementation of IClassResolver I’m going to use the NHibernate’s mapping to wire the interface of the domain (ex: IHuman) to its concrete class (ex: MyHuman) trough the entity-name. Is it not clear ? ok perhaps the code will be more clear

public class NhEntityClassResolver : IClassResolver
{
private readonly Dictionary<Type, string> serviceToEntityName = new Dictionary<Type, string>();
public NhEntityClassResolver(ISessionFactoryImplementor factory)
{
if(factory == null)
{
throw new ArgumentNullException("factory");
}
Factory = factory;
InitializeTypedPersisters();
}

private void InitializeTypedPersisters()
{
foreach (var entityName in Factory.GetAllClassMetadata().Keys)
{
serviceToEntityName
.Add(Factory.GetEntityPersister(entityName)
.GetConcreteProxyClass(EntityMode.Poco), entityName);
}
}

public ISessionFactoryImplementor Factory { get; private set; }

#region Implementation of IDisposable

public void Dispose()
{
}

#endregion

#region
Implementation of IClassResolver

public T Resolve<T>() where T : class
{
string entityName;
if(serviceToEntityName.TryGetValue(typeof(T), out entityName))
{
return Resolve<T>(entityName);
}
return null;
}

public T Resolve<T>(string service) where T: class
{
return Factory.GetEntityPersister(service).Instantiate(null, EntityMode.Poco) as T;
}

#endregion
}

The ISessionFactoryImplementor is one of the interfaces implemented by the NH SessionFactory. The method Resolve<T>(string) are using the parameter service as the entity-name. The hard-work is done by the method InitializeTypedPersisters; what I’m doing there is map each interface with its entity-name… nothing more.

Conclusions

“Program to an interface and not to an implementation” is really wonderful.

Do you really have some doubt about how NHibernate implements “Persistence ignorance” ?

Code available here.


Posted oct 31 2008, 07:53 p.m. by Fabio Maulo

Comments

bunceg wrote re: entity-name in action: Entity Abstraction
on 11-20-2008 8:41

I'm struggling with doing exactly this problem and, close as this is, it doesn't solve it. The problem is the requirement to use ISET in my domain specific interfaces ... i.e. a link to NHibernate. This breaks the rule of persistance ignorance.

I could use IList and a Bag but I actually don't want to use IList. I want to use (e.g.) IAnimals because IAnimals also exposes some methods specific to dealing with Animal collections.

I have yet to find a way around this. Even creating my own domain collection class that implements IList<T> and IList, that Animals inherits from, throws a "cannot cast PersistentBag" exception.

Any suggestions?

Fabio Maulo wrote re: entity-name in action: Entity Abstraction
on 11-20-2008 9:32

ISet don't is a requirement; It is an extension of the .NET FW.  In .NET3.5 there is a new implementation but, without an interface. BTW you can use any kind of implementation if you domain can expose ICollection.

About "IAnimals" you can implements your own IUserCollectionType; this is required if you want use lazy-loading and extra-lazy-loading. If you don't want do it you can map the collection with a field accessor and expose what you want in your entity.

Fabio Maulo wrote re: entity-name in action: Entity Abstraction
on 11-20-2008 9:50

Ah... another little thing... the use of ISet don't require a reference to NHibernate assembly but a reference to Iesi.Collections.

bunceg wrote re: entity-name in action: Entity Abstraction
on 11-20-2008 11:17

Thanks for the reply Fabio. I am really stuck on this,  as IList extends ICollection so by default IAnimal, which implements IList also implements ICollection... unless I misunderstand something fundamental. Regardess, even if I added ICollection to IAnimal (e.g. IAnimal : IList<T>, IList, ICollection it still didn't work.

If you say it does (and I am using .NET 3.5) then at least I know I'm making a mistake somewhere, but an update to your example doing something like that (i.e. pure OO with Interfaces... no dependency on ISet or IUserCollectionType, and using something like IAnimals instead of IList or IList<T>) would help me a lot :)

In my example, IAnimals is implemented by a specialised domain class that wraps an internal List<T> by the way. This class implements all IList, IList<T>, ICollection, IAnimals interfaces... something that I don't think is too crazy? :)

As regards Iesi.Collections and IUserCollectionType... I take your point but they are still part of the NHibernate API .. hence a dependency on the NHibernate product and, a far I consider it, breaks Persistence Ignorance

Fabio Maulo wrote re: entity-name in action: Entity Abstraction
on 11-20-2008 12:29

I hope that your doctor don't said you to use ISet. The Iesi.Collection.Set is a specialized collection and an implementation of something similar is available in .NET3.5 unfortunately without an appropriate interface. The use of ISet don't mean to have a reference to NH. You can create the same example of this post using IList<T> or ICollection<T> or IEnumerable<T>.

About IAnimals... do you can send me, in a private mail, two Collections interface needed by your domain ?

I know various BO FW and I don't saw specialized collection for each collection used in the domain.

About IUserCollectionType... probably you don't know what it is, because use it don't mean that you will have a reference to NH in your domain.

bunceg wrote re: entity-name in action: Entity Abstraction
on 11-21-2008 16:03

Thanks Fabio, started a private conversation thread.

Konstantin wrote re: entity-name in action: Entity Abstraction
on 08-28-2009 7:48

Hello, Fabio! Entity-Name with Poco is the cool stuff. Can i use this feature to map one class to the several tables in one hbm.xml?

Like this:

<class entity-name="ObjectType.TableRecordClass1" name="TableRecordClass" table="DirectoriesSection1">

<id name="Id">

<column name="Id" sql-type="bigint" not-null="true" />

<generator class="native"></generator>

</id>

<property name="Name">

<column name="Name" length="50" not-null="true" />

</property>

<property name="Type" />

<property name="ParentId" />

</class>

<class entity-name="ObjectClass.TableRecordClass1" name="TableRecordClass" table="DocumentsSection1">

<id name="Id">

<column name="Id" sql-type="bigint" not-null="true" />

<generator class="native"></generator>

</id>

<property name="Name">

<column name="Name" length="50" not-null="true" />

</property>

<property name="Type" />

<property name="ParentId" />

</class>

<class entity-name="ObjectClass.TableRecordClass2" name="TableRecordClass" table="DocumentsSection2">

<id name="Id">

<column name="Id" sql-type="bigint" not-null="true" />

<generator class="native"></generator>

</id>

<property name="Name">

<column name="Name" length="50" not-null="true" />

</property>

<property name="Type" />

<property name="ParentId" />

</class>

Fabio Maulo wrote re: entity-name in action: Entity Abstraction
on 08-28-2009 18:40

No.

Powered by Community Server (Commercial Edition), by Telligent Systems