Migrering till Fluent NHibernate

Posted by Kenny Eliasson | Posted in , , , , | Posted on 08:45

Läste Jimmy Bogards blog-post angående migrering till Fluent NHibernate och tänkte beskriva hur vi gick till väga när vi tog steget från hbm-mappningsfiler till Fluent NHibernate.

Till att börja med, varför gjorde vi switchen ifrån fungerande mappnings-filer till Fluent Nhibernate? Om vi inte skulle använda auto-mappning så är det enda vi gör att flytta mappningen ifrån xml-filer till cs-filer. Visst vi tjänar type-safety men med ReSharper så är det oftast inget problem med att mappa via Xml.

Så det vi ville åt var auto-mappningen! Och då projektet vi jobbar på är hyffsat Greenfield (vart igång i ett år nu, och redan hunnit med att byggas om ifrån ett egenutvecklad MVP-ramverk uppepå WebForms till att nu vara byggt i MVC (en helt annan blog-post ;) ) så är även databasen Greenfield.

En snabb check på databasens tabeller och kolumner visade att vi använder rätt enkla conventions som man kan implementera. T.ex. är alla tabeller i plural, dvs med ett "s". Våra Foreign-keys slutar på dessutom på ID.

Att implementera dessa conventions i Fluent NHibernate var lätt.

public class ReportalMappingConventions : IReferenceConvention, IHasManyConvention, IClassConvention, IJoinedSubclassConvention, IIdConvention
{
public void Apply(IManyToOneInstance instance)
{
instance.Column(instance.Property.Name + "ID");
}

public void Apply(IOneToManyCollectionInstance instance)
{
instance.Key.Column(instance.EntityType.Name + "ID");
instance.LazyLoad();
instance.Inverse();
instance.Cascade.SaveUpdate();

if ((typeof(INotCacheable).IsAssignableFrom(instance.Relationship.Class.GetUnderlyingSystemType())))
return;

instance.Cache.ReadWrite();
}

public void Apply(IClassInstance instance)
{
instance.Table(instance.EntityType.Name + "s");
if ((typeof(INotCacheable).IsAssignableFrom(instance.EntityType)))
return;
instance.Cache.ReadWrite();
}

public void Apply(IJoinedSubclassInstance instance)
{
//Fixa detta mer generiskt?
if(instance.EntityType == typeof(ClusterAnswerOption)) {
instance.Table("ClusterOptionFilter"); //TODO: Denna tabellen borde inte heta så här FÖLJ CONVENTIONS!
instance.Key.Column(instance.EntityType.BaseType.Name + "ID");
} else {
instance.Key.Column(instance.EntityType.BaseType.Name + "ID");
instance.Table(instance.EntityType.Name + "s");
//instance.Key.Column(instance.EntityType.Name + "ID");
}
}

public void Apply(IIdentityInstance instance)
{
instance.UnsavedValue("0");
}
}
Som ni ser är det vi gör att lägga till ID samt "pluralisera" tabellnamnen. Vi använder oss också utav ett Marker-interface på våra entiter kallat INotCacheable för att bestämma om entiter skall använda sig utav NHibernates 2nd level-cache eller inte.

Såklart finns det tillfällen då automappningen inte räcker till eller då man tyvärr byggt tabeller och/eller kolumner med felnamn och måste mappa detta manuellt. På de allra flesta av våra entiter räcker det att implementera IAutoMappingOverride som nedan

public class ProjectMap : IAutoMappingOverride<Project>
{
public void Override(AutoMapping<Project> mapping)
{
mapping.HasMany(x => x.ResultSources).Access.CamelCaseField(Prefix.Underscore).Cascade.AllDeleteOrphan();
}
}

Nu skriver vi bara över ResultSources propertyn och låter Fluent NHibernate mappa resten.

Detta funkar kanon och jag har inte ångrat mig en dag att vi migrerade.

Den absolut största fördelen med migrering är såklart att alla nya entiter man skapar är mappade från början så länge de ärver från våran "bas-entitet". Och det som är underbart med är att man slipper skapa databasen själv, utan tar schemat som NHibernate spottar ut och kör på databasen, och voíla, nya tabeller genererade! Ett annat plus är ju att tabellerna skapas utifrån projektets conventions och man då inte i första taget går runt dem.

(Ser även att jag behöver nåt för att göra kod snyggare, nån som har nåt tips?)

Comments (0)

Skicka en kommentar