Back to Blog
6 min read

.NET 7 Preview: Performance and Cloud-Native Features

.NET 7 preview is here with exciting features focused on performance, cloud-native development, and developer productivity. Build 2022 showcased what is coming for the .NET ecosystem.

Key Features Overview

  • Generic math interfaces
  • Required members
  • Improved minimal APIs
  • Native AOT compilation
  • Enhanced performance

Generic Math

One of the most anticipated features is generic math support:

using System.Numerics;

public class MathOperations
{
    // Generic method that works with any numeric type
    public static T Sum<T>(IEnumerable<T> values) where T : INumber<T>
    {
        T sum = T.Zero;
        foreach (var value in values)
        {
            sum += value;
        }
        return sum;
    }

    public static T Average<T>(IEnumerable<T> values) where T : INumber<T>
    {
        var count = values.Count();
        if (count == 0) return T.Zero;

        var sum = Sum(values);
        return sum / T.CreateChecked(count);
    }

    public static T Clamp<T>(T value, T min, T max) where T : INumber<T>
    {
        if (value < min) return min;
        if (value > max) return max;
        return value;
    }
}

// Usage
var integers = new[] { 1, 2, 3, 4, 5 };
var intSum = MathOperations.Sum(integers); // 15

var doubles = new[] { 1.5, 2.5, 3.5 };
var doubleAvg = MathOperations.Average(doubles); // 2.5

var decimals = new[] { 100.0m, 200.0m, 300.0m };
var decimalSum = MathOperations.Sum(decimals); // 600.0m

Required Members

Ensure object initialization completeness:

public class Person
{
    public required string FirstName { get; init; }
    public required string LastName { get; init; }
    public required string Email { get; init; }
    public DateOnly? DateOfBirth { get; init; }

    // Computed property
    public string FullName => $"{FirstName} {LastName}";
}

public class Order
{
    public required Guid Id { get; init; }
    public required string CustomerId { get; init; }
    public required List<OrderItem> Items { get; init; }
    public required decimal TotalAmount { get; init; }
    public DateTime CreatedAt { get; init; } = DateTime.UtcNow;
    public OrderStatus Status { get; set; } = OrderStatus.Pending;
}

public record OrderItem
{
    public required string ProductId { get; init; }
    public required int Quantity { get; init; }
    public required decimal UnitPrice { get; init; }
}

public enum OrderStatus { Pending, Processing, Shipped, Delivered, Cancelled }

// Usage - compiler ensures required members are set
var person = new Person
{
    FirstName = "John",
    LastName = "Doe",
    Email = "john@example.com"
};

var order = new Order
{
    Id = Guid.NewGuid(),
    CustomerId = "CUST-001",
    Items = new List<OrderItem>
    {
        new OrderItem { ProductId = "PROD-001", Quantity = 2, UnitPrice = 29.99m }
    },
    TotalAmount = 59.98m
};

Enhanced Minimal APIs

Improved minimal API features for cleaner code:

using Microsoft.AspNetCore.Http.HttpResults;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
builder.Services.AddScoped<IProductService, ProductService>();

var app = builder.Build();

app.UseSwagger();
app.UseSwaggerUI();

// Typed results for better OpenAPI documentation
app.MapGet("/products", async (IProductService service) =>
{
    var products = await service.GetAllAsync();
    return TypedResults.Ok(products);
})
.WithName("GetProducts")
.WithOpenApi();

app.MapGet("/products/{id}", async Task<Results<Ok<Product>, NotFound>> (
    int id,
    IProductService service) =>
{
    var product = await service.GetByIdAsync(id);
    return product is not null
        ? TypedResults.Ok(product)
        : TypedResults.NotFound();
})
.WithName("GetProduct")
.WithOpenApi();

app.MapPost("/products", async Task<Results<Created<Product>, ValidationProblem>> (
    Product product,
    IProductService service) =>
{
    var validationResult = await service.ValidateAsync(product);
    if (!validationResult.IsValid)
    {
        return TypedResults.ValidationProblem(validationResult.Errors);
    }

    var created = await service.CreateAsync(product);
    return TypedResults.Created($"/products/{created.Id}", created);
})
.WithName("CreateProduct")
.WithOpenApi();

// Route groups for organization
var ordersGroup = app.MapGroup("/orders").WithTags("Orders");

ordersGroup.MapGet("/", async (IOrderService service) =>
    TypedResults.Ok(await service.GetAllAsync()));

ordersGroup.MapGet("/{id}", async Task<Results<Ok<Order>, NotFound>> (
    Guid id,
    IOrderService service) =>
{
    var order = await service.GetByIdAsync(id);
    return order is not null
        ? TypedResults.Ok(order)
        : TypedResults.NotFound();
});

ordersGroup.MapPost("/", async (CreateOrderRequest request, IOrderService service) =>
{
    var order = await service.CreateAsync(request);
    return TypedResults.Created($"/orders/{order.Id}", order);
});

app.Run();

public record Product(int Id, string Name, decimal Price, string Category);
public record CreateOrderRequest(string CustomerId, List<OrderItemRequest> Items);
public record OrderItemRequest(int ProductId, int Quantity);

Native AOT Compilation

Ahead-of-time compilation for faster startup:

<!-- Project file configuration -->
<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net7.0</TargetFramework>
    <PublishAot>true</PublishAot>
    <InvariantGlobalization>true</InvariantGlobalization>
    <StripSymbols>true</StripSymbols>
  </PropertyGroup>
</Project>

AOT-compatible code:

using System.Text.Json;
using System.Text.Json.Serialization;

// Use source generators for JSON serialization (AOT compatible)
[JsonSerializable(typeof(WeatherForecast))]
[JsonSerializable(typeof(List<WeatherForecast>))]
public partial class AppJsonContext : JsonSerializerContext { }

public class WeatherForecast
{
    public DateOnly Date { get; set; }
    public int TemperatureC { get; set; }
    public string? Summary { get; set; }
}

// AOT-compatible API
var builder = WebApplication.CreateSlimBuilder(args);
builder.Services.ConfigureHttpJsonOptions(options =>
{
    options.SerializerOptions.TypeInfoResolver = AppJsonContext.Default;
});

var app = builder.Build();

app.MapGet("/weather", () =>
{
    var forecasts = Enumerable.Range(1, 5).Select(index =>
        new WeatherForecast
        {
            Date = DateOnly.FromDateTime(DateTime.Now.AddDays(index)),
            TemperatureC = Random.Shared.Next(-20, 55),
            Summary = "Sunny"
        })
        .ToList();

    return forecasts;
});

app.Run();

Publish with AOT:

dotnet publish -c Release -r linux-x64 --self-contained

Performance Improvements

On-stack replacement for better JIT optimization:

public class PerformanceDemo
{
    // This method benefits from OSR (On-Stack Replacement)
    public long SumWithOSR(int iterations)
    {
        long sum = 0;
        for (int i = 0; i < iterations; i++)
        {
            sum += ProcessItem(i);
        }
        return sum;
    }

    private long ProcessItem(int value)
    {
        // Complex computation that benefits from JIT optimization
        return (long)(Math.Sin(value) * Math.Cos(value) * 1000);
    }
}

// Improved Regex source generators
public partial class Validators
{
    [GeneratedRegex(@"^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$", RegexOptions.IgnoreCase)]
    private static partial Regex EmailRegex();

    [GeneratedRegex(@"^\+?[1-9]\d{1,14}$")]
    private static partial Regex PhoneRegex();

    public static bool IsValidEmail(string email) =>
        EmailRegex().IsMatch(email);

    public static bool IsValidPhone(string phone) =>
        PhoneRegex().IsMatch(phone);
}

Improved LINQ

New LINQ methods and performance:

public class LinqImprovements
{
    public void DemoNewMethods()
    {
        var numbers = Enumerable.Range(1, 100);

        // Order - simpler than OrderBy for simple cases
        var ordered = numbers.Order();
        var descendingOrdered = numbers.OrderDescending();

        // Index operator
        var items = new[] { "a", "b", "c", "d", "e" };
        var lastTwo = items.Take(^2..); // ["d", "e"]
        var middle = items.Take(1..^1); // ["b", "c", "d"]

        // Chunk - split into batches
        var chunks = numbers.Chunk(10); // 10 chunks of 10 items each

        foreach (var chunk in chunks)
        {
            ProcessBatch(chunk);
        }
    }

    private void ProcessBatch(int[] batch)
    {
        Console.WriteLine($"Processing batch of {batch.Length} items");
    }
}

Rate Limiting Middleware

Built-in rate limiting:

using System.Threading.RateLimiting;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddRateLimiter(options =>
{
    options.GlobalLimiter = PartitionedRateLimiter.Create<HttpContext, string>(context =>
        RateLimitPartition.GetFixedWindowLimiter(
            partitionKey: context.User.Identity?.Name ?? context.Request.Headers.Host.ToString(),
            factory: partition => new FixedWindowRateLimiterOptions
            {
                AutoReplenishment = true,
                PermitLimit = 100,
                Window = TimeSpan.FromMinutes(1)
            }));

    options.AddPolicy("api", context =>
        RateLimitPartition.GetSlidingWindowLimiter(
            partitionKey: context.User.Identity?.Name ?? "anonymous",
            factory: partition => new SlidingWindowRateLimiterOptions
            {
                AutoReplenishment = true,
                PermitLimit = 10,
                Window = TimeSpan.FromSeconds(10),
                SegmentsPerWindow = 2
            }));

    options.OnRejected = async (context, token) =>
    {
        context.HttpContext.Response.StatusCode = 429;
        await context.HttpContext.Response.WriteAsync(
            "Too many requests. Please try again later.", token);
    };
});

var app = builder.Build();

app.UseRateLimiter();

app.MapGet("/", () => "Hello!").RequireRateLimiting("api");

app.Run();

Summary

.NET 7 preview brings:

  • Generic math for flexible numeric operations
  • Required members for better object initialization
  • Enhanced minimal APIs with typed results
  • Native AOT for faster cold start
  • Built-in rate limiting middleware
  • Performance improvements across the board

These features make .NET 7 excellent for cloud-native and high-performance applications.


References:

Michael John Peña

Michael John Peña

Senior Data Engineer based in Sydney. Writing about data, cloud, and technology.