"Nice to know" om strings

by DotNetNerd 1. November 2007 15:30
For ikke ret lang tid siden gik det op for mig hvor lidt den gennemsnitlige programmør egentlig ved om den datatype der nok bruges allermest - altså strings. Jeg skal på ingen måde gøre mig bedre end andre for min viden omkring operationer på strings var heller hverken værre eller bedre end de fleste andres, før jeg læste "CLR via C#".

Derfor vil jeg lige skrive lidt "nice to know" information om strenge, dog startende med ting de fleste nok i nogen grad er klar over bare for at få alle up to speed.

1. Strings er immutable, hvilket vil sige at når en streng først er oprettet på heapen kan dens indhold på ingen måde ændres. Operationer med strings vil altid medfører at en ny string skabes. Dette er hovedårsagen til klassen StringBuilder som istedet arbejder på et char-array således at der ikke skabes en string før ToString() kaldes. Der er dog naturligvis også et overhead ved brug af StringBuilder, så to gode råd er at bruge den hvis mere end 2 konkatineringer foretages, og at angive en størrelse i constructoren så vidt det er muligt - da det vil betyde at arrayet redimentioneres så få gange som muligt.

2. .NET frameworket giver mulighed for noget der kaldes string interning, som går ud på at en string oprettes i en intern hashtable. På den måde undgår man at have flere ens strenge idet der vil returneres en reference til den eksisterende streng hvis man interner en streng der allerede findes - se metoderne string.Intern og string.IsInterned. Dette skal dog benyttes sparsomt, da en interned string sandsynligvis ikke vil blive garbage collected før app domainet lukker!

3. På grund af string interning - og fordi det giver pænere kode - er string.Empty at foretrække frem for "" sådan at man slipper for at oprette et streng objekt blot for at angive en tom streng. Ved sammenligning med en tom streng anbefales det dog istedet at man enten bruger string.IsNullOrEmpty, som er en pæn måde at checke både for null og empty, eller "myString.Length = 0" - som faktisk er den hurtigste måde at checke for empty rent performancemæssigt.

4. Som standard bruger == operatoren en Ordinal algoritme til sammenligning af strenge. Det vil sige at der ikke tages højde for Culture, men at der er forskel på små og store bogstaver. Med andre ord kan man opnå en pæn hastighedsoptimering ved specifikt at bruge en algoritme som OrdinalIgnoreCase til at sammenligne strenge der kun bruges i kode, og man kan opnå en bedre sammenligning af Culture specifik kode ved at bruge CurrentCulture / CurrentCultureIgnoreCase. Faktisk anbefaler Microsoft at man altid bruger Equals metoden med specifik angivelse af StringComparison enumeration, som beskriver hvilken algoritme der bruges, da koden derved er mere beskrivende for hvilken sammenligning man ønsker.

Hvis du vil se hvor stor forskellen rent faktisk er på algoritmernes hastighed er her en test udført af en af Microsofts egne udviklere: http://blogs.msdn.com/noahc/archive/2007/06/29/string-equals-performance-comparison.aspx

5. string.ToUpperInvariant har modtaget særbehandling i frameworket, således at den er optimeret i forhold til ToLower/ToLowerInvariant/ToUpper, derfor kan den med fordel benyttes som den foretrukne løsning hvis man ønsker at lave en streng hvor alle tegn er ens cased.

Tags:

Data Containere - lean or mean

by DotNetNerd 25. October 2007 17:45

Datacontainere - kort fortalt

I .NET findes der flere forskellige datacontainere som DataSet, Strongly typed DataSet og XmlDocument. Disse containere kan man bruge "lige ud af boksen" hvis man skulle have lyst til det, men man kan også vælge enten at lave en wrapper der arbejder på en af disse typer eller også man kan lave sine egne custom entiteter.

Hvilken container man ender med at bruge i sin applikation er oftest afgjort ud fra personlig præference og funktionelle krav til applikationen man er ved at udvikle. Da valget af containere er meget centralt i designet af en applikation mener jeg det er vigtigt at man ved hvilke konsekvenser valget har for udviklingstiden og for det overordnede design.

Det jeg vil se på i den her artikel er hvordan man vælger den "bedste" container til sin applikation og hvilke fordele og ulemper et sådan valg fører med sig.

Hvordan vælger jeg hvilken der passer bedst til min applikation?

Hvilken datacontainer der er "best" findes der ikke noget entydigt svar på, da de alle har deres fordele og ulemper. For at kunne vælge den rette datacontainer til sin applikation har jeg valgt 3 faktorer jeg vil fokusere på:

  • Design kriterier
  • Udviklingstid/levetid
  • Funktionelle krav

Design kriterier

Inden man begynder at træffe beslutninger omkring design er det altid vigtigt (ikke kun ved valg af datacontainer) at opstille nogle design kriterier, som man kan vurdere forskellige løsninger udfra. Ofte vælger man nogle begreber som er centrale for ens design, eller også tager man en fast liste af begreber og vurderer hvilke der er vigtigst for det aktuelle design. Herunder er en liste over begreber, der er relevante ved valg af datacontainere, som man typisk kan bruge til at beskrive designkriterier.

Skalerbart - skal det være muligt at skalere applikationen så den f.eks ligger på flere servere?

Genbrugbart - skal dele af applikationen kunne genbruges i andre lignende applikationer?

Integrerbart - skal applikationen integreres med andre applikationer?

Performance - er der nogen krav til hvor hurtigt en given funktion skal afvikles?

Fleksibelt - er der nogen krav til at systemet skal kunne ændres når det er taget i brug?


Funktionelle krav

Udover de generelle designkriterier kan man se på de funktionelle krav der stilles til applikationen. Skal data kunne konverteres til xml? Skal det være muligt at lave sortering? Skal data distribueres? Hvis ja, skal de så kun distribueres ud til ens egne applikationer eller også til kunder? Skal de distribueres via en webservice eller via remoting i et client/server scenarie?

Alle disse typer overvejelser er relevante for valget af datacontainer. Et Dataset kan f.eks konverteres til xml og understøtter sortering samtidig med det er en type webservices genkender og kan genskabe hos klienten. På den anden hånd kan et dataset ikke udvides med ekstra funktionalitet, og det er ikke typestærkt, hvilket kan lede til uskøn kode hvis man skal lave mange metoder til at arbejde med data som måske ligefrem bliver nød til at findes i flere lag af ens applikation.

Hvis du bruger et strongly typed dataset bliver det godt nok typestærkt, men det er stadig ikke muligt at tilføje egne funktioner, og det er mindre generisk end "lillebror".

Custom entiteter giver mulighed for at tilføje funktionalitet, men lider under den sygdom at funktionerne ikke sendes med over en webservice. Tilgengæld kan de skræddersyes så du får præcist den funktionalitet du drømmer om, og du kan distribuere denne funktionalitet ud til en klient via remoting. Overvejelserne er mange men de bør gennemtænkes for den enkelte applikation da de er meget væsentlige, og har stor betydning for hvordan de funktioner man skal udvikle kan udformes.


Udviklingstid/levetid

Sidst men bestemt ikke mindst kommer den faktor som din kunde, projektleder eller chef nok går rigtigt meget op i, og som du selv ville ønske ikke var så væsentlig. Det kan være enormt fristende at give sig i kast med at udvikle custom entiteter, da de giver maksimal frihed, men det kræver ekstra udviklingstid og er dermed til at fordyre applikationen. Derfor skal valget af custom entiteter ofte være begrundet i at det kan forlænge en applikations levetid igennem øgedet fleksibilitet eller at det giver nogle muligheder som man ikke har ved at bruge DataSet, XmlDocument eller lignende.


Forskellige datacontainere og deres datastruktur

Når man har vurderet design kriterier, funktionelle krav og udviklingstid kan man se på hvilken type datacontainer der er mest velegnet til applikationen. I nogen typer applikationer viser der sig måske ligefrem at det bedste resultat er at bruge en type container til nogle specifikke moduler, og en anden container til andre moduler.

For at kunne vælge må vi se på hvilke fordele og ulemper der er forbundet med de forskellige datacontainere. Jeg vil her gå lidt mere i dybden med den mest gængse.

  • DataSet
  • Strongly typed DataSet
  • Custom entitet
  • XmlDocument
  • XmlDataDocument

Disse datacontainere kan deles ind i 3 kategorier efter hvordan data struktureres.
Der findes de relationelle datastrukturer, som DataSet og Strongly typed DataSet, der er bygget på samme måde som en databasestruktur, med tabeller, keys og relationer.
Custom entiteter er objekt-orienterede så de indeholder typer og collections det kan bygges op i en hirakisk struktur.
Og den sidste kategori XmlDokumenter arbejder som navnet antyder med Xml som er hirakisk opbyggede tags.

Relationelle og objekt-orienterede datastrukturer er normalt dem man vælger imellem når man skal udvikle en rigtig enterprise applikation, da dette er et stort emne er set et afsnit helt for sig selv.
XmlDokumenter bruges til at løse specifikke problemstillinger, hvor man ikke ønsker at have data liggende i en database. Xml er f.eks godt til at beskrive et sitemap eller til at strukturere tekster og billeder så man slipper for at de ligger og roder i databasen.

XmlDocument klassen giver mulighed for at arbejde effektivt med data i et XMl format. Dette gøres ved hjælp af XPath queries, som der findes en hel seperat artikel om her på sitet :-)

En lidt finurlig kombination af to verdener finder man i XmlDataDocument klassen. Denne klasse kombinerer, som man måske kan gætte, brugen af DataSet og XmlDocument. Det vil sige at man kan få det bedste af funktionaliteten ved at bruge XmlDocument til rådighed så det kan bruges til at arbejde med et DataSet ved at lave et XmlDataDocument der arbejder på DataSettet. Det vil være fordyrende at sende et XmlDataDocument fra lag til lag i en applikation. Man vil derfor oftest nøjes med at sende DataSettet, og så oprette sit XmlDataDocument (der kan ses som en slags wrapper) når man har brug for denne udvidede funktionalitet.


Strulturelt vs Objekt-Orienteret

Valget imellem relationelle og objekt-orienterede datastrukturer har efterhånden udviklet sig til en hel religionskrig, som jeg vil prøve så vidt muligt at holde mig fri af. Det er imidlertid nødvendigt lige at knytte et par kommentarer til emnet.
Overordnet set er det muligt at løse de samme problemer med begge typer datastrukturer, den store forskel ligger omkring abstraktionsniveauet.

Relationelle datastrukturer afspejler databasen de anvendes oven på, hvilket nogen vil argumentere for er et problem, da man derved ikke får abstraheret databasens opbygning bort og eventuelle ændringer vil forplante sig ud i applikationen.

Objekt-orienterede strukturer derimod afspejler virkelige objekter og modeleres efter udviklerens ønsker. Dette giver ganske vidst en bedre abstraktion, således at strukturelle ændringer i databasen kun medfører ændringer i de metoder der sørger for at mappe data imellem databasen og objekterne. Ulempen ved denne fremgangsmøde er tilgengæld at strukturen tit kommer til at minde om tabeller alligevel. Dette skyldes at objekterne oftest ender med at blive sendt til en brugergrænseflade hvor de skal databindes - og denne process kræver et tabulart format. Med andre ord ser man tit at klasser ender med at beskrive kolonner via properties, og da de enkelte objekter bliver så til rækkerhvor collections så svarer til en tabel.

Prøver man bevidst på at bygge en hirakisk domæne model løber man hurtigt ind i bøvl når der skal databindes og man ender med at skrive en ordentlig bunke løkker til at finde et givent objekt i en collection. Selvom det måske kan lyde uskyldigt nok har det den sideeffekt at jo flere niveauer du har i dit hiraki, jo flere løkker har du inden i hinanden, hvilket er virkelig skidt for performance. Tænk f.eks på hvad det vil kræve for at kunne finde en medarbejder i en afdeling i en virksomhed. Egentlig et simpelt eksempel som sagtens kunne blive værre (virksomheden kunne være i en by som kunne være i en kommune i et land!), men allerede her vil man typisk skrive 3 løkker inden i hinaden således at man for hver virksomhed checker alle afdelingers medarbejdere.
Alternativet er naturligvis at skrive nogle metoder i sit data access lag som kan hente mindre dele af strukturen ud. Dette er imidlertid heller ikke altid det man ønsker, da det vil betyde at man skal skrive rigtigt mange lavniveau metoder for at kunne søge efter specifikke objekter - hvilket også kan have en effekt på helt ens design efterhånden som man har en overflod af metoder.

Designer man sin objekt struktur så flad som muligt kan der imidlertid godt være noget hentet i at man kan abstrahere databasen bort og indkapsle mapningen i sit eget data access lag. Dette kan imidlertid også godt gøres ved hjælp af f.eks et DataSet, da det strengt taget ikke behøver ligne den underliggende database - men det fungerer helt klart bedst hvis det gør.

Personligt står jeg med et ben i hver lejr, da jeg mener det er helt an på hvad man er ved at udvikle. Pointen med denne diskussion er derfor ikke at komme med en afgørelse, men derimod blot at gøre opmærksom på at der er disse 2 måder at "se verden på" og at forklare de fordele og ulemper der er forbundet med dem hver især.


Wrappere

Ved at bruge wrappere som f.eks indkapsler et DataSet eller et XmlDataDocumentkan kan tilføje alt den ekstra funktionalitet man kan tænke sig. Det giver samtidig mulighed for at trække på den eksisterende funktionalitet som der wrappede objekt har. Man betaler imidlertid den pris at man skal bryde indkapslingen hvis funktionerne skal tilgås udefra. Wrappere er desude med til at tilføre ekstra overhead, hvilket kan være et problem hvis der skal instantieres mange objekter af typen.

Den mest oplagte klasse at wrappe er nok DataSet, da det kan indeholder hvad som helst og man derved kan lave generisk funktionalitet som kan bruges på et hvilket som helst datagrundlag. XmlDataDocument er et eksempel på en wrapper der pakker et DataSet ind i XmlDocument funktionalitet.


Fordele og ulemper

Til sidst vil jeg se mere konkret på de enkelte klasser og beskrive deres fordele og ulemper. Herved håber jeg at ende med en liste som, når man har læst ovenstående, kan bruges til at træffe et fornuftigt og velovervejet valg af datacontainer.

DataSet

Har funktionalitet til at sortere, søge, filtrere i data og til at konvertere til xml.

Er meget fleksibelt, da det er generisk og data beskrives via schema information.

Kan sendes over en webservice og være fuldt funktionelt på klienten.

Skal fyldes med schema information som kan gøres på 3 måder:

  • Manuelt ved at lave et lag der sørger for at hælde data i dine DataSet.
  • Ved at læse schema information fra en XSD via ReadXmlSchema metoden.
  • Ved at hende schema information fra databsen ved hjælp af DataAdapterens FillSchema metode - dette er dog meget tungt og bør bruges med omhu.
Bruger meget hukommelse hvis man kun skal hente en enkelt række.

Strongly typed DataSet

Har samme funktionalitet som DataSet til sortering, søgning, filtrering og xml konvertering.

Er typestærkt og giver derved kønnere kode der er lettere at skrive.

Indeholder schema information som beskrives på design tidspunktet.

Klienter der henter via webservice får kopi af schema information og mister ikke funktionalitet.

Kan forbedre hastighed i forhold til et almindeligt DataSet da peopertys kalder via index.

Skal regenereres ved strukturændringer.

Større overhead than dataset.

Der er mange scenarier hvor det ikke er helt har den funktionalitet man ønsker sig.

Custom entities

Giver fuld fleksibilitet i forhold til funktionalitet.

Minimalt interface der kun stiller der til rådighed man ønsker.

Kan enten være intelligente, helt dumme (Data Transfer Objekter) eller midt imellem.

Logik kan distribueres imellem lag og imellem klienter.

Der er kun properties der sendes med over webservice, så klienten har ikke gavn af metoder og attributter.

På trods af den Objekt-Orienterede natur vil strukturen oftest ende med at ligne en tabel, af hensyn til databinding.

Indeholder ingen funktionalitet til søgning og denne kan være omfattende og problematisk ved uheldigt strukturelt design.

God performance ved få entiteter, men taber i forhold til DataSet ved store datamængder.

XmlDocument

Stærkt til at beskrive hirakiske data.

Data kan gemmes eksternt i filer og kræver ikke database eller lignende persistensmedie.

Datatilgang via query sprog.

Er ikke typestærkt.

Fladt format, da det basalt set er tekst.

Tendens til at blive rodet og tungt ved store mængder komplekse data.

XmlDataDocument

Kombinerer funktionaliteten fra DataSet og XmlDocument.

Data kan gemmes i database eller som XML.

Søgning kan laves både via Xpath queries og direkte tilgang til DataSet.

Kan anvendes efter behov og behøves ikke sendes med ved distrubution, hvor man enten sender Xml eller DataSet alene.

Kan føre til uklar kode og struktur ved blanding af XPath queries og datatilgang via DataSet.

Idet objektet består af DataSet og XmlDokument bliver objektet tungere end de to typer er hver især.

DataSet Wrapper

Man kan tilføje ønsket funktionalitet efter behov.

Kan gøres fleksibelt med hensyn til datagrundlag og tilføre generisk funktionalitet.

Kræver at man bryder indkapslingen for at kunne stille funktionalitet til rådighed.

Det er kun DataSettet der er tilgængeligt hvis der sendes objekter over en webservice - metoder og attributter frafalder.

Data behandles dybest set stadig som et DataSet, men tilfører ekstra overhead.

Silverlight - fremtiden inden for rige webklienter

by DotNetNerd 21. May 2007 18:56

Silverlight - tidligere Windows Presentation Foundation Everywhere.

Silverlight er det nye navn for det det tidligere var kendt som WPF/E - eller Windows Presentation Foundation Everywhere. Silverlight er videreudviklingen af Windows Presentation Foundation som Microsoft introducerede som del af .NET 3.0 - sammen med Windows Communication Foundation og Windows Workflow Foundation.

Silverlight er fornylig udgivet som Beta 1, og planen er at der er endelig release her til sommeren 2007. Det helt spændende er at Silverlight giver mulighed for at man kan udvikle rige brugerflader der kan køre hvor som helst, og som kan programmeres i mange forskellige sprog - herunder C# og VB som er de relevante sprog for mit vedkomne.

Derudover forbedres mulighederne for samarbejde imellem designere og udviklere markant via nye værktøjer der arbejder med XAML. Dette er noget jeg personligt har savnet igennem længere tid, da det kan føles akavet at få et design og sidde og skifte html-elementer ud med kontroller, og i sidste ende vrage nogle af de ting, da det kan være uhensigtsmæssigt at implementere.

En sidste stor pointe er at Silverlight tilbyder komponenter til lettere at vise og arbejde med video i et helt nyt omfang. For eksempel vil det være muligt at sætte bookmarks ind i video, som man så kan programmere op imod sådan at der sker noget når et bestemt punkt spilles - dette kunne være at lade brugeren deltage aktivt eller måske bare vise en reklame ved siden af. Det vil også være muligt via Expression Studio at arbejde med hvordan video vises - så videoen kan fades ind eller klippes ud som puslespil hvis man skulle have lyst til at lave den slags lir.

Med hensyn til visning af medier tilbyder Microsoft samtidig det de kalder Silverlight streaming service, hvor man kan uploade sine videoer og derefter bruge dem på sit site - der er dog en 4 GB max grænse til at starte med.

Der findes allerede en række demoer af hvad man kan lave der spænder fra online realtime videoredigering, over bestilling af flybilletter med visning af ruten, til roterende 3D-terninger som alternativ til den gode gamle dropdownboks.

.NET crossbrowser og crossplatform.

At Silverlight kan køre "hvor som helst", lægger naturligvis op til en lidt mere præcis forklaring. Det der ligger i det er at Microsoft har shippet en .NET runtime der kan køre på tværs af platform og browser, hvilket jeg indtil videre har set demonstreret på Windows og Mac med både Internet Explorer og Firefox. Det eneste det kræver er et plugin, som kan installeres på under 20 sekunder! Med andre ord er det ikke nogen egentlig hindring at skulle smide dette plugin ind som det har været tilfældet med andre plugins igennem tiden.

Det kan som en sidekommentar også nævnes at debugging også er crossplatform, så du kan sidde og debugge noget der kører i Firefox på en Mac. Om du arbejder med ASP.NET, PHP eller hvad du nu kunne finde på er heller ikke afgørende, da Silverlight kan køre sammen med hvad som helst.

Hvad er så fordelene ved at køre .NET direkte i browseren?

Der er naturligvis mange fordele ved at kunne arbejde i et fuldgyldigt programmeringssprog, men jeg vil vælge at ligge mig op af dem som Scott Guthrie fremhævede ved MIX 2007 som er.

Multi-language support: inklusiv C#, VB, Ruby, Python og andre .NET sprog (mere end 37 sprog i alt).

Høj performance runtime: eller rettere fantastisk performance, der virkelig sparker røv på alternativer som f.eks javascript hvor benchmarks har vist at det er 300-700 gange så hurtig.

Rige kontroller med grafik, medier og interaktivitet: kontroller kan se ud og reagere lige som man ønsker, idet man ikke længere er bundet til at bruge html elementer, bitmap billeder og embedded video.

Html DOM integration: der er inkluderet et html bibliotek der giver mulighed for at tilgå elementer på siden, inklusiv statusbar, window frame og lign.

Robust networking: gør det muligt at kalde hvilket som helst type endpoint og udveksle data.

Flexible data support: LINQ query API understøttelse (se evt min LINQ artikel om dette emne).

XAML og samarbejde imellem design og udvikling.

Med hensyn til samarbejde imellem designere og udviklere består det i at der kommer en række værktøjer som er målrettet begge faggrupper, således at de kan arbejde på de samme projekter.

Expression Studio er til designere og indeholder værktøjer til at designe komponenter og arbejde med grafik, medier og endcoding - som hedder Expression Design, Blend og Media Encoder.

Cider er tilsvarende tilsigtet udviklere, således at de kan arbejde lidt videre med komponenterne i forbindelse med integration.

Den underliggende teknologi eller det underliggende sprog om man vil er XAML, som du kan læse mere om her. Kort fortalt er det et XML baseret sprog til at udarbejde vector baseret grafik og animation, lidt i stil med Flash. Derved kan man faktisk læse koden og ændre i den manuelt, men derudover er der som nævnt flere forskellige værktøjer på vej, sådan at man kan arbejde med det på et noget højere abstraktionsniveau som vi er vant til.

Tags: ,

LINQ - Language Integrated Query

by DotNetNerd 10. May 2007 16:32

LINQ - Language Integrated Query.

LINQ som er den store nyskabelse i .NET 3.0 står for Language Integrated Query. Idéen er at skabe et querysprog som er generelt anvendeligt uanset hvilken type data der er tale om, og at gøre querys til "first class citizens" som Anders Hejlsberg ynder at udtrykke det. Med andre ord vil det sige at querys skal væk fra at være noget man skjuler i et datalag og op hvor de hører hjemme som en naturlig del af logikken. Derudover for man compile time type checking og intellisence da det ikke blot er strenge man arbejder med.

Som udgangspunkt findes der 3 datadomæner der udgør 5 typer som LINQ kan arbejde med, men lige som med alt andet i .NET er det naturligvis muligt at lave sine egne klasser til at håndrere andre former. .NET shippes med support for LINQ to Objects, LINQ to XML, LINQ to SQL, LINQ to DataSet og LINQ to Entities.

LINQ basics.

LINQ kan arbejde med alt der implementerer IEnumerable<T>, da det giver mulighed for at data kan traverseres sekventielt. Klasserne til at behandle sekventielle querys findes i System.Query.Sequence.

Funktioner, som where, from, select, union osv. er defineret som extension methods (se evt artikel om .NET 3.5 og Orcas), med en høj grad af inferens sådan at de kan bruges som udtryk uden at alle typer skal specificeres minutiøst. Inferens er vigtig for kodens læsbarhed da man derved kan skrive "from c in customers" uden at skulle skrive specifikt at c er af typen Customer.

IQueryable<T> arver fra IEnumerable<T> og tilføjer på overfladen blot de to metoder CreateQuery<T> og Execute<T>, men det er dem der gør det muligt at bygge et expression tree ved hjælp af klasserne i System.Query.Queryable og afvikle det som et query imod sine data. Det er derved muligt at lave optimerede og dynamiske querys hvis det skulle være nødvendigt. Det er denne type optimerede querys vi omtaler som LINQ to SQL, LINQ to XML osv. som er rettet imod at lave effektive querys imod specifikke datakilder.

LINQ to Objects.

I forbindelse med querys imod objekter for man mulighed for at finde frem til objekter med bestemte egenskaber på en pænere måde end man traditionelt kan idet man er nød til at skrive loops inden i loops for at gennemløbe hirakisk struktureret data.
Et simpelt eksempel som du kan se her viser syntaksen for hvordan man i en liste af customers kan finde customers der kommer fra en by der starter med bogstavet "O".

IEnumerable<Customer> list = from customer in customers
                                where customer.City.StartsWith("O")
                                select customer;

Men udover de helt banale metoder til at filtrere data er der en række metoder til f.eks. at lave unions, paging, order by, returnere resultatet som List eller Array osv. Her et et eksempel hvor den 3 og 4 customer hvis navn starter med "C" i 2 collections returneres sorteret efter navn som en Liste der bruges som argument til instantieringen af en Collection<Customer>.

Collection <Customer> list = new Collection<Customer>(
                                         (from customer in customers.Union(customers2)
                                          where customer.Name.StartsWith("C")
                                          orderby customer.Name
                                          select customer).Skip(2).Take(2).ToList());


LINQ to XML.

LINQ to XML giver naturligvis de samme muligheder for at skrive querys og udtrække de data man har behov for. Udover det følger der en række klasser med der bruges til at arbejde med XML, og disse kan genkendes ved at de er prefixet med X. Her er et eksempel på hvordan man kan bygge et simpelt xml dokument.


XElement element = new XElement("contacts",
                               new XElement("contact", new XAttribute("name", "Christian")),
                               new XElement("contact", new XAttribute("name", "Mia")));

VB får faktisk mulighed for at gøre dette endnu enklere, idet xml'en kan skrives direkte inline. Herefter vil compileren automatisk generere kode som i ovenstående eksempel.

Når man så enten har bygget eller loadet sit xml fra en fil kan querys laves med følgende syntaks:

var names = from el in element.Descendants("contact")
            where el.Attribute("name").Value.StartsWith("C")
            select el.Attribute("name");

LINQ to SQL || LINQ to Entities || LINQ to Dataset.

LINQ to SQL og LINQ to Entities er til at arbejde med data fra en database, men derudover shippes også LINQ to Dataset til at lave querys imod disconnectede data. LINQ to SQL og Entities fungerer ved at man designer en mapningsstruktur lidt på samme måde som ved brug af strongly typed datasets. Editoren til at gøre dette kan undværes, da der er også er et commandline tool ved navn sqlmetal. Det er muligt ved hjælp af switchen "/sprocs" af få genereret metoder til at bruge stored procedures.

sqlmetal /server:localhost /database:AdventureWorks /user:username
/password:password /sprocs /code:AdventureWorks.cs

En overordnet måde at se denne slags mapning på er:

Database -> DataContext
Table -> Class
Column -> Property
Stored Procedure -> Method

LINQ to Entities bygger på Entities frameworket som er en del af ADO og er et superset af den funktionalitet LINQ to SQL tilbyder. Det er derved muligt at lave en logisk model oven på sin database, og arbejde med arv, mange-til-mange relationer og sammensatte klasser bestående af værdier fra forskellige tabeller i databasen. Alternativt kan man arbejde med LINQ to SQL der er mere simpelt, og så enten binde direkte til datasources, eller lave sin egen mapning af objekter.

Når LINQ kommer kan man altså enten arbejde direkte med data eller bruge en container i form af datasets eller entities.

For at give et indtryk af hvordan man arbejder med databaser er her en række eksempler.

Et typisk eksempel på et query imod et dataset kan så se ud som sådan:

var query = from dataRow in ds.customerTable.AsEnumerable()
            where r.Field<string>("LastName") == "Nielsen"
            select r.Field<string>(“FirstName”);


Ligeledes vil et query der returnerer autogenererede custom entities se sådan ud:

Databases.MyDB db = new Databases.MyDB(connectionString);

var nameList = from c in db.Customers
               join a in db.Adresses
               on c.AdressID equals a.ID
               orderby c.Name
               select new Customer{Name = c.Name};         

     
Sidst men ikke mindst skal man jo også kunne submitte ændringer tilbage til databasen. Dette er så enkelt som det kan være, så jeg vil lade et insert og delete kodeeksempel tale for sig selv.

MyDB db = new MyDB(connection);
  Customer c = new Customer();
  c.Name = "Luffe";
  Customer c2 = new Customer();
  c2.Name = "Ole";
  db.Customers.Add(c);
  db.Customers.Remove(c2);
  db.SubmitChanges();

Som man næsten kan gætte er updates lige så enkle, men for god ordens skyld er her et eksempel.

var customer = db.Customers.Single( c => c.Name == "Bo" );
customer.Name = "Ib";
db.SubmitChanges();

Dynamiske querys.

Til lidt mere advanceret brug er det også muligt at bygge sine querys dynamisk. Det følgende eksempel viser hvordan man eksempelvis kan konstruere et query der finder Customers fra Odense.
IQueryable queryable = customers.ToQueryable();

ParameterExpression parameter = Expression.Parameter(typeof(Customer), "c");

Expression body = Expression.EQ(
    Expression.Property(parameter, typeof(Customer).GetProperty("City")),
        Expression.Constant("Odense"));

LambdaExpression predicate = QueryExpression.Lambda(body, parameter);

Expression where = QueryExpression.Where(queryable.Expression, predicate);

queryable = queryable.CreateQuery(where);

 

Tags: , , , ,

.NET 3.5 og Visual Studio 2008

by DotNetNerd 15. April 2007 16:26

Vistual Studio 2008.

Jeg har kigget på den nye udgave af Visual Studio 2008 der efter planen skal udkomme engang i løber af anden halvdel af 2007. VS2008 shippes med en del features som enhver udvikler har savnet tidligere, samtidig med det er udviklet til at arbejde med den nye .NET 3.5 framework der udgives ved sammen lejlighed.

I kategorien af features som har været savnet kan jeg nævne at VS2008 giver mulighed med at arbejde med alle tidligere udgaver af frameworket tilbage til 2.0, således at man ikke skal have de "gamle" udgaver af Visual Studio installeret blot for at kunne vedligeholde et eksisterende projekt. En ekstra bonus i den sammenhæng er at man derved kan bruge de forbedrede debugging værktøjer når man f.eks arbejder med et .NET 2.0 projekt.

Udover bagudkompatibilitet er der kommet intellisence og debugging funktionalitet til JavaScript (herunder også Ajax). Det er ihvertfald en ting jeg selv har savnet i lang tid, så man kan komme væk fra at debugge via alert statements, og man slipper for at sidde og ærge sig over at man ikke kan huske hvad en member hedder. Til brug sammen med javascript er der udviklet nye xmlkommentar standarder der passer til beskrivelse af ting der er javascript specifikke.

Sidst men ikke mindst kan jeg nævne at der naturligvis også kommer en række værktøjer til at understøtte .NET 3.0 - hvor især et tool ved navn Cider til at arbejde med XAML til Windows Presentation Foundation virker interessant. Samarbejdet med andre applikationer som for eksempel Expression Studio skulle også være noget der etr tænkt meget over, så der er uden tvivl nogle interessante ting i vente der også.

.NET 3.5 - whats new?!.

Det helt store samtale emne omkring .NET 3.5 har været LINQ som står for Language Integrated Query. Idéen med LINQ er at gøre det lettere at arbejde med data - noget som vores egen Anders Hejlsberg har været en af de store bagmænd til. Udover og i forbindelse med udviklingen af LINQ kommer der også en række udvidelser til både C# og VB som nu udkommer i versionerne C# 3.0 og VB 9.

Både C# og VB udvides med extention methods samt object- og collection initializers. Begge sprog får også nogle sprogspecifikke udvidelser, som for C# inkluderer automatic properties og mulighed for implicit typespecifik deklarering.

C# 3.0 og VB 9 udvidelser.

Som sagt udvides begge sprog med det der kaldes extention methods, object- og collection initializers.

Extention methods er en måde hvorpå man kan tilføje metoder til en type, som bedst er illustreret med et lille eksempel. Hvis du idag skal checke om en streng validerer som en gyldig email vil du typisk lave en metode på en anden klasse der kan lave valideringen. Med extention methods kan du istedet føje metoden til string således at du kan skrive:

if (txtEmail.IsValidEmail)
{
   
}

Det eneste det kræver er at du definerer en statisk klasse, med din metode der tager typen der skal udvides som parameter og bruger keywordet "this" foran.

public static class MyExtensions
{
    public static bool IsValidEmail(this string s)
    {
        Regex regex = new Regex(@"^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$");
        return regex.IsMatch(s);
    }
}


Når der er gjort kan du blot importere klassen ved hjælp af en using statement og så er metoden tilgængelig på typen string.

using MyExtentions;

Object initializers er en mere elegant måde at initialisere et objekt på, således at instantiering og tildeling af værdier til properties gøres mere kompakt. Igen er det nok bedst illustreret ved et kort eksempel

Customer customer = new Customer { Name = "Allan", City = "Nyborg" };

På samme vis giver collection initializers mulighed for at skabe collections og fylde dem på en mere kompakt måde.

List<Customer> customers = new List<Customer>()
{
    new Customer{Name = "Allan", City = "Nyborg"},
    new Customer{Name = "Lena", City = "Hundslev"}
};


Sprogspecifikke udvidelser.

Som tidligere nævnt er der også kommet nogle sprogspecifikke udvidelser til både C# og VB. Til C# er der tale om automatic properties og så et nyt keyword "var".

Automatic properties går at man kan deklarere properties uden at skulle have en underliggende private member til at holde værdien. Naturligvis skal der stadig være noget til at holde værdien, men dette er nu håndterert af compileren så det er muligt at deklarere en property blot ved at skrive:

public string Name
{
    get;
    set;
}


Var keywordet er en udvidelse der grunder i at LINQ syntaksen skal have mulighed for at man implicit kan lave en variabel der alligevel har den rigtige type og ikke blot er af typen object.

var customer = new Customer { Name = "Allan", City = "Nyborg" };

Kort fortalt vil det sige at ovenstående "customer" handle har fat i et objekt af typen Customer, de typen udledes automatisk.

VB får den samme mulighed, men dog uden et nyt keyword da sproget allerede idag har keywordet Dim - objekter bliver frem over bare instantieret med den rigtige tyåe frem for som object selvom man ikke skriver "As Customer".

En anden smart feature der kommer til VB er muligheden for at arbejde med XML inline i koden, således at man kan skrive:

XElement element = <customer>
                                  <name>Christian</name>
                                  <city>Odense</city>
                              </customer>

LINQ - en ny måde at arbejde med data.

Det absolut mest omtalte emne i forbindelse med .NET 3.5 har været LINQ. Da dette er et større emne gemmer jeg det til en seperat artikel, men jeg kan dog lige nævne kort at LINQ er en ny måde at arbejde med data på hvad enten det skal hentes fra en database, et xml dokument eller fra nogle objekter i memory.

Idéen er at lave en fælles syntaks til at definere funktioner der kan bearbejde data på en måde der i nogen grad minder om SQL. Derved slipper man for at skulle arbejde på en forskellig måde alt efter om man arbejder med en sql, xml eller hvad det nu kan være, og samtidig arbejder man med typestærke og extensible objekter - frem for querys der er definerede som strenge.

Tags: , , ,

C# til VB - lidt hjælp til konvertitter

by DotNetNerd 10. March 2007 16:11

C# til VB - lidt hjælp til konvertitter

Da jeg skal til at arbejde med VB har jeg brugt lidt tid på at bore i forskellene imellem C# og VB. Det har vist sig at der er en række interessante forskelle, så jeg har derfor valgt at skrive denne artikel således at andre der skal til at skifte sprog eller eventuelt bare skal vælge sprog i det hele taget kan lade sig inspirere af mine opdagelser. Jeg forgiver på ingen måde at dette er et komplet opslagsværk over forskelle (da der allerede findes den slags lister), da det udelukkende er de ting jeg personligt mener man skal vide for at komme igang krydret med nogle af de ting jeg finder interessante.

De største forskelle imellem de to sprog ligger i syntaksen, men der er også enkelte features som er specifikke for de enkelte sprog - og så er der forskel på hvad visual studio har af funktionalitet til at støtte op om sprogene.

Struktur og syntaks forskelle

Det første man får øje på når man sammeligner sprogene er hvordan scope angives,og hvordan man afslutter en linie. C# benytter tuborgklammer til at angive scope, hvorimod der i VB er et reserveret ord (eller to om man vil) der angiver at en given konstruktion slutter. Afslutning af en linie i C# sker ved at man skriver et semikolon, og i VB skifter man bare linie så det er end of line karakteren der egentlig afslutter linien. Gode eksempler til at illustrere dette kan være at se på en enkel forgrening eller løkke konstruktion.


C#

if (myVar == 1 && yourVar == 2)
{
    //Do stuff
}

VB

If (myVar = 1 AndAlso yourVar = 2) Then
    'Do stuff
End If

Eksemplet illustrerer forskellen i angivelse af scope med henholdsvis klammer og et End nøgleord. Derudover kan man se at operatorer også er forskellige idet C# bruger en mere matematisk notationsform, hvor VB nærmere ligner engelsk.

C#

for (int index = 0; index<collection.Count; index++)
{
    //Do stuff
}

VB

For index As Integer = 0 To 8 Step 2
    'Do stuff
Next index

Forløkken adskiller sig mere rent rent syntaktisk, da det igen er tydeligt at notationsformen i C# er mere matematisk hvor VB søger at ligne engelsk. En detalje der er værd at bemærke er at incrementering og decrementering i C# kan gøres ved hjælp af shorthand notation som ++ og -- hvilket ikke findes i VB. Andre syntaktiske forskelle er instantiering af objekter, brug af indexere og generics. Disse ting er hurtigst og bedst illustreret ved et lille eksempel.

C#

string navn = "";
myHashTable["Hans"]
List myList;

VB

Dim navn AS string = ""
myHashTable("Hans")
myList as List(Of Integer)

Parameter passing er endnu et sted hvor sprogene adskiller sig markant idet C# passer simple typer by value og komplekse typer by reference. I VB er alt derimod passed by reference med mindre man angiver det med nøgleordet ByVal.

Og en sidste lille detalje til det her afsnit er conditional statements hvor man i C# laver en ?: konstruktion findes der i VB en IF funktion.

C#

int i = obj==null?0:(int)obj;

VB

Dim i As Integer = IF(obj IS Nothing, 0, DirectCast(obj, Integer))
Klasse og metode signaturer

Omkring signaturer er den mest markante forskel ved angivelse af returntypes, men der er også enkelte modifiers der hedder noget forskelligt. I C# angiver man hvad de returnerer og void hvis de ikke returnerer noget. I VB derimod angiver man en Sub hvis der ikke returneres noget og ellers angives en Function.
Metode der ikke returnerer noget:

C#

void DoStuff()
{
    //Do stuff
}

VB

Sub()
    'Do stuff
End Sub

Metode der returnerer en string:

C#

string DoStuff()
{
    return "Hello world";
}

VB

Function() As String
    return "Hello world"
End Function

Derudover består forskellen i navngivningen, idet ting der kun er synlige indenfor et assembly angives med henholdsvis internal og Friend, metoder der ikke kræver en instans af et objekt hedder static og Shared og man gemmer metoder med henholdsvis new og Shadow.

Type håndtering

En markant underliggende forskel på de to sprog ligger i håndteringen af typer. C# er meget stringent og kræver præcis angivelse af casts, hvorimod VB automatisk håndterer simpel typekonvertering. Syntaktisk foregår eksplicit casting heller ikke helt på samme måde som det fremgår af følgende eksempel.

C#

string s = (string)hash("Hans");

VB

Dim s As String = DirectCast(hash("Hans"), String)

Alternativt kan man i VB også bruge funktionerne CType der ligeledes kan bruges til casting eller en række specifikke funktioner som CInt, CDbl osv. Forskellen er at DirectCast egentlig er til casting og CType er til konverteringer.

Sprogspecifikke features

C# kan en større ting som ikke er muligt i VB og det er at afvikle unsafe kode. Derudover findes stiller Visual Studio nogle flere værktøjer til rådighed i forbindelse med udvikling. Der findes er menupunkt i VS som hjælper med refactoring af kode, og codesnippet delen er bygget samme med autocompletion i større grad end den er til VB.

VB tilgengæld har nogle sproglige ting der er med til at gøre koden mere læsevenlig og så er flere funktioner tilgængelige enten direkte eller via namespacet my. Jeg vil ikke gå nærmere ind i at liste disse funktioner men kan nævnet at f.eks matematiske udtryk kan bruges direkte som f.eks Rnd istedet for Math.Round

With nøgleordet giver mulighed for at sætte flere attributter på samme objekt uden at gentage handle navnet på følgende måde.

C#

kunde.Navn = "Per Petersen";
kunde.Adresse = "Odensevej 321";
kunde.Telefon = "12345678";

VB

With kunde
    .Navn = "Per Petersen"
    .Adresse = "Odensevej 321"
    .Telefon = "12345678"  
End With
Event håndtering

Som en afsluttende detalje vil jeg nævne eventhåndtering, som kan gøres på samme måde - hvis man ser bort fra syntaks. VB giver dog mulighed for at man kan erklære at en metode skal håndtere et bestemt event frem for at man skal skrive det i kode. Først lad os se et basalt eksempel på eventhåndtering.
C#

public delegate void AlarmEventHandler(object sender, AlarmEventArgs e);
public event AlarmEventHandler Alarm;  

...

if (chocolateInStock < 10)
{
    AlarmEventArgs e;
    ...
    Alarm(this, e);
}

VB

Public Delegate Sub AlarmEventHandler(sender As Object, e As AlarmEventArgs)
Public Event Alarm As AlarmEventHandler
...
If (chocolateInStock < 10) Then
    Dim e as AlarmEventArgs
    ...
    RaiseEvent Alarm(Me, e)
End If

Som det fremgår virker tilføjelsen af en event handler meget ens på de to sprog. VB tillader imidlertid at man gør følgende som ikke er muligt i C#.

    Private WithEvents chokoManager As ChokoladeManager
    Sub Handler1(e As AlarmEventArgs) Handles chokoManager.Alarm
        '*** handler method implementation
    End Sub

Alternativet som ses her er dog muligt i begge sprog.

C#

clock.Alarm += new AlarmEventHandler(OnAlarmRang);

VB

AddHandler clock.Alarm, AddressOf OnAlarmRang

Konklusion

Bevæbnet til tænderne med denne artikel og med google klar i baghånden skulle det være en form sag at komme igang med at skifte imellem C# og VB. Det håber jeg ihvertfald da jeg snart selv skal til og igang med det for alvor.

Jeg vil lade helt værd med at gå ind i en diskussion om hvilket sprog der er "bedst" for det vil altid være enormt subjektivt, og i sidste ende er det også irrelevant. Det interessante er bare at se hvad de to sprog tilbyder og så glæder jeg mig personligt til at stå i en situation hvor jeg kan hoppe imellem de to sprog uden nærmere omtanke.

Tags: , ,

Who am I?

My name is Christian Holm Diget, and I work as an independent consultant, in Denmark, where I write code, give advice on architecture and help with training. On the side I get to do a bit of speaking and help with miscellaneous community events.

Some of my primary focus areas are code quality, programming languages and using new technologies to provide value.

Microsoft Certified Professional Developer

Microsoft Most Valuable Professional

Month List