Hobbyprojekt, förr, nu och i framtiden.

Posted by Kenny Eliasson | Posted in , , , , | Posted on 09:00

Efter att ha startat och lagt ner ett flertal hobbyprojekt så börjar nu lusten att starta upp nåt nytt att leka med på fritiden komma tillbaka.

Efter att ha startat upp en budget-site, en e-shop lösning till min pappa och massa annat småpill utan att komma i mål med nåt av projekten så känns det nästan lite dödsdömt från början. Jag undrar varför det blir så? Varför kan jag inte slutföra mina hobbyprojekt?

Dels är det såklart tidsaspekten, jag orkar helt enkelt inte sätta mig vid datorn och fortsätta programmera när jag kommer hem. Som tur är har jag ett jobb där jag har möjlighet att utforska nya grejer på arbetstid. Samtidigt missar jag ju tekniker som jag inte jobbar med på jobbet, och om vi då bara snackar om .NET stacken så missar jag t.ex. Silverlight och WCF . Python och Ruby skulle vara oerhört intressant att lära sig mer om även om jag får sitta med Python ibland så vill jag gärna sitta mer med det för att komma in i det "dynamiska tänket"

Men de hobbyprojekt som jag ändå drar igång har en tydlig tendens att dö ut när jag kommer till design-biten. Jag är inte världens bästa designer och tröttnar lätt på nåt jag knåpat ihop själv, vilket får mig att tappa intresset för hela projektet.

Med denna kunskap i bagaget kanske man skulle ge sig på att bygga nåt utan ett alltför grafiskt gränssnitt.

Idag hade jag tänkt mig att bygga en "Nytt projekt mappstruktur" så att man slipper skapa mappar för lib, src, doc och att den automatiskt skapar en solution fil. Och allt detta ska skötas ifrån consolen :)

En annan tanke är att skapa ett väldigt lätt ramverk ovanpå .NET MVC. Det jag vill uppfylla är nåt liknande Django där modellerna och admingränssnitt autogenereras, samt att mycket av de kommandon man kör i Python-consolen har sina motsvarigheter i mitt proejkt (t.ex. startproject, syncdb etc). Alltså nåt som man lätt kommer igång med.

VMWare player och mig

Posted by Kenny Eliasson | Posted in , , , , , , | Posted on 09:30

Satt och kikade på Tekpubs senaste avsnitt om NHibernate när jag kikade på vidare på vad de erbjöd för mer videos. Hittade då en om Linux och Ruby on Rails och i den visade dom hur man installerar Ubuntu på en virtuell maskin.

Och eftersom jag aldrig använt virtuella maskiner innan så tänkte jag att nu var det dags. Så sagt och gjort gick hem installerade VMWare player och laddade hem nyaste Ubuntu.

Installationen gick hur smidigt som helst och tog inte mer än 10 minuter innan jag var inne i operativet och började utforska Linux igen. Sist var nog för 5 år sedan och då körde jag Slackware. Och jisses sicken skillnad på operativ då och nu, kan absolut se mig gå över till en dist av Linux i framtiden. Synd bara att Visual Studio och C# inte finns fullt implementerat.

Saker man direkt faller "in love" med är apt-get för att hämta hem t.ex. Ruby och Django med alla dependencies. Så allt installeras utan att du behöver tänka på nåt. Sen är det skönt att ha en vettig terminal, det är trots allt lite "h4xX0r" känsla på det :)

Man får kanske installera VMWare och köra Windows virtuellt så att man kan fortsätta programmera på .NET plattformen? :)

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?)

NHibernate, skillnader mellan Load, Get och Criteria

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

Stötte på ett konstigt problem häromdagen när jag satt och refaktorerade data-access kod via NHibernate till ett av de första projekten där vi använde det. Problemet som skulle lösas var att ConnectionStrings var hårdkodade i vårt hemgjorda assembly.

För att lösa problemet använde jag kod från ett nyare projekt.

I projekten har vi ett Bas-repository som vi använder oss utav och när jag satt och kikade på skillnaderna såg att GetById() metoden skiljde sig åt i de båda klasserna.

I den gamla klassen anropades Session.CreateCriteria() istället för det Load() eller Get() som man kanske hade kunnat förutspå.

Skillnaderna mellan Criteria, Get och Load som jag fattat det är att


  • Load()
    Hämtar aldrig ifrån databasen utan returnerar bara en proxy. Det vill säga, använder du Load() med ett ID som inte finns i databasen kommer du inte få ett fel förrens du försöker komma åt objektets egenskaper. Denna lösningen är den att rekommendera när du vill koppla på referenser till t.ex. nya skapade objekt men inte vill ladda upp alla dessa referenser bara för att kunna spara det nya objektet.

  • Get
    Samma som Load() men kommer att gå till databasen/cachen för att veta att ID:et verkligen finns.

  • CreateCriteria
    CreateCriteria är lite svårare, kommer man ifrån en värld där man är van att skriva Sql själv så tänker man ju förstås att det måste vara den korrekta vägen att gå. Men CreateCriteria hoppar över cachen (om inte specifierat annorlunda) och objektet man får tillbaka cachas inte.

  • Källa
    Läs mer här Ayende om Load, Get och Query By Id


Jag tänkte inte särskilt mycket mer på att vi använde CreateCriteria utan bytte helt sonika ut den mot en Load. Efter mycket pill så hade jag lyckats byta ut den gamla implementation mot den nya.

Tryckte på F5 och körde igång projektet. Och till min stora förvåning funkade allt perfekt, ett tag iallafall. Helt plötsligt fick jag ett exception ifrån NHibernate.

(No persister exists for QS.Product.Core.Interfaces.ICustomer)

Nu tänker man såklart, detta kanske har med att jag glömt embedda en mapping fil, men si, så lätt var det inte. Jag gick till mappningen och kollade, såg att vi mappade till klassen som interfacet implementerade och använde interfacet som proxy-klass.

Koden i som kastade felet såg ut så här:

_session.Load<ICustomer>(531);

Med tanke på felmeddelandet så måste ju detta betyda att ICustomer inte var mappad, vilket den ju inte heller var, det var ju implementation av Interfacet som var mappat. Men hur kunde det funkat innan? Jag bytte ju bara ut bas-repositoriet, inte alla repositoryn som användes, inte heller ändrade jag configuration eller mappningen.

Problemet låg i att CreateCriteria() kan hantera detta, varför, det vet jag inte :)

Men lösningen för mig var att typa om alla mina repositories till att använda klassen istället för interfacet.

Det vore intressant att veta varför CreateCriteria funkar men inte Load/Get :)

Detta blev ett långt första inlägg, får hålla mig lite kortare i framtiden.