Avatar billede jonas_h Nybegynder
26. januar 2009 - 18:04 Der er 9 kommentarer og
1 løsning

Best practice med LinQ's datacontext

Når man udvikler web applikationer som bruger linq2sql, hvad er så best practice med brugen af datacontext-klassen? Jeg har siddet og tænkt over nogle forskellige løsninger, men synes det er svært at få det hele til at passe sammen.

Man kan bruge singleton-pattern og have en global datacontext, men dette er ikke hensigtsmæssigt i forhold til at det kan være svært at overskue "SubmitChanges" f.eks.

Jeg har lavet em løsning hvor hver custom entity i mit framework har sin egen datacontext. Dette er dog heller ikke så hensigtmæssigt, da jeg løb ind i nogle forhændringer når man f.eks skal hente mange entities ud. Der skal så laves datacontext til de mange objekter og det bliver også noget rod i min løsning.

Men kunne være fedt at høre hvad der bliver gjort i praksis i de større løsninger.
Avatar billede torbenkoch Nybegynder
26. januar 2009 - 21:07 #1
Som udgangspunkt skal en DataContext altid leve så kort tid som muligt, da det så begrænser hvor længe connections til databasen står åben, og dermed øger muligheden for at bruge connection pooling.

Den nemmeste måde at gøre dette på i praksis er at bruge noget globalt/singleton-halløjsa, som kender din connection string og dermed kan give dig en DataContext on-demand.

Lad os antage, at du har sådan et Data Access Layer (DAL), med sådan statisk metode til at give dig en DataContext. Så vil den korrekte måde at bruge DataContext være:

using (var context = DAL.GetDataContext())
{
    // arbejd med dine data
}

Dette sikrer at Dispose() bliver kaldt på context, når du er færdig.

Men, og dem er der mange af. Hvis du fra en metode returnerer en IQueryable<> må du IKKE Dispose din kontekst, da den skal bruges, på det tidspunkt, hvor IQuerable<> rent faktisk bliver eksekveret.

Et andet problem er når du skal update dine data. Eftersom data jo bliver postet fra webbrowseren til din applikation er din DataContext fra da du hentede data længe væk. Antag, at du har en Save() metode på din Entity:

public void Save()
{
    // First we get the current values from the database
    // RetrieveEntityById is a static method on MyEntity

    MyEntity oldEntity = RetrieveEntityById(id); //

    using (var context = DAL.Getcontext())
    {
        // This (Attach) alerts the data context about the changed we have made
    // MyEntities is representing the MyEntity table (in the .dbml)
        context.MyEntities.Attach(this, oldEntity);

        // and makes it possible for the data context to save our changes
        context.SubmitChanges();
    }
}

Når man skal hente mange entities ud, f.eks. en liste af MyEntity, laver jeg typisk en MyEntityList klasse, som har statiske metoder til at søge det ud jeg skal bruge. Disse methoder returnerer så oftest en IQueryable<MyEntity>. Det er i disse metoder, du IKKE skal Dispose() din DataContext.

Det næste problem er så, når du skal update mange entities og gerne vil gøre det i en transaktion. Det er rigtigt besværligt, og jeg har ikke lige en entydig opskrift på det, men det involverer som minimum, at man laver sin egen DataContext, hvor man overloader den metode, der returnerer en DatabaseConnection. Gør man ikke det, vil .NET åbne flere connections til databasen, hvilket starter Distributed Transaction Coordinator (DTC), som altså gør ondt i performance.

Jeg har set eksempler på nettet, der løser dette. Ingen af dem er særligt elegante.

Håber det hjælper lidt?
Avatar billede jonas_h Nybegynder
26. januar 2009 - 21:25 #2
Tak for svaret!

Det er desværre lidt af de samme konklusioner jeg er kommet frem til. Har også læst lidt rundt på nettet, og til webapplikationer ser det ud til, at det er "pænest" at have en datacontext som lever under selve requestet. Dvs man associerer den til httpcontext. Det vil stort set virke på samme måde som singleton design pattern du også snakker om.

Men synes geenrelt det er svært at finde best practice eksempler på brug af linq. Også hvordan man bedst deler data access laget og business-laget op.
Avatar billede torbenkoch Nybegynder
26. januar 2009 - 21:57 #3
Ja, linq er da så absolut hovedsageligt et skridt fremad, hvis man ser bort fra DataContext, som de vist ikke lige blev helt færdige med ;-)

Linq, med den i VS2008 indbyggede entity-dims (.dbml-dimsen), lægger jo voldsomt op til brug af Active Record pattern, hvilket jeg synes fungerer rigtigt godt langt hen ad vejen.

Oven på dette kan du så lægge dit business-logic-lag, som f.eks. kan bestå af en række controllere (C'et i MVC).

Hvis du bruger Use Cases (måske også Stories i XP?) og får lavet nogle fornuftige af disse kan du stort set mappe dem direkte til controllere. Du skal selvfølgelig være opmærksom på ligheder mellem dine controllere og refaktorere til generel kode, når muligt.

At lave controllere på denne måde giver nemt en stor mængde klasser (det kan nemt give en controller per knap i GUI f.eks.), men logikken bliver pakket rigtigt pænt ind.
Avatar billede jonas_h Nybegynder
26. januar 2009 - 22:30 #4
Men hvordan plejer du at bygge et business-lag ovenpå? Jeg har tænkt mig at lave mine egne entities som simpelthen blot mapper alle linq entity properties over i og derudover selvfølgelig får tilføjet noget logik.
Avatar billede torbenkoch Nybegynder
26. januar 2009 - 23:07 #5
Controllerne er i praksis mit businesslag. De indeholder logikken, som arbejder med entities.

Når du siger businesslag samtidigt med at du vil duplikere dine Entities, så glemmer du det faktum, at business logic (som oftest) arbejder på tværs af entities. Dine entities er dine data - naturligvis kan du have validering af dine data - f.eks. at et beløb skal være i et eller andet interval.

Når VS2008 genererer en Entity - f.eks. Employee - bliver den lavet som partial class. Dvs. hvis du vil lave yderligere validering, yderligere metoder, så laver du en ny fil med en partial Employee class i.

Employee har en partiel metode:

partial void OnValidate(System.Data.Linq.ChangeAction action)

som bliver kaldt på et tidspunkt (ka ikke lige huske hvornår). I denne kan du checke om alle dine properties er ok og hvis ikke kaste en exception.

Du kan også arbejde på de enkelte properties. Lad os sige, Employee har et PostNummer felt. Så vil den autogenererede Employee kalde den partielle metode OnPostNummerChanged().

Hvis du implementerer den, kan du så checke om Postnummer er korrekt, måske opdatere bynavn automatisk, og kaste en exception, hvis noget ikke er som det skal være.

Det er en svær disciplin at lave BOLs og DALs og der er ingen entydig måde, der virker for alle typer projekter. Men controller/entity-metoden giver en fornuftig adskillelse og data og logic og giver et fornuftigt overblik over logikken.

Lyste det lidt op? ;-)
Avatar billede jonas_h Nybegynder
27. januar 2009 - 10:07 #6
Tror bare lige jeg skal vænne mig til linQ :) Jeg synesd det virker som om, at man blander selve datalaget med alt andet når man bruger linQ entities.

For mig er linq entities stadig nede på data access niveau. Men kan godt være jeg bare har misforstået det.
Avatar billede torbenkoch Nybegynder
27. januar 2009 - 10:22 #7
Du har helt ret, linq entities er stadig på data access niveau. De er modelleret over Active Record pattern - google det engang. Det er et fortrinligt pattern at lave sin data access efter i langt de fleste tilfælde. De færreste applikationer har brug for højere abstraktioner end det. Man kan sagtens lave MVC med linq entities - så udgør de jo bare M'et i MVC.

Man kan godt vælge at lægge et Business Object Lag oven på, men hvis du gør det, så skal du ikke bare duplikere properties fra din Entity, for så har du kun vundet noget molboarbejde. Og som sagt - de færreste applikationer har brug for en yderligere abstraktion.

Nøjes med at lave controller-klasserne, så de arbejder med dine entities. Det er nemt,  overskueligt og stadig ganske fornuftigt afkoblet.
Avatar billede jonas_h Nybegynder
27. januar 2009 - 10:56 #8
Du har måske ret i at jeg tænker lidt for "abstrakt". Ville bare gerne lave en applikation som andre kan bygge videre på - og så giver man måske ret meget adgang til dem ved at lade dem arbejde med linq-entities.

Men det må blive min løsning. Hvus man skal lægge sine egne entities ovenpå vil det blive slavearbejde uden lige, og som du skriver vil man nok ikke få så meget ud af det.

Men smid eet svar og så skal du få nogle point :)
Avatar billede torbenkoch Nybegynder
27. januar 2009 - 10:57 #9
Et svar ;-)
Avatar billede jonas_h Nybegynder
27. januar 2009 - 11:32 #10
Har lige siddet og prøvet at se det lidt for mig... Synes ovenstående metode gør det meget uskalerbart. Har du så samtlige entities i samme namespace i dine projekter?
Avatar billede Ny bruger Nybegynder

Din løsning...

Tilladte BB-code-tags: [b]fed[/b] [i]kursiv[/i] [u]understreget[/u] Web- og emailadresser omdannes automatisk til links. Der sættes "nofollow" på alle links.

Loading billede Opret Preview
Kategori
Kurser inden for grundlæggende programmering

Log ind eller opret profil

Hov!

For at kunne deltage på Computerworld Eksperten skal du være logget ind.

Det er heldigvis nemt at oprette en bruger: Det tager to minutter og du kan vælge at bruge enten e-mail, Facebook eller Google som login.

Du kan også logge ind via nedenstående tjenester