Try fast search NHibernate

28 May 2009

Auditing with NHibernate

Provide some good example about how implements Auditing in NHibernate, perhaps, is one of the most difficult thing to do. There are a lot of way to implements the Auditing and, in general, ours examples are oriented to the less useful and the most intrusive; the IAuditable.

public interface IAuditable
{
DateTime CreatedAt { get; }
string CreatedBy { get; }
DateTime UpdatedAt { get; }
string UpdatedBy { get; }
}

The implementation of this kind of thing can’t be called “Auditing” because:

  • You can’t know which was the change of an update.
  • It is invasive; any entity is auditable and the Auditing, in general, is outside the scope of a domain-entity.
  • You lost any information after a delete.

For real our examples are not oriented to show you how implement the auditing but only to show you a possible usage of listeners/interceptor.

I’m working in some others implementations in uNhAddIns. My intention is implements from AuditLog to something closer to ParallelModel.

The real challenge is a no intrusive and dynamic implementation of ParallelModel with EventSourcing, perhaps with a “sponsor” it would be faster.

Stay tuned.

26 May 2009

Para que ?

Hay veces que me pregunto para que escribir posts sobre como empezar con NHibernate.

Hoy vi una perlita:

Un query statico (siempre el mismo) escrito usando Criteria API y pocas lineas mas abajo un query dinamico (cambiante según determinada condiciones) escrito usando string concatenation para generar un HQL, y como postre, ambos en un DAO static.

Será que tampoco le pego cuando escribo en español ?

24 May 2009

NHibernate IoC integration

Do you remember this post ?

As you can see you can use Dependency Injection even for entities, but what about all others classes needed by NHibernate ?

Can you inject something in a custom Dialect or in a custom UserType or UserCollectionType or Listener and all others extensions points ?

Sure you can ;)

NHibernate 2.1.0Alpha3, the fresh released today, has IObjectsFactory. As the ProxyFactoryFactory, the ReflectionOptimizer even the ObjectsFactory is a responsibility of the ByteCodeProvider.

To be short… an implementation of a IUserType now can look like this:

public class InjectableStringUserType : IUserType
{
private readonly IDelimiter delimiter;

public InjectableStringUserType(IDelimiter delimiter)
{
this.delimiter = delimiter;
}

The implementation of IPostInsertEventListener now can look like this:

public class YourPostInsertListener : IPostInsertEventListener
{
private readonly IPersistentAuditor auditor;

public YourPostInsertListener(IPersistentAuditor auditor)
{
this.auditor = auditor;
}

public void OnPostInsert(PostInsertEvent @event)

If you want use Dependency-Injection for both entities and all others NH stuff, in uNhAddIns you can find two full implementation for Castle and Spring.

Enjoy NHibernate injectability.

P.S. Part of it (tests), was a live implementation before start the Alt.NET VAN today.

23 May 2009

TOP SECRET

When you have an issue in NHibernate please send us something like this

public class xparent
{
    public virtual long? Id { get; set; }
    public virtual long? A { get; set; }
    public virtual Iesi.Collections.Generic.ISet<xchild> Children { get; set; }
}
public class xchild
{
    public virtual long? Id { get; set; }
    public virtual long? B { get; set; }
    public virtual xparent Parent { get; set; }
}

<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" default-cascade="save-update">
    <class name="xxx.xparent, xxx" table="xparent">
        <id name="Id" column="Id" type="Int64">
            <generator class="identity"/>
        </id>
        <property name="A" type="Int64"/>
        <set name="Children" inverse="true">
            <key column="ParentId"/>
            <one-to-many class="xxx.xchild, xxx"/>
        </set>
    </class>
    <class name="xxx.xchild, xxx" table="xchild">
        <id name="Id" column="Id" type="Int64">
            <generator class="identity"/>
        </id>
        <property name="B" type="Int64"/>
        <many-to-one name="Parent" column="ParentId" class="xxx.xparent, xxx"/>
    </class>
</hibernate-mapping>

And please don’t send me the code to recreate the issue because it is TOP SECRET, send me only the stack trace of the exception or even better an obfuscated dll and obviously don’t forget to use the nullable type for the Id.

Thanks.

21 May 2009

NHibernate: Versioning

NHibernate allow to easy manage optimistic-locking through a where-all, where-changed or a version column.

Yesterday I saw a new way to implement versioning using NHibernate.

The behavior I showing is related to this test:

public void SaveUpdate()
{
using (ISession session = sessionFactory.OpenSession())
{
using (ITransaction transaction = session.BeginTransaction())
{
var sample = new Sample { Description = "sample" };
session.Save(sample);
sample.Description = "modified sample";
transaction.Commit();
}
}
}

Peace & Love versioning

As the POID even the version shouldn’t have business-meaning. We are using the version only to have a more efficient way of optimistic-lock; nothing more, nothing less.

So given this class

public class Sample
{
public virtual int Version { get; private set; }
public virtual string Description { get; set; }
}

the mapping is

<class name="Sample">
<
id type="int">
<
generator class="hilo"/>
</
id>
<
version name="Version" access="backfield"/>
<
property name="Description"/>
</
class>

and queries results of the test are

INSERT
INTO
Sample
(Version, Description, id)
VALUES
(@p0, @p1, @p2);
@p0 = 1, @p1 = 'sample', @p2 = 32768

UPDATE
Sample
SET
Version = @p0,
Description = @p1
WHERE
id = @p2
AND Version = @p3;
@p0 = 2, @p1 = 'modified sample', @p2 = 32768, @p3 = 1

You can see the optimistic-lock working in the UPDATE and in both queries the batcher is working.

Resuming: an INSERT, an UPDATE with optimistic-lock, 2 round-trips.

Mortal Combat versioning

How we can complicate the Peace&Love versioning ? In few steps.

  1. Remove the HighLow generator and use your best friend “identity”.
  2. Give a business meaning to the version as, for example, last-update-moment.
  3. To have a more consistent last-update-moment make it generated by MsSQL forgetting the problem MsSQL have with DateTime precision.
  4. Override the insert SQL, generated by NH, using a trigger (please don’t ask me why).

Now the class will look like this

public class SampleDt
{
public virtual DateTime Version { get; private set; }
public virtual string Description { get; set; }
}

and the mapping look like this

    <class name="SampleDt">
<
id type="int">
<
generator class="identity"/>
</
id>
<
version name="Version" access="backfield" type="Timestamp" generated="always"/>
<
property name="Description"/>
</
class>

<
database-object>
<
create>
<![CDATA[
CREATE TRIGGER t_sampleInsert ON SampleDt
INSTEAD OF INSERT
AS
BEGIN
SET NOCOUNT ON

INSERT INTO SampleDt (
[Version],
[Description])
SELECT
GetDate(),
[Description]
FROM inserted

SELECT scope_identity();
END
]]>
</
create>
<
drop>
<![CDATA[
IF EXISTS (SELECT * FROM sys.triggers WHERE object_id = OBJECT_ID(N'[dbo].[t_sampleInsert]'))
DROP TRIGGER [dbo].[t_sampleInsert]
]]>
</
drop>
<
dialect-scope name="NHibernate.Dialect.MsSql2005Dialect"/>
</
database-object>

And now the result is:

INSERT
INTO
SampleDt
(Description)
VALUES
(@p0);
select
SCOPE_IDENTITY();
@p0 = 'sample'

SELECT
sampledt_.Version as Version0_
FROM
SampleDt sampledt_
WHERE
sampledt_.id=@p0;
@p0 = 1

UPDATE
SampleDt
SET
Description = @p0
WHERE
id = @p1
AND Version = @p2;
@p0 = 'modified sample', @p1 = 1, @p2 = 21/05/09 08:23:47

SELECT
sampledt_.Version as Version0_
FROM
SampleDt sampledt_
WHERE
sampledt_.id=@p0;
@p0 = 1

Resuming: an immediate INSERT outside the batcher and overriden by something outside the trace, a SELECT to retrieve the new Version, an UPDATE with optimistic-lock, a SELECT to retrieve the new Version, 4 round-trips, something more to maintain each time you need to change the class or if you want change the RDBMS (somebody should maintain the trigger) and you should pray to not have 2 updates inside 3ms (the precision of DateTime in MsSQL).

Definition

Using my father words : “L’arte di trasformare una cosa facile in una difficile attraverso un procedimento inutile.”

Translation : “The art of turning an easy thing in a difficult one, through a unnecessary procedure.”

20 May 2009

Entity Framework and T4

“Entity Framework 4.0 will use T4 (Text Template Transformation Toolkit), which makes customizing Code Generation easy, flexible and powerful, and the experience is also fully integrated into Visual Studio.”

So flexible that you can generate all you want ?

Fantastic!!!

Hopefully, soon, NHibernate will have even a graphic designer fully integrated with VisualStudio to generate mappings and classes and directly developed by Microsoft.

Awesome how things are changing, no ? ;-)

18 May 2009

NHibernate Modeller

After the first step, few weeks ago, now is ready for download.

NHModeller is based on a custom DSL and represent an alternative way to generate classes and mappings.

More details are available in the NHModeller site.

16 May 2009

Complex configuration

This is the configuration of TolerantQueryCache (using NHibernate configuration exetension)

configuration.QueryCache()
.ResolveRegion("SearchStatistics")
.Using<TolerantQueryCache>()
.TolerantWith("MusicCDs");

A NH’s user said that it is “a complex configuration”.

Probably I’m working in systems of another planet.


13 May 2009

Entity Framework come in the real world

Perhaps, in few months, we will see something more useful than we saw sofar.

Entity Framework 4.0 will allow POCOs and lazy-loading of POCOs.

Microsoft will show us his muscles, soon, even in ORM.

10 May 2009

Alt.NET Argentina meeting

Una reunión interesante con intercambio de idea y experiencias. La organización fluyó como el aceite.

En lo personal participé a:

  • UIX
  • Open source Made in Argentina
  • Tecnicas ORM
  • DDD

La reunión sobre Open-source se desvirtuó un poco, respecto a lo que me esperaba, ya que hablamos mas de algunos aspectos del mismo más que de OS made en AR, pero bueno ese es el espíritu del open-space e igual me sirvió mucho.

Tengo unos apuntes con nombres de tools que me llevará un tiempito investigarlos todos.

De 18 a 21 hubo un Open-space, a cielo abierto con cerveza y cenicero, con la participación de la máquina de Jhonny Halife y me parece que a esa reunión no le cabe otro título que “Tools brain storm”.

Un agradecimiento especial a Carlos Peix y Martin Salias.

08 May 2009

NHibernate 2.1.0: HQL With clause

A simple SQL:

SELECT * FROM Animal AS a1

INNER JOIN Animal AS a2 on a1.mother_id = a2.ID and a1.body_weight < :aLimit

Another:

SELECT * FROM Animal AS a1 INNER JOIN Animal AS a2 on a1.mother_id = a2.ID

WHERE a1.body_weight < :aLimit

Which is the difference in term of results ? There isn’t.

Some DB engine does not make difference, between the two SQLs, even in term of execution plan but… you know, there are some RDBMS not so smart (guess which one ;-) ).

With the new HQL parser, based on ANTLR, we are supporting a new clause named “with”.

The HQL:

from Animal a inner join a.mother as m with m.bodyWeight < :someLimit

The result SQL using this mapping :

select
animal0_.id as id14_0_,
animal1_.id as id14_1_,
animal0_.description as descript2_14_0_,
animal0_.body_weight as body3_14_0_,
animal0_.mother_id as mother4_14_0_,
animal0_.father_id as father5_14_0_,
animal0_.zoo_id as zoo6_14_0_,
animal0_.serialNumber as serialNu7_14_0_,
animal0_1_.bodyTemperature as bodyTemp2_15_0_,
animal0_3_.pregnant as pregnant17_0_,
animal0_3_.birthdate as birthdate17_0_,
animal0_4_.owner as owner18_0_,
animal0_7_.name_first as name2_21_0_,
animal0_7_.name_initial as name3_21_0_,
animal0_7_.name_last as name4_21_0_,
animal0_7_.nickName as nickName21_0_,
animal0_7_.height as height21_0_,
animal0_7_.intValue as intValue21_0_,
animal0_7_.floatValue as floatValue21_0_,
animal0_7_.bigDecimalValue as bigDecim9_21_0_,
animal0_7_.bigIntegerValue as bigInte10_21_0_,
case
when animal0_2_.reptile is not null then 2
when animal0_5_.mammal is not null then 5
when animal0_6_.mammal is not null then 6
when animal0_4_.mammal is not null then 4
when animal0_7_.mammal is not null then 7
when animal0_1_.animal is not null then 1
when animal0_3_.animal is not null then 3
when animal0_.id is not null then 0
end as clazz_0_,
animal1_.description as descript2_14_1_,
animal1_.body_weight as body3_14_1_,
animal1_.mother_id as mother4_14_1_,
animal1_.father_id as father5_14_1_,
animal1_.zoo_id as zoo6_14_1_,
animal1_.serialNumber as serialNu7_14_1_,
animal1_1_.bodyTemperature as bodyTemp2_15_1_,
animal1_3_.pregnant as pregnant17_1_,
animal1_3_.birthdate as birthdate17_1_,
animal1_4_.owner as owner18_1_,
animal1_7_.name_first as name2_21_1_,
animal1_7_.name_initial as name3_21_1_,
animal1_7_.name_last as name4_21_1_,
animal1_7_.nickName as nickName21_1_,
animal1_7_.height as height21_1_,
animal1_7_.intValue as intValue21_1_,
animal1_7_.floatValue as floatValue21_1_,
animal1_7_.bigDecimalValue as bigDecim9_21_1_,
animal1_7_.bigIntegerValue as bigInte10_21_1_,
case
when animal1_2_.reptile is not null then 2
when animal1_5_.mammal is not null then 5
when animal1_6_.mammal is not null then 6
when animal1_4_.mammal is not null then 4
when animal1_7_.mammal is not null then 7
when animal1_1_.animal is not null then 1
when animal1_3_.animal is not null then 3
when animal1_.id is not null then 0
end as clazz_1_
from
Animal animal0_
left outer join
Reptile animal0_1_
on animal0_.id=animal0_1_.animal
left outer join
Lizard animal0_2_
on animal0_.id=animal0_2_.reptile
left outer join
Mammal animal0_3_
on animal0_.id=animal0_3_.animal
left outer join
DomesticAnimal animal0_4_
on animal0_.id=animal0_4_.mammal
left outer join
Cat animal0_5_
on animal0_.id=animal0_5_.mammal
left outer join
Dog animal0_6_
on animal0_.id=animal0_6_.mammal
left outer join
Human animal0_7_
on animal0_.id=animal0_7_.mammal
inner join
Animal animal1_
on animal0_.mother_id=animal1_.id
and (
animal1_.body_weight<@p0
)
left outer join
Reptile animal1_1_
on animal1_.id=animal1_1_.animal
left outer join
Lizard animal1_2_
on animal1_.id=animal1_2_.reptile
left outer join
Mammal animal1_3_
on animal1_.id=animal1_3_.animal
left outer join
DomesticAnimal animal1_4_
on animal1_.id=animal1_4_.mammal
left outer join
Cat animal1_5_
on animal1_.id=animal1_5_.mammal
left outer join
Dog animal1_6_
on animal1_.id=animal1_6_.mammal
left outer join
Human animal1_7_
on animal1_.id=animal1_7_.mammal;
@p0 = 1

First… easy HQL with complex mapping mean complex SQL but… not a big pain for NH’s users ;-)

Note this:

inner join
Animal animal1_
on animal0_.mother_id=animal1_.id
and (
animal1_.body_weight<@p0
)

stock_stack……stumb…sfrfrfrfrfrfr (biglia, sponda e filotto).

07 May 2009

NHibernate 2.1.0 : Executable queries

I’m proud to announce NH2.1.0 is passing all tests (same of H3.3.1) for bulk actions using HQL.

Some HQL examples:

insert into Animal (description, bodyWeight, mother) select description, bodyWeight, mother from Human

insert into Pickup (id, Vin, Owner) select id, Vin, Owner from Car

insert into Animal (description, bodyWeight) select h.description, h.bodyWeight from Human h where h.mother.mother is not null

update Human h set h.description = 'updated' where exists (select f.id from h.friends f where f.name.last = 'Public' )

update versioned IntegerVersioned set name = :name

update Human set name.first = :correction where id = :id

update Animal a set a.mother = (from Animal where id = 1) where a.id = 2

update Animal set description = :newDesc where description = :desc

update Animal set bodyWeight = bodyWeight + :w1 + :w2

delete SimpleEntityWithAssociation e where size(e.AssociatedEntities ) = 0 and e.Name like '%'

delete Animal where mother is not null

delete from EntityWithCrazyCompositeKey e where e.Id.Id = 1 and e.Id.OtherId = 2

To understand what that mean think about that all executable-queries are working with <subclass>, <joined-subclass>, <subclass> + <join>, <union-subclass>, various POID generators, versioned entities ad son on.

For example using the mapping of this post executing this

insert into Animal (description, bodyWeight) select h.description, h.bodyWeight from Human h where h.mother.mother is not null

the SQL is

insert 
    into
        Animal
        ( description, body_weight ) select
            human0_2_.description as col_0_0_,
            human0_2_.body_weight as col_1_0_ 
        from
            Human human0_ 
        inner join
            Mammal human0_1_ 
                on human0_.mammal=human0_1_.animal 
        inner join
            Animal human0_2_ 
                on human0_.mammal=human0_2_.id,
            Animal animal1_ 
        where
            human0_2_.mother_id=animal1_.id 
            and (
                animal1_.mother_id is not null
            )

… stock… stumb… stumb… stumb… stack… sfrfrfrfrfrfr … (the sound of “goriziana”).


kick it on DotNetKicks.com

04 May 2009

Oh… beautiful SQL

Given a complex mapping like this

<class name="Animal">
<
id name="id">
<
generator class="native"/>
</
id>
<
property name="description"/>
<
property name="bodyWeight" column="body_weight"/>
<
many-to-one name="mother" column="mother_id"/>
<
many-to-one name="father" column="father_id"/>
<
many-to-one name="zoo" column="zoo_id"/>
<
property name="serialNumber"/>
<
set name="offspring" order-by="father_id">
<
key column="mother_id"/>
<
one-to-many class="Animal"/>
</
set>
<
joined-subclass name="Reptile">
<
key column="animal"/>
<
property name="bodyTemperature"/>
<
joined-subclass name="Lizard">
<
key column="reptile"/>
</
joined-subclass>
</
joined-subclass>
<
joined-subclass name="Mammal">
<
key column="animal"/>
<
property name="pregnant"/>
<
property name="birthdate" type="date"/>
<
joined-subclass name="DomesticAnimal">
<
key column="mammal"/>
<
many-to-one name="owner"/>
<
joined-subclass name="Cat">
<
key column="mammal"/>
</
joined-subclass>
<
joined-subclass name="Dog">
<
key column="mammal"/>
</
joined-subclass>
</
joined-subclass>
<
joined-subclass name="Human">
<
key column="mammal"/>
<
component name="name">
<
property name="first" column="name_first"/>
<
property name="initial" column="name_initial"/>
<
property name="last" column="name_last"/>
</
component>
<
property name="nickName"/>
<
property name="height"/>

<
property name="intValue"/>
<
property name="floatValue"/>
<
property name="bigDecimalValue"/>
<
property name="bigIntegerValue"/>

<
bag name="friends">
<
key column="human1"/>
<
many-to-many column="human2" class="Human"/>
</
bag>
<
map name="family">
<
key column="human1"/>
<
map-key column="relationship" type="string"/>
<
many-to-many column="human2" class="Human"/>
</
map>
<
bag name="pets" inverse="true">
<
key column="owner"/>
<
one-to-many class="DomesticAnimal"/>
</
bag>
<
set name="nickNames" lazy="false" table="human_nick_names" sort="natural">
<
key column="human"/>
<
element column="nick_name" type="string" not-null="true"/>
</
set>
<
map name="addresses" table="addresses">
<
key column="human"/>
<
map-key type="string" column="type"/>
<
composite-element class="Address">
<
property name="street"/>
<
property name="city"/>
<
property name="postalCode"/>
<
property name="country"/>
<
many-to-one name="stateProvince" column="state_prov_id" class="StateProvince"/>
</
composite-element>
</
map>
</
joined-subclass>
</
joined-subclass>
</
class>

Which should be the result of s.CreateQuery("delete Animal").ExecuteUpdate() ?

Oh… beautiful SQL

create table #Animal (id BIGINT not null) 

insert into #Animal SELECT animal0_.id as id FROM Animal animal0_

DELETE FROM Human WHERE (mammal) IN (select id from #Animal)

DELETE FROM Dog WHERE (mammal) IN (select id from #Animal)

DELETE FROM Cat WHERE (mammal) IN (select id from #Animal)

DELETE FROM DomesticAnimal WHERE (mammal) IN (select id from #Animal)

DELETE FROM Mammal WHERE (animal) IN (select id from #Animal)

DELETE FROM Lizard WHERE (reptile) IN (select id from #Animal)

DELETE FROM Reptile WHERE (animal) IN (select id from #Animal)

DELETE FROM Animal WHERE (id) IN (select id from #Animal)

drop table #Animal

Do you see the temp table ? Do you see the order of queries ?

“se me cayó una lagrima”



kick it on DotNetKicks.com

02 May 2009

NH2.1: Executable HQL

Mapping:

<class name="SimpleClass" table="TSIMPLE">
<
id type="int">
<
generator class="native" />
</
id>
<
property name="Description"/>
</
class>

Class:

public class SimpleClass
{
public virtual string Description { get; set; }
}

DB fill:

using (var s = OpenSession())
using (var tx = s.BeginTransaction())
{
s.Save(new SimpleClass {Description = "simple1"});
s.Save(new SimpleClass {Description = "simple2"});
tx.Commit();
}

So far doing this:

using (var s = OpenSession())
using (var tx = s.BeginTransaction())
{
s.Delete("from SimpleClass");
tx.Commit();
}

the log is:

OldDeleteLog

But from today (the day of worker), doing this:

using (var s = OpenSession())
using (var tx = s.BeginTransaction())
{
s.CreateQuery("delete from SimpleClass").ExecuteUpdate();
tx.Commit();
}

the log is :NewDeleteLog


what it mean is clear, no ?  ;)

Soon some news about bulk insert and update, in HQL.

Now you know one of the reasons because NH2.1.0 release, was postponed.


kick it on DotNetKicks.com

01 May 2009

Empezando con NH: mappings

Posts anteriores: Empezando con NHibernate
Configuración
Session

El mapping es el “leguaje de programación” de NHibernate. Cada línea de mapping se compila en el momento de construir el session-factory (configuration.BuildSessionFactory).

Hasta antes que saliera NH1.2.0 la regla que divulgaba, para el mapping, era “decile a NH lo que ya sabes; no lo hagas trabajar demás”. Esta regla funcionaba muy bien en tanto uno supiera que es lo que le está diciendo a NH. Mientras trabajábamos en NH2.0.0 me di cuenta que el copy&paste es una actividad muy usada, escribiendo mappings, y el resultado, en muchos casos, era un desastre (especialmente si no se miran las SQLs generadas). Estaba claro que había que trabajar para cambiar la regla.

La regla maestra para escribir mappings (a parte de conocer bien las técnicas de ORM) es:

“No aclares que oscurece”

Este es un mapping que uso en mis cursos:

<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
assembly="Starting.NH"
namespace="Starting.NH.OptimisticLock.Revision">

<
class name="Task">
<
id type="int">
<
generator class="hilo"/>
</
id>
<
version name="Version"/>
<
property name="Description"/>
</
class>

</
hibernate-mapping>

Notar que el “type” del POID está allí solo porque la implementación de la entity no tiene una propiedad para el ID.

Cuando uno empieza con NHibernate lo primero que busca es “algo” que escriba los mappings; me pasó también a mi (recuerdo haberle dedicado un par de horas, pero todo empezaba por las tablas). Como le he contado en un post anterior trabajar con NH y no conocer ORM no es una opción y lo peor que pueden hacer es generar los mappings usando como origen las tablas. Si buscan generadores de mappings busquen algo que trabaje con un DSL que describa su entorno de negocio.

Ahora viene lo difícil… El mapping de NH es muy flexible y la cantidad de combinaciones posibles es muy, pero muy, grande; intentaré definir una serie de recomendaciones más bien genérica.

Las recomendaciones

  • Estudien las técnicas de ORM; los mappings son ORM aplicado. No todo es <subclass> así como tampoco todo es <joined-subclass>.

  • No aclaren que oscurece; escriban lo menos posible. Con que la sintaxis sea correcta, por lo general, alcanza.

  • Copien el file nhibernate-mapping.xsd en las “external lib” del proyecto y no en las carpetas de VisualStudio.

  • Mientras escriban el mapping usen siempre el schema; el intellisense es una masa y hay que aprovecharlo.

  • Trabajen por diferencia; si saben que un valor es el default no lo escriban (por ejemplo el default para el nombre de la columna es el nombre de la propiedad).

  • No usen unsaved-value si van a escribir el default del type del Id o de la versión y, en lo posible, no inventen cosas raras solo para escribir un unsaved-value (si un id jamás puede ser cero ¿que razones hay de usar otro valor? )

  • Definan assembly y namespace en el header del mapping (ver ejemplo arriba).

  • Si abren un mapping y le cuesta leerlo (le duelen los ojos) es porque no cumplieron con la regla maestra.

  • No usen default-cascade=”save-update”; el cascade hay que declararlo bien y donde corresponde.

  • Si van usar implementaciones de IUserType, IUserCollectionType o IUserVersionType usen el tag <typedef>.

  • Escriban todos queries estáticos en el mapping y no en el código; los queries en el mapping sobre todo son compilado, son intercambiables entre HQL y SQL, las características de comportamiento se definen en el mapping (por ejemplo por lo que concierne la cache).

  • Recuerden que el cascade no es un solo valor (es comma-separated-values) y no es todo all-delete-orphan.

  • Usen default_schema, default_catalog de la configuración del session-factory y los attributes schema, catalog, del header del mapping solo si trabajan multi-rdbms.

  • Usen los tags DDL oriented (como length, precision, scale, not-null etc.) cuando corresponde. Escribir “string(100)” o “Double(18,5)” tambien es bueno.

  • No usen el nodo <column> si no es necesario; casi siempre los attributes del nodo <property> alcanzan.

  • Para las collections no existe solo <bag>; estudien los otros tipos de collections.

  • No le tengan miedo al <set>; cuando Microsoft nos escuche y ponga una interface parecida a ISet usaremos la del framework. Notar que usar Iesi.Collections no significa poner una dependencia a NH en sus entidades, para nada, a parte que en la entity pueden usar ICollection<T> mapeado a un <set>.

  • Si piensan necesitar los tags where, order-by, formula asegúrense de no estar delegando cuestiones de negocio a NHibernate y tengan en mente que de servidor de DB hay uno, y es caro, y de servidores de aplicación hay varios y son más barato.

Fluent-NHibernate

Fluent-NH es una opción para escribir mappings pero todas las recomendaciones anteriores siguen valiendo. Tengan presente que Fluent-NH genera XML y que el XML generado hace doler los ojos. Por otro lado Fluent-NH no suporta todas las features que suporta el mapping XML así como el mapping XML no suporta todas la features que se pueden implementar usando C#.

No soy adivino y no sé qué pasará cuando empezaremos NH3.0.0. Lo que si se, es que NH tendrá su mapping basado en fluent interface (Loquacious como lo llamamos en NHV) y que no generará XML (creará directamente los meta-data).