Når man arbejder med Azure SQL Database eller Azure SQL Edge, kræves der normalt et brugernavn og en adgangskode til autentificering, modsat lokale SQL Server-instanser, der kan bruge Windows Integrated Security uden adgangskode. Tilslutningen til databasen sker via en forbindelse, der typisk åbnes med en timeout, som her er sat til 10 sekunder for at undgå lange ventetider ved forbindelsesproblemer. Det er vigtigt at forstå, at en mislykket forbindelse kan skyldes flere faktorer såsom forkerte loginoplysninger eller netværksproblemer, hvilket resulterer i specifikke SQL-fejlmeddelelser. Ved f.eks. forkert password modtages en “Login failed” fejl, mens en forkert serveradresse udløser en netværksrelateret fejl, som indikerer, at serveren ikke kunne findes eller tilgås.
Når forbindelsen til databasen er etableret, åbnes muligheden for at eksekvere SQL-kommandoer og hente data ved hjælp af ADO.NET’s SqlCommand og SqlDataReader. Ved at definere en kommando med den rette SQL-tekst kan man hente rækker fra en tabel og aflæse felterne sekventielt. Her præsenteres produktdata med ID, navn og pris i et pænt formateret output, hvor valutaen tilpasses operativsystemets aktuelle kultur, hvilket sikrer korrekt visning afhængigt af brugerens lokalitet.
Parametre i SQL-kommandoer er essentielle for at undgå SQL-injektion og samtidig gøre forespørgsler dynamiske. Ved at tilføje parametre til kommandoen, som f.eks. en minimumspris, kan man filtrere resultaterne effektivt uden at ændre selve SQL-strengen direkte. Det er væsentligt at validere input, f.eks. sikre at brugeren indtaster et gyldigt decimaltal for prisen, inden det sendes til databasen.
Asynkrone operationer er afgørende for at forbedre applikationens ydeevne og responsivitet, især i multitrådede miljøer eller GUI-applikationer, hvor blokering af brugergrænsefladen skal undgås. ADO.NET tilbyder asynkrone metoder til åbning af forbindelser, udførelse af kommandoer, læsning af data og lukning af ressourcer. Dette sikrer, at databasen håndteres effektivt uden at standse andre processer, hvilket er særligt relevant i webapplikationer og applikationer med mange samtidige brugere.
Ud over simple forespørgsler anbefales brug af stored procedures til gentagne eller komplekse operationer. Stored procedures kompileres og optimeres på serversiden, hvilket reducerer eksekveringstid og netværkstrafik. Parametre i stored procedures kan have forskellige retninger: input (inddata), output (uddata) og return value, hvilket muliggør fleksibel kommunikation mellem applikation og database.
Det er vigtigt at forstå, at korrekt håndtering af forbindelser, kommandoer og data er fundamentet for at bygge robuste og effektive datadrevne applikationer. Dette indebærer både sikkerhed, fejlhåndtering og optimering gennem parameterisering og asynkron programmering. Desuden bør man være opmærksom på datakultur og formatering, som kan variere geografisk, samt netværksforhold, der påvirker forbindelsens stabilitet og hastighed.
Endvidere bør man være klar over, at selv om standard timeout for forbindelse til SQL Server er 30 sekunder, kan man justere denne for at forbedre brugeroplevelsen ved fejl. Et kortere timeout giver hurtigere feedback ved problemer, men kræver at netværks- og serverforhold er stabile. Desuden bør man overveje sikkerhedsaspekter som kryptering og certifikatstyring, som nævnt i forbindelsesstrengen med parametre som Encrypt=True og Trust Server Certificate=True.
Den indbyggede fleksibilitet i ADO.NET, kombineret med en forståelse af databaseforbindelser og kommandoudførelse, danner grundlaget for at arbejde effektivt med relationelle data, uanset om det drejer sig om lokale SQL Server-instanser eller cloudbaserede løsninger som Azure SQL.
Hvordan opretter og administrerer man en Cosmos DB i Azure?
I denne artikel dykker vi ned i, hvordan du opretter og administrerer en Cosmos DB i Azure ved at bruge Cosmos SDK og Core (SQL) API til CRUD-operationer på data. Vi tager udgangspunkt i et eksempel, hvor vi arbejder med databasen "Northwind" og containeren "Products", som vi opretter og administrerer i Azure Cosmos DB, både lokalt via emulatoren og i skyen.
For at komme i gang med Azure Cosmos DB, skal vi først konfigurere en CosmosClient. Dette kræver en endpointURI og en primærnøgle, som anvendes til autentifikation. Hvis du arbejder lokalt, kan du bruge følgende kode:
Er du derimod i skyen, skal du bruge dine specifikke kontooplysninger:
Med disse oplysninger kan du oprette en CosmosClient og begynde at oprette Cosmos DB-ressourcer som databaser og containere.
Oprettelse af database og container
Når du opretter en database i Cosmos DB, skal du specifikt angive både et navn og et throughput i RUs (Request Units) per sekund. I dette eksempel opretter vi databasen "Northwind" med et throughput på 400 RUs per sekund:
For containeren, som i vores eksempel hedder "Products", skal du angive et partitioneringsnøgle (i dette tilfælde "/productId") samt en eventuel indeksingspolitik. Det betyder, at du kan vælge, hvordan dataene i containeren skal indekseres for at optimere læsehastigheden:
Containeren oprettes ved at angive en partitioneringsnøgle og den ønskede throughput:
Når disse ressourcer er oprettet, kan du få oplysninger om dem, såsom partitioneringsnøgle, indekseringspolitik og metadata, som er nyttige til optimering af ydeevnen.
CRUD-operationer med Core (SQL) API
Når du har oprettet dine ressourcer, kan du begynde at udføre CRUD-operationer på data. Cosmos DB bruger Core (SQL) API, som giver dig mulighed for at manipulere JSON-dokumenter i databasen ved hjælp af SQL-lignende syntaks.
De vigtigste metoder til CRUD-operationer er:
-
Læsning af data:
-
ReadItemAsync(id, partitionKey)bruges til at hente et enkelt element baseret på dets ID og partitioneringsnøgle. -
ReadManyItemsAsync(idsAndPartitionKeys)bruges til at hente flere elementer baseret på deres ID'er og partitioneringsnøgler.
-
-
Oprettelse af data:
-
CreateItemAsync(object)bruges til at indsætte et nyt objekt i containeren.
-
-
Sletning af data:
-
DeleteItemAsync(id, partitionKey)bruges til at slette et element ved hjælp af dets ID og partitioneringsnøgle.
-
-
Opdatering af data:
-
PatchItemAsync(id, partitionKey, patchOperations)bruges til at opdatere et element. -
ReplaceItemAsync(object, id)bruges til at erstatte et eksisterende element med et nyt.
-
-
Upsert:
-
UpsertItemAsync(object, id)bruges til enten at oprette et nyt element, hvis det ikke eksisterer, eller opdatere et eksisterende element.
-
Når du kalder disse metoder, modtager du et svar, der indeholder:
-
Resource: Det element, der blev hentet, oprettet, slettet eller opdateret.
-
RequestCharge: En værdi, der angiver omkostningerne ved operationen, målt i RUs.
-
StatusCode: HTTP-statuskoden, f.eks. 404, hvis et element ikke blev fundet.
-
Headers: HTTP-svarkoder og relaterede metadata.
-
Diagnostics: Brugbar information til fejlfinding.
Håndtering af fejl og statuskoder
Når du arbejder med Cosmos DB, vil du ofte støde på situationer, hvor ressourcer allerede eksisterer, eller der opstår fejl i forbindelse med forespørgsler. For eksempel, når en database eller container allerede er oprettet, kan svaret være:
Fejlhåndtering er også vigtig. Hvis du f.eks. forsøger at oprette ressourcer, mens emulatoren ikke kører, eller der er et problem med netværket, kan du få en HttpRequestException, som du skal være forberedt på at håndtere.
I praktiske scenarier vil du ofte få brug for at teste dine operationer ved at slette en eksisterende database og derefter oprette den igen, hvilket sikrer, at din kode korrekt opretter de nødvendige ressourcer:
Denne test kan sikre, at du ikke har problemer med eksisterende data, før du begynder at migrere eller opdatere større mængder af data, som f.eks. produkter fra en SQL Server-database.
Vigtige overvejelser
Det er vigtigt at forstå, at når du arbejder med Cosmos DB, er det ikke kun et spørgsmål om at oprette databaser og containere. Indeksering og partitionering er fundamentale aspekter, som påvirker ydeevnen for dine applikationer. Partitionering sikrer, at data bliver distribueret effektivt, mens indeksering hjælper med at optimere forespørgsler på store mængder data.
Derudover skal du være opmærksom på, at Cosmos DB tilbyder automatisk skalering af throughput, men du kan vælge at justere RUs manuelt afhængigt af dine behov. Det kan være nødvendigt at justere RUs baseret på trafikmønstre eller for at optimere omkostningerne ved at balancere belastningen mellem dine operationer.
For at sikre, at din applikation kan håndtere store mængder data, bør du også være opmærksom på eventuelle fejlhåndteringsmekanismer og hvordan du arbejder med retries, især hvis du opretter en løsning, der skalerer op eller ned på din Azure Cosmos DB-resource.
Hvordan håndtere ratebegrænsning i API'er med ASP.NET Core
I dagens webtjenester er ratebegrænsning en vigtig mekanisme for at beskytte API'er mod overbelastning og misbrug. Med indførelsen af ASP.NET Core 7 er ratebegrænsning blevet nemmere at implementere ved hjælp af det nye middleware, der leveres som en separat NuGet-pakke. Denne funktion giver udviklere mulighed for at styre, hvor mange API-anmodninger der kan foretages af en klient indenfor et bestemt tidsvindue.
For at demonstrere brugen af ratebegrænsning i en simpel API, kan vi starte med at se på et scenario, hvor en klient forsøger at få adgang til en API, der returnerer produktdata. I eksemplet er der implementeret et system, der begrænser antallet af API-kald, som en klient kan foretage pr. tidsenhed.
I begyndelsen af koden oprettes en HTTP-klient, som vil sende anmodninger til API'et. Her tilføjes en header til anmodningen, som angiver klientens unikke ID, hvilket gør det muligt for serveren at identificere og spore de anmodninger, der stammer fra en bestemt klient. Det er vigtigt, at klienten også inkluderer en "Accept"-header, der specificerer, at den forventer JSON-svar fra API'et.
Når anmodningen sendes, modtager klienten et svar fra serveren. Hvis anmodningen er vellykket (HTTP 200 OK), modtager klienten en liste af produkter. Hvis antallet af anmodninger overskrider serverens ratebegrænsning, vil serveren sende et svar med statuskode 429, hvilket indikerer, at klienten har overskredet den tilladte kvote af API-kald. Samtidig vil serveren inkludere en "Retry-After"-header, som angiver, hvor mange sekunder klienten skal vente, før den kan forsøge igen.
Når klienten modtager denne information, kan den implementere en logik, der gør det muligt for den at vente det anbefalede antal sekunder, før den prøver at sende en ny anmodning. Dette sikrer, at klienten overholder de begrænsninger, der er fastsat af API'et.
I dette eksempel er det også muligt at bemærke, hvordan ratebegrænsningens adfærd kan variere afhængigt af klientens ID. Dette giver mulighed for at differentiere ratebegrænsningen mellem forskellige klienter, hvilket kan være nyttigt i scenarier, hvor nogle klienter skal have højere tilladelse end andre.
ASP.NET Core's ratebegrænsningsmiddleware er især nyttig i scenarier med højt trafiktryk, hvor man ønsker at undgå, at systemet bliver overbelastet af for mange anmodninger fra én klient. Middleware-pakken giver udviklere mulighed for at implementere fleksible regler, der kan tilpasses for at beskytte API'et uden at miste brugervenligheden.
Der er flere vigtige elementer at tage højde for, når man implementerer ratebegrænsning i et API. For det første skal man overveje, hvilken type kvotering der giver mening for ens API: skal det være pr. minut, time eller pr. dag? Derudover er det vigtigt at tage højde for, hvordan klienterne håndterer statuskoden 429, og sikre at de reagerer korrekt ved at vente det anbefalede antal sekunder.
En anden vigtig overvejelse er, hvordan man håndterer forskellige typer klienter. Det kan være nyttigt at oprette regler, der giver visse klienter eller brugere mere leeway end andre, især i tilfælde hvor nogle klienter er mere betroede eller har betalt for højere adgangsgrænser.
Desuden skal man sikre, at serverens logning af ratebegrænsningshændelser er tilstrækkelig detaljeret til at muliggøre fejlfinding og analyse af, hvordan API'et bliver brugt. Dette kan omfatte logning af den specifikke anmodning, der blev blokeret, samt eventuelle fejlmeddelelser og headers, som f.eks. "Retry-After", der indikerer, hvornår klienten kan forsøge igen.
Det er også vigtigt at bemærke, at ratebegrænsning ikke kun beskytter API'et mod misbrug, men også sikrer, at API'et kan opretholde stabilitet, selv når den samlede trafik er høj. Ratebegrænsning hjælper med at fordele belastningen jævnt og forhindre "traffic spikes", der kan få servere til at gå ned.
Ved implementeringen af ratebegrænsning bør man altid være opmærksom på balancen mellem brugervenlighed og beskyttelse. For mange begrænsninger kan føre til, at brugerne oplever forstyrrelser, mens for få begrænsninger kan føre til, at serveren bliver overbelastet.
Hvordan konfigureres og testes OData-tjenester i en ASP.NET Core-applikation?
OData-tjenester i ASP.NET Core muliggør en fleksibel og effektiv måde at eksponere data via web-API’er. Ved korrekt konfiguration kan man understøtte avancerede forespørgselsfunktioner og sikre, at klientapplikationer kan tilgå data struktureret, dokumenteret og kontrolleret. Et centralt aspekt ved opsætning af OData i en applikation er registreringen af OData-modeller gennem AddRouteComponents, hvor hver model bindes til en bestemt rute, såsom "catalog" eller "ordersystem". Samtidig aktiveres forespørgselsfunktioner som $select, $expand, $filter, $orderby, $top og $count for at tillade mere granulære og præcise datatransaktioner. SetMaxTop(100) sætter en grænse for, hvor mange elementer der maksimalt returneres i én forespørgsel, hvilket er vigtigt for ydeevne og sikkerhed.
I konfigurationsfilen launchSettings.json justeres applikationens port til eksempelvis 5101 for HTTPS-adgang. Dette gør det nemt at interagere med tjenesten lokalt og konsistent. Testning af OData-modellerne sker indledningsvist gennem Swagger, som automatisk dokumenterer de tilgængelige endpoints og OData-entity sets. En GET-forespørgsel til /catalog returnerer metadata om de eksponerede entiteter som "Categories", "Products" og "Suppliers", mens en anmodning til /catalog/$metadata afslører strukturen og de relationelle forbindelser i EDM-modellen.
Efter validering af modellerne implementeres OData-controllere, en for hver entitet. Disse klasser nedarver fra ODataController og anvender EnableQuery-attributten til at tillade OData-forespørgsler. Controlleren injiceres med en instans af datakonteksten, her NorthwindContext, og definerer typisk to metoder: én til at returnere alle instanser af entiteten og én til at returnere en enkelt instans ud fra en nøgle.
Når controllerne for fx "Categories", "Products" og "Suppliers" er oprettet, udvider Swagger automatisk sin dokumentation. En anmodning til /catalog/Categories vil eksempelvis returnere alle kategorier i JSON-format, med felter som CategoryId, CategoryName, Description og Picture. Under overfladen logges SQL-forespørgslerne, og det fremgår, hvordan OData genererer SQL, der udtrækker præcist de nødvendige data uden at overføre hele datamængden til serveren først. Dette understreger ODatas evne til at udføre effektiv datahåndtering.
Til testformål er Swagger nyttig, men hurtigt uoverskuelig. Her tilbyder Visual Studio Code med udvidelsen REST Client en langt mere fleksibel metode. Ved at oprette en .http-fil, eksempelvis odata-catalog.http, kan man definere HTTP-forespørgsler direkte og sende dem uden at forlade editoren. Anmodninger kan adskilles med ### og derved simulere forskellige scenarier. En simpel anmodning til https://localhost:5101/catalog/categories returnerer samme JSON-svar som Swagger, men i en mere udviklervenlig og repetérbar kontekst. Flere anmodninger kan nemt tilføjes, fx for at hente én kategori via nøgle: catalog/categories(3).
Det er afgørende for læseren at forstå, at OData ikke blot er et transportlag, men en semantisk rig protokol, som tillader klienter at forme deres dataadgang dynamisk. Dette stiller krav til serverens eksponering og sikkerhedsmodeller, men giver samtidig en bemærkelsesværdig grad af fleksibilitet og interoperabilitet, især i microservice-arkitekturer og dataintensive applikationer.
Et vigtigt aspekt, som bør tilføjes, er forståelsen af hvordan autorisation og autentifikation skal integreres i OData-endpoints. Uden disse mekanismer er alle dataeksponeringer potentielt sårbare. Brugen af policies og scopes sammen med f.eks. JWT-baseret autentifikation bør overvejes grundigt fra begyndelsen. Ligeledes bør datakonsistens og concurrency-strategier diskuteres, særligt når man udvider OData til at håndtere POST, PUT eller DELETE-operationer. Det er også væsentligt at forstå, at selvom OData tillader rige forespørgsler, skal man som udvikler kontrollere, hvilke forespørgsler man ønsker at tillade, for at undgå uventet belastning eller data leaks.

Deutsch
Francais
Nederlands
Svenska
Norsk
Dansk
Suomi
Espanol
Italiano
Portugues
Magyar
Polski
Cestina
Русский