Steinar

Kollerud

Utvikler

Utvikler

6. november 2025

Utvikling

Med .NET Conf 2025 neste uke nærmer vi oss lanseringen av .NET 10 og C# 14, og det er mye spennende i vente. Etter å ha delt en rekke LinkedIn-poster om de viktigste nyhetene, tenkte jeg å samle alle punktene i en oversiktlig artikkel. Her får du en rask gjennomgang av de mest interessante endringene.

Ny syntax for extensions i C# 14 – en ny måte å organisere extension-metoder

En av de mest aktuelle featurene i C# 14 er uten tvil den nye syntaxen for extension-metoder. Dette er først og fremst en syntaktisk forbedring rettet mot lesbarhet og organisering.

#1 - Egen syntax for extensions

Nå blir det langt enklere å se hva som faktisk er en extension. Man slipper å lete etter "this" i parameter-listen, og det er ye tydeligere hva som foregår.

public static class MyExtensions
{
    // Old extensions syntax
    public static IEnumerable<Employee> StartDateLaterThan(
        this IEnumerable<Employee> employees,
        DateTimeOffset dateTime)
        => employees.Where(e => e.StartDate > dateTime);

    // New extensions syntax
    extension(IEnumerable<Employee> employees)
    {
        public IEnumerable<Employee> StartDateLaterThan(
            DateTimeOffset dateTime)
            => employees.Where(e => e.StartDate > dateTime

#2 - Naturlig gruppering basert på receiver

Extensions kan grupperes mer logisk ut fra hvilken receiver de hører til. Det gir en mer intuitiv kodebase som er enklere å navigere.

public static class MyExtensions
{
    // Extensions block for all extensions on receiver
    // IEnumerable<Employee>
    extension(IEnumerable<Employee> source)
    {
        public IEnumerable<Employee> StartDateLaterThan(
            DateTimeOffset dateTime)
            => employees.Where(e => e.StartDate > dateTime);

        public IEnumerable<Employee> IsActive()
            => employees.Where(e => e.IsActive

#3 - Extension properties og statiske extensions

C# 14 gir oss også støtte for extension properties. Det betyr at man endelig kan bruke property-syntax selv om man ikke har tilgang til den opprinnelige klassen. Et stort pluss for ryddighet og konsistens i koden.

public static class MyExtensions
{
    // Extension properties
    extension(Employee employee)
    {
        public string FullName
            => $"{employee.FirstName} {employee.LastName}"

Field og null-conditional assignment – kult, men litt blandete følelser

To av nyhetene som jeg er usikker på jeg liker eller hater er "field" for automatisk backing field og null-conditional assignment. Begge retter seg mot å kutte litt kode, men er det utelukkende positivt?

#1 - Field-nøkkelordet

Du slipper å skrive eksplisitte backing fields når du har behov for logikk i property accessors. Smart på papiret, men er det egentlig en god idé å legge mer logikk i get og set?

// Old property accessor body syntax
private string _firstName;

public string FirstName
{
    get => _firstName;
    set => _firstName = string.IsNullOrWhiteSpace(value)
        ? throw new ArgumentException(
            "Value cannot be null or whitespace.",
            nameof(value))
        : value;
}

// New property accessor body syntax
public string FirstName
{
    get;
    set => field = string.IsNullOrWhiteSpace(value)
        ? throw new ArgumentException(
            "Value cannot be null or whitespace.",
            nameof(value))
        : value

#2 - Null-conditional assignment

I C# 14 kan du skrive person?.Age = 30 uten å sjekke om person er null med en tradisjonell if-statement? Det sparer noen linjer kode, men om det er en forbedring er jeg usikker på.

// Old null-conditional assignment
if (employee is not null)
{
    employee.FirstName = "Ola";
    employee.LastName = "Normann";
    employee.LastUpdated = DateTimeOffset.Now;
}

// New null-conditional assignment
employee?.FirstName = "Ola";
employee?.LastName = "Normann";
employee?.LastUpdated = DateTimeOffset.Now

Disse to endringene er typiske eksempler på syntactic sugar. Det ser stilig ut, men man bør spørre seg om det faktisk forbedrer kodebasen eller kun gjør den mer uleselig.

Testing får et løft – enklere ende til ende tester

En av de mest praktiske forbedringene i .NET 10 finner du på testfronten. Jeg er selv tilhenger av integrasjonstester, og nå blir disse enda kraftigere.Ved å kombinere UseKestrel() og StartServer() med WebApplicationFactory<TEntryPoint> kan man starte applikasjonen som en ekte server, direkte fra testene. Det betyr at du kan kjøre fullverdige ende til ende tester der frontend og backend snakker sammen i samme testløp, uten å hoste backend separat.

// InitializeAsync() and DisposeAsync() of a class implementing
// WebApplicationFactory<TEntryPoint>

public async Task InitializeAsync()
{
    await _sqlContainer.StartAsync();

    // use overload UseKestrel(int port) to specify port
    UseKestrel();
    StartServer();

    HttpClient = CreateClient();
}

public new Task DisposeAsync()
{
    return _sqlContainer.StopAsync

Dette kommer på toppen av det vi allerede fikk med Testcontainers, og åpner for et langt mer helhetlig og realistisk testmiljø.

Filbaserte apper – .NET uten prosjektfil

Microsoft ønsker å senke terskelen for å komme i gang med .NET, og filbaserte apper er et godt eksempel på det.

#1 - Kjør .NET med én enkelt .cs-fil

Nå trenger du ingen .csproj eller prosjektstruktur for å skrive små apper. Du kan kjøre en enkelt fil med dotnet run app.cs.

dotnet run app.cs


// app.cs
Console.WriteLine("Hello .NET 10 from C# script!"

#2 - Innebygd støtte for pakker og SDK

Du kan legge til NuGet-pakker og angi SDK direkte i filen med #:package og #:sdk.

// app.cs
package System.Text.Json;
// #r:sdk <SDK name>
// #r:property

using System.Text.Json;

var p = new Person("Ola", 42);
Console.WriteLine(JsonSerializer.Serialize(p));
Console.WriteLine($"Hello .NET 10 from C# script!");

record Person(string Name, int Age

#3 - Konvertering til fullt prosjekt

Skulle appen vokse kan du konvertere den til et vanlig prosjekt med én kommando. Praktisk og fleksibelt.

dotnet project convert app.cs

Dette kommer kanskje ikke til å endre hverdagen for erfarne .NET-utviklere, men er enda en feature som Microsoft håper skal gjøre plattformen mer tilgjengelig for nybegynnere og de som kommer fra mer skriptorienterte språk.

LINQ og EF Core – Nye typer og joins

I .NET 10 får både LINQ og Entity Framework Core flere etterlengtede utvidelser.

#1 - LeftJoin og RightJoin i LINQ

Endelig kan du skrive LeftJoin eller RightJoin direkte, uten å ty til workarounds med GroupJoin og SelectMany. Mye renere, mye lettere å lese.

// Left join before .NET 10 :(
var q =
    ctx.Students
        .GroupJoin(
            ctx.Departments,
            s => s.DeptId,
            d => d.Id,
            (s, deps) => new { s, deps })
        .SelectMany(
            x => x.deps.DefaultIfEmpty(),
            (x, d) => new
            {
                x.s.Name,
                Dept = d is not null ? d.Name : "[NONE]"


// Left join in .NET 10 :)
var q =
    ctx.Students
        .LeftJoin(
            ctx.Departments,
            s => s.DeptId,
            d => d.Id,
            (s, d) => new
            {
                s.Name,
                Dept = d is not null ? d.Name : "[NONE]"

#2 - JSON-kolonner i EF Core

Du slipper å lagre JSON som tekst og gjøre manuell serialisering. EF Core 10 gir innebygd støtte for JSON-kolonner i SQL Server og Azure SQL.

// Old json columns
public class Document
{
    public int Id { get; set; }

    // Stored as nvarchar(max)
    public string MetadataJson { get; set; } = "{}";
}

// Query on raw JSON string
var docs = await ctx.Documents
    .Where(d => d.MetadataJson.Contains("\"lang\":\"en\""))
    .ToListAsync


using System.Text.Json.Nodes;

// New support for json columns
public class Document
{
    public int Id { get; set; }

    [Column(TypeName = "json")]
    public JsonObject Metadata { get; set; } = new()
    {
        ["lang"] = "en",
        ["tags"] = new JsonArray("search", "ai"

#3 - Komplekse typer som JSON

Du kan mappe verdier som objekter direkte til én kolonne og filtrere på innholdet i JSON uten rå SQL. En funksjon som virkelig gjør EF Core mer moderne og fleksibel.

public class Customer
{
    public int Id { get; set; }
    public ContactInfo Contact { get; set; } = new();
}

[ComplexType]
public class ContactInfo
{
    public string Email { get; set; } = "";
    public Address Address { get; set; } = new();
}

public class Address
{
    public string Line1 { get; set; } = "";
    public string City { get; set; } = "";
    public string Country { get; set; } = ""


public class AppDbContext : DbContext
{
    public DbSet<Customer> Customers => Set<Customer>();

    protected override void OnModelCreating(
        ModelBuilder modelBuilder)
    {
        var c = modelBuilder.Entity<Customer>();

        // Store Contact as one JSON column
        c.ComplexProperty(x => x.Contact).ToJson();
        c.Property<int>("Id");
    }
}


// Find customers in a city
// EF translates into JSON path in SQL

var oslo = await ctx.Customers
    .Where(c => c.Contact.Address.City == "Oslo")
    .ToListAsync();

// Bulk update a nested JSON property (no manual SQL)
await ctx.Customers
    .Where(c => c.Contact.Email.EndsWith("@contoso.com"))
    .ExecuteUpdate(s => s.SetProperty(
        c => c.Contact.Address.Country, "NO"

Oppsummert

Alt i alt føles releasen av .NET 10 og C# 14 litt tynnere enn forventet. Det er ikke mange banebrytende endringer, men de få nyhetene som faktisk kommer har likevel potensial til å forbedre måten vi skriver, organiserer og tester kode på. Dette er etter min mening de mest relevante og interessante funksjonene, men hva synes du? Er det nok nytt til å skape entusiasme, eller burde det kommet enda mer?

Med .NET Conf 2025 neste uke nærmer vi oss lanseringen av .NET 10 og C# 14, og det er mye spennende i vente. Etter å ha delt en rekke LinkedIn-poster om de viktigste nyhetene, tenkte jeg å samle alle punktene i en oversiktlig artikkel. Her får du en rask gjennomgang av de mest interessante endringene.

Ny syntax for extensions i C# 14 – en ny måte å organisere extension-metoder

En av de mest aktuelle featurene i C# 14 er uten tvil den nye syntaxen for extension-metoder. Dette er først og fremst en syntaktisk forbedring rettet mot lesbarhet og organisering.

#1 - Egen syntax for extensions

Nå blir det langt enklere å se hva som faktisk er en extension. Man slipper å lete etter "this" i parameter-listen, og det er ye tydeligere hva som foregår.

public static class MyExtensions
{
    // Old extensions syntax
    public static IEnumerable<Employee> StartDateLaterThan(
        this IEnumerable<Employee> employees,
        DateTimeOffset dateTime)
        => employees.Where(e => e.StartDate > dateTime);

    // New extensions syntax
    extension(IEnumerable<Employee> employees)
    {
        public IEnumerable<Employee> StartDateLaterThan(
            DateTimeOffset dateTime)
            => employees.Where(e => e.StartDate > dateTime

#2 - Naturlig gruppering basert på receiver

Extensions kan grupperes mer logisk ut fra hvilken receiver de hører til. Det gir en mer intuitiv kodebase som er enklere å navigere.

public static class MyExtensions
{
    // Extensions block for all extensions on receiver
    // IEnumerable<Employee>
    extension(IEnumerable<Employee> source)
    {
        public IEnumerable<Employee> StartDateLaterThan(
            DateTimeOffset dateTime)
            => employees.Where(e => e.StartDate > dateTime);

        public IEnumerable<Employee> IsActive()
            => employees.Where(e => e.IsActive

#3 - Extension properties og statiske extensions

C# 14 gir oss også støtte for extension properties. Det betyr at man endelig kan bruke property-syntax selv om man ikke har tilgang til den opprinnelige klassen. Et stort pluss for ryddighet og konsistens i koden.

public static class MyExtensions
{
    // Extension properties
    extension(Employee employee)
    {
        public string FullName
            => $"{employee.FirstName} {employee.LastName}"

Field og null-conditional assignment – kult, men litt blandete følelser

To av nyhetene som jeg er usikker på jeg liker eller hater er "field" for automatisk backing field og null-conditional assignment. Begge retter seg mot å kutte litt kode, men er det utelukkende positivt?

#1 - Field-nøkkelordet

Du slipper å skrive eksplisitte backing fields når du har behov for logikk i property accessors. Smart på papiret, men er det egentlig en god idé å legge mer logikk i get og set?

// Old property accessor body syntax
private string _firstName;

public string FirstName
{
    get => _firstName;
    set => _firstName = string.IsNullOrWhiteSpace(value)
        ? throw new ArgumentException(
            "Value cannot be null or whitespace.",
            nameof(value))
        : value;
}

// New property accessor body syntax
public string FirstName
{
    get;
    set => field = string.IsNullOrWhiteSpace(value)
        ? throw new ArgumentException(
            "Value cannot be null or whitespace.",
            nameof(value))
        : value

#2 - Null-conditional assignment

I C# 14 kan du skrive person?.Age = 30 uten å sjekke om person er null med en tradisjonell if-statement? Det sparer noen linjer kode, men om det er en forbedring er jeg usikker på.

// Old null-conditional assignment
if (employee is not null)
{
    employee.FirstName = "Ola";
    employee.LastName = "Normann";
    employee.LastUpdated = DateTimeOffset.Now;
}

// New null-conditional assignment
employee?.FirstName = "Ola";
employee?.LastName = "Normann";
employee?.LastUpdated = DateTimeOffset.Now

Disse to endringene er typiske eksempler på syntactic sugar. Det ser stilig ut, men man bør spørre seg om det faktisk forbedrer kodebasen eller kun gjør den mer uleselig.

Testing får et løft – enklere ende til ende tester

En av de mest praktiske forbedringene i .NET 10 finner du på testfronten. Jeg er selv tilhenger av integrasjonstester, og nå blir disse enda kraftigere.Ved å kombinere UseKestrel() og StartServer() med WebApplicationFactory<TEntryPoint> kan man starte applikasjonen som en ekte server, direkte fra testene. Det betyr at du kan kjøre fullverdige ende til ende tester der frontend og backend snakker sammen i samme testløp, uten å hoste backend separat.

// InitializeAsync() and DisposeAsync() of a class implementing
// WebApplicationFactory<TEntryPoint>

public async Task InitializeAsync()
{
    await _sqlContainer.StartAsync();

    // use overload UseKestrel(int port) to specify port
    UseKestrel();
    StartServer();

    HttpClient = CreateClient();
}

public new Task DisposeAsync()
{
    return _sqlContainer.StopAsync

Dette kommer på toppen av det vi allerede fikk med Testcontainers, og åpner for et langt mer helhetlig og realistisk testmiljø.

Filbaserte apper – .NET uten prosjektfil

Microsoft ønsker å senke terskelen for å komme i gang med .NET, og filbaserte apper er et godt eksempel på det.

#1 - Kjør .NET med én enkelt .cs-fil

Nå trenger du ingen .csproj eller prosjektstruktur for å skrive små apper. Du kan kjøre en enkelt fil med dotnet run app.cs.

dotnet run app.cs


// app.cs
Console.WriteLine("Hello .NET 10 from C# script!"

#2 - Innebygd støtte for pakker og SDK

Du kan legge til NuGet-pakker og angi SDK direkte i filen med #:package og #:sdk.

// app.cs
package System.Text.Json;
// #r:sdk <SDK name>
// #r:property

using System.Text.Json;

var p = new Person("Ola", 42);
Console.WriteLine(JsonSerializer.Serialize(p));
Console.WriteLine($"Hello .NET 10 from C# script!");

record Person(string Name, int Age

#3 - Konvertering til fullt prosjekt

Skulle appen vokse kan du konvertere den til et vanlig prosjekt med én kommando. Praktisk og fleksibelt.

dotnet project convert app.cs

Dette kommer kanskje ikke til å endre hverdagen for erfarne .NET-utviklere, men er enda en feature som Microsoft håper skal gjøre plattformen mer tilgjengelig for nybegynnere og de som kommer fra mer skriptorienterte språk.

LINQ og EF Core – Nye typer og joins

I .NET 10 får både LINQ og Entity Framework Core flere etterlengtede utvidelser.

#1 - LeftJoin og RightJoin i LINQ

Endelig kan du skrive LeftJoin eller RightJoin direkte, uten å ty til workarounds med GroupJoin og SelectMany. Mye renere, mye lettere å lese.

// Left join before .NET 10 :(
var q =
    ctx.Students
        .GroupJoin(
            ctx.Departments,
            s => s.DeptId,
            d => d.Id,
            (s, deps) => new { s, deps })
        .SelectMany(
            x => x.deps.DefaultIfEmpty(),
            (x, d) => new
            {
                x.s.Name,
                Dept = d is not null ? d.Name : "[NONE]"


// Left join in .NET 10 :)
var q =
    ctx.Students
        .LeftJoin(
            ctx.Departments,
            s => s.DeptId,
            d => d.Id,
            (s, d) => new
            {
                s.Name,
                Dept = d is not null ? d.Name : "[NONE]"

#2 - JSON-kolonner i EF Core

Du slipper å lagre JSON som tekst og gjøre manuell serialisering. EF Core 10 gir innebygd støtte for JSON-kolonner i SQL Server og Azure SQL.

// Old json columns
public class Document
{
    public int Id { get; set; }

    // Stored as nvarchar(max)
    public string MetadataJson { get; set; } = "{}";
}

// Query on raw JSON string
var docs = await ctx.Documents
    .Where(d => d.MetadataJson.Contains("\"lang\":\"en\""))
    .ToListAsync


using System.Text.Json.Nodes;

// New support for json columns
public class Document
{
    public int Id { get; set; }

    [Column(TypeName = "json")]
    public JsonObject Metadata { get; set; } = new()
    {
        ["lang"] = "en",
        ["tags"] = new JsonArray("search", "ai"

#3 - Komplekse typer som JSON

Du kan mappe verdier som objekter direkte til én kolonne og filtrere på innholdet i JSON uten rå SQL. En funksjon som virkelig gjør EF Core mer moderne og fleksibel.

public class Customer
{
    public int Id { get; set; }
    public ContactInfo Contact { get; set; } = new();
}

[ComplexType]
public class ContactInfo
{
    public string Email { get; set; } = "";
    public Address Address { get; set; } = new();
}

public class Address
{
    public string Line1 { get; set; } = "";
    public string City { get; set; } = "";
    public string Country { get; set; } = ""


public class AppDbContext : DbContext
{
    public DbSet<Customer> Customers => Set<Customer>();

    protected override void OnModelCreating(
        ModelBuilder modelBuilder)
    {
        var c = modelBuilder.Entity<Customer>();

        // Store Contact as one JSON column
        c.ComplexProperty(x => x.Contact).ToJson();
        c.Property<int>("Id");
    }
}


// Find customers in a city
// EF translates into JSON path in SQL

var oslo = await ctx.Customers
    .Where(c => c.Contact.Address.City == "Oslo")
    .ToListAsync();

// Bulk update a nested JSON property (no manual SQL)
await ctx.Customers
    .Where(c => c.Contact.Email.EndsWith("@contoso.com"))
    .ExecuteUpdate(s => s.SetProperty(
        c => c.Contact.Address.Country, "NO"

Oppsummert

Alt i alt føles releasen av .NET 10 og C# 14 litt tynnere enn forventet. Det er ikke mange banebrytende endringer, men de få nyhetene som faktisk kommer har likevel potensial til å forbedre måten vi skriver, organiserer og tester kode på. Dette er etter min mening de mest relevante og interessante funksjonene, men hva synes du? Er det nok nytt til å skape entusiasme, eller burde det kommet enda mer?

Kanskje du liker:

Kanskje du liker:

Kanskje du liker:

Utvikling

6. november 2025

Det er ikke mange banebrytende endringer, men de få nyhetene som faktisk kommer har likevel potensial til å forbedre måten vi skriver, organiserer og tester kode på.

Utvikling

6. november 2025

Det er ikke mange banebrytende endringer, men de få nyhetene som faktisk kommer har likevel potensial til å forbedre måten vi skriver, organiserer og tester kode på.

Utvikling

4. august 2025

Denne artikkelen av Adrian Brenne viser steg-for-steg hvordan du bruker både system- og bruker-tilordnet Managed Identity for å sikre tilgang til tjenester som Azure Storage og Key Vault med minimal risiko og maksimal effektivitet.

Utvikling

4. august 2025

Denne artikkelen av Adrian Brenne viser steg-for-steg hvordan du bruker både system- og bruker-tilordnet Managed Identity for å sikre tilgang til tjenester som Azure Storage og Key Vault med minimal risiko og maksimal effektivitet.

kotlinconf2025

Utvikling

5. juni 2025

Dette var personlig min aller første konferanse. For å være helt ærlig visste jeg ikke helt hva jeg kunne forvente. Er det som en bedriftspresentasjon der JetBrains er bedriften? Hvorfor drar man egentlig på konferanse?

kotlinconf2025

Utvikling

5. juni 2025

Dette var personlig min aller første konferanse. For å være helt ærlig visste jeg ikke helt hva jeg kunne forvente. Er det som en bedriftspresentasjon der JetBrains er bedriften? Hvorfor drar man egentlig på konferanse?

Utvikling

6. november 2025

Det er ikke mange banebrytende endringer, men de få nyhetene som faktisk kommer har likevel potensial til å forbedre måten vi skriver, organiserer og tester kode på.

Utvikling

4. august 2025

Denne artikkelen av Adrian Brenne viser steg-for-steg hvordan du bruker både system- og bruker-tilordnet Managed Identity for å sikre tilgang til tjenester som Azure Storage og Key Vault med minimal risiko og maksimal effektivitet.

Utvikling

6. november 2025

Det er ikke mange banebrytende endringer, men de få nyhetene som faktisk kommer har likevel potensial til å forbedre måten vi skriver, organiserer og tester kode på.

Utvikling

4. august 2025

Denne artikkelen av Adrian Brenne viser steg-for-steg hvordan du bruker både system- og bruker-tilordnet Managed Identity for å sikre tilgang til tjenester som Azure Storage og Key Vault med minimal risiko og maksimal effektivitet.

Footer Logo

Når riktig partner utgjør all forskjell



822 704 042

Pløens gate 7

0181 Oslo

hei@alv.no

+47 91 92 92 18

Copyright ©2025. All rights reserved.

Footer Logo

Når riktig partner utgjør all forskjell



822 704 042

Pløens gate 7

0181 Oslo

hei@alv.no

+47 91 92 92 18

Copyright ©2025. All rights reserved.

Footer Logo

Når riktig partner utgjør all forskjell



822 704 042

Pløens gate 7

0181 Oslo

hei@alv.no

+47 91 92 92 18

Copyright ©2025. All rights reserved.

Footer Logo

Når riktig partner utgjør all forskjell



822 704 042

Pløens gate 7

0181 Oslo

hei@alv.no

+47 91 92 92 18

Copyright ©2025. All rights reserved.