Logo

NHibernate

The object-relational mapper for .NET

Less than “Few” is GoF

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.


Posted Sun, 12 October 2008 10:40:00 AM by fabiomaulo
Filed under: proxy, Castle, NH2.1, Dynamic Entities, Tuplizers, AOP

comments powered by Disqus
© NHibernate Community 2024