Jeg hader DTO’er!

by Jesper august 24, 2009 10:55

DTO som i Data Transport Objects. Objekter der bare har en række properties, så man kan overfører data mellem DAL og BLL og mellem BLL og UI. Selvfølgelig er overførslen af data mellem de forskellige lag ikke nødvendigvis de samme objekter, og man kan sagtens have et sæt DTO’er til DAL og et andet sæt til UI. Det er bare en pinsel at vedligeholde så meget kode, som man ikke har nogen som helst brug for.

Da jeg startede med C# 1.0, kom jeg fra JavaScript ASP, og jeg må indrømme at jeg blev meget facineret af den typestærke omgang med objekter, og deraf den dejlige intelli-sense i Visual Studio. Et af de store hype emner dengang var også Typed DataSet, hvor omfanget af uberørte kodelinier var helt enormt – hvor mange har brugt dem til andet end dejlig VS.NET oplevelser.

Nu har vi heldigvis fået en masse funktionelle værktøjer, og vi har fået generics. Nu kan vi faktisk helt undvære typestærke DTO’er. Men gør vi så det? Nej – vi forsætter med kb efter kb af ligegyldig kode. Jo man kan godt nok bruge NHibernate eller andre ORM frameworks til at danne klasserne i DAL-laget, men vi er stadig bundet i transporten mellem BLL og UI, og reelt set er der jo stadig masser af ligegyldig mapningskode.

Anonyme typer kan i nogen omfang bruges som DTO’er, men det kræver at man laver lidt reflektions gymnastik på modtagersiden, for man kender jo af gode grunde ikke typen på forhånd. Det er det man gør i MVC framevorket, når man kalder den generiske dictionary på Viewdata objektet. Ikke helt godt synes jeg. Jeg har derfor ofte brugt frameworkets indbyggede Tuple: KeyValuePair. Den er kendt af både afsender og modtager, og er super fin som DTO for f.eks. listedata. Men hvad nu når man har 3 eller 4 værdier? Så kan man jo bare indlejre flere objekter:

var tripleDTO = new KeyValuePair<string ,KeyValuePair<int ,int>>();

Ikke ligefrem intuitivt, og super indviklet at initialicerer. Jeg har derfor lavet et par simpel generiske Tuple klasse, som jeg kan bruge i stedet:

/// 
/// Single
/// 
public class Tuple
{
    public Tuple(T1 first)
    {
        this.Item1 = first;
    }

    public T1 Item1 { get; set; }
}

/// 
/// Double
/// 
public class Tuple : Tuple
{
    public Tuple(T1 first, T2 second)
        : base(first)
    {
        this.Item2 = second;
    }

    public T2 Item2 { get; set; }
}

/// 
/// Triple
/// 
public class Tuple : Tuple
{
    public Tuple(T1 first, T2 second, T3 third)
        : base(first, second)
    {
        this.Item3 = third;
    }

    public T3 Item3 { get; set; }
}

/// 
/// Quadruple
/// 
public class Tuple : Tuple
{
    public Tuple(T1 first, T2 second, T3 third, T4 fourth)
        : base(first, second, third)
    {
        this.Item4 = fourth;
    }

    public T4 Item4 { get; set; }
}

/// 
/// Quintuple
/// 
public class Tuple : Tuple
{
    public Tuple(T1 first, T2 second, T3 third, T4 fourth, T5 fifth)
        : base(first, second, third, fourth)
    {
        this.Item5 = fifth;
    }

    public T5 Item5 { get; set; }
}

Nu kan jeg transporterer typestærke værdisæt rundt mellem lag, uden at skulle lave hjernedøde DTO’er.

For at gøre initialiceringen lettere, har jeg lavet en statisk Tuple klasse, med facktory metoder: Create:

public static class Tuple
{
    public static Tuple Create(T1 first)
    {
        return new Tuple(first);
    }
    public static Tuple Create(T1 first, T2 second)
    {
        return new Tuple(first, second);
    }
    public static Tuple Create(T1 first, T2 second, T3 third)
    {
        return new Tuple(first, second, third);
    }

    public static Tuple Create(T1 first, T2 second, T3 third, T4 fourth)
    {
        return new Tuple(first, second, third, fourth);
    }
    public static Tuple Create(T1 first, T2 second, T3 third, T4 fourth, T5 fifth)
    {
        return new Tuple(first, second, third, fourth, fifth);
    }
}

Det giver følgende initialicering:

var quadDto = Tuple.Create(12, “Jesper”, “Jensen”, true);

Nu har jeg reduceret antallet af custom DTO’er til 0, og det er jo dejligt, når man som jeg: Hader DTO’er.

Bemærk i øvrigt at en Tuple klasse lignenede denne, kommer i c# 4.0, hvor den kan initialiceres med op til 8 værdier.

Code on…

Tags:

C# | Tip

Kommentarer

24-08-2009 14:14:55 #

Mark S. Rasmussen

Tupler er OK til intern storage i en funktion, yderst sjældent vil det kunne forsvares at bruge dem som DTO på tværs af funktioner og aldrig på tværs af lag. Du lægger basalt set alt ansvaret ned til klienten, mht. at kende hvilke tupel felter der indeholder hvilke værdier. Du kan dokumentere dig ud af det, men det bliver hurtigt ekstremt tungt at arbejde med, hvis der skal kigges i dokumentationen hver gang man modtager en tupel.

Hvor tupler er rare fordi de giver statisk typesikkerhed så giver de ingen semantisk sikkerhed. Hér vil en simpel key/value collection give langt højere værdi da du trods alt kan tilgå et givent felt via dets navn, frem for dets index i tuplen.

Mark S. Rasmussen Denmark

24-08-2009 15:00:19 #

Søren Spelling Lund

Et lille værktøj, som kan hjælpe med at reducere smerten ved brug af DTO'er, er AutoMapper. Den mapper simpelthen mellem to objekter baseret på et meget simpel sæt regler. Det fjerner en hel del af de irritationsmomenter, som eksisterer ved at arbejde med DTO'er.

Søren Spelling Lund Denmark

24-08-2009 22:02:08 #

Jesper

@Mark> Jeg kan godt se din pointe, og Fowler har samme forbehold som dig i "Patterns of Enterprise Application Architecture". Han foretrækker også en dictionary med nøgler der har meningsfyldte navne som DTO'er. Jeg kan godt følge jer, men i langt de fleste tilfælde, er jeg herre over både Dal og model, og så kan jeg langt bedre lide at bruge en typestærk array af objekter - de er hurtige at lave og udvidde. For fremmede øjne er de sikkert ikke sjove at gennemskueSmile

Jesper Denmark

25-08-2009 09:27:19 #

Mark S. Rasmussen

@Jesper
"I langt de fleste tilfælde, er jeg herre over både Dal og model"

Ovenstående er et meget uheldigt argument at basere din holdning på, imho. Du er herre over alle lagene lige p.t., men hvad sker der den dag en anden bliver hevet ind på projektet og skal arbejde sammen med dig? Eller du forlader virksomheden?

Herudover - så snart projektet når en hvis størrelse så vil du selv ikke længere kunne huske hvad der er hvad, uanset om du har skrevet alle lagene. Det kan godt tænkes projektet er i en overskuelig størrelse nu, men dagen _vil_ komme hvor du pludselig begynder at miste overblik og så er du på herrens mark.

Mark S. Rasmussen Denmark

25-08-2009 11:23:16 #

Jesper

@Mark> Du har en pointe, men jeg mener nu ikke man kan sætte det så sort/hvidt op. I min verden bliver langt de fleste projekter ikke til frameworks, og jeg søger at give overskuelighed med god navngivnig. Hvis jeg kommer den mindste smule i tvivl om hvad en metode skal gøre med et objekt, den modtager (fra en anden komponent). Ja så har jeg en kandidat til et 'rigtigt' DTO, og så vil jeg personligt ikke holde mig tilbae fra at implementerer det (selvom jeg hader DTO'er).

Jesper Denmark

25-08-2009 13:02:37 #

Mark S. Rasmussen

@Jesper
"Fleste projekter bliver ikke til frameworks"

Det er så absolut sandt, men irrelevant. Der er behov for tiering, layering og DTO'er i rigtig mange projekter som ikke fungerer som frameworks, men blot er forskellige lag/tiers af en stor applikation. Personligt arbejder jeg på et relativt lille projekt ~100kloc men her har vi da også lagdeling og kommunukation via DTO'er.

Herudover kan vi også vende dit argument om - såfremt du er herre over både DAL og og BL, hvorfor så overhovedet benytte DTO'er (hvadend det er i form af tupler eller normale DTO objekter)?

Mark S. Rasmussen Denmark

26-08-2009 08:18:34 #

Jesper

@Mark> Tag nu f.eks. et OrdreLinie objekt - det afstedkommer potentielt masser af DTO'er: OrdreLinieOversigtDto, OrdreLinieStatistikDto, OrdreLinieSumDto, OrdreLinieKvartalsoversigtDto, OrdreLinieSælgerOversigtDto, OrdreLinieYouNameItDto....
Det giver et helt utroligt hieraki af klasser der kun er 'dumme' databærere, og som man skal slæbe med ind og ud af lag. Det er dybest set det jeg er træt af, og det er det jeg kan undgå med et generisk objekt. Hvis man så samtidig er omhyggelig med navngivning og dokumentation, så tror jeg ikke det giver de forvirringsproblemer du forudser.
Update(PersonStatistikDto p) --> UpdatePersonStatistik(Tuple<int,double...> p)

Jesper Denmark

Kommentarerne er lukkede

Powered by BlogEngine.NET 1.5.0.7
Theme by Mads Kristensen | Modified by Mooglegiant

About

Mit navn er Jesper Jensen, og jeg arbejder til dagligt som web-udvikler hos DGI, hvor mit speciale er klientside applikationer. Før det var jeg nogle år i robotbranchen, hvor jeg arbejdede med 3D simulering og system koordinering. Jeg elsker webudvikling, og specielt JavaScript har min interesse. Jeg har blogget om mine oplevelser med udvikling siden 2004

Calendar

<<  marts 2010  >>
mationtofr
22232425262728
1234567
891011121314
15161718192021
22232425262728
2930311234

View posts in large calendar

RecentComments

Comment RSS