Are you sure you’ve squeezed everything out of .NET 10 Preview 7? Most teams don’t – and leave real perf, security, and DX wins on the table.
In this practical guide I’ll unpack the Preview 7 changes that matter for day‑to‑day dev work across libraries, ASP.NET Core, EF Core, and .NET MAUI. You’ll get concise explanations, copy‑pasteable snippets, and notes from trying these features in a real codebase.
What to try now:
- Zero‑copy JSON: Deserialize directly from
PipeReader
; stream events in minimal APIs. - WebSocketStream = Stream: Replace manual
ReceiveAsync
loops with a regularStream
. - Modern crypto: ML‑DSA (experimental), Composite ML‑DSA, and AES‑KWP (RFC 5649) for key wrapping.
- Safer web defaults: APIs return 401/403 instead of cookie redirects;
.localhost
hostnames; cleaner exception diagnostics. - EF Core 10:
LeftJoin
/RightJoin
, smarterIN
translation for parameter arrays, simplerExecuteUpdateAsync
. - .NET MAUI: Opt‑in XAML Source Generator, EXIF in
MediaPicker
, and SafeArea/layout polish.
How to get Preview 7 quickly
# SDK + runtime
winget install Microsoft.DotNet.SDK.Preview
# Confirm
dotnet --info | findstr ".NET SDKs installed"
# In a repo, lock to preview
echo '{"sdk":{"version":"10.0.100-preview.7"}}' > global.json
Tip: on CI, pin with
rollForward":"latestMinor"
if minor previews move.
Libraries: faster I/O, simpler websockets, and modern crypto
System.Text.Json
now reads directly from PipeReader
If you already process incoming bytes through System.IO.Pipelines
, you used to bridge to a Stream
just to deserialize. Not anymore.
using System.IO.Pipelines;
using System.Text.Json;
var pipe = new Pipe();
_ = ProduceAsync(pipe.Writer);
// New: deserialize directly from PipeReader
var person = await JsonSerializer.DeserializeAsync<Person>(pipe.Reader);
Console.WriteLine($"Hi {person!.Name}");
static async Task ProduceAsync(PipeWriter writer)
{
await JsonSerializer.SerializeAsync(writer, new Person("Alice"));
await writer.CompleteAsync();
}
public record Person(string Name);
Why you care:
- Fewer copies → less GC pressure on high‑throughput servers.
- Keep your data in the pipeline from socket → JSON without detours.
WebSocketStream
: use WebSockets like a stream
Socket code should be boring. With WebSocketStream, you can read/write as if it were any other Stream
and let the framework handle message framing.
using System.Net.WebSockets;
using System.Text;
using var cws = new ClientWebSocket();
await cws.ConnectAsync(new Uri("wss://echo.websocket.events"), CancellationToken.None);
await using var wsStream = WebSocketStream.Create(cws, WebSocketMessageType.Text);
// Write
await wsStream.WriteAsync(Encoding.UTF8.GetBytes("ping\n"));
await wsStream.FlushAsync();
// Read
var buffer = new byte[1024];
var read = await wsStream.ReadAsync(buffer);
Console.WriteLine(Encoding.UTF8.GetString(buffer.AsSpan(0, read)));
Why you care:
- No manual
ReceiveAsync
loops. - Plays nicely with APIs expecting
Stream
.
Note: the stream boundary corresponds to WebSocket messages; big messages are still framed correctly by the stream abstraction.
Post‑quantum crypto & AES‑KWP made approachable
ML‑DSA and Composite ML‑DSA (experimental) land in the BCL with ergonomic APIs.
using System.Security.Cryptography;
// Sign/verify with ML‑DSA (experimental in Preview 7)
using var signingKey = MLDsa.GenerateKey(MLDsaAlgorithm.MLDsa65);
byte[] data = "hello"u8.ToArray();
byte[] sig = signingKey.SignData(data);
using var pubKey = MLDsa.ImportFromPem(signingKey.ExportSubjectPublicKeyInfoPem());
Console.WriteLine(pubKey.VerifyData(data, sig)); // True
AES‑KWP (RFC 5649) is great when you need to wrap a DEK with a KEK (think CMS EnvelopedData with multiple recipients).
using System.Security.Cryptography;
ReadOnlySpan<byte> kek = RandomNumberGenerator.GetBytes(32); // AES‑256 KEK
ReadOnlySpan<byte> dek = RandomNumberGenerator.GetBytes(24); // 192‑bit DEK
using var aes = Aes.Create();
aes.SetKey(kek);
Span<byte> wrapped = stackalloc byte[dek.Length + 16];
int len = aes.EncryptKeyWrapPadded(dek, wrapped);
Span<byte> unwrapped = stackalloc byte[32];
int dekLen = aes.DecryptKeyWrapPadded(wrapped[..len], unwrapped);
Console.WriteLine($"Same DEK: {dek.SequenceEqual(unwrapped[..dekLen])}");
Also notable:
- Find certificates by thumbprints other than SHA‑1.
.NET
client TLS 1.3 on macOS.
Launch Windows processes in a new process group
Long‑running workers sometimes spawn children which ignore your signals. Creating a new process group gives you clean isolation for lifecycle management.
using System.Diagnostics;
var psi = new ProcessStartInfo
{
FileName = "cmd.exe",
Arguments = "/c ping 127.0.0.1 -n 10",
UseShellExecute = false,
CreateNoWindow = true,
// Preview 7: isolate the new process group (Windows only)
CreateNewProcessGroup = true
};
using var p = Process.Start(psi)!;
// Later: terminate cleanly, or kill the entire group/tree if needed
if (!p.WaitForExit(3000))
{
p.Kill(entireProcessTree: true);
}
Works well with shutdown hooks in Windows Services to avoid orphaned children.
ASP.NET Core: safer defaults, smoother auth, better diagnostics
Cookie auth no longer redirects for known API endpoints
By default, unauthenticated/forbidden requests to API endpoints return 401/403 instead of redirecting to a login page.
What counts as “known API endpoints”?
[ApiController]
endpoints- Minimal APIs that read/write JSON
- Endpoints returning TypedResults
- SignalR hubs
If your SPA relied on redirects, re‑enable them explicitly:
builder.Services.AddAuthentication().AddCookie(options =>
{
options.Events.OnRedirectToLogin = ctx =>
{
ctx.Response.Redirect(ctx.RedirectUri);
return Task.CompletedTask;
};
options.Events.OnRedirectToAccessDenied = ctx =>
{
ctx.Response.Redirect(ctx.RedirectUri);
return Task.CompletedTask;
};
});
Configure suppressing exception handler diagnostics
If your IExceptionHandler
marks an exception as handled, diagnostics are now suppressed by default. You can opt back in at the middleware level if your telemetry pipeline expects those events.
Passkeys (FIDO2/WebAuthn) improvements
New templates and Identity endpoints keep smoothing the passkey story. For a Blazor app with individual auth:
dotnet new blazor -au Individual -n PasskeyDemo
Then configure default HTTPS and try registering a passkey in the scaffolded UI. You’ll get a front‑to‑back flow wired for WebAuthn without extra packages.
Support for the .localhost
TLD
You can now use hostnames like https://api.myservice.localhost
and have them treated as loopback. This is handy for multi‑tenant dev setups, or when you model subdomains locally without editing hosts
.
Use PipeReader with JSON in minimal APIs
Pair the new serializer overloads with HttpRequest.BodyReader
for high‑throughput endpoints.
app.MapPost("/events", async (HttpRequest req) =>
{
await foreach (var e in JsonSerializer.DeserializeAsyncEnumerable<Event>(req.BodyReader))
{
// process chunked events
}
return Results.Accepted();
});
public record Event(string Type, DateTime Timestamp);
Enhanced validation for classes and records
ASP.NET Core validation keeps getting closer to how you actually model data: better alignment with C# record
/required
members and consistent error metadata in responses (great for UI frameworks and API clients).
EF Core 10 (Preview): simpler LINQ, smarter SQL
Parameterized collections → better default translation
EF now prefers scalar parameters for collection Contains
(instead of always JSON arrays), preserving plan cache stability and cardinality info.
int[] ids = [ 10, 20, 30 ];
var blogs = await ctx.Blogs.Where(b => ids.Contains(b.Id)).ToListAsync();
// Translates to: WHERE [b].[Id] IN (@ids1, @ids2, @ids3)
Tuning knobs still exist when you know more about your data shape.
First‑class LeftJoin
/ RightJoin
Stop juggling GroupJoin
+ DefaultIfEmpty
for left joins.
var q = ctx.Students
.LeftJoin(
ctx.Departments,
s => s.DepartmentId,
d => d.Id,
(s, d) => new { s.FirstName, s.LastName, Department = d!.Name ?? "[None]" });
Quality‑of‑life
ExecuteUpdateAsync
now accepts a regular lambda, so you can build updates conditionally without expression‑tree acrobatics.- Many small translation & perf tweaks (e.g.,
DateOnly
,COALESCE
→ISNULL
in SQL Server).
.NET MAUI: faster builds and richer device features
XAML Source Generator (opt‑in in Preview 7)
Compile XAML to code and surface errors earlier. To try it today:
<!-- .csproj -->
<PropertyGroup>
<EnablePreviewFeatures>true</EnablePreviewFeatures>
</PropertyGroup>
// AssemblyInfo.cs (or any source-available file)
using Microsoft.Maui.Controls.Xaml;
[assembly: XamlProcessing(XamlInflator.SourceGen)]
Benefits you’ll feel:
- Faster builds and app startup (less runtime parsing)
- Better IntelliSense on generated types
MediaPicker
gains EXIF support
Quick sample that reads orientation:
using Microsoft.Maui.Media;
FileResult photo = await MediaPicker.PickPhotoAsync();
if (photo is not null)
{
using var stream = await photo.OpenReadAsync();
var exif = await ExifReader.ReadAsync(stream); // helper from your lib/wrapper
Console.WriteLine($"Orientation: {exif.Orientation}");
}
SafeArea improvements + new control APIs
Safer defaults around notches/dynamic islands and handy events on pickers & tabs make polishing layouts less painful.
Practical migration notes (from my project notebook)
- Pin the SDK in
global.json
so coworkers/CI don’t drift to a different preview. - Feature flags: MAUI’s XAML source gen is opt‑in – keep a branch to flip easily if you hit tooling bugs.
- Auth flows: if your SPA relied on cookie redirects, switch to reading 401/403 and redirect on the client.
- WebSockets: the
WebSocketStream
abstraction cleans up code, but remember: a message boundary still matters (don’t expect arbitrary partial records unless you design for it). - Crypto: ML‑DSA types are experimental. Guard with
#if
orIsSupported
checks if you ship cross‑platform.
FAQ: upgrading to Preview 7 without surprises
No – not in this drop. C# 14 features continue to bake, but nothing new landed specifically in Preview 7.
If you use cookie auth: yes for known API endpoints. You’ll now get 401/403. Override cookie events if you need old behavior.
For new templates with Individual auth, it’s already wired. Existing apps can integrate via Identity’s endpoints or your preferred FIDO2 library.
Client use is supported; avoid hardcoding SslProtocols
. Let the OS choose the best protocol.
The collection‑parameter translation default changed. If you’ve tuned a query for JSON‑array mode, review plans; you can still choose the old mode per‑query.
Conclusion: ship something with it this week
Preview 7 isn’t just housekeeping – it’s practical:
- Stream your JSON directly from pipelines.
- Simplify real‑time code with
WebSocketStream
. - Get safer defaults in web apps and fewer auth surprises.
- Try MAUI’s XAML codegen to claw back build time.
Upgrade to Preview 7, pick one area (JSON I/O, WebSocketStream, cookie‑auth behavior, or MAUI XAML Source Gen), and ship a small improvement today. What will you try first – and why? Tell me in the comments; I read them all.