RSSAmplifier

Sebastian Gingter · Aug 3, 2026

.NET News July 2026

0
Sign in to vote or save

gingter.org

Summer months in .NET usually have a certain rhythm: a preview lands on Patch Tuesday, a few CVEs get closed, everyone goes on vacation. July 2026 did not read that memo.

Three things happened that will show up in real codebases. Mono stopped being a runtime option for .NET MAUI, ending a fifteen-year era in a single preview release. Seventeen security advisories shipped on one day, which is more than the previous few servicing releases put together, and two of them sit at CVSS 8.8 in an authentication handler. And the MCP C# SDK went to 2.0 on the back of the biggest protocol revision since MCP launched, one that finally makes MCP behave like ordinary HTTP instead of something you have to build sticky sessions around.

Underneath all that, .NET 11 Preview 6 kept doing the unglamorous work: unions grew from a compiler feature into something that flows through serialization, ASP.NET Core, and OpenAPI; CSRF protection became a default instead of a checklist item; and the async machinery got cheaper again.

And there is a date you should put in your calendar, because July was the month it stopped being far away.

.NET MAUI: the Mono era is over

Let me start here, because this is the item with the longest tail.

As of .NET 11 Preview 6, CoreCLR is the only runtime for .NET MAUI mobile apps. Not the default (that happened back in Preview 4), the only one. The build property that let you select Mono has been removed. If you build for net11.0-android, net11.0-ios, or net11.0-maccatalyst, you are on CoreCLR, the same runtime that has been under ASP.NET Core and your cloud services all along.

The numbers Microsoft is quoting are honest rather than triumphant, which I appreciate. On iOS and Mac Catalyst, CoreCLR is generally faster than Mono. On Android, startup and app size land within ten percent of Mono. That is not a win on Android, that is parity, and the team says so instead of dressing it up. Parity is the correct bar here anyway: the point was never that CoreCLR would make your Android app faster this year, the point is that mobile .NET stops being a fork of the platform. One runtime, one JIT, one diagnostics story, one set of performance work that benefits everybody. Every improvement that lands in CoreCLR for a web API now lands for your phone app too. That compounding is worth more than ten percent.

Two practical notes before you upgrade:

  • Test on real devices, in Release, and measure. Cold start, warm start, package size. The team is explicitly asking for that feedback, and Preview 6 is the right moment to give it, because “functionally complete” is not the same as “tuned”.
  • Microsoft.Maui.Controls.Compatibility is gone. That is a real breaking change and it will bite anyone still leaning on the Xamarin.Forms compatibility layer. Preview 6 is where you find out how much of that you still have.

Blazor WebAssembly is not affected. WebAssembly still runs on Mono, and that is not changing in .NET 11. So Mono is not dead, it just handed over the mobile job.

And that job was not a small one. Mono carried .NET onto phones for more than fifteen years, through Xamarin, through the acquisition, through the whole “can you even do that with .NET” era. Every MAUI app that exists today exists because Mono did that work first. It is a bit odd to write a fond paragraph about a runtime in a news post, but this one earned it.

.NET 11 Preview 6: unions stop being a language demo

Preview 6 shipped on July 14, on Patch Tuesday as usual. Last month I wrote that public union Pet(Cat, Dog, Bird); compiling in a real SDK was the thing from June that would still matter in ten years. Preview 6 is the follow-through, and it is the more important half.

The union support types now ship in the box. System.Runtime.CompilerServices.UnionAttribute and IUnion are part of the framework instead of something you hand-write to make the compiler happy. That sounds like plumbing, and it is, but plumbing is exactly what a language feature needs before anybody sane puts it in a shared library.

More interesting is where unions showed up next:

  • System.Text.Json serializes unions natively, with a new JsonUnionAttribute, JsonUnionCaseInfo, a JsonTypeInfoKind.Union contract, and JsonSerializerOptions.TypeClassifiers for customization. Reflection-based and source-generated serializers both handle it. The active case serializes as itself: a string case becomes "hello", an int case becomes 42. No wrapper object, no $type discriminator you did not ask for.
  • ASP.NET Core understands unions end to end: minimal APIs, MVC, Razor Pages, SignalR, and Blazor. And OpenAPI describes a union return as anyOf with each case type listed, without duplicating components.

Put those together and the feature stops being a modeling convenience inside your own code and becomes something you can put on a wire contract. A handler that returns “either an Order, or one of three known failure shapes” now has a compiler-checked type, a sensible JSON representation, and an OpenAPI schema that describes it accurately. That is the whole loop closed in one preview. I did not expect it this fast.

A few refinements landed too: non-public single-parameter constructors are now allowed, the not pattern applies to the union value itself rather than the contained value (which is the behaviour you want, and the one that would have surprised people), and the compiler gives better errors when the required union APIs are missing.

Unions are still preview, so you need <LangVersion>preview</LangVersion>.

Extension indexers joined the party as well, extending the extension members work from .NET 10 with this[...] support:

public static class ReadOnlyListExtensions
{
    extension<T>(IReadOnlyList<T> list)
    {
        public T this[Index index] => list[index.GetOffset(list.Count)];
    }
}

That gives you log[^1] on an IReadOnlyList<T>, which has never worked. Getters, setters, multiple parameters, and list patterns are all supported. Extension indexers only kick in when the type has no matching instance indexer, which is the right resolution order and the one that keeps this from becoming a debugging nightmare.

ASP.NET Core: two defaults worth knowing about

The ASP.NET Core list in Preview 6 has one item that changes behaviour for everybody and several that change behaviour for the people who need them.

Automatic CSRF protection is on by default. Apps built with WebApplication.CreateBuilder now reject unsafe cross-origin requests based on browser-supplied headers, across minimal APIs, MVC, Razor Pages, and Blazor. No configuration. You can opt out per endpoint with .DisableAntiforgery() or globally via configuration.

I like this a lot, and I want to be precise about why. It is not that the old antiforgery story was broken, it is that it was opt-in, and opt-in security features protect exactly the developers who already knew they needed them. Defaults protect everyone else. That said: this is a behaviour change in a framework you upgrade, so if you have a cross-origin client hitting your endpoints with something other than a browser-shaped request, Preview 6 is where you find out. Better now than in November.

Async validation finally exists. You can derive from AsyncValidationAttribute and implement IsValidAsync, or implement IAsyncValidatableObject and return IAsyncEnumerable<ValidationResult>. On the library side there are new Validator.ValidateObjectAsync, TryValidateObjectAsync, ValidatePropertyAsync, and ValidateValueAsync methods, plus an IAsyncStartupValidator in Microsoft.Extensions.Options. The framework runs independent validators concurrently, so collection items validate in parallel instead of one round trip at a time.

This is one of those gaps you only notice when you hit it, and then you never stop noticing it. “Is this SKU still orderable” and “is this email already taken” are validation questions, they need I/O, and DataAnnotations has been forcing people to either block a thread or move the check out of validation entirely. Now it fits where it belongs. Yes, this is also me being pleased that one more reason to write sync-over-async just disappeared.

The rest of the web stack:

  • OpenAPI 3.2 is the default for generated documents. No code changes needed.
  • [ShortCircuit] marks an endpoint to run immediately after routing, skipping the rest of the middleware pipeline. Health checks and robots.txt are the obvious cases.
  • SignalR can refresh authentication tokens without dropping the connection. The server exposes a refresh endpoint and reports token lifetime, and the .NET client re-authenticates before expiry. If you have ever had long-lived hub connections die on token expiry and hand-rolled a reconnect dance around it, that dance is over.
  • Clients can cancel non-streaming hub invocations by passing a CancellationToken to InvokeAsync, which surfaces on the server side.
  • Blazor Virtualize got InitialIndex and ScrollToIndexAsync(), with out-of-range indexes clamped and user scrolling winning over programmatic scrolling. Deep-linking into a long virtualized list stops being a hack.
  • WithBrowserOptions() lets the server configure Blazor client startup (log level, reconnection, enhanced navigation DOM preservation, WebAssembly environment and culture) instead of hand-written JavaScript in your host page.
  • The Blazor Gateway can proxy through YARP. The WebAssembly client only makes same-origin calls to the Gateway, which forwards them server-to-server, so there is no CORS configuration on either side. That is a nice reduction in the number of things that can be misconfigured.

Runtime, libraries, SDK: the compounding stuff

The runtime work in Preview 6 is mostly async, and it is the good kind of async work.

The JIT now compiles dedicated async versions of synchronous task-returning methods, removing a layer of indirection. Suspension points get tail-merged to cut code size, continuations for task thunks are cached and reused, and pooled methods opt out of the async paths so they do not do the work twice. Separately, async continuations can skip the ExecutionContext capture and restore cycle entirely when there is no AsyncLocal<T> state to carry, across Task, Task<T>, ValueTask, and ValueTask<T>.

That last one is worth pausing on, because it hits hardest in exactly the code that runs most: high-throughput paths using ConfigureAwait(false). The runtime-async story has been the long quiet thread running through all of .NET 11, and every preview it gets one step less expensive.

Elsewhere in the runtime: Math.BigMul(long, long, out long) compiles to a single MUL r/m64 on x64 instead of a helper call, the single-instruction-group restriction on function prologues is gone, the JIT folds conditional selects where both branches return the same constant, and NativeAOT routes interface calls through a shared dispatch helper instead of direct fat-pointer calls (smaller binaries, better throughput on interface-heavy code). Mobile platforms got in-process crash report logging that captures a managed stack trace, module list, and runtime state before the process dies.

In the libraries, my favourite item is the least exciting one: stream adapters. Four new types (ReadOnlyMemoryStream, WritableMemoryStream, ReadOnlySequenceStream, StringStream) let you hand in-memory data to a Stream-shaped API without an intermediate buffer. Passing a string to something that takes a Stream no longer requires a byte[] round trip. Every codebase has a helper for this. Now nobody needs one.

Also in there:

  • Activity tracing by configuration rules. Microsoft.Extensions.Diagnostics.AddTracing() replaces manual ActivityListener wiring with tracing.EnableTracing(sourceName: "MyCompany.Orders") and a matching DisableTracing for specific operations. Plus ActivitySourceFactory and an unsealed ActivitySource.
  • Cross-lane vector operations across Vector64/128/256/512<T> and Vector<T>: Zip, ZipLower/ZipUpper, Unzip, UnzipEven/UnzipOdd, Concat, Reverse, and sequence constructors.
  • Process picked up more work, continuing May’s overhaul: ProcessStartInfo.StartSuspended (Windows, resume via SafeProcessHandle.Resume), Process.TryGetProcessById() that returns false instead of throwing, and SafeProcessHandle.Open/TryOpen.

One breaking change to watch: Process.Run and Process.RunAsync gained a bool silent = false parameter before the existing optional parameters. If you call those positionally, the compiler will not necessarily save you.

On the SDK side, the NativeAOT CLI now parses, validates, and renders --help for every command rather than a small subset, so dotnet --version, help queries, and solution operations run natively and skip roughly 600 to 700 milliseconds of managed startup. dotnet test got --no-dependencies, a DOTNET_TEST_RUNNER variable, exclusion patterns with ! in --test-modules, per-assembly test counts, live in-flight progress, two-stage Ctrl+C, and a --device option for MAUI. The xUnit and NUnit templates now support Microsoft.Testing.Platform (--xunit-version v3, --test-runner Microsoft.Testing.Platform). File-based apps can reference compiled DLLs with #:include without a feature flag. And Podman can now build multi-arch container images, which makes rootless multi-arch workflows on Linux a real option.

EF Core 11 Preview 6

EF Core’s list is mostly query translation, plus one dependency change you should not skim past.

  • Queryable.FullJoin translates to SQL FULL OUTER JOIN. Last month LINQ got the operator, this month EF Core speaks it. The join story that started with LeftJoin and RightJoin in .NET 10 is now complete.
  • Complex-type property traversal works in HasKey, HasAlternateKey, and HasIndex, so you can index a nested property instead of flattening your model to please the model builder.
  • IsConstrained(bool) marks a relationship as having no database constraint. Queries use LEFT JOIN and migrations skip the constraint. Useful for cross-service or cross-schema references where the FK genuinely cannot exist, and a footgun everywhere else. Use it deliberately.
  • Translation cleanups: NULLIF generated for conditionals that return null on a constant match, redundant IS NOT NULL checks removed from CASE expressions, List<T>.Exists(predicate) translating to EXISTS instead of falling back to client evaluation, TimeOnly.Hour/Minute/Second mapping to strftime on SQLite, and string.Join/Concat over ordered groupings using group_concat with ORDER BY.
  • Migrations use DROP_EXISTING for index changes on SQL Server (no window where the index is missing), stop emitting redundant ALTER COLUMN for computed column CLR type changes, and dotnet ef accepts * as a wildcard for --context.
  • Cosmos DB gained JSON, composite, include/exclude, and full-text index configuration, and handles Convert operations so math functions and string concatenation no longer force client evaluation.

And the one to actually read: Microsoft.Data.Sqlite now depends on the SQLite3 Multiple Ciphers bundle instead of e_sqlite3. You get encryption support in the box, which is genuinely useful, but this is a native dependency swap in a widely-used package. If you ship SQLite in a constrained environment, or you have anything pinned, verified, or size-budgeted around the native bits, check it now while it is a preview and not in November when it is not.

The July servicing release: seventeen advisories in one day

.NET 10.0.10, 9.0.18, and 8.0.29 shipped on July 14, closing seventeen CVEs. For comparison, June closed three. This was not a routine Patch Tuesday.

The two highest scores are the ones to look at first:

  • CVE-2026-47300 and CVE-2026-47303 — Elevation of privilege in the ASP.NET Core Negotiate authentication handler, both CVSS 8.8. One is improper validation, the other is improper parsing, and the weakness list on the second one includes LDAP injection. The mitigation note on 47300 is specific: you are affected if you use Negotiate authentication and LDAP to retrieve role information. If that is your setup (and in enterprise intranet apps it very often is), this is the patch you apply today. An authentication handler that can be talked into giving someone the wrong roles is about as bad as it gets.
  • CVE-2026-50528 — Security feature bypass in TLS/SSL (System.Net.Security), CVSS 8.2. An attacker can exploit the SslStream implementation to bypass authorization checks during secure communication.

The rest, grouped by where they live:

  • XML encryption (EncryptedXml), five advisories: CVE-2026-47302, 50525, 50527, 50648 (denial of service, CVSS 7.5 each, one of them a stack-based buffer overflow) and CVE-2026-47304 (security feature bypass, CVSS 8.1, improper verification of a cryptographic signature). If you process encrypted XML from anywhere you do not fully control, that last one is not a DoS, it is someone reading data they should not.
  • WPF XAML parsing, three advisories: CVE-2026-50646 and CVE-2026-50649 (remote code execution, CVSS 7.8) and CVE-2026-50650 (elevation of privilege, CVSS 7.8). All three need crafted XAML input and user interaction. WPF is still running an enormous amount of line-of-business software, and “we load XAML from somewhere” is more common in those apps than people remember.
  • TLS handshake DoS (CVE-2026-50524, 7.5) and X.509 certificate parsing DoS (CVE-2026-57108, 7.5, Linux and macOS only, in CryptoNative_GetX509NameInfo).
  • HTTP/2 out-of-memory DoS in System.Net.Http (CVE-2026-50651, 7.5).
  • SignalR stateful reconnect DoS (CVE-2026-56170, 7.5). Only affects you if stateful reconnect is enabled.
  • SMTP client spoofing in System.Net.Mail (CVE-2026-50659, 6.5), where an attacker can spoof messages during routing.
  • .NET SDK container build tampering (CVE-2026-50526, 7.0). A local attacker can inject resources into container images built by other users on the same machine. Read that again if you run shared build agents.

There were also .NET Framework 3.5 and 4.8.1 cumulative updates this month, and Visual Studio 18.7.4 shipped with fourteen of these advisories addressed.

All of this ships with the runtime, so updating your .NET installation closes them. No application code changes. Which means the only reason any of these are still open in your environment a week from now is that nobody updated the runtime, rebuilt the container base images, or patched the build agents. Do all three.

November 10 is now a deadline twice over

Here is the calendar item.

.NET 11 goes GA on November 10, 2026. It is an STS release, supported through November 9, 2028.

On that same day, .NET 8 and .NET 9 both go out of support. .NET 8 because its three LTS years are up, .NET 9 because its two STS years are up. Same date, two runtime versions, gone together.

If you are still on 8 or 9, you have roughly three months, and this is the moment to plan it rather than the moment to notice it. The default answer is .NET 10: it is LTS, it has been GA since last November, and it is supported through November 14, 2028.

There is a wrinkle in that recommendation worth knowing about. .NET 11’s support ends November 9, 2028. .NET 10’s ends November 14, 2028. Five days apart. For this particular pair, picking the STS release costs you essentially nothing in runway. That is unusual, it is a quirk of where the dates fall rather than a policy change, and it does not hold for the next cycle. But if you were going to skip .NET 11 purely on “STS means a shorter support window” grounds, that reasoning does not apply this time. Skip it for the reason that actually matters (fewer upgrades is less work) or take it for the reason that actually matters (unions, CSRF defaults, async validation, CoreCLR on mobile). Just do not decide it on a support-window argument that is five days wide.

MCP C# SDK 2.0: the protocol learned HTTP

On July 28 the MCP C# SDK reached 2.0, implementing the 2026-07-28 revision of the MCP specification, which is the largest revision since the protocol launched.

The headline is that MCP is now stateless by default. The protocol version and capabilities travel with each request instead of being negotiated once and stored in a session. In practice that means you can run an MCP server across multiple instances without sticky routing or session synchronization, and ordinary load balancers and proxies can route MCP traffic because the HTTP surface is standardized. There is also an [McpHeader] attribute that promotes tool parameters into headers, which is what makes geo-distributed routing possible at all.

The feature I find most interesting is Multi Round-Trip Requests. An interactive tool that needs input mid-execution throws an InputRequiredException, and the client re-issues the call with the collected response. No session state, no long-lived connection, and yet the tool can still ask a question. That is a genuinely clever way to get interactivity out of a stateless protocol, and it removes the main reason people were building session infrastructure around MCP in the first place.

Rounding it out: discovery-first negotiation, caching hints, stronger OAuth and token-cache safety, and Apps and Tasks moved into separate opt-in extension packages instead of living in the core. Keeping the base lean is the right call for something this many people are going to depend on.

v2.0 is backward compatible. Existing v1 code keeps compiling and running, deprecations show up as warnings rather than breaks, and v2 clients fall back to the legacy handshake when they meet an older server. The one exception: the experimental Tasks from v1.3 and v1.4 are not wire-compatible with the redesigned Tasks extension.

I wrote about MCP in practice a year ago, and the thing I was most sceptical about back then was operational: MCP looked like HTTP but did not behave like it, so everything you knew about scaling HTTP services did not transfer. This revision is the fix for exactly that. A year is fast for a protocol to go back and rethink its own foundation, and the fact that they did it without breaking v1 users is the part that deserves the credit.

Visual Studio 18.8: skills, a new agent, and a usage meter that talks

Visual Studio 2026 18.8.0 landed on July 14 (with 18.8.1 on July 22 and 18.8.2 on July 28), and it is a Copilot-heavy release.

Built-in agent skills are the headline. Microsoft shipped a set of skills authored by the .NET and Azure teams, available in the tool picker under a “Built-in” category: dotnet-webapi generates ASP.NET Core endpoints with correct OpenAPI metadata and error handling, analyzing-dotnet-performance scans for roughly fifty performance anti-patterns, and there are more. They are off by default, and you can hover to see the description and path, or open the full skill file from the three-dot menu.

That last detail is the one that matters to me. A skill is a file you can read, review, version, and disagree with. That is a fundamentally different thing from a vendor stuffing more opinions into an invisible system prompt. If an agent is going to apply “expert .NET guidance” to my codebase, I want to be able to open the guidance and check whether I actually agree with it. Off-by-default plus readable-on-disk is the right shape.

Also in 18.8:

  • A new Agent (Preview) built on the same GitHub Copilot SDK that powers the Copilot CLI. Microsoft’s pitch is “less chatty, more done on the first try”, which is a refreshingly specific claim for an AI feature. Find it in the agent picker at the bottom of Copilot Chat.
  • Organization-level custom instructions, so org owners can set Copilot preferences across all repos in a GitHub organization. They show up in the reference list during interactions, and you can turn them off under Tools, Options, GitHub, Copilot, Copilot Chat.
  • Review Selection: select code, right-click, Copilot Actions, Review Selection, and get inline comments powered by the same engine as GitHub Copilot code review.
  • The Copilot usage window keeps evolving, with proactive alerts when you approach your limit, hit it, or slide into overage, and a configurable warning threshold. This is the second month in a row that usage visibility is a headline feature, which tells you something about the shift to token-based billing. I did the full Kassensturz on what that actually costs in German, and I stand by the conclusion: the meter does not fix the psychology of rationing your own tooling, but flying blind was worse.
  • Git: attach a branch to Copilot Chat as context (right-click in the Git Repository window, Add to Chat), emoji reactions on PR comments for GitHub and Azure DevOps, and PRs can pop out into their own tab for side-by-side review.
  • C++: MSVC build tools auto-discovery across Visual Studio installations via <EnableVCToolsVersionDiscovery>, so a pinned VCToolsVersion resolves even when it lives in a different install.

Agents that ship as code: dotnet/skills is turning into something

Two .NET Blog posts in July point at the same trend, and I think it is worth naming.

On July 31, the team published a unit-test generation agent called code-testing-generator, living in the dotnet/skills repository as part of the dotnet-test plugin. It is polyglot (.NET, Python, TypeScript, Java, Go, Rust, and more), and the workflow is the interesting part: it analyzes the repository first (language, test framework, existing patterns, build and run commands), picks a strategy, maps behaviours to tests following local conventions, and then validates its own output with mutation testing and assertion checks before handing it back.

The benchmark numbers Microsoft published: 92.1% task completion versus 78.9% for stock Copilot, and on deliberately vague prompts 79 of 89 versus 59 of 89. The improvement held across Claude Opus, GPT-5.5, and Claude Haiku. You install it with /plugin marketplace add dotnet/skills and /plugin install dotnet-test@dotnet-agent-skills, and it runs in the GitHub Copilot CLI, VS Code (preview), and eventually Visual Studio.

The reason I care is not the test generation. It is the shape. A skill is a versioned, reviewable, model-agnostic artifact that encodes “how we do this here”, and it demonstrably beats a generic agent by a wide margin on the same models. That is the same lesson as the Visual Studio built-in skills, arriving from a different direction in the same month: the leverage is not in the model, it is in the context you hand it, and that context should be a file in a repository.

Also from July: .NET Modernization for Beginners, a free four-chapter open-source course (assessment, planning, upgrade and execution, cloud with Azure) at microsoft/dotnet-modernization-for-beginners, which produces assessment.md, plan.md, and tasks.md as editable artifacts rather than doing magic behind a button. And a post on analyzing MSBuild binary logs with Copilot in VS Code, which is exactly the sort of tedious-but-mechanical job that machine assistance is genuinely good at. If you have ever stared at a binlog trying to work out why one project rebuilds every time, you know.

Ecosystem: ten days of Critter Stack

Jeremy Miller and the JasperFx crew apparently decided that July was also not a vacation month. Between mid-July and the end of the month: Wolverine 6.20 through 6.23.1, Marten 9.16 through 9.20, and Polecat 5.1 through 5.7, plus supporting libraries. The themes were operational: classified async daemon shard failures, graceful shutdown drains, agent assignment reliability fixes, and a batch of Polecat performance work.

Three items stand out.

Wolverine.HTTP learned the QUERY verb (6.17.0, July 15). QUERY is the HTTP method from RFC 10008 that resolves the oldest annoyance in search endpoint design: GET cannot reliably carry a body, POST throws away the semantics of safe and idempotent. QUERY is safe and idempotent like GET, and carries a body like POST. In Wolverine it is one attribute:

[WolverineQuery("/search")]
public static SearchResults Search(SearchRequest request)
{
    // ...
}

Middleware decisions stay dependency-based rather than verb-based, which is the right design: use Marten’s IQuerySession in a read-only handler and you do not get transactional wrapping, regardless of the HTTP method. One honest caveat that Jeremy calls out himself: OpenAPI 3.1 cannot represent QUERY operations, so Wolverine omits them from the generated document rather than producing a broken one. Given that ASP.NET Core just moved to OpenAPI 3.2 by default in Preview 6, that gap may close on its own sooner than expected.

Marten got binary event serialization. Events can now be stored with MemoryPack, MessagePack, or a custom IEventBinarySerializer instead of JSON, opt-in per event type via [BinaryEvent] or opts.Events.UseBinarySerializer<TEvent>(serializer). The mechanics are elegant: a new nullable bdata bytea column sits next to the existing data jsonb, and the discriminator is per row (bdata IS NULL means JSON, otherwise binary). Old and new events live in the same table, no migration. The tradeoff is stated plainly: you give up some of JSON’s ergonomics for throughput and storage size on hot streams. Schema evolution uses versioned event types (TripStarted to TripStartedV2) instead of upcasters, with aggregates handling both. If you have ever maintained a long upcaster chain, that choice will make sense to you immediately.

Marten and Polecat can stream raw JSON from the database straight to the HTTP response, with typed wrappers (StreamOne<T>, StreamMany<T>, StreamPaged<T>, and friends) that skip deserialization entirely. Paged responses get metadata and documents in a single round trip. For read-heavy endpoints over documents you already store as JSON, deserializing to an object just to serialize it back was always pure ceremony.

The roadmap post from July 24 puts CritterWatch 1.0 at August 3, which is today as I write this, along with cron-based message scheduling (Quartz.NET and TickerQ integration), projection rebuild scheduling, and Event Modeling visualization. The JasperFx AI Skills library also hit 1.6.0 with 81 agent skills covering the whole stack, including troubleshooting guides keyed to exact error messages. Which brings the “skills as shipped artifacts” theme full circle: it is not just Microsoft doing this.

What July was, really

June was the month a language feature arrived. July was the month the platform did the work around the things that arrived.

Unions went from a compiler feature to something that survives a round trip through JSON, ASP.NET Core, and an OpenAPI document. MAUI stopped being a separate runtime story and became part of the same platform as everything else, quietly ending a fifteen-year era on the way. MCP went back and rebuilt its own foundation so it behaves like the HTTP it always claimed to be. And ASP.NET Core turned a security feature that used to require knowing about it into a default that protects the people who do not.

The seventeen CVEs are the sour note, and two of them in an authentication handler at 8.8 are not a footnote. Patch, rebuild your base images, update your build agents.

And put November 10 in your calendar. It is .NET 11’s GA date and the end of the road for both .NET 8 and .NET 9, which is a coincidence of the release calendar rather than a plan, but it is your problem either way. Three months is enough time to do that upgrade calmly. It is not enough time to do it twice.

Sources

Read the original on gingter.org

Comments

Nothing yet. Say the first thing.

    Sign in to join the conversation.