Try fast search NHibernate

12 November 2008

Mapping Source: How map a class without use XML

I don’t know how many times you heard a mad man talking about “mapping source” in NHibernate…

Map a class in NH without use XML at all ? only a crazy man can say that.

Hello! I’m that mad man.

As usual an entity implementation:

public class Animal
{
public virtual int Id { get; private set; }
public virtual string Description { get; set; }
}

Is a simple class because map something else, and begin a new framework, is not the target of this post.

Now an empty method, to write an integration test, of a new class inherited from the NHibernate configuration class:

public class Configuration : NHibernate.Cfg.Configuration
{
public void Register(Type entity){}
}

The integration test, basically, include the SchemaExport, a CRUD and an HQL; what is interesting here is only the setup of the test:

protected Configuration cfg;
protected ISessionFactoryImplementor sessions;

[TestFixtureSetUp]
public void TestFixtureSetUp()
{
cfg = new Configuration();
cfg.Configure();
cfg.Register(typeof(Animal));
new SchemaExport(cfg).Create(false, true);
sessions = (ISessionFactoryImplementor)cfg.BuildSessionFactory();
}

As you can see is similar to a common setup except for cfg.Register(typeof(Animal)). The others parts of the test are available from the download link.

Now I can start the dance…

In NHibernate all classes metadata are completely decoupled from XML; this mean that SchemaExport, and everything else in NH, absolutely don’t need XML files to be used. What I must do is inject everything after call the method cfg.Configure(). The place where all metadata are available is the namespace NHibernate.Mapping. The holder of metadata is the class Configuration trough the class NHibernate.Cfg.Mappings. The provider of an instance of NHibernate.Cfg.Mappings is the Configuration itself trough the method:

/// <summary>
///
Create a new <see cref="Mappings" /> to add classes and collection
/// mappings to.
/// </summary>
public Mappings CreateMappings(Dialect.Dialect dialect)

That method stay there from loooong time ago.

As we are doing in NH-Core each “binder” must use at least two classes (to create new metadata):

  1. an instance of NHibernate.Cfg.Mappings
  2. the instance of the configured Dialect

The configuration extension

public class Configuration : NHibernate.Cfg.Configuration
{
public void Register(Type entity)
{
Dialect dialect = Dialect.GetDialect(Properties);
Mappings mappings = CreateMappings(dialect);
SetDefaultMappingsProperties(mappings);
new EntityMapper(mappings, dialect).Bind(entity);
}

private static void SetDefaultMappingsProperties(Mappings mappings)
{
mappings.SchemaName = null;
mappings.DefaultCascade = "none";
mappings.DefaultAccess = "property";
mappings.DefaultLazy = true;
mappings.IsAutoImport = true;
mappings.DefaultNamespace = null;
mappings.DefaultAssembly = null;
}
}

For each class, I’m going to register, I’m getting the configured dialect and a new instance of Mappings class. Then I’m setting some default values and at the end I’m biding the entity type (EntityMapper(mappings, dialect).Bind(entity)).

The EntityMapper

Without boring you, with the full code, the heart is here

public void Bind(Type entity)
{
var rootClass = new RootClass();
BindClass(entity, rootClass);
}

private void BindClass(Type entity, PersistentClass pclass)
{
pclass.IsLazy = true;
pclass.EntityName = entity.FullName;
pclass.ClassName = entity.AssemblyQualifiedName;
pclass.ProxyInterfaceName = entity.AssemblyQualifiedName;
string tableName = GetClassTableName(pclass);
Table table = mappings.AddTable(null, null, tableName, null, pclass.IsAbstract.GetValueOrDefault());
((ITableOwner) pclass).Table = table;
pclass.IsMutable = true;
PropertyInfo[] propInfos = entity.GetProperties();

PropertyInfo toExclude = new IdBinder(this, propInfos).Bind(pclass, table);

pclass.CreatePrimaryKey(dialect);
BindProperties(pclass, propInfos.Where(x => x != toExclude));
mappings.AddClass(pclass);

string qualifiedName = pclass.MappedClass == null ? pclass.EntityName : pclass.MappedClass.AssemblyQualifiedName;
mappings.AddImport(qualifiedName, pclass.EntityName);
if (mappings.IsAutoImport && pclass.EntityName.IndexOf('.') > 0)
{
mappings.AddImport(qualifiedName, StringHelper.Unqualify(pclass.EntityName));
}
}

Including everything the EntityMapper.cs have 198 lines.

Metadata classes used are: RootClass, PersistentClass, Table, SimpleValue, Property and Column.

Conclusions

To use NHibernate without write a single XML mapping, is possible. Create mapped classes or others artifacts (as typedef, database-objects, named-queries, stored-procedures, filters and so on) without use XML, is possible. Because I’m extending the NHibernate.Cfg.Configuration, add some other artifacts or override mapping written using XML is possible. Write a “bridge” using EntityFramework attributes instead XMLs, is possible.

Write a new framework avoiding XML at all, is possible that is completely different than “is easy”. In general a framework is to make something easy to the framework users and not to the framework developers.

Code available here.


Technorati Tags: ,

07 November 2008

La concorrenza é l’anima del commercio

Que linda frase es esa: La competencia es el alma del comercio.

Hace un tiempo largo en NHibernate implementé IProxyFactoryFactory (porting, con adaptación, desde Hibernate). Como me está pasando, últimamente, nadie entendía para que “esa cosa” es necesaria, hasta que un día un committer me escribe en privado diciéndome: “al final hay alguien que entiende para que hiciste ese trabajo” y me engancha un link. El link llevaba al blog de William C. Pierce que usó IProxyFactoryFactory junto a un tool para generar proxies que pudieran trabajar en médium-trust. Junto con la movida que hicimos para dar a la luz NH-Forge, Bill Pierce, no solo aceptó entrar en NHibernate-Contrib si no que aceptó “rever” su trabajo y cambiarle de nombre a NHibernate.ProxyGenerators (notar el plural). Hasta ese entonces el plural era, lamentablemente, solo una mera intención…

Un día, de la nada, aparece un branch en NH-Core que llevaba todo el código de NH y una implementación de un ProxyFactoryFactory para usarlo con PostSharp (come venia pensándolo hace un tiempo); el problema del branch es que todavía estaba todo incrustado a parte que no pasa la mayoría de los tests de NH. Quédense en este día porque fue el disparador…

En todo este periodo había unos asuntos que me estaban molestando no poco:

  • Pocos son lo que entienden que el hecho de declarar métodos “virtual” no es una invasión de NH.
  • NH parecía no funcionar si no existiera Castle.
  • Quien usa Spring igualmente se tiene que llevar Castle.
  • En Castle no quieren liberar release y quieren que todos trabajemos con el trunk.
  • NH tenía una dependencia a algo sin versiones.
  • La gente que usaba NH con Castle Windsor y/o ActiveRecord se volvía loca al intentar mantener todos los proyectos sincronizado.

De buen tano calentón que soy (tano=Italiano en Buenos Aires) ese día me cansé y decidí dedicar el día a “separar los tantos” y la mañana siguiente pude anunciar la anhelada separación de dependencia desde Castle.DynamicProxy2 versión x.y.z (cualquiera porque no tiene, nunca fue liberado desde casi dos años).

Bueno… lo único que faltaba era solo empezar a generar alternativas… Den una vista a este link, al fondo, y fíjense cuan larga fue esta historia; falta muy poco para que cumpla un año. Hoy puedo anunciar la primera alternativa a Castle.DynamicProxy2 en NHibernate. Nació NHibernate.ProxyGenerators.LinFuDynamicProxy.

LinFuDynamicProxy será, probablemente, la alternativa que usarán quien no usa ningún IoC-FW en su proyecto ya que es una sola DLL, pesa 23KB (para NET3.5) y tiene una leve mejora en performance respecto a Castle (no estoy completamente seguro pero me pareció midiendo los tiempos de ejecución de los tests de NH).

Para quien usamos IoC falta un paso; ¿no se imaginan cual? No sé exactamente para cuando, pero espero pronto sea disponible NHibernate.ProxyGenerators.SpringDynamicProxy.

¡A ver si se logra trabajar con algo que tenga versión! ;-)

 Technorati Tags: ,,,,

26 October 2008

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.