F# funktionelle typer og objekt-orienteret programmering

by DotNetNerd 27. January 2008 17:51

F# funktionelt eller objektorienteret?

En stor del af dem der har hørt om F# har hørt at det er et funktionelt programmeringssprog, hvilket er sandt nok - det er bare ikke udelukkende funktionelt. Faktisk understøtter sproget i lige så høj grad at man kan programmere objekt orienteret med imperative teknikker. Det er faktisk netop derfor F# er brugbart i ”den virkelige verden” i modsætning til eksempelvis Haskell der stort set udelukkende benyttes til undervisning i praksis.
Jeg vil i dag kigge på forskellige teknikker til at arbejde med typer, først med udgangspunkt i funktionel programmering og sidenhen med fokus på objektorientering.

Simple typer til funktionel programmering

I funktionel programmering arbejder man meget med objekter på to måder, nemlig med records og discriminated unions. Records svarer til det man ellers kalder en entitet, som er et helt simpelt databærende objekt.

type Employee =
  { Name: string;
    HiredDate: System.DateTime; }

Som så kan bruges således, hvor type udledes via ducktyping - if it walks like a duck and it talks like a duck, its probably a duck.

let employee1 =
  { Name = "Christian";
    HiredDate = System.DateTime.Parse("11/07/2003") }

Eller således.

let employee2 =
  { new Employee
    with Name = "Hans"
    and HiredDate = System.DateTime.Parse("11/07/2003") }

En smart feature der skal nævnes I den forbindelse er at det er nemt at klone records, så hvis man skal lave en ny record der næsten er magen til en anden kan man eksempelvis gøre det sådan her.

let employee1Cloned = { employee1 with HiredDate = System.DateTime.Parse("15/10/2005") }

Discriminated Unions bruges til at modellere et endegyldigt og lukket sæt af muligheder der ikke kan ændres. Man arbejder typisk med med discriminated unions ved først at definere nogle type forkortelser, som ganske enkelt består i at man giver en eksisterende type et alias.

type Route = int
type Make = string
type Model = string

Derefter kan man så bygge sit discriminated union ved hjælp af forkortelserne sådan her.

type transport =
  | Car of Make * Model
  | Bicycle
  | Bus of Route

Som man forhåbentlig kan se ud af det her er det en meget præcis måde at modellere del slags data på. Ud fra ovenstående kan man nu arbejde med typerne sådan her.

let myMeansOfTransportation = [ Car("Skoda", "Fabia"); Bicycle; Bus 128 ]

Det har muligvis allerede slået dig at den her måde at modellere på minder om ”enumerations på speed”, og rent faktisk opbygges enums i F# sådan her.

[<Struct>]
type Shape =
  | Circle = 1
  | Rectangle = 2
 

Objektorienteret programmering

Den vigtigste egenskab ved objekter er at de indkapsler mutable værdier, og bruges dermed til at indkapsle tilstand - således at omdrejningspunktet for koden er tilstands checks og tilstands ændringer.
Som jeg skrev i sidste post er værdier som udgangspunkt immutable i F#, hvilket er en stor styrke idet man derved ligger op til at tænke i at skrive kode der ikke afhænger af tilstandsstyring hvilket hurtigt kan blive rodet og svært at vedligeholde. Det kan imidlertid være nødvendigt med mutable værdier, og i nogen tilfælde er det med til omvendt at gøre kode lettere at læse og mere effektivt.
Helt enkelt gøres en værdi mutabel ved hjælp af nøgleordet ”mutable” og den kan derefter modificeres ved hjælp af operatoren <- på samme måde som man kender der fra VB og C# med = tegnet.

let mutable n = 0
n <- 6
printfn "%d" n

En anden velkendt teknik er at anvende mutable reference celler ved hjælp af nøgleordet ref og operatorerne := til at sætte en værdi og ! til at hente den, som du kan se brugt her.

let n = ref 6
n := 7
printfn “%d” !n

Andre uundværlige teknikker fra objektorienteret programmering er boxing/unboxing af værdityper og upcasting/downcasting imellem typer. Boxing af en type vil sige at wrappe dem til objekter, således at de kan gives til metoder der tager typen object.

let o = box 6
let i = unbox<int> o  // kan også skrives som let i = unbox o : int

Upcasting bruges til at caste en type til en type den arver fra.

let stringObject = ("qwerty" :> obj)

Downcasting bruges til at caste en variable til en mere nedarvettype som den er en instans af.

let myString = stringObject  :?> string
 

Members - objektets ansigt udadtil

Som tidligere nævnt er indkapsling en nøgleegenskab i objektorienteret programmering. Ligesom i andre .NET sprog  kan værdier pyntes med modifieren private hvis de ikke skal være tilgængelige udefra. Derudover kan man naturligvis lave members der stilles til rådighed for andre som vi nu skal se nærmere på.
Mutable properties er nok den mest anvendte membertype, idet de helt enkelt bruges til at stille data til rådighed. En property ser sådan her ud i F#.

let mutable text = ""
member t.Text
  with get() =
    text
  and set v =
    text <- v

Indexers bruges på samme måde, men bruges til at tilgå data som en collection af data der tilgås via en eller flere indeks parametre.

let elems = [| "A"; "B" |]
member t.Item
  with get(id) = elems.[id]
 

Argumenter der holder

I F# kan man benytte sig af teknikker som optionelle- og navngivne argumenter. Optionelle argumenter angives ved hjælp af ? hvilket gør at værdien bliver en option og den kan altså dermed være ingenting.
defaultArg funktionen bruges til at trække værdien ud eller alternative en default værdi hvis optionen er None.

type LabelInfo(?text:string) =
  let text = defaultArg text ""

Named arguments er en anden teknik der relaterer sig til at arbejde med argumenter. Ved at bruge named arguments angiver man værdien for et argument præcist frem for ud fra argumentets placering. En smart feature i den forbindelse er at man kan angive argumenter som der ikke nødvendigvis er en constructor til så længe typen har en public property til den angivne parameter.

let form = new Form(Visible=true, TopMost=true)
 

Implementation af interfaces

Nu er det vidst ved at være tid til at se på hvordan man implementerer en klasse som vi kender den fra VB eller C#. For at starte fra bunden ses her hvordan man deklarerer et interface.

type IShape =
  abstract Contains : Point -> bool

Ovenstående interface kan derefter implementers af en funktion på følgende måde.

let Circle() =
  { new IShape with
    member x.Contains(p:Point) = true
  }

Teknikken der er anvendt her kaldes object expressions og det er en simpel teknik der anvendes ofte når man implementerer interfaces eller extender typer.
Definitionen af en object expression er sådan her.

{ new Type optionalle-argumenter with
            Member-definitioner
  Optionalle-ekstra-interface-definitioner }

En implementation af et interface på en konkret type som man nok oftere vil bruge den kan se sådan her ud.

type MutableCircle() =
  let mutable center = Point(x=0, y=0)
  member c.Center with get() = center and set(v) = center <- v
  interface IShape with
    member c.Contains(p:Point) = true
 

Brug af delvist konstruerede typer

Delvist konstruerede typer er typer der har abstrakte members, som skal implementeres af en specialiseret type. Alternativt kan man angive en default implementation som kan overskrives af den type der nedarver.

type Formatter() =
  abstract Format : string -> string
  abstract ReplaceWhitespace : string -> string
  default x.ReplaceWhitespace(s:string) = s.Replace(" ", s)

En sådan klasse kan implementers ved hjælp af object expressions.

let myFormatter =
  { new Formatter() with
    member f.Format(s) = s }

Eller den kan ligeledes implementeres via implementation inheritence.

type HtmlFormatter() =
  inherit Formatter()
  default f.Format(s) = s

I forbindelse med implementation af en given member kan nøgleordene override og default bruges til at indikere om man overskriver en metode der har en implementation i forvejen eller ej.

Statiske metoder og extension methods

Statiske metoder i F# implementeres ikke på en klassen, men på et module. Ønsker man at en statisk metode skal ligge på en klasse (eller dele navn med den om man vil) skal modulet dekoreres med en CompilationRepresentation attribute.

[<CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix)>]
module MyOperations =
  let MyStaticMethod(i:int, i2:int) = "Taller er " + (i + i2).ToString()

En relateret teknik er deklerationen af extension methods som også for nylig er kommet med i VB og C#.
En extension method i F# deklareres som en del af modulet med følgende syntaks.

type System.Int32 with
    member i.Add(i2:int) = MyStaticMethod(i, i2)

For at bruge extension metoden skal man derefter blot åbne modulet, på samme måde som i VB.

open MyOperations

let i = 1
let s = i.Add(12)
 

Afrunding - muligheder for fremtiden

Jeg håber at have illustreret med ovenstående gennemgang at man kan bruge F# til at arbejde fuldt ud objekt-orienteret, på holde med det man kan i andre .NET sprog. Netop det at man kan mix and matche imellem paradigmer er efte rmin mening den helt store styrke ved F#. Jeg er selv kun ved at komme igang med sproget og kaster mig i næste uge ud i at lave mit første modul i sproget, som bliver et stregkodeparser til mig FLOwer Tracker system. Jeg håber det har givet andre lyst til at prøve kræfter med sproget som jeg nok ender med at skrive meget mere om med tiden efterhånden som det vinder frem og bliver kørt ind i .NET frameworket fuldt ud som planen er fra Microsofts side.

 

Tags:

F# igang med funktionel programmering

by DotNetNerd 29. December 2007 18:46

F# - brugbar funktionel programmering

For en god uges tid siden dumpede bogen "Expert F#" endelig ind af min brævsprække - noget jeg har glædet mig til i et stykke tid, da jeg har hørt mange spændende ting om sproget. Inspireret af at jeg engang læste at man bør lærer sig et nyt programmeringssprog hvert år synes jeg det var det perfekte næste skridt for mig. Jeg har absolut ingen erfaring med funktionel programmering udover det jeg har rodet med LINQ - hvilket ikke er helt "rigtig" funktionel programmering men rettere funktionel funktionalitet indarbejdet i et Objekt Orienteret sprog.

Idet jeg næppe er den eneste der ikke har rodet med funktionel programmering, da det hovedsageligt har hørt hjemme på universiteterne hidtil, vil jeg skrive en smugle om nogen af de basale ting i funktionel programmering og F#. Hvis du tænker "hvis det hvedsageligt hører hjemme på uni, hvad kan jeg så bruge det til i praksis", så er pointen netop at F# udover at være et funktionelt sprog, også giver mulighed for at tilgå .NET frameworkets mange herligheder, og arbejde imperativt hvis man vil. Jeg tror F# har gode muligheder for at vinde frem netop
af den årsag, da det tilbyder fantastisk ekspressivitet sammen med adgang til de ting vi bruger .NET frameworket til allerede.

Lidt basics

Lidt ligesom der i VB findes option strict og explicit er der i F# "#light", som aktiverer det der kaldes light syntaks. Med andre ord slipper man for at skrive en masse ting og afslute linier manuelt osv. Eller kort sagt, bare skriv det øverst i din kode!

Kommentarer i F# minder langt hen ad vejen om den måde man skriver dem på i C#, med undtagelse af flerlinies kommentarer.

// Her er en enlinies kommentar.

(* Her står der en
flerlinies kommentar *)

///<summary>Xml kommentarer ligner også sig selv som i C#</summary>

Lidt ligesom man har Dim, var o. lign. ord der deklarerer en variabel har man i F# ordet "let" der definerer starten på en funktion. For at illustrere og vise hvordan scope fungerer er her et eksempel:

let powerOfTwo number
  let result = number * number
  result

Her definerer man en funktion der hedder powerOfTwo, som tager et argument "number". Funktionen består af en lokal funktion der er tallet ganget med sig selv. Man kan også se at inferens af typer bruges i meget vid udstrækning og at indrykning bruges til at afgrænse scope ligesom i (Iron)Python.

For at undgå at skulle ud i den helt langt gennemgang af simple typer og operatorer vil jeg nøjes med som udgangspunkt at sige at de langt hen ad vejen er de samme som i C# og ellers er der masser af steder man kan finde oversigter over dem via vores allesammens bedste ven google.

Det er vigtigt at vide at operatorer er unchecked. Operationer der giver overflow kaster altså ikke exceptions men de wrappes istedet. En anden ting der er vigtig at forstå er at de fleste typer er immutable med mindre det explicit angives at de skal være mutable. Det giver en række fordele, da man derved kan være sikker på at en værdi ikke ændres "bag om rykken" på en. Noget der især er rart når man arbejder trådet da man så ikke skal bekymre sig så meget om "concurrent access".

Grundlæggende data strukturer

Fleksible datastrukturer er en af hovedingredienserne i et funktionelt sprog, da de gør det muligt at skrive meget ekspressiv kode til at arbejde med mængder af data.

Tuple:

Tupler bruges til at returnerer flere værdier fra en funktion, som ikke nødvendigvis er af samme type. En tupel angives helt enkelt med en parentes hvori værdierne adskilles af en komma. En funktion der tæller ord kunne for eksempel returnere, totalt antal ord og antallet af unikke ord.

let wordCount = (10, 8)

fst og snd er keywords der bruges til at anvende det første og andet element i en touple - og dermed til at arbejde med par.

let totalWordCount = fst wordCount

En anden mulighed er at decompose tuplen i flere værdier.

let totalWordCount, totalUniqueWordCount = wordCount

Array:

Arrays kender de fleste nok ganske godt så dem vil jeg gå lidt let hen over. En vigtig ting at nævne er dog at arrays er den eneste af de collection strukturer jeg skriver om her der er mutable. Et array i F# defineres således:

let myArray = [|1;2;3;4;5|]

En anden funktion der er værd at nævne er at man kan definere et litteral byte array som:
let myArray = "MAGIC"B // Det svarer til [|77uy; 65uy; 71uy; 73uy, 67uy|]

Array slicing som ellers er en ting man godt kan savne lidt i andre .NET sprog kan desuden laves således.

let slicedArray = myArray.[1 .. 3]

List:

List i F# er en implementation af en singularly linked list som er en struktur der læses hurtigt hvis man læser elementerne igennem fra
start til slut.
 
let oneHalfOfTheFamily = ["Claus";"Lena";"AC"]
let anotherHalfOfTheFamily = ["Allan";"Janni";"Peter";"Jacob"]
let bothHalvesOfTheFamily = oneHalfOfTheFamily @ anotherHalfOfTheFamily

Her set man to lister blive oprettet og smeltet sammen. Det er vigtigt at huske at lister er immutable, så efter dette er sket er værdierne af de to første lister uændrede.

Set:

Sets indeholder ordnede lister hvor der kun kan være 1 af hver værdi, og sets er case sensitive. Et eksempel illustrerer meget godt hvordan den bruges.

let myset = Set.of_list ["BAC";"ABC";"ABC"]

Vil give en Set<string> med indholdet ["ABC";"BAC"]

Sets kan laves til lister eller laves fra lister ved hjælp af to_list og of_list

Option:

Options bruges til at beskrive en struktur der enten har en værdi eller er None. Keywordet her er "Some" og kan bruges således.

let childAndParents = [ ("Jens", Some("Ib", "Hanne"));"Jonas", None]

Options har funktioner som get der returnerer værdien af en some option og is_option der returnerer true for en some option.

Sequence:

Sequences er en abstraktion over datastrukturer der kan læses sekventielt og er alt der implementerer IEnumerable, som man kender det fra andre .NET sprog. I F# har sekvenser deres eget keyword som er seq. Det vil sige alle typer der er IEnumerable kan arbejdes med som sekvenser, det inkluderer arrays, lists osv. En smart F# feature er at man kan beskrive ranges af værdier i sekvenser på følgende måde.

let myNumbers = seq {0 .. 5}

Som vil give en sequence af værdier fra 0 til 5. Denne liste er lazy således at værdierne ikke genereres før de skal bruges. Dette er meget vigtigt hvis man skal arbejde med rigtigt lange sekvenser. Man kan desuden definere et interval i sevensen således.

let myNumbers = seq {1 .. 2 .. 6}

Som vil give værdierne [1;3;5]

Eller man kan definere en sequence ud fra f.eks. et for expression:

let powersOfTwo = seq { for i in 0 .. 10 -> i*i }

List og Seq indeholder desuden en række funktioner som eksempelvis map, iter, filter, to_array og of_array. De først nævnte gør det nemt at arbejde med strukturerne ved at passe en funktion ind.

powersOfTwo.iter(fun number -> Printf.printf "Number = %s\n" (number)

Functionelle værdier

En anden vigtig bestantdel i et funktionelt sprog er naturligvis muligheden for at arbejde med funktionerne. En række metoder som for eksempel map og iterate der findes på blandt andet List og Sequence tager imod en funktion som argument. Map bruges til at omforme alle elementer i strukturen og iterate bruges til at iterere strukturen og udføre funktionen på hvert element. En funktion kan som shorthand skrives som:

let namesWithH = Names.Filter(fun name -> name.StartsWith("J"))

Man har desuden mulighed for at "pipe" funktioner, sådan at en funktion kaldes med retur værdien fra den forrige funktion. Piping er en god
måde at gøre kode mere let læseligt på da det kan læses fra højre mod venstre og ikke indefra ud som man ellers tit ender med at skulle.


let PrintNamesWith prefix =
  NamesWith(prefix) |> List.iter(fun name -> Printf.printf "Name = %s\n" (name))


Man kan også arbejde med sammensatte funktioner og partiel applikation. Disse to teknikker viser hvor fleksibelt sproget er når man arbejder med funktioner.

let getNames s = String.split [';'] s
let countNames = getNames >> List.filter(fun (name:string) -> name.StartsWith("M")) >> List.length
let NameCount = countNames "Mia;Christian;Mig"

Eksmeplet viser hvordan man givet en funktion "getNames", kan definere en ny funktion countNames, som er en sammensætning af denne, List.filter og List.length - som derved kan bruges til at tælle antallet af navne i en semikolon separeret streng. F# har en meget powefull engine til at sammensætte effektive funktioner på denne måde.


let shift (x, y) (x1, y1) = (x1+x, y1+y)
shiftright = shift(1,0)

Dette eksempel viser helt enkelt at man kan definere en ny funktion ved at kalde en anden funktion med kun nogen af de argumenter den kræver. Dette giver mulighed for at være meget beskrivende i sin kode ved at lave en række partielle funktioner der løser specifikke opgaver på baggrund af en mere generel funktion.

Konklusion

Jeg håber ovenstående givet et godt indblik i nogle af de mest basale strukturer og teknikker der anvendes i F# i forbindelse med funktionel programmering. Som tidligere nævnt er F# ikke et "rent" funktionelt sprog så man kan også arbejde med imperativ programmering i sproget. Det kan være det bliver et emne jeg tager op senere. Selvom jeg er ny indenfor funktionel programmering synes jeg hurtigt man får et indtryk af hvor ekspressiv kode man kan skrive og at det er vejen frem især hvis man skal kode en applikation til et trådet miljø.

Tags:

Opsætning af IIS 7.0 og Windows Communication Foundation i Vista

by DotNetNerd 16. December 2007 14:50

Jeg opdagede idag at det er knap så "lige ud af boksen" som man kunne ønske sig at afvikle ASP.NET og Windows Communication Foundation services under Vista og IIS 7. Man kan kun undre sig over at ihvertfald ASP.NET ikke automatisk installeres ved installation af IIS, men det er åbenbart ikke tilfælder. Det lykkedes dog efter at have rodet lidt at få det til at virke og jeg vil derfor lige skrive en guide hvis andre skulle støde på samme problem. 

Installation af IIS og ASP.NET på Windows Vista

For at kunne afvikle en ASP.NET applikation skal man først installere IIS og den tilhørende ASP.NET komponent. Dette kræver administrator rettigheder og gøres ellers ved at:

  • Gå til kontrolpanel -> Programmer og funktioner.
  • Herfra vælger du "Slå windows funktioner til eller fra" i sidemenuen til venstre i vinduet.
  • Vælg internet Information Service og udvid punktet.
  • Udvid "værktøjer til webmanagement" og "kompatibilitet med IIS 6 management".
  • Vælg "Kompatibilitet med IIS-metabase og IIS 6-konfiguration".
  • Udvid også "World wide web services" og Funktioner til programudvikling.
  • Vælg ASP.NET checkboksen.

Du kan nu afvikle ASP.NET applikationer ved at oprette dem på almindeligvis via IIS management værktøjet - eller bruge default sitet ved at smide det i inetpub/wwwroot.

Installation af WCF service modellen

For at kunne afvikle en Windows Communication Foundation service skal service modellen registreres.

Dette gøres ved at:

  • Åben en kommandoprompt med admin rettigheder.
  • Gå til %Windows%\Microsoft.Net\Framework\v3.0\Windows Communication Foundation\
  • Check status ved at skrive ServiceModelReg -vi og se om service modellen allerede er installeret.
  • Er den ikke installeret skriv ServiceModelReg -i og så skulle modellen blive installeret.

Og nu kan du så også afvikle en Windows Communication Service.

Lige for en god ordens skyld skal det siges at for at hoste en WCF service under IIS 7 skal man blot smide en svc fil med følgende indhold i roden af sitet.

<%@ ServiceHost Language="C#" Debug="true" Service="Syndication.FeedService" %>

Hvor servicen skal pege på klassen der implementerer din service. Dll'en skal ellers bare ligge i en bin mappe som den plejer.

Tags: , ,

Silverlight del 3 - ready, set, draw

by DotNetNerd 2. December 2007 16:56

I sidste blogindlæg om silverlight nåede jeg lige kort at berøre Path elementet, som har en Path.Data collection bestående af Geometry elementer som tegnes.

Der findes 4 basale typer geometry elementer som er:

  • RectangleGeometry
  • EllipseGeometry
  • LineGeometry
  • PathGeometry

De 3 første fungerer på samme måde som de tilsvarende elemeter jeg gennemgik i sidste blogindlæg, og RectangleGeometry blev ligeledes berørt i eksemplet på at bruge Path.

PathGeometry

PathGeometry er et meget komplekst element der kan bestå af en række segmenter, som findes i 7 forskellige typer:

  • LineSegment
  • PolyLineSegment
  • ArcSegment
  • BezierSegment
  • PolyBezierSegment
  • QuadraticBezierSegment
  • PolyQuadraticBezierSegment

Disse elementer kan tilføjes til et PathFigure element - som der så igen kan være flere af i et PathGeometry element.
PathFigure har en StartPoint property som beskriver hvor der tegnes fra og derefter tegnes de tilføjede segmenter efter hinanden. Som altid siger et eksempel mere end 1000 ord. 


<Canvas Name="Canvas1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Width="640" Height="480">
 <Path Name="Car" Stroke="Green" Canvas.Left="100" Canvas.Top="100" StrokeThickness="1">
  <Path.Data>
   <PathGeometry>
    <PathFigure StartPoint="50,50">
     <LineSegment Point="100,100" />
     <ArcSegment Point="150,150" Size="20,20" />
     <BezierSegment Point1="250,300" Point2="200,50" Point3="0,0" />
     <ArcSegment Point="50,50" Size="20,20" />
    </PathFigure>
   </PathGeometry>
  </Path.Data>
 </Path>
</Canvas>

Som det fremgår kan man tegne virkeligt komplekse ting med meget enkle midler. 

Geometri elementer som strenge

En smart feature er at Geometries kan beskrives som strenge, hvilket tillader en væsentligt mere kompakt syntaks. Læsevenligheden kan man tilgengæld godt stille spørgsmålstegn ved, men det kan være det bliver lettere når man bliver lidt mere rutineret.

Jeg vil undgå at gennemgå alle kommandoer, da der alligevel allerede findes omfattende sites hvor man kan slå den slags ting op, men vil da give et eksempel på nogen af de mest brugte ved at tegne den ovenstående figur igen ved hjælp af en kommandostreng.

M angiver StartPoint og er derved altid angivet. Ved at slutte med Z kan man iøvrigt sørge for at figuren lukkes - dette svarer til at sætte propertien IsClosed="True" på PathFigure.
L angiver at der skal tegnes en linie hen til et punkt.
A angiver at der skal tegnes en arc ud fra formlen "A radiusX radiusY grader IsLargeArc Clockwise x,y".
C angiver at der skal tegnes et beziersegment ud fra formlen "C punkt1X,punkt1Y punkt2X,punkt2Y x,y".

Alle kommandoer kan desuden skrives som lowercase hvilket bevirker at de fortolkes relativt til nuværende punkt frem for som absolute koordinater.

For at tegne den figur vi havde tidligere kan vi derfor gøre følgende.

<Path Name="Car" Stroke="Green" Canvas.Left="100" Canvas.Top="100"
 Data="M 50,50 L 100,100 A 20,20 0 0 0 150,150 C 250,300 200,50 0,0 A 20,20 0 0 0 50,50" StrokeThickness="1" />

Dette virker nok knap så intuitivt at læse, men da man nok oftest vil arbejde med et værktøj som expression blend er det smart at have en kompakt syntaks, da man derved også begrænser hvor meget ens XAML skal fylde.

Clipping ved hjælp af Geometries

Geometries kan ikke kun bruges til at tegne elementer, men også til at beskære andre elementer.  Dette gøres ved at sætte et hvilket som helst elements Clip element til et Geometry element. For eksempel kan en Ellipse beskæres af en anden ellipse således.

<Ellipse Width="250" Height="150" Fill="Orange" Stroke="Black" StrokeThickness="3">
  <Ellipse.Clip>
   <EllipseGeometry RadiusX="150" RadiusY="200" />
  </Ellipse.Clip>
 </Ellipse>

Dette giver især spændende muligheder hvis man f.eks arbejder med billeder eller video, hvor det også er muligt at benytte denne teknik. Derved kan man lade en film afspille i hvilken som helst form man kan tænke sig, eller man kan lade alle billeder på ens side blive beskåret ens så der er endnu en måde at lave et custom design på.

Tags:

Silverlight del 2 - back to the drawingboard.

by DotNetNerd 24. November 2007 10:25

Basal viden om grafiske elementer i Silverlight 

I Silverlight del 1 snuste jeg en lille smugle til det at lave en brugerflade i XAML, og kom igennem det mest basale med at man skal have et canvas som rod element og jeg viste hvordan man kan indsætte en TextBlock. Nu vil jeg så gå videre og se på en række andre elementer man kan bruge til at tegne brugergrænseflader med.

Faktisk kan man bruge TextBlocken fra sidst som udgangspunkt, da den viser nogle af de grundlæggende properties som findes på alle grafiske elementer: Name, Canvas.Left og Canvas.Top. Disse properties vil man sandsynligvis sætte på et hvert element sådan at det har et navn man kan bruge til at finde elementet programmatisk, og sådan at man kan styre hvor elementer placeres. Hvis man skal finde et element ud fra dets navn er der en funktion som kan kaldes fra alle grafiske elementer der hedder FindName("kontrolnavn") f.eks. kan man finde en kontrol med navnet mySquare således.

var myControl = rootElement.FindName("mySquare");

Basale former 

Der findes 3 helt basale former man kan tegne i silverlight som er:

  • Rectangle
  • Ellipse
  • Polygon

Rectangle 

Rectangle er en af de mest brugte former, da den giver en meget enkel måde at tegne rektangler på. Det er faktisk så enkelt at jeg vil lade et eksempel tale for sig selv.

<Rectangle Width="200" Height="100" Fill="Green" Stroke="Black" StrokeThickness="5" />

Hvad der gør ekementet endnu smartere er at det har to Properties der hedder RadiusX og RadiusY, som kan bruges til at lave runde hjørner. RadiusX kan antage en værdi fra 0 og op til halvdelen af elementets Width - og tilsvarende kan RadiusY være op til halvdelen af Height.

Ellipse

Selvom man faktisk med hjælp af RadiusX og RadiusY som beskrevet kan lave et Rectangle til en ellipse findes der også et element beregnet til formålet hvis man ønsker at regne ellipser.

<Ellipse Width="200" Height="100" Fill="Green" Stroke="Black" StrokeThickness="5" />

Her beskriver Width og Height det areal som ellipsen vil fylde ud, hvilket gør det enormt nemt at arbejde med. Man kan ved at sætte propertien Stretch til enten "None", "Uniform" eller "UniformToFill" ændre på måden ellipsen skaleres på. None gør at ellipsen ikke fylder noget, og det vil derfor kun være Stroke (altså elementets kant) man kan se. Uniform og UniformToFill sørger for at elementet altid er lige så højt som det er bredt. Forskellen består i at Uniform vælger den mindste værdi sådan at ellipsen kan være inden for arealet man definerer hvor UniformToFill vælger den største så kan fylder arealet ud og den overskydende del af ellipsen skæres væk.

Polygon

Den sidste basale form er polygon eller "mangekant som det hedder på dansk". For at definere en sådan form angiver man en række points som er koordinatsæt (x,y) der beskriver skæringspunkterne.

<Polygon Fill="Green" Stroke="Black" StrokeThickness="5" Points="10,10 100,100 200,5" />

Idet en polygon kan være meget kompleks og have flere krydsende punkter kan det være svært at afgøre hvilke områder der er indenfor og ydenfor formen, og der med hvilke der skal fyldes af Fill farven. Til at styre dette har polygoner en property FillRule som kan sættes til "EvenOdd" eller "NonZero". EvenOdd er default og fylder kun regioner hvis man vil krydse et ulige antal segmenter for at gå fra området til et område uden for polygonen. NonZero er en mere kompleks algoritme som oftest vil betyde at alle arealer indenfor polygonen fyldes.

Linier

En anden mulighed for at lave grafiske elementer er at tegne linier. Også her findes der 3 elementer som vi kan benytte os af.

  • Line
  • Polyline
  • Path

Line

Line elementet bruges til at tegne en helt enkel streg fra punkt 1 til punkt 2. 

<Line X1="10" Y1="10" X2="100" Y2="100" Stroke="Black" StrokeThickness="5" />

Polyline

Polyline er som du måske har gættet til at tegne flere linier efter hinanden. Dette gøres ligesom for polygoner ved at definere et antal Points.

<Polyline Points="10,10 100,100 200,5" Stroke="Black" StrokeThickness="5" />

Hvis man sætter Fill propertien på en polyline der ikke udgør en lukket figur vil arealet det bliver fyldt svare til hvis man trækker en linie fra det første til det sidste punkt.

Path

Path er en mere kompleks struktur der kan udtrykke hvilken som helst form. Dette gøres ved at sætte dennes Data element der kan sættes til et vilkårligt geometry element. Da geometry elementer er et helt emne i sig selv vil jeg nøjes med at vise et eksempel der viser hvordan man laver et rektangel.

<Path Fill="Blue">
 <Path.Data>
  <RectangleGeometry Rect="0,0 80,80" />
 </Path.Data>
</Path>

Ligesom diskuteret tidligere har RectangleGeometry også RadiusX og RadiusY properties til at lave afrundede hjørner.

Tags:

Silverlight del 1 - helloWorld.

by DotNetNerd 11. November 2007 11:06

Igang med Silverlight

Jeg har besluttet mig for at lege lidt med nogle nye teknologier, og har anskaffet mig nogle bøger omkring Silverlight og F#. Jeg vil derfor det næste stykke tid blogge lidt omkring de ting jeg roder med, på en guide baseret måde som forhåbentlig kan bruges til at komme igang hvis du selv synes et af emnerne lyder interessant.

Her den seneste halve uge har jeg været igang med bogen "Silverlight 1.0 Unleashed", og er begyndt at lege lidt med at kode Silverlight. Jeg vil absolut godt anbefale bogen hvis man vil igang med Silverlight, da den kommer godt omkring teknologien og indeholder både gode eksempler og illustrationer samtidig med at der bliver meldt klart ud omkring bugs og mangler der måtte være rundt omkring.

Hvad angår Silverlight 1.0 er det tydeligt hvilke muligheder det giver fremover, men det er også klart at der mangler en hel del ting som betyder at det først bliver rigtigt anvendeligt når version 1.1 kommer ud. Men hvis man vil igang med at lære teknologien er 1.0 imidlertid et rigtigt godt udgangspunkt, da det indeholder rigeligt til at man kan lære at skrive XAML, håndtere input events og arbejde med animation og video.

Jeg vil derfor blogge lidt omkring nogle af de basale ting man skal vide for at komme igang, og så vil tiden vise hvor mange afsnit jeg skriver og hvor meget af teknologien jeg dækker.

Visning af en Silverlight kontrol

For at få vist en Silverlight kontrol på en webside bruges object eller embed tags alt afhængig af hvilken browser der anvendes. Heldigvis har Microsoft lavet en javascript fil til at håndtere instantieringen af kontrollen, sådan at vi som udviklere slipper for at spekulere over om der skal anvendes det ene eller det ander tag.

Et typisk eksempel kan se ud som vist herunder. I det her tilfælde vises XAML filen HelloWorld.xaml og denne indsættes i container tagget der er navngivet MyContainer.

function createSilverlight()
{   
 Silverlight.createObjectEx(
  {
          source:"xaml/HelloWorld.xaml",
          parentElement: MyContainer,
          id:'SilverlightControl',
          properties:
    {
          width:"350", height:"350", background:"#FFFFFFFF",
   inplaceInstallPrompt: true, version:"1.0"
    },
          events: {onLoad: myOnLoadFunction },
          context: null
 });
}

Som det fremfår kan man angive en række attributter der beskriver en masse ting så som størrelse, farve, version og flere andre ting som jeg dog ikke vil gå i dybden med her. Jeg vil dog nævne attributten inplaceInstallPrompt, som bevirker at man kan installere Silverlight direkte fra siden hvis man ikke allerede har det installeret. Er denne ikke angivet vil man istedet bliver ført videre til en installations side som man kender det fra Flash. Der findes også en anden javascript funktion der hedder blot createObject, men jeg foretrækker createObjectEx, da den er baseret på JSON syntaks, som jeg følger giver er bedre overblik. Som man kan se kan man associere en function med eventet onload - der findes ligeledes et onError event til at lave exceptuion handling. Context værdien giver mulighed for at passe initialiseringsdata til applikationen.

Der er en konvention der foreskriver at denne javascript funktion ligges i en fil der navngives createSilverlight.js.

Med det som udgangspunkt kunne ens html se ud som følger.

<html xmlns="http://www.w3.org/1999/xhtml" >
<head>
    <title>Silverlight HelloWorld</title>
    <script type="text/javascript" src="js/Silverlight.js"></script>
    <script type="text/javascript" src="js/createSilverlight.js"></script>
</head>
<body style="position:absolute;left:100px;top:50px;">
    <div id="MyContainer">
    <script type="text/javascript">
        createSilverlight();
    </script>
    </div>
</body>
</html>

HelloWorld

Traditionen tro vil jeg, som du måske allerede har gættet, starte med at lave et helloworld eksempel. Det er lidt en fortærsket cliche men det giver nu engang et godt udgangspunkt for at komme igang.

<Canvas Name="Canvas1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Width="640" Height="480">

<TextBlock
      Name="myTextBlock"
      Canvas.Left="100" Canvas.Top="25"
      FontFamily="Verdana"
      Foreground="Red"
      FontSize="24"
      Text="Hello world!" />
</Canvas>

Først kan man se der oprettes et canvas, som skal bruges som container for andre kontroller. Dette er ikke strengt nødvendigt, men da det er et XML format må der kun være ét rodelement. Det vil sige at hvis applikationen skulle udvides vil det være nødvendigt at have et canvas eller andet som den yderste container. Rodelementet SKAL definere et namespace som beskriver det XAML format der anvendes. Er er angivet både det format der er beregnet til Windows Presentation Foundation og det til Silverlight applikationer.

Næste og eneste andet element er en TextBlock som bruges til at vise tekst. Det første man nok studser over er måden Left og Top er angivet på. Syntaksen Canvas.Left skyldes det er det man kalder en attached property som kan angives på alle elementer, og ikke kun det som definerer attributten. FontFamily fungerer som man kender det fra css ved at man kan angive en komma separeret liste af fonte - men som udgangspunkt kan man dog kun anvende 9 forskellige fonte som er defineret som værende platform uafhængige. Der findes nogle forskellige teknikker hvis man vil anvende andre fonte, men det er et emne der må vente til en anden gang.

Foreground minder også om noget man kender, men hvad man ikke umiddelbart kan se er at der er associeret en TypeConverter med propertien. TypeConverters bruges hvis værdien er kompleks, for at give en kort og koncis syntaks. I det her tilfælde et det et komplekst objekt, da typeconverteren faktisk dækker over at der underlæggende laves et brush objekt, som kan meget andet end blot definere en enkelt farve idet man kan lave gradienter eller brushes baseret på billeder og videoklip!

For lige at berøre eventhåndtering kort har jeg som du så tidligere i createObjectEx defineret at der skal kaldes en onLoad funktion ved navn myOnLoadFunction. Normalt vil man bruge funktionen til at initialisere sin kontrol, men for lige at holde os til helloworld scenariet kan du skrive en funktion som den her.

function myOnLoadFunction()
{
    alert("Hello world!");
}

Konklusion

Silverlight kontroller embeddes på en webside på samme måde som en Flash applikation. Til at beskrive brugergrænseflader bruges markup sproget XAML, som giver en fantastisk fleksibilitet der langt overstiger hvad man kan med en traditionel html baseret aspx side. Vi har rundet nogle af de helt grundlæggende ting, så som hvordan man embedder en kontrol på en webside, og hvordan en helt basal XAML fil ser ud. Dette burde give et godt udgangspunkt for selv at lege lidt og google sig frem til eksempler på hvordan man bruger andre tags til at tegne ellipser, rektangler, billeder og hvad man ellers kan finde på.

Tags: ,

Dynamic Language Runtime

by DotNetNerd 3. November 2007 23:36

DLR - Dynamic Language Runtime.

Jeg fik for et stykke tid siden øje på en blogpost der omhandlede endnu et tiltag Microsoft arbejder på til .NET frameworket - nemlig en Dynamic Language Runtime. Umiddelbart tænkte jeg "fint nok men hvad skal jeg bruge det til". Det spørgsmål fik jeg så besvaret nu her da jeg faldt over en video fra MIX07 omkring dette emne. Jeg vender tilbage til videoen senere, da den indeholdt et temmeligt fedt eksempel som jeg vil gengive.

Kort fortalt er styrken ved dynamiske sprog at de "bare er tekst" ligesom en aspx- eller xaml-fil. Derved giver de nogle muligheder for især webudviklere ved egentlig at vende en smule tilbage til webudviklingens barndom. Idet sprogene er fortolkede, og altså "bare tekst" der ikke skal kompileres for at kunne afvikles, kan man arbejde uden konstant at skulle igennem en code-compile-review cyklus.

Dette kan især være rart hvis man skal udgive rettelser til en webapplikation, men også under selve udviklingen da man kan sidde med browseren fremme på den ene skærm imens man retter i koden på den anden og ser ændringerne slå igennem med det samme.

Hvilke sprog kan man så kode i?

Til at at starte med arbejder Microsoft på at udvikle implementationer baseret på sprogene:

  • IronRuby
  • IronPython
  • JavaScript
  • Dynamic VB

Ligesom med den almindelige CLR er det meningen at det skal være muligt at udvide med flere sprog hen ad vejen. De 4 sprog der er med til at starte med er valgt ud fra at de for det første er meget udbredte, og så for JavaScripts vedkomne fordi det giver mulighed for at man meget let kan genanvende sine eksisterende Ajax scripts i f.eks en Silverlight applikation. VB er som altid representeret af hensyn til Microsofts eget Office team. IronRuby og IronPython er enormt populære sprog der er svære at komme uden om - og de har en force i at de begge har en lækker syntaks der er meget lidt verbos.

Mix and Match

Der før omtalte eksempel fra MIX07 synes jeg gav en virkelig god idé om hvilke muligheder der vil være for at lege "Mix and Match" i fremtiden, da det illustrerer hvor enkelt man kan blande forskellige sprog - hvad end det er CLR eller DLR sprog, og hvad enten det er ASP, Silverlight eller helt almindelig HTML.

Eksemplet er en Silverlight applikation skrevet i Ruby og benytter en knap skrevet i C#, som kører et animationsscript skrevet i DLR JavaScript der viser tekstelementer der floater rundt. Teksterne er hentet fra en service skrevet i VB, udfra et ord der er indtastet i en HTML textbox som tilgås via Silverlights HTML DOM.

require 'Silverlight.Samples.Controls.Version=0.0.0.0'

Controls = Silverlignt.Samples.Controls
Document = System.Windows.Browser.HtmlPage.Document

JS = require '3dText.js'
VB = require 'technorati.vbx'

def initialize(s, e)
    button = Controls.Button.new
    button.Text = "Click me"

    JS.initialize

    root.children.add button

    button.click do |s, e|
        JS.clearList
        term = Document.txtSearchTerm.Value
        items = VB.GetTitles(term)
        items.each { |item| JS.initializeName(item)}
        JS.playAnimation
    end
end

Som man kan se er Ruby meget letvægts, og hvis man tænker over antallet af komponenter skrevet i forskellige sprog fra forskellige miljøer der arbejder sammen her kan man ikke undgå at blive lidt imponeret over hvor smertefrit CLR og DLR sprog taler sammen.

Det er værd at bide mærke i de første 5 linier fra eksemplet hvor man ser hvor let det er at importerer komponenter fra serveren, samt at referere til dynamisk kode skrevet i andre sprog. Det er ganske enkelt bare at bruge en simpel require statement.

Derefter viser eksemplet en initialize metode, som det er defineret i den tilhørende XAML fil skal kaldes ved init. I initialize instantieres så en knap, og en tilsvarende initialize metode på JavaScript komponenten kaldes. Knappen tilføjes så til et canvas der er navngivet root. Knappen for derefter tildelt en anonym eventhandler, der clearer den liste af tekster og tilføjer nye ord hentet fra VB servicen. Til sidst er det blot at starte animationen og så er koden kørende.

Hvordan platformen blev til.

En sjov historie fra videoen er at en af dem der har været med til at lave det oprindeligt startede med at ville skrive en artikel for at lave grin med Microsoft, da han havde hørt at .NET var elendigt at bygge dynamiske sprog oven på.
Han skrev så en implementation af Python oven på CLR'en - og til hans store overraskelse sparkede den røv på den originale Python. Med andre ord viste benchmarks at hans implementation performede dobbelt så godt! Derefter endte han så med at arbejde for Microsoft og har været med til at bygge denne platform til at hoste dynamiske sprog.

Model-View-Controller - seperation of concerns

by DotNetNerd 2. November 2007 16:19

Model-View-Controller Framework

Frameworks der bygger på et Model-View-Controller pattern er blevet meget populære indenfor webudvikling, især Ruby on Rails vil mange kunne nikke genkendende til.

En af de store fordele ved MVC frameworks er at de giver "seperation of concerns" hvilket vil sige at man får adskilt datamodellen og brugergrænsefladerne. ASP.NET har været kritiseret for at netop det at opnå en klar adskillelse kan være meget svært idet en url mapper direkte til en aspx, som indeholder både markup og codebehind.

En anden force ved MVC frameworks er at de gør det muligt - og ligefrem naturligt - at basere sin udvikling på unittests. Dette er vigtigt ved udviklingen af større systemer, da man derved kan refaktorere uden at være nervøs for ikke at kunne overskue konsekvenserne.

Som alternativ til den almindelige aspx model er Microsoft derfor gået igang med at udvikle deres eget MVC framerwork, som skal passes direkte ind i .NET frameworket og understøtte både CLR og DLR sprog. MVC frameworket designes udfra at det skal være "extensible and pluggable", hvilket vil sige at der skal være muligt for udviklere at skifte ALLE enkeltdele af modellen ud hvis de ønsker at lave deres egen implementation. Derudover bliver der gjort meget ud af at frameworket skal performe godt og være "mockable" - altså det skal være muligt at lave unittesting baseret på mockups.

Routing regler 101

Routing er et kernebegreb i MVC frameworks, som går ud på at mappe en url til en given controller der skal håndtere det aktuelle request. Som standard vil MVC frameworket understøtte to former for mapning:

<Controller>/<Action>/<Param>
<SomeName>/<Action>/<Param> => <Controller>

Eksempelvis kunne man dermed lave en struktur der tillader redigering af produkter, hvor de følgende vil mappes til "rediger produkt med Id 4".

ProductsController/Edit/4
Products/Edit/4 => ProductsController

Udover at mappe til Controllers er det også muligt at mappe til ControllerFactories, som giver et abstraktionslag der kan dirigere videre til forskellige controllere alt efter hvad der skal udføres.

Routing kan f.eks sættes op i ApplicationStart eventet på Global.asax, og i de eksempler der har været vist indtil videre gør man sådan her:

Router.Routes.Add(
new Route("Products", "/Products/[action]/[id]", typeof(ProductController));


Controllers - systemets knudepunkt

Som tidligere nævnt er målet at designet skal være "extensible" og ud fra den devise er der flere mulige klasser der kan extendes, når man skal implementer sin egen controller.

Hvis man vil lave sin helt egen kan man tage udgangspunkt i interfacet IController, som har én metode kaldet Execute der modtager en IHttpContext og RouteData. RouteData er en dictionary af tokens fra url'en der udgør de parametre siden kaldes med.

public void Execute(IHttpContext context, RouteData routeData)
{
   
}

Alternativt kan man bygge videre på klasserne ControllerBase, Controller, Controller<T> eller IControllerFactory.
Det mest oplagte er at arbejde med Controller<T>, som er en typestærk standard implementation af IController.

Hver metode mapper her til en action, og parametre til de efterfølgende tokens i urlen. ControllerAction attributten fortæller at der er tale om en action, og ved at sætte DefaultAction=true indikerer man at denne metode skal kaldes hvis ingen action er givet af url'en. Udover denne attribut kan man tilføje andre til f.eks at håndtere caching.

class Products : Controller<ProductData>
[ControllerAction(DefaultAction=true)]
[OutputCache(Duration=10)]
public void List(int? number)
{
    ViewData["number"] = number;
    ProductData viewData = new ProductData();
    viewData.Number = number;
    RenderView("ProductView", ViewData); 
}

public class ProductData
{
    public int Number { get; set; }
}

ViewData bruges til at holde de data der skal passes til viewet. Der er som udgangspunkt en Dictionary til dette, med ved at bruge Controller<T> kan man angive en type man selv definerer.

Metoden RenderView bruges til at kalde et specifikt view og passe data til viewet.

Komplekse objekter kommer også til at kunne bruges som parametre ved hjælp af Type Descriptors som kan plugges ind.

Views - brugerflader rensede for logik

Views kan være aspx eller ascx filer med alt hvad de normalt kan, inclusiv brug af masterpages, bortset fra postbacks ikke er mulige og dermed undgår man at bruge viewstate.

Dette skyldes at views skal tilgås igennem en Controller, hvilket man ville bryde med ved at tillade postbacks.

Til at understøtte links ud kommer der blandt andet en hjælpeklasse der hedder html samt nogle extention methods. Skal man generere et link kan man f.eks. gøre følgende.

string link = Html.Link("Products", new {Action="Edit", param="4"});
string link = Html.Link<Products>(controller => controller.Edit(4));

På samme vis findes der en .Url metode der udelukkende returnerer url'en.

I den lidt mere fikse afdeling findes der også en metode til at lave links til paging som en pæn xhtml ul/li liste. Dette kræver at den liste man arbejder på implementerer IPagedList, som indeholder properties der beskriver det samlede antal elementer og index.

string paginationLinks = Html.PaginationLinks(viewData, "Products", "List", "10");

Af andre smarte metoder jeg lige vil nævne kort er der RedirectToAction til at håndtere redirects, og en UpdateFrom extention method, til at tage data fra Request.Form, et dataset eller andet og udfore en update.

RedirectToAction<Products>(c => c.List(0))

product.UpdateFrom(Request.Form)
Data kan fra views tilgås som tidligere antydet igennem ViewData, som kan være en type man selv definerer ellers igennem en Dictionary. Det er muligt på views det implementerer ViewPage<T> at tilgå data både via indexer og ved at tilgå properties direkte.

public class ProductView : ViewPage<ProductData>
{
    int i;
    i = ViewData["Number"];
    i = ViewData.Number
}

Ønsker man at benytte ajax vil der blive lavet sådan at man kan lave et kald og indsætte det returnerede html i en ny type panel.

Astoria - next generation dataservices

by DotNetNerd 2. November 2007 15:53

Astoria Data Services - Data in the cloud.

Microsoft arbejder for tiden på et projekt under kodenavnet "Astoria" som skal gøre det muligt at lave dataservices der lader applikationer tilgå og manipulere data via almindelige HTTP requests ved hjælp af et URI format. Idéen er at man skal kunne arbejde med ren data i sine klientapps - altså Ajax og Silverlight implementationer. Data kan efterspørges som XML eller JSON der er simple dataformater, som kan beskrive datastrukturer på en generisk måde.

Standard HTTP verber som GET, POST, PUT og DELETE definerer operationen der skal afvikles - altså om det er en select, update, insert eller delete operation.

"Under kølerhjelmen" består Astoria Dataservices af en Windows Comminication Foundation Service der stiller data til rådighed, som er defineret ved hjælp af entities frameworket over en database. Astoria er altså bygget på eksisterende teknologier, hvor der er bygget en HttpHandler over som tillader at et query defineres som en URI.

JavaScript Object Notation vs Extensible Markup Language.

Som nævnt kan data efterspørges i formaterne JSON og XML. Overordnet set kan man sige at JSON sigter imod Ajax implementationer og XML imod Silverlight, da det er "the format of choice" for tiden for de to domæner.

Da JSON er knap så kendt som XML vil jeg lige beskrive det kort. Formatet er fordelagtigt at bruge i Ajax applikationer, da det er et meget kompakt format der er velunderstøttet i Javascript, idet man kan hente en JSON.js eller bruge Microsofts implementation der følger med frameworket fra .NET 3.5.

Kort fortalt defineres et objekt således

{name:value,name:value}

og et array således

[value,value]

Ud fra disse definitioner kan man definere arrays hvor værdierne er objekter og omvendt, og det er derved muligt at beskrive komplekse datastrukturer med meget lidt overhead.

JSON serialisering.

Ved hjælp af ASP.NET Ajax er det meget enkelt at arbejde med JSON, hvilket jeg vil illustrere med et lille eksempel.

<asp:ScriptManager ID="ScriptManager1" runat="server"></asp:ScriptManager>
<script>

var value = new Person();
value.Name = "Jens";

var result = Sys.Serialization.JavaScriptSerializer.serialize(value);

alert(result);

//Objektet kan derefter gendannes enten ved at deserialisere eller ved hjælp af eval
var obj = Sys.Serialization.JavaScriptSerializer.deserialize(result);
obj = eval("(" + result + ")");

function Person()
{
    this.Name;
}
</script>

På samme måde kan man naturligvis også serialisere objekter til JSON i .NET også.
using System.Web.Script.Serialization;

...

JavaScriptSerializer serializer = new JavaScriptSerializer();

Person person = new Person();
person.Name = "Hans";

string result = serializer.Serialize(person);

Response.Write(result);

Person person2 = serializer.Deserialize<Person>(result);

Response.Write(person2.Name);
Querying via URI.

Den URL-baserede query syntax understøtter en del forskellige operatorer, men lad os lige først se på et par eksempler som jeg har fra et screencast på MSDN der bygger på Northwind databasen.

Helt enkelt kan man tilgå data ved at "slashe" ('/') sig igennem en struktur og bruge hårde klammer ('[]') til at angive nøgler som sådan:

northwind.svc/Customers[ALKFI]/Orders

Næste skridt er at man kan bruge simple operatorer, som her hvor jeg efterspørger Kunder fra London:

northwind.svc/Customers[City eq 'London']

Og slutteligt er der nogle mere advancerede operatorer - den nedenstående gør at ordrene fra kunden "ALKFI" flettes ind i de data der returneres. Det vil sige at man effektivt set kan hente kunde og ordre oplysninger som ét kald.

northwind.svc/Customers[ALKFI]?$expand=Orders

Simple query operatorer.

Operatorerne er vidst så selvbeskrivende at jeg vil nøjes med at liste dem.

eq (Equal)  /Customers[City eq 'London'] 
ne (Not equal)  /Customers[City ne 'London'] 
gt (Greater than)  /Orders[OrderDate gt '1998-5-1'] 
gteq (Greater than or equal)  /Orders[Freight gteq 800] 
lt (Less than)  /Orders[Freight lt 1] 
lteq (Less than or equal)  /Orders[OrderDate lteq '1999-5-4'] 


Advancerede query operatorer.

De advancerede operatorer giver mulighed for at hente sammensat data og lave paging. Derved er en de mest almindelige scenarier understøttet "ud af boksen".

?$expand=Orders  inkluderer ordrer inline, således at man undergår at lave to kald. 
?$orderby=ProductName  Sorterer efter ProductName. 
?$top=2  Returnerer de 2 første rækker. 
?$skip=2  Springer de 2 første rækker over. 


Custom Service Methods.

Hvis man ønsker at definere et query som er for advanceret til at kunne løses med de eksisterende operatorer er det muligt selv at skrive metoder der også kan kaldes ved hjælp af Astorias URI format.

Eksempelvis kan man lave så det er muligt at hente kunder ud fra hvad stat de bor i - hvor queriet kunne se sådan her ud:

/CustomersByState?state=WA

Implementationen af servicen er enormt simpel, da der er en Astoria template i Visual Studio man kan tage udgangspunkt i. Derefter kan metoden skrives sådan her.


[WebGet]
public static ObjectQuery<Custumers> CustomersByCity(NorthWindEntities db, string city)
{
 if (city == null || city.length < 3) {throw new Exception("Bad city");}
  
 return db.Customers.where("it.City = @city", new ObjectParameter("City", city));
}


Hvordan ser den payload jeg modtager ud?

For at give syn for sagen kan du her se det der returneres fra det simple kald til Customers[ALKFI] hvis man efterspørger XML.
<DataService xml:base="http://myserver/data.svc">
 <Customers>
  <Customer uri="Customers[ALFKI]">
   <CustomerID>ALFKI</CustomerID>
   <CompanyName>Alfreds Futterkiste</CompanyName>
   <ContactName>Maria Anders</ContactName>
   <ContactTitle>Sales Representative</ContactTitle>
   <Address>Obere Str. 57</Address>
   <City>Berlin</City>
   <Region />
   <PostalCode>12209</PostalCode>
   <Country>Germany</Country>
   <Phone>030-0074321</Phone>
   <Fax>030-0076545</Fax>
   <Orders href="Customers[ALFKI]/Orders" />
  </Customer>
 </Customers>
</DataService>

Og hvis man efterspørger JSON får man det her tilbage.

[
    {
        __metadata: {
            Type: "Customer",
            Base: "http://myserver/data.svc",
            Uri: "Customers[ALFKI]"
        },
        CustomerID: "ALFKI",
        CompanyName: "Alfreds Futterkiste",
        ContactName: "Maria Anders",
        ContactTitle: "Sales Representative",
        Address: "Obere Str. 57",
        City: "Berlin",
        Region: null,
        PostalCode: "12209",
        Country: "Germany",
        Phone: "030-0074321",
        Fax: "030-0076545",
        Orders: {
            __metadata: {
                Uri: "Customers[ALFKI]/Orders"
            }
        }
    }
]

Hvordan forbruger jeg så en service?

Næste skridt er så naturligvis at kalde servicen, hvilket jeg vil demonstrere ud fra både en Ajax og en Silverlight kontekst. Ved at bruge Microsofts Scripting Library, som er en del af ASP.NET Ajax, kan en service tilgås på den her måde:
function GetCustomer(id)
{
 var req = newe Sys.Net.WebRequest();
 req.set_url("northwind.svc/Customers[" + id + "]/Orders");
 req.get_headers()["Accept"] = "application/json";
 req.add_completed(OrdersCompleted);
 req.invoke();
}

function OrdersCompleted(response)
{
 if (response.get_statuscode() != 200){//Error}
 else
 {
  var orders = response.get_object();
  var html = Sys.StringBuilder("<ul>");

  for (var i = 0; i<orders.length; i++)
  {
   html.append("<li>" + orders[i].OrderDate + "</li>");
  }

  html.append("</ul>");

  $get(Orders).innerHTML = html.toString();
 }
}
Ligeledes kan man i Silverlight udforme et query, og der er endvidere muligt ved hjælp af et tool generere proxy klasser så man kan arbejde med strongly typed data.
WebDataContext dataContext = new WebDataContext(txtDataserviceUrl.Text);

foreach (Customer customer in dataContext.CreateQuery<Customer>("/Customers"))
{
 cmbCustomers.Items.Add(customer.Name);
}
if (cmbCustomers.Items.Count > 0)
{
 cmbCustomers.SelectedIndex = 0;
}
Hvis man vil lave inserts er det også rimeligt enkelt, hvilket jeg har fundet et eksempel på som viser hvordan en Note føjes til en Artikel i et typisk Artikel system.
Article article = (Article)lstArticles.SelectedItem;

Note note = new Note();
note.Comments = txtNewNote.Text;
note.Article.RelatedObject = article;

dataContext.Add(note, "Notes");
dataContext.SaveChanges();

lstNotes.Items.Add(note.Comments);
txtNewNote.Text = "";

Blod på tanden?

Idet trenden går imod at webapplikationer skal minde mere og mere om "rigtige" applikationer, er RIAS (Rich Interactive Applications) et af de mest hotte emner verden over inden for webudvikling. I den forbindelse er teknologier som Astoria en vigtig brik, da de gør det muligt at lave generiske applikationer der direkte fra klienten selv kan tilgå og vise data.

Jeg finder derfor emnet meget interessant og jeg håber artiklen her har giver dig blod på tanden, da jeg selv glæder mig til at teknologien bliver færdigudviklet så jeg selv kan komme til at anvende den i mine løsninger.

Tags:

DataBinding - performance, læsevenlighed og fleksibilitet

by DotNetNerd 2. November 2007 15:41

Jeg har ved flere lejligheder hørt forskellige udviklere snakke for og imod forskellige måder at lave databinding på - så nu besluttede jeg mig for at lave en test for at se hvad forskellen reelt er.

Udgangspunktet er en Person klasse med 1 property (Name), som der skabes en liste af der bindes til et gridview.
Jeg har lavet 3 scenarier, der henholdsvis binder ved hjælp af binding expressions med og uden brug af Eval, og ved at håndtere RowDataBound eventet på et gridview.

Lige for at gøre klart hvordan man databinder via binding expressions med og uden eval er her de to tags der er tale om:

Binding med eval:

<%# Eval("Name") %>

Binding uden eval:

<%# ((Person)Container.DataItem).Name %>

Efter at have kørt disse 3 binding scenarier en række gange fremgår det tydeligt at Eval performancemæssigt er noget tungere, og tager ca. dobbelt så lang tid som de to andre scenarier der performer rimeligt ens. Dette skyldes naturligvis at Eval benytter reflection. Det skal dog siges at vi snakker henholdsvis 1 og 2 millisekunder ved den her mængde data så vi snakker detaljer med mindre der er virkelig meget data.

Konklusion:

Håndtering af RowDataBound og brug af binding expressions uden eval er klart at foretrække ud fra et rent performancemæssigt synspunkt.

Personligt synes jeg at det er uskønt at skulle skrive en RowDataBound eventhandler i helt simple tilfælde, da det er knap så læsevenligt som at bruge et binding expression og da det kræver mere kode.

Derfra kan man så vælge ud fra om det vigtigste kriterie er performance eller fleksibilitet. Eval har en fordel i og med at aspx'en i højere grad fungerer som template det er fri for at vide hvilken type der bindes til den. I nogen tilfælde kan denne fleksibilitet veje tungere end performancemæssige hensyn - da det ved relativt små datamængder stadig vil være svært at mærke forskel. Skal man derimod lave en simpel binding af store mængder data virker expression binding ved at tilgå Container.DataItem som det rigtige valg.

 

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