I dag er serverløse arkitekturer et kraftfuldt værktøj til at bygge moderne, skalerbare applikationer. Azure Functions er en af de mest populære platforme, der understøtter serverløs computing, og giver udviklere mulighed for at reagere på forskellige typer hændelser via "triggers". Dette kan være HTTP-anmodninger, timers eller en række andre begivenheder, som starter en funktion uden at skulle administrere servere.

I dette kapitel gennemgår vi, hvordan man kan oprette en funktion, der reagerer på HTTP-anmodninger, samt hvordan man kan implementere en timer-triggeret funktion, der for eksempel kan hente data fra en hjemmeside periodisk. Dette giver et konkret eksempel på, hvordan Azure Functions kan bruges til at automatisere opgaver, som f.eks. at hente opdateret information fra en webside.

Først opretter vi en simpel HTTP-triggeret funktion, der konverterer tal til ord, og derefter bygger vi en timer-triggeret funktion, der henter data fra en Amazon-side med jævne mellemrum.

Når du har oprettet din funktion i Azure, kan du teste den ved at bruge en browser og indsætte en query string, der angiver et tal. Funktionen vil returnere tallet som ord, for eksempel "et hundrede og treogtyve tusinde, fire hundrede og femoghalvfjerds" for tallet 123456. Denne type funktion kan være meget nyttig i mange scenarier, hvor du har brug for at omdanne numeriske data til læsbart format.

Men hvordan fungerer Azure Functions egentlig i praksis? Når du kører funktionen lokalt, som beskrevet i de første trin, kan du bruge et simpelt kommandoprompt eller terminalvindue til at observere, hvordan din funktion eksekveres. Det er værd at bemærke, at når funktionen modtager en ugyldig anmodning, for eksempel et tal, der ikke er et helt tal eller helt fraværende, vil den returnere en HTTP-statuskode 400, hvilket betyder, at der er sket en fejltagelse i anmodningen. Dette er et godt eksempel på, hvordan du kan implementere fejlhåndtering i dine funktioner.

Når du har forstået den grundlæggende funktionalitet i en HTTP-triggeret Azure Function, kan vi begynde at bygge en mere avanceret funktion, der ikke reagerer på HTTP-anmodninger, men på en timer-trigger. Formålet med denne funktion er at hente information fra en specifik webside, f.eks. for at holde styr på rangeringen af en bog på Amazon. Denne funktion kan køre på et fast tidsinterval, som for eksempel hver time.

Først skal du oprette et nyt projekt og tilføje nødvendige pakker til at arbejde med Azure Functions og HTTP-anmodninger. Her vil vi bruge en HTTP-klientfabrik til at konfigurere anmodningerne, som vi sender til Amazon. Ved at tilføje en klassedefinition for AzureFunctionsStartup, kan du konfigurere afhængighederne for klienten og sikre, at den er klar til at håndtere HTTP-anmodninger korrekt.

Når klienten er opsat, kan vi implementere en timer-triggeret funktion, som henter Amazon-siden ved at sende en HTTP GET-anmodning til den ønskede URL. Denne anmodning returnerer som regel en GZIP-komprimeret fil, som vi skal dekomprimere for at få adgang til sidens indhold. Når vi har dekomprimeret indholdet, kan vi analysere det og udtrække den nødvendige information, som for eksempel bogens Best Seller Rank.

Selvom denne funktion er et simpelt eksempel, demonstrerer det kraften og fleksibiliteten ved Azure Functions. Timer-triggers kan bruges til en bred vifte af opgaver, som kræver regelmæssige opdateringer, såsom at hente data fra eksterne API'er, kontrollere status for tjenester eller automatisk generere rapporter.

Ved at forstå hvordan timer- og HTTP-triggerede funktioner fungerer, er du godt rustet til at bygge serverløse nanoservices, der kan reagere på forskellige typer begivenheder og interagere med eksterne systemer på en effektiv måde.

Det er også vigtigt at være opmærksom på, at det er muligt at integrere Azure Functions med andre Azure-tjenester som f.eks. Azure Storage, Event Grid eller Service Bus for at udvide funktionaliteten af dine serverløse applikationer. Dette kan være nyttigt, hvis du ønsker at arbejde med store datamængder eller har brug for at håndtere asynkrone begivenheder på tværs af systemer.

Yderligere ressourcer og pakker kan bruges til at forbedre funktionens ydeevne, såsom at konfigurere retry-mekanismer, logge detaljeret information om anmodninger og svar, og håndtere fejlsituationer på en effektiv måde. Når man arbejder med timer-triggerede funktioner, er det vigtigt at tage højde for de tidsrammer, som funktionerne kører indenfor, og sikre, at systemet kan håndtere den mængde af samtidige opgaver, som der måtte opstå under høj trafik.

Når du bygger sådanne funktioner, skal du også være opmærksom på den sikkerhed, der kræves for at sikre, at din applikation ikke bliver udsat for uautoriserede adgange eller dataindbrud. Brug af autentificering og autorisation er essentiel for at beskytte både dine funktioner og de data, du arbejder med.

Hvordan arbejder man med Entity Framework Core og SQL Server: Et praktisk kig på opbygning og konfiguration

Når du arbejder med Entity Framework Core (EF Core) og SQL Server, er der flere vigtige aspekter at forstå, fra generering af modelklasser til konfiguration af databasen og udførelse af forespørgsler. Denne proces kan involvere brug af flere værktøjer, som dotnet-ef, og en forståelse for, hvordan EF Core håndterer datamodeller og relationer. Lad os gennemgå de væsentligste trin, der skal tages for at bygge og konfigurere en applikation, der bruger EF Core sammen med SQL Server.

Når du først starter, skal du bruge en kommando som denne for at scafolde en DbContext og generere modelklasser fra en eksisterende database:

css
Microsoft.EntityFrameworkCore.SqlServer --output-dir Models --namespace Northwind.Console.EFCore.Models --data-annotations --context NorthwindDb

Denne kommando er essentiel, da den genererer de nødvendige C#-klasser, som repræsenterer tabellerne i din database. Du skal være opmærksom på følgende:

  • Databasetilslutning: Det er vigtigt at vælge den rigtige forbindelsesstreng afhængig af, om du bruger en lokal SQL Server eller Azure SQL Database. Til Azure skal forbindelsesstrengen naturligvis justeres.

  • Databaseudbyder: I dette tilfælde bruger vi Microsoft.EntityFrameworkCore.SqlServer.

  • Outputmappe: Modellerne gemmes i en "Models"-mappe.

  • Navnerum: Et korrekt navnerum sikrer, at de genererede klasser nemt kan importeres og bruges i din applikation.

  • Brug af DataAnnotations og Fluent API: Kommandoen sikrer, at de nødvendige DataAnnotations anvendes, og at Fluent API kan benyttes til at styre relationer og andre konfigurationer.

Når du har kørt kommandoen, vil du bemærke, at der genereres et stort antal modelklasser, som repræsenterer tabellerne i din database. I det konkrete eksempel for Northwind-databasen, vil du finde en klasse som Category.cs, som repræsenterer en række i Categories-tabellen. Denne klasse vil indeholde attributter som [Key] for at markere den primære nøgle, og [InverseProperty] for at angive relationen mellem tabellerne, som er relevant for objektorienteret design.

Et interessant aspekt ved EF Core er, hvordan relationer mellem tabeller defineres. Tag for eksempel Category-klassen:

csharp
[Index("CategoryName", Name = "CategoryName")] public partial class Category { public Category() { Products = new HashSet<Product>(); } [Key] public int CategoryId { get; set; } [StringLength(15)]
public string CategoryName { get; set; } = null!;
[
Column(TypeName = "ntext")] public string? Description { get; set; } [Column(TypeName = "image")]
public byte[]? Picture { get; set; }
[
InverseProperty("Category")] public virtual ICollection<Product> Products { get; set; } }

Her er det væsentligt at bemærke:

  • [Index] attributten: Introduceret i EF Core 5.0, hjælper den med at definere indekser direkte i modellen. Dette er nyttigt, når du arbejder med store mængder data, og du ønsker at optimere forespørgsler.

  • Relationen mellem tabellerne: Med [InverseProperty] kan du definere, hvordan relationerne skal håndteres i både den parent- og child-klasse (i dette tilfælde mellem Category og Product).

I tilfældet med ProductsAboveAveragePrice.cs, vil du se, at denne klasse repræsenterer en række, som er hentet fra en databasevisning, snarere end en tabel, og er derfor markeret med [Keyless].

Når du åbner og gennemgår NorthwindDb.cs-klassen, ser du, hvordan DbContext er opbygget, og hvordan tabellerne forbindes til deres respektive DbSet-egenskaber. Her defineres forbindelsen og de nødvendige relationer via Fluent API i OnModelCreating-metoden:

csharp
protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.Entity<Product>(entity => { entity.HasOne(d => d.Category) .WithMany(p => p.Products) .HasForeignKey(d => d.CategoryId) .HasConstraintName("FK_Products_Categories"); }); }

Fluent API er utrolig kraftfuld, da det giver dig mulighed for at konfigurere dine datamodeller uden at være afhængig af dataannotations alene. Dette gør det muligt at tilpasse konfigurationer og relationer, som ikke nødvendigvis er lette at udtrykke med standardattributter.

Når det drejer sig om oprettelsen af din NorthwindDb-klasse, skal du være opmærksom på, at den er erklæret som partial. Dette betyder, at du kan tilføje yderligere funktionalitet i en separat fil uden at bekymre dig om at miste dine ændringer, hvis du regenerator klasserne på et senere tidspunkt. Det er en praktisk teknik til at gøre koden mere modulær og fleksibel.

Et af de mere praktiske skridt er at kunne tilpasse hvordan datakonfigurationen håndteres. Her skal du sikre dig, at følsomme oplysninger, som forbindelsesstrengen, ikke bliver hårdkodet i koden. For at beskytte disse oplysninger kan du benytte dig af konfigurationer fra eksterne kilder som f.eks. appsettings.json i ASP.NET Core applikationer, eller bruge miljøvariabler.

Når du nu har opbygget din model og konfigureret din datakontekst, er det tid til at begynde at lave forespørgsler. Du kan eksempelvis oprette en forespørgsel, der henter produkter, der koster mere end en bestemt pris:

csharp
using Microsoft.EntityFrameworkCore; var productsAbovePrice = context.Products .Where(p => p.UnitPrice > 50) .ToList();

Denne forespørgsel viser, hvordan du kan bruge LINQ sammen med EF Core til at hente data fra din database baseret på bestemte kriterier.

Det er vigtigt at forstå, at EF Core ikke kun handler om at mappe tabellerne i databasen til C#-klasser. Det handler i høj grad om at sikre, at datamodellerne er korrekt konfigureret til at understøtte de relationer og de forretningslogik, der findes i applikationen. Dette inkluderer at sikre korrekt håndtering af nøgler, relationer, indekser og standardværdier i databasen.

Hvordan fungerer OData i ASP.NET Core, og hvordan implementeres det effektivt?

OData, der blev udviklet af Microsoft i 2007 og senere standardiseret under OASIS i 2014, repræsenterer en effektiv måde at bygge og konsumere RESTful APIs på, hvor klienten får langt større kontrol over de data, der returneres. I modsætning til traditionelle web-API'er, hvor serveren definerer alle metoder og datamængder, anvender OData URL-forespørgsler til at specificere, hvilke data klienten ønsker at modtage. Denne tilgang muliggør en mere præcis og fleksibel dataudveksling, som samtidig minimerer antallet af forespørgsler frem og tilbage mellem klient og server.

Det centrale ved OData er brugen af query-strings, hvor klienten via parametre som $filter, $orderby, $select og $expand kan præcisere søgninger, filtrere og sortere data, samt udvide relationer mellem entiteter. For eksempel kan en klient i en Northwind database nøjes med at hente produkter, hvis navn indeholder “burger”, hvor prisen er under 4.95, sorteret efter land og pris, og samtidig inkludere leverandørinformation via $expand. Det gør det muligt at skræddersy dataudtræk, som både reducerer netværkstrafikken og øger effektiviteten.

Implementeringen af OData i ASP.NET Core sker ikke via en dedikeret projekt-skabelon, men gennem tilføjelse af nødvendige pakker til et standard ASP.NET Core Web API projekt. Det anbefales at bruge den nyeste version af ASP.NET Core OData-pakken, som understøtter OData version 4.0, da ældre pakker som Microsoft.Data.OData kun understøtter versioner 1-3 og ikke længere vedligeholdes. Projektet skal konfigureres med relevante dependencies og referencer, herunder databasen Northwind, som fungerer som et eksempel på en relationel datakilde.

OData-modeller defineres eksplicit gennem ODataConventionModelBuilder, hvor man vælger præcis hvilke entitetssæt, der skal eksponeres. Dette kan være alt fra produktkataloget til kundeordrer, og modellen kan tilpasses til kun at inkludere de nødvendige tabeller, hvilket sikrer en fleksibel og sikker dataeksponering. Samtidig gør denne tilgang det muligt at have flere OData-modeller i samme applikation, hvilket kan give forskellige views eller adgangspunkter til de underliggende data.

Ved at integrere OData i ASP.NET Core kan man aktivere avancerede features som projektion (udvælgelse af specifikke felter), filtrering, sortering og ekspandering af relationer, direkte i service-laget. Denne fleksibilitet forenkler udviklingen af API’er, hvor klienten kan styre dataudtræk præcist efter behov uden at skulle lave specialiserede endpoints for hver mulig forespørgsel.

Det er vigtigt at forstå, at selvom OData giver stor frihed til klienten, begrænses denne frihed af serverens konfiguration og sikkerhedspolitikker. Serveren kontrollerer, hvilke entitetssæt og felter der kan tilgås, og hvilke operationer der er tilladte. Dermed sikres dataintegritet og undgås potentielle sikkerhedsproblemer som følge af ukontrollerede forespørgsler.

Desuden bør man være opmærksom på, at OData ikke kun er begrænset til relationelle databaser eller Entity Framework Core. Data kan eksponeres fra en bred vifte af kilder, så længe de kan mappes til OData-standarden. Dette gør OData til en yderst alsidig teknologi i moderne API-design.

Yderligere viden om performance-optimering og sikkerhed ved OData er relevant for at undgå overbelastning og misbrug. For eksempel bør begrænsninger på query-dybde og resultatstørrelse sættes for at beskytte backend-ressourcer. Desuden kan implementering af caching og rate limiting være afgørende for at sikre stabil drift under stor belastning.

OData’s styrke ligger i dens evne til at skabe dynamiske, fleksible API’er, hvor klienten får den nødvendige kontrol uden at gå på kompromis med serverens styring. For udviklere i .NET-miljøet åbner ASP.NET Core OData således nye muligheder for at bygge robuste, skalerbare og let vedligeholdelige datatjenester.