Some years ago a phrase broke my mind:
“Program to an interface and not to an implementation”
As you probably know the phrase is one of the famous Gang of Four.
Is the phrase valid even for entities ?
I definitive think that the answer is yes for various reason; from dyn-proxy, for lazy-loading, to the use of DTO in View.
If we want use GoF suggestion, do we must write a lot of “artifacts” ?
Let me make an example… (fasten your seat belts)
First of all I start defining a simple entity:
public interface IPerson
{
long Id { get; set; }
string Name { get; set; }
}
Now I want persist it using NHibernate2.1.0 (the next mayor version) but without write a real implementation. What I want is write a generic implementation for the whole domain; to do it I’m going to use Castle.DynamicProxy.
The responsibility of my proxy will be hold entity-data so basically I need two things: the DataProxy and a “proxy marker” to discover if an object-instance is an instance of my proxy.
public interface IProxyMarker
{
DataProxyHandler DataHandler { get; }
}
public sealed class DataProxyHandler : IInterceptor
{
private readonly Dictionary<string, object> data = new Dictionary<string, object>(50);
private readonly string entityName;
public DataProxyHandler(string entityName, object id)
{
this.entityName = entityName;
data["Id"] = id;
}
public string EntityName
{
get { return entityName; }
}
public IDictionary<string, object> Data
{
get { return data; }
}
#region IInterceptor Members
public void Intercept(IInvocation invocation)
{
invocation.ReturnValue = null;
string methodName = invocation.Method.Name;
if ("get_DataHandler".Equals(methodName))
{
invocation.ReturnValue = this;
}
else if (methodName.StartsWith("set_"))
{
string propertyName = methodName.Substring(4);
data[propertyName] = invocation.Arguments[0];
}
else if (methodName.StartsWith("get_"))
{
string propertyName = methodName.Substring(4);
invocation.ReturnValue = data[propertyName];
}
else if ("ToString".Equals(methodName))
{
invocation.ReturnValue = entityName + "#" + data["Id"];
}
else if ("GetHashCode".Equals(methodName))
{
invocation.ReturnValue = GetHashCode();
}
}
#endregion
}
Note: the IInterceptor is Castle.Core.Interceptor.IInterceptor.
Now I want persist the IPerson but before write the mapping I need some more artifacts related to some new features of NH2.1. I’m going to use Tuplizers.
The IInstantiator implementation:
public class EntityInstantiator : IInstantiator
{
private static readonly ProxyGenerator proxyGenerator = new ProxyGenerator();
private readonly Type t;
public EntityInstantiator(Type entityType)
{
t = entityType;
}
public object Instantiate(object id)
{
return
proxyGenerator.CreateInterfaceProxyWithoutTarget(t, new[] {typeof (IProxyMarker), t},
new DataProxyHandler(t.FullName, id));
}
public object Instantiate()
{
return Instantiate(null);
}
public bool IsInstance(object obj)
{
try
{
return t.IsInstanceOfType(obj);
}
catch (Exception e)
{
throw new Exception("could not get handle to entity-name as interface : " + e);
}
}
}
Note: the ProxyGenerator is Castle.DynamicProxy.ProxyGenerator.
The override of PocoEntityTuplizer:
public class EntityTuplizer : PocoEntityTuplizer
{
public EntityTuplizer(EntityMetamodel entityMetamodel, PersistentClass mappedEntity)
: base(entityMetamodel, mappedEntity) {}
protected override IInstantiator BuildInstantiator(PersistentClass persistentClass)
{
return new EntityInstantiator(persistentClass.MappedClass);
}
}
Has you know when we create a proxy the System.Type will take a value that nobody can predict; to allow NH recognize my entity I use a simple NHibernate.IInterceptor implementation:
public class EntityNameInterceptor : EmptyInterceptor
{
public override string GetEntityName(object entity)
{
string entityName = ExtractEntityName(entity) ?? base.GetEntityName(entity);
return entityName;
}
private static string ExtractEntityName(object entity)
{
// Our custom Proxy instances actually bundle
// their appropriate entity name, so we simply extract it from there
// if this represents one of our proxies; otherwise, we return null
var pm = entity as IProxyMarker;
if (pm != null)
{
var myHandler = pm.DataHandler;
return myHandler.EntityName;
}
return null;
}
}
Because I will need to have a instance of “something” in my application I need a sort of EntityFactory. Here a quick implementation:
public class EntityFactory
{
private static readonly ProxyGenerator proxyGenerator = new ProxyGenerator();
public T NewEntity<T>()
{
Type t = typeof (T);
return (T)
proxyGenerator.CreateInterfaceProxyWithoutTarget
(t, new[] {typeof (IProxyMarker), t}, new DataProxyHandler(t.FullName, 0));
}
}
Before write a persistence test I need the last little thing: the mapping.
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="LessThanFew" namespace="LessThanFew.Domain"> <class name="IPerson"> <tuplizer class="LessThanFew.EntityTuplizer, LessThanFew" entity-mode="poco"/> <id name="Id"> <generator class="hilo"/> </id> <property name="Name"/> </class> </hibernate-mapping>
Now I can write my test…
[TestFixture]
public class PersistenceFixture : FunctionalTest
{
[Test]
public void PersonCrud()
{
object savedId;
var person = entityFactory.NewEntity<IPerson>();
person.Name = "Katsumoto";
using (var session = sessions.OpenSession())
using (var tx = session.BeginTransaction())
{
savedId = session.Save(person);
tx.Commit();
}
using (var session = sessions.OpenSession())
using (var tx = session.BeginTransaction())
{
person = session.Get<IPerson>(savedId);
Assert.That(person, Is.Not.Null);
Assert.That(person.Name, Is.EqualTo("Katsumoto"));
session.Delete(person);
tx.Commit();
}
using (var session = sessions.OpenSession())
using (var tx = session.BeginTransaction())
{
person = session.Get<IPerson>(savedId);
Assert.That(person, Is.Null);
tx.Commit();
}
}
}
The test work but… to persist my little entity I write 6 more classes than usual… yes but now I have an AOP approach of my application even for entities. Ok let me complicate the domain a little bit:
public interface IPerson
{
int Id { get; set; }
string Name { get; set; }
IAddress Address { get; set; }
ISet<IPerson> Family { get; set; }
}
public interface ICustomer : IPerson
{
ICompany Company { get; set; }
}
public interface ICompany
{
int Id { get; set; }
string Name { get; set; }
}
public interface IAddress
{
int Id { get; set; }
string Street { get; set; }
string City { get; set; }
string PostalCode { get; set; }
}
and the new “matrix”:
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="LessThanFew" namespace="LessThanFew.Domain"> <class name="IPerson" discriminator-value="person" abstract="false"> <tuplizer class="LessThanFew.EntityTuplizer, LessThanFew" entity-mode="poco"/> <id name="Id"> <generator class="hilo"/> </id> <discriminator type="string" force="false"/> <property name="Name"/> <many-to-one name="Address" cascade="all" column="addr_id"/> <set name="Family" cascade="all" generic="true"> <key column="pers_id"/> <one-to-many class="IPerson"/> </set> <subclass name="ICustomer" discriminator-value="customer" abstract="false"> <tuplizer class="LessThanFew.EntityTuplizer, LessThanFew" entity-mode="poco"/> <many-to-one name="Company" cascade="none" column="comp_id"/> </subclass> </class> <class name="ICompany" abstract="false"> <tuplizer class="LessThanFew.EntityTuplizer, LessThanFew" entity-mode="poco"/> <id name="Id"> <generator class="hilo"/> </id> <property name="Name"/> </class> <class name="IAddress" abstract="false"> <tuplizer class="LessThanFew.EntityTuplizer, LessThanFew" entity-mode="poco"/> <id name="Id"> <generator class="hilo"/> </id> <property name="Street"/> <property name="City"/> <property name="PostalCode"/> </class> </hibernate-mapping>
If I need INotifyPropertyChanged, for all entities of my domain, what I must change is the implementation of DataProxyHandler, by the way the intention of this post is only to show another NH2.1 feature : Tuplizers.
The code is available here (note that it not contain the deploy of NH2.1).
The last little thing… the Italian and Spanish translation of “FEW” is POCO.