01. december 2005 - 22:26Der er
21 kommentarer og 1 løsning
Objekter og databaser
Hej Alle
Jeg er ret ny indenfor objektorienteret programmering og ASP.NET og sidder lige nu og tumler med nogle tanker om hvordan man laver de ting jeg tidligere har lavet i ASP om til objektorienteret .NET... Og her gik jeg så i stå ved hele forbindelsen mellem objekterne og databasen. Hvis jeg f.eks. har en portal, hvor jeg har nogle brugere, så ville jeg umiddelbart mene at det var smart at have en bruger-klasse? Men hvordan skal denne klasse så designes når den skal forbindes med en database? Man kunne jo selvfølgelig i constructoren hente alle data om brugeren i databasen og så sætte nogle attributter til at indeholde disse data og så arbejde videre derfra, men det virker temmeligt voldsomt at skulle hente alle data ud, for f.eks. blot at ændre en attribut? Hvordan håndterer man sådanne ting smart?
I dette særtema om aspekter af AI ser vi på skiftet fra sprogmodeller til AI-agenter, og hvordan virksomheder kan navigere i spændet mellem teknologisk hastighed og behovet for menneskelig kontrol.
well... det er jo unægteligt et område der har trukket en del omtale i tidens løb :o) som udgangspunkt er det du skriver en helt normal måde at gøre det på. umiddelbart synes jeg det virker endnu mere vildt at håndtere opdatering af properties på en person enkeltvist.
desuden .... i din objektmodel (og for den sags skyld i domænet) vil din person være en helhed som det ville være uhendsigtsmæssigt at stå med en delmængde af (selvom du har ret i, at der self. godt kunne være noget effektivt i blot at sende den del man har ondt i til lægen... istedet for at skulle troppe op med alt hvad man har :o)
en smart løsning.... jeg synes at en or-mapper er en enkelt måde at fikse det på - f.eks. denne (der er mange): http://sourceforge.net/projects/gopf
Hvis man så står og blot skal vise personens navn - så skal den hente alle properties for den person, for blot f.eks. at printe navnet. Og hver eneste gang man skal gøre dette skal alle data hentes ud af databasen - er det virkelig måden man gør det på?
under normale omstændigheder er data til et objekt så små at databasen læser dem fra disk i en enkelt read og typisk også vil kunne levere dem til et andet system over nettet (hvis app og db er på forskellige systemer) i en enkelt pakke
og så er der ingen performance forskel på et komplet objekt og en barberet objekt
der er undtagelser - hvis du har en attached CLOB/BLOB i MB størrelse så kan det nok godt betale sig at holde den seperat
der er også den lille pointe hvis vi har en 1:stort M relation, hvor du kan få en liste med mange små objekter
de gode O/R mappere understøtter imidlertid lazy loading så den slags først hentes når de skal bruges
kan anbefale wilson ORMapper hvis man ikke har rodet med det før. Den er meget nem at gå til, og er non-intrusive ved din kode, dvs. at dine klasser skal ikke nedarve fra noget bestemt da det hele styres fra en xml-mapping fil.
Ok, det lyder jo som en god ide at bruge en ORMapper... Men for bare lige at få det på plads, hvis nu man ikke vælger at bruge sådan en.
Hvis vi nu forestiller os at vi har følgende struktur:
Forlag - Ansatte | Forfatter | Bog
Hvis man nu f.eks. skal have fat i et forfatter objekt - ville man så hver gang hente alle data ind i forfatter-objektet, samt populere hele rækken af bog-objekter? Et forlagsobjekt ville jo så resultere i at man skal læse stort set hele databasen over i objekter?
Hmm... man ville vel nærmere lave en metode i klassen forfatter der returnerer en liste over bøger, right? Så man kun henter boglisten, hvis den er nødvendig...
Så et lidt nyt spm.(håber det er ok) Hvis man deler op i flere lag, data access, Business logic og presentation: Ville man så i dataaccess lave en klasse for hver tilsvarende klasse i businesslogic - der har til opgave at hente data og sende det videre til business og tilsvarende gemme det igen. Eller ville man f.eks. lave én skrive-klasse og en læse-klasse, og så have en metode for hver business logic-klasse der skal hente data? Hvis i evt. kunne komme med links til nogle beskrivelser af hvordan man håndterer sådan en situation...
nej.. altså.. du har en Forfatter-klasse der deri indeholder en Bøger-property som returnerer en liste af Bog-objecter.
De fleste OR-mappere understøtter LazyLoad (inkl. Wilson ORMapper), der betyder, at selvom du opretter en instans af en forfatter, så vil alle bøgerne fysisk først blive hentet fra db når du forespørger på dinForfatter-Bøger propertien.
Ang. konkret implementation foretrækker jeg følgende:
Alle klasser der repræsenterer data fra database implementerer et Interface, ie. IDataObject. Dette interface indeholder en Property og to metoder:
Så hvis du vil ændre navnet på en bestemt forfatter kan du:
Forfatter f = Forfatter.RetrieveByName("Jens"); f.Navn = "Mads"; f.Persist();
Omkring dine lag vil følgende nok være en passende beskrivelse
UI. ASPX/ASCX Business. Dine data-klasser (Forfatter, Bog, Forlag m.m.) Data. ORMapperen. I mit eksempel; ObjectSpace som er navnet på Wilson ORMapper.
Du kan evt. lave en DataManager-klasse som internt indeholder en IConnection og nogle standard metoder til at snakke med databasen. Så bliver din Persist-metode til noget i denne retning
public void Persist() { DataManager dm = DataManager.Current; // evt. brug parametre dm.ExecuteCommand("UPDATE tblForfattere SET navn = '"+ this.Navn +"' WHERE id = "+ this.ID +"); }
En anden mulighed er at benytte attributer i dine businessobjecter og så, via reflection, bruge en datamanager-klasse til at opdatere værdierne:
public void Persist() { DataManager dm = DataManager.Current; dm.Persist(this); }
men så er vi ovre i ORMapping igen, og så kan du lige så godt benytte en allerede lavet ORMapper.
Nu handler det her ikke om at lave et konkret projekt der nødvendigvis skal munde ud i et superprodukt, men om at lære at skrive et ordentligt system - og det tror jeg på at man gør ved at lave mest muligt af det selv - og ikke benytte alt for mange færdige produkter der klarer det sure arbejde for en :-) Derfor vil jeg egentlig gerne skrive koden uden en ORMapper til at starte med, selvom dette måske ikke er det smarteste - og så måske skrive skidtet om, så jeg benytter en ORMapper. Derfor alle de dumme spørgsmål - det er altså ikke et forsøg på for en værd pris at komme uden om en ORMapper.
tja. det er selvfølgelig rigtig nok. Men så kan du også sige, at så ikke skrive i IL istedet for at benytte en compiler der klarer det sure arbejde for dig.
:)
Selvfølgelig et ekstremt tilfælde. Men nu hvor der også er kommet en OR Mapper med i .Net 2.0 frameworket vil jeg tro at det bliver mere og mere udbredt, og måden man gør det på. MS's 100% satstning på DataSettet som basis for al tilgang med databasen du'r bare ikke når man har et stort objektdomæne.
Så er der også de religionskrige der kører på om man bør bruge OR mappers (ORM) i det hele taget, og det er vel en smagssag på højde med SPROC (Stored Procedures) og D-SQL (Dynamisk SQL). Personligt foretrækker jeg D-SQL fremfor SPROC, samt ORM fremfor at skrive de trivielle persistens-procedurer.
Men den overordnede opbygning af dine BO'er bør indeholde en Persist og en Delete metode, som så internt gør de ting der skal til for at slette eller gemme state'n af dit object. Det kan være med en ORM eller kald direkte til ADO.Net eller en mellemliggende DataManager du selv laver der evt. har noget caching indbygget.
En sidste mulighed er at bruge en Object database istedet for Relationel database, det giver dog problemer når andre systemer skal hente data fra din database.
DSQL er egentlig bare når du skriver f.eks. sådan her i din kode:
string qry = "SELECT * FROM tblForfattere"; IDataReader = dinCmd.ExecuteReader(qry);
Alternativet er at bruge SPROC hvor alt SQL'en er defineret inde i databasen, og fra koden kalder du så bare nogle funktioner, de såkaldte Stored Procedures.
Mange tak for de mange gode kommentarer! Jeg har helt sikkert fået en langt bedre forståelse for hvordan man sammenkobler sin applikation med en database. Så er det bare igang med at øve sig :-) Smid et svar alle jer der vil ha' point!
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.