DEV Community

Cristian Sifuentes
Cristian Sifuentes

Posted on

New Features in .NET 10 & C# 14 — The Expert’s Playbook (2025)

New Features in .NET 10 & C# 14 — The Expert’s Playbook (2025)

New Features in .NET 10 & C# 14 — The Expert’s Playbook (2025)

.NET 10 (LTS) and C# 14 dropped today — November 11, 2025. As an LTS release, .NET 10 is supported through November 14, 2028. This post is your concise, code‑first tour of what's new across the stack: runtime, C#, ASP.NET Core, and EF Core 10.

Why this post?

Because this release meaningfully changes how you start small (file‑based apps), how you compose APIs (Minimal API validation + OpenAPI 3.1), and how you model data (EF Core 10 complex types & JSON). And C# 14 is packed with quality‑of‑life and performance wins.


Table of Contents


What’s New in .NET 10

1) File‑Based Apps (single‑file C#)

C# now behaves like a first‑class scripting language for CLIs and utilities. You can run a single *.cs file with dotnet run — no .sln or .csproj required.

dotnet run main.cs
Enter fullscreen mode Exit fullscreen mode

File‑based apps support SDK and NuGet references via #: directives at the top of your file:

#:sdk Microsoft.NET.Sdk.Web
#:package Microsoft.EntityFrameworkCore.Sqlite@9.0.0

using Microsoft.EntityFrameworkCore;

var builder = WebApplication.CreateBuilder();
builder.Services.AddDbContext<OrderDbContext>(o => o.UseSqlite("Data Source=orders.db"));
var app = builder.Build();

app.MapGet("/orders", async (OrderDbContext db) => await db.Orders.ToListAsync());
app.Run();
return;

public record Order(string OrderNumber, decimal Amount);
public class OrderDbContext(DbContextOptions<OrderDbContext> options) : DbContext
{
    public DbSet<Order> Orders => Set<Order>();
}
Enter fullscreen mode Exit fullscreen mode

Reference existing projects too:

#:project ../ClassLib/ClassLib.csproj
Enter fullscreen mode Exit fullscreen mode

Cross‑platform shell scripts

#!/usr/bin/env dotnet
chmod +x app.cs
./app.cs
Enter fullscreen mode Exit fullscreen mode

Grow up when needed

Convert your script to a full project at any time:

dotnet project convert app.cs
Enter fullscreen mode Exit fullscreen mode

Multi‑file scripting is expected to expand in future versions, but this single‑file flow already unlocks fast prototypes and ops tools.


What’s New in C# 14

C# 14 focuses on ergonomics and performance. Highlights below with paste‑ready snippets.

1) Extension Members / Extension Blocks

Group instance & static extensions (methods and properties) for a receiver in one block.

public static class StringExtensions
{
    extension(string value)
    {
        public bool IsNullOrEmpty() => string.IsNullOrEmpty(value);
        public string Truncate(int max) => string.IsNullOrEmpty(value) || value.Length <= max
            ? value : value[..max];

        // static extension on the receiver type
        public static bool IsAscii(char c) => c <= 0x7F;
    }
}
Enter fullscreen mode Exit fullscreen mode

2) Extension Properties

Make intent obvious and templates cleaner:

public static class EnumerableExtensions
{
    extension<T>(IEnumerable<T> src)
    {
        public bool IsEmpty => !src.Any();
        public int Count => src.Count();
    }
}
Enter fullscreen mode Exit fullscreen mode

3) Private fields & caching inside extension blocks

public static class CacheExtensions
{
    extension<T>(IEnumerable<T> src)
    {
        private List<T>? _list;
        public List<T> Materialized => _list ??= src.ToList();
        public bool IsEmpty => Materialized.Count == 0;
    }
}
Enter fullscreen mode Exit fullscreen mode

4) Static extension members

public static class ProductExtensions
{
    extension(Product)
    {
        public static Product CreateDefault() => new() { Name = "Unnamed", Price = 0 };
        public static bool IsValidPrice(decimal price) => price >= 0;
    }
}
Enter fullscreen mode Exit fullscreen mode

5) Null‑conditional assignment

Assign with ?. without manual null checks:

user?.Profile = LoadProfile();
Enter fullscreen mode Exit fullscreen mode

6) The field keyword (backing field access)

Cleaner properties without manual fields:

public class ConfigReader
{
    public string FilePath
    {
        get => field ??= "data/config.json";
        set => field = value ?? throw new ArgumentNullException(nameof(value));
    }
}
Enter fullscreen mode Exit fullscreen mode

7) Lambda parameter modifiers without types

delegate bool TryParse<T>(string text, out T result);
TryParse<int> parse = (text, out result) => int.TryParse(text, out result);
Enter fullscreen mode Exit fullscreen mode

8) Partial constructors & partial events

Perfect for source generators:

public partial class User
{
    public partial User(string name);
    public partial event Action<string> Saved;
}
Enter fullscreen mode Exit fullscreen mode

9) User‑defined compound assignment operators

Improve performance for in‑place ops:

public struct Money(string currency, decimal amount)
{
    public decimal Amount { get; private set; } = amount;
    public string Currency { get; } = currency;

    public void operator +=(Money b)
    {
        if (Currency != b.Currency) throw new InvalidOperationException();
        Amount += b.Amount;
    }
}
Enter fullscreen mode Exit fullscreen mode

10) nameof for unbound generics & implicit Span conversions

Console.WriteLine(nameof(List<>)); // "List"
// Many calls now infer ReadOnlySpan<T> without type noise.
Enter fullscreen mode Exit fullscreen mode

What’s New in ASP.NET Core in .NET 10

1) Built‑in validation for Minimal APIs

builder.Services.AddValidation();

app.MapPost("/products",
    ([Range(1, int.MaxValue)] int productId, [Required] string name) =>
        TypedResults.Ok(new { productId, name })
);
Enter fullscreen mode Exit fullscreen mode

Disable on a route if needed:

app.MapPost("/raw", (int id, string name) => TypedResults.Ok(id))
   .DisableValidation();
Enter fullscreen mode Exit fullscreen mode

2) Server‑Sent Events (SSE)

Lightweight real‑time streams via TypedResults.ServerSentEvents.

public record StockPriceEvent(string Id, string Symbol, decimal Price, DateTime Timestamp);

public class StockService
{
    public async IAsyncEnumerable<StockPriceEvent> Generate([EnumeratorCancellation] CancellationToken ct)
    {
        var symbols = new[] { "MSFT", "AAPL", "GOOG", "AMZN" };
        while (!ct.IsCancellationRequested)
        {
            yield return new StockPriceEvent(DateTime.UtcNow:o, symbols[Random.Shared.Next(symbols.Length)],
                                             Math.Round((decimal)(100 + Random.Shared.NextDouble()*50), 2),
                                             DateTime.UtcNow);
            await Task.Delay(TimeSpan.FromSeconds(2), ct);
        }
    }
}

builder.Services.AddSingleton<StockService>();
app.MapGet("/stocks", (StockService s, CancellationToken ct) =>
    TypedResults.ServerSentEvents(s.Generate(ct), eventType: "stockUpdate"));
Enter fullscreen mode Exit fullscreen mode

3) OpenAPI 3.1 + YAML

builder.Services.AddOpenApi(o => o.OpenApiVersion = Microsoft.OpenApi.OpenApiSpecVersion.OpenApi3_1);

if (app.Environment.IsDevelopment())
{
    app.MapOpenApi("/openapi/{documentName}.yaml");
}
Enter fullscreen mode Exit fullscreen mode

4) JSON Patch with System.Text.Json

dotnet add package Microsoft.AspNetCore.JsonPatch.SystemTextJson --prerelease
Enter fullscreen mode Exit fullscreen mode

What’s New in EF Core 10

1) Complex Types (incl. optional & JSON mapping)

modelBuilder.Entity<Customer>(b =>
{
    b.ComplexProperty(c => c.ShippingAddress);
    b.ComplexProperty(c => c.BillingAddress, c => c.ToJson());
});

public class Customer
{
    public int Id { get; set; }
    public Address ShippingAddress { get; set; } = default!;
    public Address? BillingAddress { get; set; }  // optional
}

public struct Address
{
    public required string Street { get; set; }
    public required string City { get; set; }
    public required string ZipCode { get; set; }
}
Enter fullscreen mode Exit fullscreen mode

2) LeftJoin / RightJoin (LINQ operators)

var q = context.Students.LeftJoin(
    context.Departments,
    s => s.DepartmentID,
    d => d.ID,
    (s, d) => new { s.FirstName, s.LastName, Department = d.Name ?? "[NONE]" });
Enter fullscreen mode Exit fullscreen mode

3) ExecuteUpdate for JSON columns

await context.Blogs.ExecuteUpdateAsync(s =>
    s.SetProperty(b => b.Details.Views, b => b.Details.Views + 1));
Enter fullscreen mode Exit fullscreen mode

4) Named query filters

modelBuilder.Entity<Blog>()
    .HasQueryFilter("SoftDelete", b => !b.IsDeleted)
    .HasQueryFilter("Tenant", b => b.TenantId == tenantId);

var all = await context.Blogs.IgnoreQueryFilters(["SoftDelete"]).ToListAsync();
Enter fullscreen mode Exit fullscreen mode

5) Regular lambdas in ExecuteUpdateAsync

await context.Blogs.ExecuteUpdateAsync(s =>
{
    s.SetProperty(b => b.Views, 8);
    if (nameChanged) s.SetProperty(b => b.Name, "foo");
});
Enter fullscreen mode Exit fullscreen mode

Other Changes in .NET 10

  • Performance: broader JIT & GC improvements across runtime.
  • SDK: better dotnet CLI UX for script→project workflows.
  • Libraries: incremental APIs refinements; Aspire updates.

Migration Notes & Practical Tips

  • Target frameworks: bump to net10.0 and enable C# 14 (<LangVersion>preview</LangVersion> may not be needed once toolchain is updated).
  • Minimal APIs: adopt AddValidation(); standardize 400 responses via IProblemDetailsService.
  • OpenAPI: switch to 3.1; consider serving YAML for human‑readable docs.
  • EF Core: start with complex types for embedded value objects and named filters for multitenancy/soft‑delete.
  • Scripting: keep small CLIs as single files; convert when they need structure.
  • Perf: use compound operator overloads and implicit spans in hot paths.
  • Security: same TLS defaults; re‑audit auth flows if you expose SSE or file‑scripts in ops.

Summary

  • .NET 10 (LTS): stable base through 2028.
  • C# 14: extension members, field, null‑conditional assignment, partial constructors/events — less boilerplate, more clarity.
  • ASP.NET Core 10: Minimal API validation, OpenAPI 3.1/YAML, SSE.
  • EF Core 10: complex types, JSON updates, Left/RightJoin, named filters.

If you ship APIs, CLIs, or data‑heavy apps, this release will reduce ceremony and increase velocity.


✍️ Written by: Cristian Sifuentes — .NET/C# & architecture enthusiast. If you liked this, consider subscribing to the newsletter for more deep dives and production‑ready templates.

Top comments (0)