Avatar billede pnr Nybegynder
29. oktober 2010 - 13:59 Der er 27 kommentarer og
1 løsning

Hvilken arkitekltur skal jeg vælge at bruge med Entity Framework

Jeg har altid kodet i 3 lag (data, forretning og præsentation), der har fungeret fint. Men jeg er i tvil om jeg bør overveje en anden arkitektur i min kode, specielt når jeg gerne vil benytte mig af Entity Framework.

Jeg har altid prøvet at holde lagene godt adskilt og ikke lade præsentationslaget gå uden om forretningslaget osv. Men med EF (eller måske bare generelt med OR Mapper) synes jeg at kunne se at man implementere tingene anderledes...

Nogen der kan give nogle gode råd :-)

På forhånd mange tak!
Avatar billede bkp Nybegynder
29. oktober 2010 - 14:54 #1
Du kan sagtens lave dit datalag med EF hvor du internt i dit datalag tilgår data med EF.

Jeg bruger det selv, og jeg returnerer data som IList (.ToList()) så BLL kan arbejde videre med de data de får, eventuelt yderligere filtre via Linq.
Avatar billede pnr Nybegynder
29. oktober 2010 - 15:57 #2
Hej bkp og tak for din kommentar!

Skriver du så dine egne POCO klasser og vil disse ligge i forretnings laget?
Hvordan og hvorledes skal man forholde sig til at oprette og rette i databasen, skal man går der direkte på sin "Manager"? Som jeg umildbart ser det kommer datalaget meget tæt på UI, eller har jeg misforstået noget?
Avatar billede bkp Nybegynder
29. oktober 2010 - 16:18 #3
Jeg bruger EF designeren til at danne min orm, og den klasse ligger internt i mit datalag, da det er her den skal bruges.

Hvis datastrukturen ændres er det også kun EF der skal opdateres, og da kan jeg hurtigt se om alt er ok, da datatilgangen til de enkelte felter jo netop er typestærk på grund af EF, det er blandt andet derfor jeg bruger EF (eller en anden orm) i mit datalag istedet for at tilgå felter med strenge o.s.v.

Men dette er selvfølgelig min holdning, jeg ville da gerne høre om der er andre der har en anden mening. :-)
Avatar billede janus_007 Nybegynder
29. oktober 2010 - 16:36 #4
Ja jeg har en anden holdning, men det havde du nok regnet ud bkp :)

Jeg har selv tidligere lavet den "gamle" model der og synes ikke rigtigt den modsvarer det man kan idag med IQueryable. Imho så har datalaget fået en lidt anden betydning end i gamle dage hvor man kaldte sprocs med dynamisk sql inside det meste af tiden.

Problemet med IList/ IEnumerable modellen som reelt modsvarer den gamle 3-lags DAL-BLL-PLL er at man ikke har mulighed for dynamisk at udvælge felter, sortering mv. Man vil altid havne i situationen med mange metoder, en metode til at pille Id'er ud, en anden til en enkelt entitet, en tredie der måske kan sortere udfra et parameter input osv. Altså en masse kodegentagelse fra det ene lag til det næste.

Jeg foretrækker IQueryable hvor man uden problemer kan smide hvilke felter man ønsker retur inkl. where-clause, sortering mv.

Nu ved jeg godt.. og det er jo derfor du nævner POCO's, disse kan nu godt smides ind i EF, men jeg foretrækker nu bare at bruge de entiteter der dannes i designeren, at nogle vil mene det forurener og strider imod det ene og andet princip.
Ja jo.. måske, men tiderne ændrer sig og måske er den traditionelle 3-lags forældet. Lang diskussion :) men i bund og grund så har jeg ændret min mening ganske radikalt om hvad man bør og ikke bør, for mig handler det om at udnytte tingene optimalt og bruge sine evner på komplicerede algoritmer, threading mv. istedet for at kæmpe imod udviklingen og presse en firkant igennem en trekant, just because you can :)

Anyway... tag et kig på Repository Pattern, det er sådan en fin mellemvej som tilfredsstiller langt de fleste projekter. Og frygt ej.. den overholder stadig 3-lags, bare med en moderne tilgang og brug af IQueryable.

Tingene forholder sig naturligvis anderledes når du skal bygge distribuerede enterprise applikationer, men hvor ofte sker det lige?
Avatar billede bkp Nybegynder
29. oktober 2010 - 17:24 #5
3 lags princippet er rigtig nok en ældre sag, og jeg forstår udemærket din tankegang, og jeg vil heller ikke sige at jeg konsekvent bruger 3 lags princippet i alt hvad jeg laver (så kom jeg ud af skabet).
Men hvis du har et projekt med en hel del udviklere som måske ikke alle kender til datamodellen, og hvordan man bedst behandler sine data, så er det en fordel at dba eller en anden styrer datalaget og derved sikrer sig at data hentes og skrives mest hensigtsmæssigt.
EF har selvfølgelig den fordel at den i mange tilfælde finde den bedste og hurtigste vej til dine data, men derfor mener jeg alligevel at dem der er tæt på designfasen af databasen også skal styre datalaget.
Jeg vil helst ikke komme med eksempler (kunne komme til at støde gamle kollegaer), men jeg har siddet i en stor gruppe hvor ikke alle forstod hvordan en MS Sql arbejdede, og det gav store performance problemer, derfor indførte vi et ordentligt datalag som dengang byggede på Linq to Sql, og det gjorde en god forskel.
Avatar billede bkp Nybegynder
29. oktober 2010 - 17:30 #6
Glemte lige at jeg er helt enig i at man bør kigge nærmere på Repository Pattern, men samtidig bør dem der har indsigt i data design have ansvaret for data, og ikke den enkelte udvikler.
Det har hidtil været nemmest at styre med et datalag, men jeg vil heller ikke stoppe udviklingen, og er ikke bange for at afprøve nye metoder ;-)
Avatar billede janus_007 Nybegynder
29. oktober 2010 - 17:52 #7
Hej bkp

Ja mange udviklere kender til den traditionelle tilgang, men jeg tager altid en snak med dem som måske ikke lige er med på L2S/ EF -beatet, de fanger hurtigt konceptet. Vi plejer at lave et design pattern hvor Repository Pattern er det centrale og så er alle udviklere med på tanken.

Og ja... jeg er 100% enig når du nævner hvordan SQL arbejder, men jeg har gjort et stort slag for at alle udvikler med en sql-profiler når der skrives strukturel Linq. Det kræver til gengæld en vis disciplin at sidde på den måde, men så igen... Jeg er også ham der skriver tests osv. for mig er det intet problem :) , jeg tror det handler lidt om at give ud af sin erfaring til de udviklere som ikke lige har de fornødne kompetencer. Udviklere har nu en stor fordel... "Vi lærer hurtigt :)"

Jeg fik ikke nævnt tidligere, men sådan noget som insert, update, delete, ja dem holder jeg stadigvæk 100% i datalaget/ repositoriet om man vil. Vi kan ikke have sådanne ting flyvende rundt :)

Jeg tror vi er enige, det virker ihvertfald sådan - du må ikke opfatte det som sort eller hvidt det jeg skriver, i min verden handler det bare om at finde den bedste og mest optimale løsning. No more, no less :)
Avatar billede bkp Nybegynder
29. oktober 2010 - 19:03 #8
Jo jeg kan også læse mellem linierne at vi er meget enige.
;-)
Avatar billede bkp Nybegynder
29. oktober 2010 - 19:55 #9
Fandt lige denne video, som jeg tror giver et godt indblik i janus forslag med at bruge Repository Pattern:
http://www.asp.net/mvc/videos/creating-model-classes-with-linq-to-sql
Avatar billede pnr Nybegynder
30. oktober 2010 - 14:00 #10
Hej igen og tusind tak for alle jeres kommentare!!!!

Jeg har nu set video og hentet diverse projekter for at få en fornemmelse af hvordan jeg bruger Repository mønstret :-) Men jeg er lidt forvirret :-/ I flere af de eksempler jeg har hentet bruges der også et mønster der hedder "Unit Of Work".

Hvis jeg har forstået det rigtigt så skal det implementeres som følger:

Man har et Domain lag som indeholder ens POCO klasser, samt et repository med interfaces, som angiver funktionerne for at hente og vedligeholde data i DB. Bør der være et interface for hver POCO klasse?

Så har man et datalag, som indeholder EF modellen, samt implementeringer af de interfaces som er angivet i Repository.

Når man så vil tilgå data fra ens præsentationslag, gør man det ved hjælp af de interfaces, der er deifneret i Repository. Hvis det er sådan det skal virke giver det meget god mening. men i nogle af de eksempler jeg har hentet, tilgå de alligevel datalaget i Præsentationen (Context klassen der er genereret af EF):-/

Hvordan optræder "unit of work" i alt det her?

På forhånd mange tak for hjælpen!!!
Avatar billede pnr Nybegynder
01. november 2010 - 17:21 #11
Er i der stadig :-?
Avatar billede janus_007 Nybegynder
02. november 2010 - 00:14 #12
Hej pnr

Jep, er her stadig, havde lige andet at se til idag. Unit of Work er beskrevet her: http://www.martinfowler.com/eaaCatalog/unitOfWork.html

Mht. implementeringen, så jo, ideelt set bør der være et interface for hver POCO, imho er det lidt overflødigt, en smagssag og krav til arkitekturen.
Den ideelle model...
Præsentationslaget skal kalde til servicelaget (som du kalder domainlag), præsentationslaget kender intet til datalaget, men bruger kun de services der er til rådighed. Servicelaget kender derimod fint til Repository (datalaget om man vil lidt simplificeret), men har ikke kendskab til hvordan data persisteres eller hentes.

Nu synes jeg så at man skal udnytte IQueryable, det kan man gøre på flg. måde:
lidt pseudo:

Præsentationslag:
page_load
{
datagrid.DataSource = ForecastService.GetLatestForecast();
}

Servicelag:
class ForecastService
{
static IList<POCOForecast> GetLatestForecast()
{
var repository = new WeatherRepository();

var result = from f in repository.Forecast
where f.Date > now - 1day
orderby f.Date
              select new POCOForecast()
        {
              Id = f.Id,
              DayName = f.DayName,
              Temp = f.Temperature
          }
      return result.ToList();
  }
}

Repository:
class Weather
{
public IQueryable<Forecast> Weather()
{
return context.Forecast;
}
}
public void Delete(Forecast ent)
{

}
public void Update(Forecast ent)....
__________________________________________

Nu skal du ikke lige tage dig af indholdet, det er mere for at vise dig et pattern hvor du udnytter IQueryable fra servicelaget. Håber du kan se modellen :) Ellers bare skyd løs :)

Nu nævnte bkp nogle forbehold for at "blotte" dataforespørgslen, man kan argumentere for at udviklerne i servicelaget SKAL kende til data eller som minimum være istand til at skrive Sql der performer korrekt, heldigvis er langt de fleste jo interesseret i at lave god kode, så jeg ser ikke problemet så stort :)
Avatar billede janus_007 Nybegynder
02. november 2010 - 00:19 #13
Og mjaaa....
"Hvis det er sådan det skal virke giver det meget god mening. men i nogle af de eksempler jeg har hentet, tilgå de alligevel datalaget i Præsentationen (Context klassen der er genereret af EF):-/"

Det er ikke lige måden at gøre det på, medmindre projektet er af en begrænset størrelse og man kender domænet helt præcist. Om ikke andet er det jo en god måde lige at banke en prototype sammen på :) Det virker jo fint og nogle gange skal man også passe på ikke at overgøre tingene bare fordi man kan, oftest er projekter jo styret udfra et eller andet overordnet design/ arkitektur og så skal tingene naturligvis løses korrekt.
Avatar billede pnr Nybegynder
02. november 2010 - 07:58 #14
Hej Janus

Tusind tak for dit eksempel! jeg "leger" lige lidt med det og vender snarligt tilbage!
Avatar billede pnr Nybegynder
02. november 2010 - 13:45 #15
Det hele begynder at give lidt mere mening, men jeg har stadig et par spørgsmål :-)

Hvis vi har nedenstående struktur:

Datalag
-------
EF
Repository (implementerede klasser)

Sevicelag
---------
POCO klasser
Repository (Interfaces) ??
Service klasser

GUI / UI
--------


Du "newer" en "WeatherRepository" men din klasse i Repository'et hedder bare "Weather", er det en fejl, eller er "WeatherRepository" et interface?

Har du ikke undladt at lave interfaces af repository klasserne i servicelaget? Bruge du dine serviceklasser istedet, og lader dem tilgå datalaget uden om et interface?

Endnu engang mange tal for hjælpen og tålmodigheden :-)
Avatar billede janus_007 Nybegynder
02. november 2010 - 18:01 #16
Hej pnr

Ja naturligvis... det var en typo, Weather skal være WeatherRepository :)

Mht. interfaces så bruger jeg kun interfaces når det tjener et formål, eks.vis polymorphism, mere generelle extensions osv. Men det kunne også være at kravet til softwaren skulle være bygget omkring løskoblede komponenter og så ville jeg bruge interfaces med DI eks.vis StructureMap. Det kunne være at man skulle kunne udskifte et Repository med et andet og så er idéen jo fin med interfaces.
Hvis man skulle klemme et interface ind i ovenstående Repository, så kunne det være:

interface IRepository<T> where T : class

{
public IQueryable<T> Query  (istedet for IQueryable<Forecast> Weather())
void Delete (T ent)
void Update (T ent)
}

Men så begynder det allerede at være generisk og man skal måske kende for meget til hvad T kan være, det er noget der skal gennemtænkes - man kan naturligvis holde sine entiteter i et specifikt namespace.
Ellers skal man lave det mere specifikt, altså:
interface IWeatherRepository<T>
{
public IQueryable<Forecast> Query  (istedet for IQueryable<Forecast> Weather())
void Delete( Forecast ent)
void Update( Forecast ent)
}

Men så forsvinder idéen jo lidt med et interface og giver måske mere vedligeholdelse end gavn :) hvis du kan følge mig?
Avatar billede pnr Nybegynder
04. november 2010 - 11:18 #17
Hej igen og undskyld den lange svartid!

Endnu engang tusind tak for din tid!!!

Nå men jeg har prøvet at lave et lille projekt efter din model (#12). Det er komment "ud" som følger:

Jeg har en Model (tabel) ved navn "Employee"

Data
-----
EmployeeRepository
MinContextKlasse

Model
-------
Employee

Service
-------
Employee

Jeg er så lidt i tvivl om, jeg har forstået implementationen en repository klasse korrekt:

public class EmployeeRepository
    {
        private MinContextKlasse context;

        public EmployeeRepository()
        {
            context = new HTContext();
        }

        public IQueryable<HTModel.Employee> Employees()
        {
            return context.Employees;
        }

        public void Delete(HTModel.Employee employee)
        {
            context.Employees.Remove(employee);
        }

        public void Insert(HTModel.Employee employee)
        {
            context.Employees.Add(employee);
        }

        public void SaveChanges()
        {
            context.SaveChanges();           
        }

        public void Dispose()
        {
            if (context != null)
            {
                context.Dispose();
            }
            GC.SuppressFinalize(this);
        }
    }

Og hvad med Service klasserne vil de typisk bestå af statiske metoder?

Er jeg på rette vej?
Avatar billede Syska Mester
04. november 2010 - 13:36 #18
Din service klasse vil arbejde med en: IEmployeeRepository interface.

På den måde kan du altid lave din implementering af dit Repository om ...

Ved ikke om det allerede står i overstående posts, men du skal nok også kigge på DI og IoC:
http://martinfowler.com/articles/injection.html

Der kommer noget af det rigtig smarte nemlig ind når du har lavet det hele på den her måde.

mvh
Avatar billede pnr Nybegynder
04. november 2010 - 15:24 #19
Hej buzzzz og tak for din kommentar!

Vi har været omkring det med Interfaces i repository'et, men det mener janus ikke umildbart tjener noget formål, andet end mere arbejde.

Men det kunne være at jeg skulle overveje det igen :)
Avatar billede Syska Mester
04. november 2010 - 19:16 #20
Ja, men han skriver også:

"Det kunne være at man skulle kunne udskifte et Repository med et andet og så er idéen jo fin med interfaces."

Men her virker det også som om det Repository kun har ansvaret for en type ... jeg har gerne alle type.
Avatar billede pnr Nybegynder
05. november 2010 - 14:43 #21
Har rodet rundt med det her, og kan godt se at det begynder at blive smart, men kan ikke helt få det skruet sammen. Det kunne vel også være genialt hvis implementeringen af Repository'et var generisk, da de jo nok komme til at ligne hinanden meget? men hvis så også interfacet er generisk, så begynder det at blive svært :-/
Avatar billede janus_007 Nybegynder
05. november 2010 - 19:10 #22
Hej pnr

Det hele kan nu sagtens være generisk, men med generiske repositories oplever man pludselig også at alt data adgang skal strømlines og det er jo ikke altid tilfældet... Jeg mener det hænder ret ofte at man har brug for at kontrollere hvad ens repository gør, eks.vis entiteter som kræver specielle loadoptions og andet, det kan hurtigt blive svært at styre. Min erfaring er at man ikke nødvendigvis skal indføre en generisk tilgangsvinkel, bare fordi man kan.

pnr, som du jo nok kan se udfra min posts, så er jeg lidt mere... "jo jo , vi tager det som det kommer" og prøver at holde os så fleksible som muligt. Jeg har mange mange gange siddet på diverse projekter hvor man har måttet lave alverdens skumle trick for at overholde en arkitektur og alligevel er det endt op med en dårlig performance, senere når projektet endelig er launchet og der kommer ekstra features, så bliver vi tvunget til at lave sjove ting pga. tidspres + at kravet ikke var med fra starten, så  spørger sig selv om det nu også var umagen værd at lave alle de her "contraints" for/ på systemet. Du må ikke opfatte det som at jeg ikke mener der skal være nogle grænser = ingen design patterns, jeg holder meget af design patterns, men de må aldrig låse udvikleren fast og gøre tingene unødigt komplicerede og give  dårligere performance.

Jeg kan fortælle dig hvordan jeg gør, der er sikkert nogle der kan gøre det smartere, men lige idag har jeg eks.vis siddet med performancetuning af et linq udtryk.... det viste sig at det fyrede en sql af for hver subquery, hvilket jo giver en dårlig performance..

Linq-udtrykket lignende:

var result = repo.FooBar().Where(x => x.Active == true)
                .Select(x =>
                    new Dto.Foo()
                    {
                        Id = x.Id
                        ,
                        StartDate = x.StartDate.ToString()
                        ,
                        Shots = x.ShotValues.Select(y =>
                            new Dto.Shot()
                            {
                                Id = y.tEnt1.tEnt2.SomeId
                                ,
                                Name = y.tEnt1.tEnt2.SomeName
                                ,
                                ColorSettings = new Dto.ColorSettings()
                                {
                                    Color = y.tEnt1.Color ?? 0
                                    ,
                                    Some ID= y.Ent1.SomeId
                                    ,
                                    Number = y.tEnt1.Number

                                }
                            }).ToList()
                    });


Den resulterede rent faktisk i en query for hver række FooBar, hvis jeg nu skulle have fulgt et bestemt pattern for mit repository kunne jeg jo aldrig have gjort flg.:


public IQueryable<Datamodels.tFooBar> Foobar(DataLoadOptions options = null)
        {
            if(options != null)
                this.dataContext.LoadOptions = options;
            return this.dataContext.tFoobars;

        }



Altså som du kan se er der smidt en DataLoadOptions ind som parameter, det ville jo være svært hvis man skulle overholde et generisk design som dikterede at min metodesignatur skulle være ens. (FORUDSAT man startede sit design uden DataLoadOptions)
Avatar billede pnr Nybegynder
08. november 2010 - 14:04 #23
Hej alle

Det er vist på tide at få lukket den her, jeg er meget taknemlig for jeres hjælp!!! Jeg vil forslå følgende point fordeling:

Janus_007 : 60 point;
buzzzz:  20 point i et nyt spørgsmål
bkp: 4 point i et nyt spørgsmål

Hvis der er ok så smider janus_007 et svar her, også opretter jeg et nyt spørgsmål så buzzzz og bkp kan få point :-)

Endnu engang mange tak!
Avatar billede Syska Mester
08. november 2010 - 16:16 #24
Fint med mig ...
Avatar billede Syska Mester
08. november 2010 - 16:17 #25
btw ... jeg er også enig med janus, aldrig lav ting mere komplicerede hvis der ikke er nogen grund. Det fortryder man i sidste ende :-)

mvh
Avatar billede janus_007 Nybegynder
08. november 2010 - 20:03 #26
tiptop... jeg er bare glad for du kunne bruge de input du har fået af alle her i tråden :)
Avatar billede Syska Mester
08. november 2010 - 20:10 #27
Jeg deltager også herinde for nogen gange at komme ind i en diskussion om tingene ... og få andres synspunkter.

Det er aldrig sjovt at diskutere med sig selv, der får man for det meste altid ret :-)

mvh
Avatar billede pnr Nybegynder
19. november 2010 - 22:51 #28
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
IT-kurser om Microsoft 365, sikkerhed, personlig vækst, udvikling, digital markedsføring, grafisk design, SAP og forretningsanalyse.

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