WPF-applikationer (Windows Presentation Foundation) kan opbygges udelukkende med C#-kode, men de kan også benytte sig af eXtensible Application Markup Language (XAML) til at definere deres brugergrænseflade, hvilket gør det nemt både for mennesker og kode at forstå. Visual Studio for Windows er delvist bygget med WPF. Microsofts fremskridt med Windows 8 og videre til Windows 10 og 11 har ført til udviklingen af flere teknologier til appudvikling, hvoraf nogle er skræddersyede til bestemte platforme. Windows Store-apps, der blev introduceret i Windows 8, og Universal Windows Platform (UWP) i Windows 10, afspejler dette skift mod en mere beskyttet og sandkasse-lignende kørsel af apps. UWP-apps giver fuld adgang til WinRT API'er, men de kører kun på de nyeste versioner af Windows og kan køre på Xbox og Mixed Reality-headsets. UWP-udvikling krævede dog en ændring i tankegang, da adgangen til systemniveauet var begrænset. For at overkomme disse begrænsninger, oprettede Microsoft Project Reunion og WinUI 3, som nu er en del af Windows App SDK, og gør det muligt for udviklere at bringe moderne udviklingsfordele til deres eksisterende WPF-apps.

Med den moderne .NET-plattform er det muligt at understøtte ældre Windows-platforme som Windows Forms og WPF, hvilket gør det lettere for virksomheder at vedligeholde og opgradere deres legacy-applikationer, uden at skulle migrere helt til nyere platforme. Det gør det muligt at udnytte de nyeste .NET-funktioner, samtidig med at man bevarer kompatibiliteten med ældre systemer. Denne evne til at køre legacy Windows-applikationer på moderne .NET gør det muligt at udvide funktionaliteten uden at genopbygge applikationerne fra bunden. Det er en kritisk funktion for mange organisationer, som allerede har et omfattende væld af Windows-baserede applikationer.

Når vi ser på udviklingen af tværplatforms-applikationer, har vi at gøre med flere mobile og desktop-platforme, hvor hver platform har sine egne programmeringssprog og API'er. For mobilapplikationer er de to dominerende platforme iOS og Android, mens de to største desktop-operativsystemer er macOS og Windows. iOS bruger Objective C eller Swift sammen med UIKit, Android bruger Java eller Kotlin sammen med Android API, macOS bruger Objective C eller Swift sammen med AppKit eller Catalyst, og Windows har en bredere palet af sprog, der kan benytte Win32 API eller Windows App SDK. Dette kan gøre det svært for udviklere at holde sig opdaterede og i stand til at udvikle på tværs af platforme.

Heldigvis tilbyder .NET Multi-platform App UI (MAUI) en løsning, hvor udviklere kan bygge én applikation og få den til at køre på mange forskellige platforme. MAUI giver udviklerne mulighed for at dele både brugergrænsefladekomponenter og forretningslogik på tværs af mobile og desktop-platforme. Det gør det muligt at benytte de samme .NET API'er, der bruges i konsol-applikationer, websites og webservices. MAUI understøtter også eksisterende MVVM (Model-View-ViewModel) og XAML-mønstre, men teamet planlægger at tilføje støtte for MVU (Model-View-Update) med C#, som minder om Apple’s SwiftUI. Dette giver udviklere et enkelt rammeværk for at bygge apps, der kan udnytte funktioner på tværs af alle computing-enheder – fra servere og bærbare computere til smartphones og spilkonsoller.

Inden Microsoft lancerede .NET MAUI, blev der udviklet alternative, open-source initiativer som Uno og Avalonia. Uno platformen, som beskrives som den første C# & XAML, open-source platform, gør det muligt for udviklere at genbruge 99% af forretningslogikken og UI-laget på tværs af mobil-, web- og desktop-platforme. Uno-platformen benytter Xamarin-native platformen og Mono-WASM runtime til WebAssembly, mens det på Linux benytter Skia til at tegne brugergrænsefladen. Avalonia, på den anden side, betragtes som et arvtager til WPF og giver udviklere mulighed for at bygge tværplatforms-applikationer, der er pixel-perfekte og fungerer på alle større platforme. JetBrains brugte Avalonia til at modernisere deres WPF-baserede værktøjer og gøre dem tværplatform.

Udviklingen af apps kræver det rette udviklingsmiljø, og Visual Studio er Microsofts primære løsning til C#-programmering. Visual Studio 2022 findes til både Windows og Mac og giver udviklerne et GUI-værktøj til at bygge brugergrænseflader. Visual Studio Code er et lettere, tværplatforms værktøj, der er ideelt til webudvikling og understøtter udvikling på tværs af flere platforme. Dog har Visual Studio Code begrænset support for mobile og desktop apps i forhold til Visual Studio 2022. GitHub Codespaces giver endnu en cloud-baseret løsning til udvikling, som gør det muligt at kode direkte fra browseren.

For udviklere, der ønsker at bygge tværplatforms-apps, er det vigtigt at vælge det rette værktøj og platform. For en enkel og effektiv udviklingsoplevelse kan Visual Studio være det bedste valg, især når man arbejder med XAML-baserede platforme som WPF og MAUI. Samtidig giver Visual Studio Code udviklere fleksibilitet til at arbejde tværplatforms, hvis deres app skal køre på flere forskellige operativsystemer.

Det er også vigtigt at forstå, at valget af teknologi og værktøj bør baseres på de specifikke krav til applikationen. Hvis du arbejder på et projekt, der skal kunne køre på Windows, iOS, Android og web, kan MAUI og værktøjer som Uno og Avalonia være de bedste muligheder, da de giver dig mulighed for at udvikle applikationen én gang og få den til at køre på flere platforme.

Hvordan forstå og arbejde med Azure Cosmos DB, Azure Functions og relaterede teknologier i moderne applikationer?

I moderne softwareudvikling er integrationen af cloud-baserede tjenester som Azure Cosmos DB og Azure Functions blevet en hjørnesten for at skabe skalerbare og effektive applikationer. Azure Cosmos DB tilbyder en globalt distribueret, multi-model databaseplatform, som understøtter høj tilgængelighed, lave latenstider og fleksible konsistensmodeller. Det er vigtigt at forstå de forskellige konsistensniveauer—fra stærk konsistens til eventual konsistens—da de påvirker både dataintegritet og performance. Partitioneringsstrategier spiller også en central rolle i designet af databasen for at sikre optimal ydeevne og skalering, hvilket er essentielt, når man arbejder med store datamængder eller distribuerede applikationer.

Azure Functions repræsenterer en serverløs arkitektur, hvor udviklere kan implementere små, single-purpose funktioner, som reagerer på events, såsom HTTP-forespørgsler, meddelelser i køer eller ændringer i databaser. Hostingmodellerne spænder fra in-process til isolerede miljøer, hvilket giver fleksibilitet afhængig af kravene til applikationen. Azure Functions understøtter også avancerede koncepter som dependency injection og bindings, der gør det muligt at integrere med andre Azure-tjenester, herunder Blob Storage, Queue Storage og SignalR Service, hvilket åbner op for komplekse, event-drevne systemer med høj reaktivitet.

Udvikling og testning af disse komponenter kan understøttes af værktøjer som Azure Functions Core Tools og Azurite, som muliggør lokal udvikling og emulering af cloud-tjenester. For eksempel kan Cosmos DB-emulatoren på Windows give en udviklingsplatform, der spejler den faktiske cloud-oplevelse, uden at der er behov for konstant internetforbindelse eller ekstra omkostninger.

Lokaliseringsaspekter i applikationer spiller en væsentlig rolle for at sikre global tilgængelighed og brugeroplevelse. Det omfatter håndtering af metadata, som assemblies og ressourcefiler, samt understøttelse af sprog og kulturer via Accept-Language headers og indlejrede lokaliseringsressourcer i Razor views. En dybere forståelse af kulturtilpasning og håndtering af dato- og tidsværdier i forskellige tidszoner er afgørende for at undgå fejl og sikre konsistens på tværs af regioner.

Asynkrone programmeringsmodeller, som async/await keywords i C#, og asynkrone streams, er essentielle for at håndtere I/O-operationer effektivt uden at blokere applikationens tråde. I konteksten af Azure Cosmos DB og Azure Functions muliggør dette høj gennemløb og responsivitet, særligt ved streaming af data eller realtidskommunikation med SignalR.

Det er også nødvendigt at have et indgående kendskab til sikkerhedsprincipper såsom autentifikation og autorisation. Azure tilbyder flere niveauer af adgangskontrol, herunder brugeridentifikation, medlemskabsstyring og anvendelse af asymmetric keys, som beskytter applikationsfunktionalitet mod uautoriseret adgang. Anvendelsen af App Transport Security (ATS) og krypteringsteknikker sikrer, at dataoverførsler og lagring foregår på en sikker måde.

I udviklingen af komponenter, som for eksempel i Blazor og .NET MAUI, spiller modulopbygning, isolering af CSS og JavaScript, og routing mellem komponenter en væsentlig rolle for at skabe vedligeholdelige og genanvendelige brugergrænseflader. Brug af framework-specifikke bindings og komponenthierarkier hjælper med at integrere data og funktionalitet på tværs af applikationen.

Udover det tekniske indhold er det vigtigt at forstå de principper, der ligger bag designet af distribuerede systemer, som eventual konsistens og hvordan man håndterer potentielle race conditions ved brug af mutexer eller atomare operationer. Disse principper er med til at sikre robusthed og pålidelighed i applikationer, der skal køre på tværs af geografiske lokationer med forskellige netværksforhold.

En yderligere vigtig dimension er implementeringen af DevOps-praksis, hvor udgivelse til skyen, overvågning med værktøjer som Benchmark.NET, logning med Serilog, og brug af CI/CD pipelines via Visual Studio og Visual Studio Code understøtter en stabil og effektiv udviklingsproces.

Kendskab til protokoller og API’er som gRPC, OData, og GraphQL øger mulighederne for at skabe moderne, interoperable systemer, der kan kommunikere effektivt både internt i organisationer og med eksterne partnere.

Viden om data manipulation via SQL, DDL, DML og integration med Entity Framework Core er fundamentalt for at kunne modellere og manipulere data effektivt, uanset om man arbejder med relationelle databaser eller NoSQL-løsninger som Cosmos DB.

Det er vigtigt at fastholde en holistisk forståelse af, hvordan de enkelte teknologier og koncepter samvirker i praksis, da det er samspillet mellem cloud-tjenester, programmeringssprog, arkitekturprincipper, sikkerhedsmodeller og udviklingsværktøjer, der tilsammen skaber moderne, skalerbare og sikre applikationer.

Hvordan håndtere datoer, tidspunkter og internationalisering i software

Globalisering og lokaliseringskultur er grundlæggende begreber, når man udvikler applikationer, der skal bruges i forskellige lande og kulturer. I denne sammenhæng er det vigtigt at forstå, hvordan datoer, tidspunkter, valuta og andre kulturelle forskelle håndteres i softwareudvikling. Hver kultur har sine egne regler for formatering, og det er nødvendigt at implementere systemer, der tilpasser sig disse regler, afhængigt af brugerens placering og sprog.

For at illustrere dette kan vi tage et konkret eksempel, hvor vi ændrer den nuværende kultur i et program. I starten har vi en kultur sat til en-GB (Engelsk - Storbritannien), og systemet viser datoer som f.eks. "1st day of this year: 01 January 2022" og bruger det britiske pund (£) som valuta. Dette er et typisk format for Storbritannien, hvor datoformatet er dag-måned-år, og tusind-separatoren er komma (,).

Men hvis vi ændrer kulturen til en-US (Engelsk - USA), vil vi bemærke nogle ændringer. Datoen vil blive formateret som "Saturday, January 1, 2022", og valutaen bliver til den amerikanske dollar ($). USA bruger en anden formatering af datoer og tal, og dette afspejles i den lokale kultur.

Når vi skifter til en dansk kultur, da-DK, ser vi, at systemet automatisk tilpasser sig de danske formater. Ugedagene bliver f.eks. søndag, mandag, tirsdag osv., og månedernes navne bliver januar, februar, marts og så videre. Datoen vil blive vist som "lørdag den 1. januar 2022", og valutaen bliver ændret til danske kroner (kr.).

En anden vigtig detalje er håndteringen af tid og dato i andre kulturer. I f.eks. Polen vil datoformatet kræve, at månedens navn ændres for at være possessivt, som i "1 stycznia", hvor styczeń bliver til stycznia. Dette er et grammatikalisk krav, som er unikt for polsk.

I Iran, hvor det persiske kalenderår anvendes, er der en anden type datoformat, der anvender år-måned-dag, og året 2022 svarer til 1400 i den persiske kalender. Dette kan være en udfordring, når man arbejder med internationalisering, fordi datoen skal håndteres forskelligt afhængigt af kalenderen i den pågældende kultur.

Når et program skal håndtere disse kulturelle forskelle, kan det være nødvendigt at bruge en invariant kultur, som f.eks. den amerikanske engelske kultur (en-US), til at standardisere dataene, uanset hvor programmet køres. Dette er nyttigt, når man arbejder med systemer som f.eks. databaser eller API'er, der kræver et ensartet format for at sikre korrekt databehandling.

En løsning til at håndtere kulturelle forskelle er brugen af ressourcefiler, der indeholder de nødvendige oversættelser og formater for hver kultur. Disse ressourcefiler, som er XML-baserede og har filendelsen .resx, gør det muligt at oversætte brugergrænsefladens tekst til den lokale kultur. En applikation vil først forsøge at finde en ressourcefil, der svarer til den specifikke kultur, og hvis denne ikke findes, vil den falde tilbage på en neutral kultur og derefter på en invariant kultur.

Når man arbejder med lokaliserede applikationer, er det vigtigt at overveje, hvordan kulturelle forskelle kan påvirke brugeroplevelsen. Det er ikke kun datoer og valuta, der kan være forskellige, men også brugen af tid, ordvalg og grammatik. Dette betyder, at det at oversætte tekst fra et sprog til et andet kræver en grundig forståelse af den målrettede kultur og de nuancer, der er forbundet med sproget.

Når man skaber applikationer, der er beregnet til at blive brugt globalt, er det vigtigt at gøre sig umage med at forstå og implementere korrekt internationalisering. Det inkluderer ikke kun at ændre formaterne for dato og valuta, men også at sikre, at applikationen er i stand til at håndtere de kulturelle forskelle, der findes i de forskellige regioner.

Endelig, mens teknologi gør det muligt at tilpasse applikationer til forskellige kulturer, er det også vigtigt at have et fleksibelt system, der kan tilpasse sig nye kulturer og sprog, efterhånden som de bliver relevante. Dette sikrer, at applikationen forbliver globalt kompatibel og brugervenlig, uanset hvor den bruges.

Hvordan testes og implementeres en .NET GraphQL-klient med Strawberry Shake?

For at teste en .NET-klient, der benytter GraphQL, starter man typisk med at køre backend-projektet, eksempelvis Northwind.GraphQL, uden debugging via HTTPS-profilen. Herefter startes klientprojektet, som i dette tilfælde kunne være Northwind.Mvc.GraphQLClient, ligeledes via HTTPS-profilen. Når applikationen kører, kan man observere, at produkter bliver korrekt hentet fra GraphQL-tjenesten, som for eksempel varer i kategorien "Beverages". Det er muligt at ændre forespørgslen ved at angive forskellige kategori-id’er, hvorved man kan teste, hvordan systemet håndterer både eksisterende og ikke-eksisterende kategorier – ved en ugyldig kategori returneres for eksempel nul produkter. Denne proces sikrer, at kommunikationen mellem klient og server fungerer som forventet, samt at håndtering af fejl og uventede input er korrekt implementeret.

En vigtig del af fejlhåndteringen kan demonstreres ved bevidst at indføre en fejl i GraphQL-forespørgslen, som for eksempel at ændre feltet productId til productid. Ved genstart af klientapplikationen uden debugging og ved aktivering af detaljeret fejlinformation, vil en sådan fejl resultere i, at fejlbeskeden bliver vist sammen med detaljer om responsen, hvilket er essentielt for at kunne diagnosticere problemer i kommunikationen med GraphQL-tjenesten.

I stedet for traditionelle HTTP-klienter kan man med fordel anvende ChilliCreams klientbibliotek, Strawberry Shake, som er udviklet specifikt til at bygge .NET-klienter mod GraphQL-services. Dette bibliotek tilbyder værktøjer til nemmere oprettelse af klienter med genereret kode, afhængighedsinjektion og understøttelse af avancerede funktioner som WebSocket-subscriptioner. For at komme i gang oprettes et nyt konsolprojekt, hvor Strawberry Shake værktøjerne installeres via dotnet tool install. Projektet konfigureres til at behandle advarsler som fejl, og nødvendige NuGet-pakker tilføjes for at understøtte HTTP-transport og dependency injection.

Når projektet er oprettet og bygget, initieres klienten mod GraphQL-tjenesten ved hjælp af dotnet graphql init-kommandoen, som også genererer en konfigurationsfil .graphqlrc.json. Her specificeres blandt andet namespace og transportprofil, hvilket sikrer, at klientens kode genereres korrekt og struktureret i henhold til projektets krav. GraphQL-forespørgsler placeres i særskilte filer med .graphql-extension og navngives eksplicit. Strawberry Shake genererer på baggrund af disse filer den nødvendige C#-kode automatisk i en mappe som typisk hedder Generated.

I selve programkoden oprettes en servicekontekst, hvor den genererede klient tilføjes med korrekt baseadresse. Herefter kan klienten anvendes til at eksekvere forespørgsler mod GraphQL-tjenesten og hente data, som eksempelvis produkter i kategorien "Seafood". Resultaterne kan herefter bearbejdes og vises i konsollen, og fejl kan håndteres med indbyggede metoder som EnsureNoErrors().

Ud over forespørgsler (queries) understøtter GraphQL også mutationer, der tillader ændringer i data. Mutationer består af tre elementer: selve mutationens navn, som typisk er et verbum i camelCase, for eksempel addProduct; input-objektet, der bærer data til mutationens eksekvering, med et suffix Input, eksempelvis AddProductInput; og payload, som er det objekt der returneres efter mutationens udførelse, ofte med suffix Payload. Det er vigtigt, at disse typer er veldefinerede og kan være komplekse objektgrafer, som afspejler den operation, der skal udføres.

I praksis implementeres mutationer ved at tilføje en klassefil, for eksempel Mutation.cs, hvor mutationens record og tilhørende klasser defineres. Det giver struktur og type-sikkerhed i kodebasen, samtidig med at det sikrer klarhed i, hvordan data skal tilføjes, opdateres eller slettes i backend via GraphQL.

Det er essentielt at forstå, at effektiv brug af GraphQL i .NET ikke blot handler om at sende forespørgsler, men også om at integrere klientbiblioteker, der kan generere kode, håndtere fejl, og sikre robust kommunikation mellem klient og server. Strawberry Shake tilbyder en kraftfuld ramme for dette, som samtidig muliggør moderne praksisser som dependency injection og avancerede transportsystemer.

Udover den tekniske implementering er det vigtigt at have fokus på god praksis i udviklingen: altid at validere input og håndtere fejl eksplicit, at sikre synkronisering mellem schema og klientkode via automatiseret kodegenerering, og at forstå den semantiske betydning af queries og mutationer for at undgå utilsigtede bivirkninger i systemet. Dette sikrer både stabilitet og fleksibilitet i applikationen over tid.