Stop Pulling a NuGet Package for zstd. .NET 11 Has ZstandardStream
TL;DR: .NET 11 puts Zstandard (zstd) right next to GZipStream and BrotliStream. new ZstandardStream(output, CompressionLevel.Optimal) is the whole setup, and HttpClient can decompress zstd responses with one flag.
.NET 11 is at Release Candidate 2, which ships with a go-live support license, but it is not GA yet. Everything below was run on the .NET 11 RC 1 SDK.
The package nobody wanted to own
zstd is everywhere now. Log pipelines, databases, and HTTP all speak it.
.NET, meanwhile, made you pick a third-party wrapper, check that its native binary ships for every platform you deploy to, and hope it is still maintained next year. It is the dependency that shows up in every security review and that nobody remembers adding.
What .NET 11 adds
The new types live in System.IO.Compression:
- ZstandardStream - a Stream, used exactly like GZipStream.
- ZstandardEncoder and ZstandardDecoder - span-based, no stream needed.
- ZstandardDictionary - train a dictionary for lots of small, similar payloads.
- ZstandardCompressionOptions - quality, window size, checksums, long-distance matching.
On the HTTP side, DecompressionMethods.Zstandard and ZstandardCompressedContent are new too.
The minimal version
If you have ever written a GZipStream, you already know this API:
using System.IO.Compression;
byte[] raw = File.ReadAllBytes("app.log");
using var output = new MemoryStream();
using (var zstd = new ZstandardStream(output, CompressionLevel.Optimal))
{
zstd.Write(raw);
}
byte[] compressed = output.ToArray();
using var input = new MemoryStream(compressed);
using var unzstd = new ZstandardStream(input, CompressionMode.Decompress);
using var back = new MemoryStream();
unzstd.CopyTo(back);
Swap GZipStream for ZstandardStream and the rest of your code does not notice.
I ran all three built-in compressors over the same payload: 2,000 lines of fake JSON logs, 188,010 bytes, all at CompressionLevel.Optimal.
| Format | Compressed size | 200 runs on my machine |
|---|---|---|
| GZip | 13,499 bytes | 218 ms |
| Brotli | 11,936 bytes | 299 ms |
| Zstandard | 11,891 bytes | 150 ms |
Smallest and fastest in one row. It is a synthetic, very repetitive payload and one run on one machine, so treat the timings as a hint, not a benchmark. The .NET team's own numbers on the Canterbury Corpus are in the Preview 1 release notes linked below.
Want more squeeze? ZstandardCompressionOptions takes a Quality from -131072 to 22, and the default is 3.
var options = new ZstandardCompressionOptions
{
Quality = 19,
AppendChecksum = true
};
using var zstd = new ZstandardStream(output, options);
At quality 19 with a checksum, the same log payload came out at 7,535 bytes.
The realistic one: thousands of tiny messages
Compression is bad at tiny payloads. A 150-byte JSON event has almost no repetition inside it, so there is little to squeeze. The repetition is between messages: the same keys, the same enum values, the same shape, every time.
That is what zstd dictionaries are for. You train one on sample messages, then both sides use it:
using System.IO.Compression;
// training: byte[][] of real messages, e.g. the last 1,000 events
// message: one new event as byte[]
byte[] samples = [.. training.SelectMany(b => b)];
int[] lengths = [.. training.Select(b => b.Length)];
using ZstandardDictionary dictionary =
ZstandardDictionary.Train(samples, lengths, maxDictionarySize: 4096);
byte[] buffer = new byte[1024];
ZstandardEncoder.TryCompress(
message, buffer, out int written,
dictionary, ZstandardCompressionOptions.DefaultWindowLog2);
byte[] restored = new byte[message.Length];
ZstandardDecoder.TryDecompress(
buffer.AsSpan(0, written), restored, out int read, dictionary);
I trained a 4,096-byte dictionary on 1,000 order.created events, then compressed 200 new events it had never seen:
| | Average size |
|---|---|
| Raw JSON | 152.2 bytes |
| zstd, no dictionary | 134.7 bytes |
| zstd with dictionary | 38.6 bytes |
Without a dictionary, zstd barely dented them. With one, the average message went from 152.2 bytes to 38.6. That is the difference between "compression is not worth it for our queue" and "why did we not do this sooner".
HttpClient speaks zstd now
Automatic decompression is one flag:
using var client = new HttpClient(new SocketsHttpHandler
{
AutomaticDecompression = DecompressionMethods.Zstandard | DecompressionMethods.GZip
});
string body = await client.GetStringAsync("https://example.com/api/report");
With that handler, the client sent Accept-Encoding: gzip, zstd. My local test server answered 8,000 bytes of JSON as 33 bytes of zstd, and GetStringAsync handed back all 8,000 characters.
Going the other way, ZstandardCompressedContent compresses a request body while it is sent and sets Content-Encoding: zstd for you:
var json = new StringContent(payload, Encoding.UTF8, "application/json");
await client.PostAsync(url, new ZstandardCompressedContent(json));
300 copies of a small JSON object reached my test server as 36 bytes. Make sure the server actually accepts zstd request bodies before you turn this on.
Can I use it?
- .NET 11 only. The types ship in the shared framework; my test apps had no package references at all.
- Status: .NET 11 is at RC 2 with a go-live license; GA is still ahead.
Gotchas
1. The int after the dictionary is not quality. In TryCompress(source, dest, out written, dictionary, 3), that 3 is windowLog2, and the call threw ArgumentOutOfRangeException: windowLog2 ('3') must be greater than or equal to '10'. Pass ZstandardCompressionOptions.DefaultWindowLog2 (23), or set quality with ZstandardDictionary.Create(buffer, quality).
2. Both sides need the same dictionary. Decoding a dictionary-compressed message without it did not throw; TryDecompress just returned false. Version your dictionaries and ship the id with the message.
3. HttpClient does not opt in by itself. A default HttpClient sent no Accept-Encoding at all. When my test server sent zstd anyway, the default client got the 33 raw zstd bytes, not the JSON.
4. Preview samples are already stale. The Preview 1 notes use WindowLog; the RC has WindowLog2, MinWindowLog2, and MaxWindowLog2. Trust the API docs over old blog snippets, this one included once GA lands.
Wrap-up
Zstandard is now just another Stream in the box, with dictionaries and HTTP support on top. Grab the .NET 11 RC SDK, swap one GZipStream for ZstandardStream, and see what your payloads say.
Sources
- What's new in .NET 11 libraries - Microsoft Learn
- .NET 11 Preview 1 libraries release notes - dotnet/core
- ZstandardStream class - .NET API reference
- dotnet/runtime #119575 - Zstandard compression support
- dotnet/runtime #123531 - Zstandard in DecompressionMethods
- .NET 11 Release Candidate 2 - .NET Blog
This article was written with the help of AI and checked against the sources above.
Stop Trimming \r Off Every Regex Match. .NET 11 Has AnyNewLine
TL;DR: In .NET 11, RegexOptions.AnyNewLine makes ^, $, \Z, and . treat \r\n, \r, and the Unicode line breaks as newlines, not just \n. No more \r?$, and no more values that end in an invisible carriage return. .NET 11 is at Release Candidate 2 right now: go-live licensed, but not final yet.
The character nobody can see
You write a regex that grabs a line. It passes every test, because the test strings were typed in C# with \n. Then a real file arrives from Windows, and every value you capture ends with \r.
using System.Text.RegularExpressions;
var bug = Regex.Match("foo\r\nbar", @".+$", RegexOptions.Multiline);
Console.WriteLine(bug.Value.Length); // 4
Three letters, length 4. The fourth one is \r, and it only shows up later, when "foo" == value is mysteriously false.
The reason is old and simple. .NET's regex engine has always treated only \n as a newline. In Multiline mode, $ matches before \n, and . happily matches \r because, as far as the engine knows, it is just another character.
So everybody learned the ritual: [^\r\n]*\r?$. It looks like someone sneezed on the keyboard, and as you will see below, it is not even correct.
Meet AnyNewLine
.NET 11 adds one flag:
var fixedMatch = Regex.Match("foo\r\nbar", @".+$",
RegexOptions.Multiline | RegexOptions.AnyNewLine);
Console.WriteLine(fixedMatch.Value.Length); // 3
With it, ^, $, \Z, and . recognize all of these as line breaks: \r\n, \r, \n, \u0085 (NEL),
(line separator), and
(paragraph separator). Microsoft's docs say this follows Unicode TR18 RL1.6.
The important detail is that \r\n counts as one newline. In "a\r\nb", $ matched at positions 1 and 4: before the \r and at the end, never between \r and \n. ^ matched at 0 and 3, so the second line starts at b, not at a stray \n.
A realistic one: a config file from three operating systems
Here is a tiny key=value parser. The input mixes Windows, Unix, and classic Mac line endings, because somebody's editor had opinions.
using System.Text.RegularExpressions;
string config = "host=db.local\r\nport=5432\nuser=app\rtimeout=30\r\n";
foreach (Match m in Config.Line().Matches(config))
Console.WriteLine($"{m.Groups["key"].Value} = [{m.Groups["value"].Value}]");
static partial class Config
{
[GeneratedRegex(@"^(?<key>\w+)=(?<value>.*)$",
RegexOptions.Multiline | RegexOptions.AnyNewLine)]
public static partial Regex Line();
}
host = [db.local]
port = [5432]
user = [app]
timeout = [30]
Four keys, clean values. The source generator accepts the new flag, and so does RegexOptions.Compiled, which returned the same four values.
Now the same pattern without AnyNewLine. It found only three keys: host came back as db.local\r, and user swallowed the next line whole, as app\rtimeout=30\r. timeout vanished, because to the old engine a lone \r is not a line break at all.
And the ritual? ^(?<key>\w+)=(?<value>[^\r\n]*)\r?$ found 2 of the 4 keys. It fixes \r\n and quietly gives up on a bare \r.
Can I use it?
- RegexOptions.AnyNewLine is new in .NET 11, with the value 2048. The .NET 11 Preview 3 release notes introduced it, and the RegexOptions docs list it as available in .NET 11 and later.
- It works with new Regex(...), the static Regex methods, RegexOptions.Compiled, and [GeneratedRegex].
- .NET 11 RC2 shipped on October 13, 2026 with a go-live support license. It is still a release candidate, and general availability is expected in November.
- On the .NET 10 SDK, the code fails to build with CS0117: 'RegexOptions' does not contain a definition for 'AnyNewLine'.
I ran every snippet here as a file-based app with dotnet run file.cs on SDK 11.0.100-rc.1, on Windows.
Gotchas
1. NonBacktracking and ECMAScript say no. RegexOptions.AnyNewLine | RegexOptions.NonBacktracking threw ArgumentOutOfRangeException at construction, and so did the combination with ECMAScript. If you picked NonBacktracking for its linear-time guarantee, you keep \r? for now.
2. There is no inline letter. Multiline has (?m) and Singleline has (?s), but the docs list no inline option for AnyNewLine. You have to pass the flag in code, so a pattern stored in a config file cannot turn it on by itself.
3. Character classes are not newline-aware. The flag changes ^, $, \Z, and ., and nothing else. [^\n]+ against "foo\r\nbar" still matched 4 characters, \r included, with AnyNewLine on. If you want "anything but a line break", use . and let the flag do its job.
4. \A and \z do not move. They still mean the absolute start and end of the string. With AnyNewLine, done\Z matched "done\r\n", while done\z did not. That is by design, and it is the escape hatch when you really do mean "end of input".
5. Singleline still wins. With Singleline | AnyNewLine, a.+ against "a\r\nb" matched the whole string, newline included. Singleline means "the dot matches everything", and the new flag does not argue with it.
6. The docs disagree about \v and \f. The RegexOptions API reference says AnyNewLine also recognizes vertical tab and form feed. The options article lists only the six sequences above. In the RC1 run, . did not match \v or \f, and ^\w$ found 2 lines in "a\vb", so the API reference matches what the runtime did. If your input can contain form feeds, test it rather than trusting either page.
Wrap-up
\r has been hiding at the end of captured strings for as long as .NET has had regex. Now one flag tells the engine what a line actually is, on every OS at once. Go search your codebase for \r?$ and see how many of them can retire in .NET 11.
Sources
- What's new in .NET libraries for .NET 11: AnyNewLine option (Microsoft Learn)
- RegexOptions enum (Microsoft Learn)
- Regular expression options: AnyNewLine mode (Microsoft Learn)
- .NET 11 Preview 3 libraries release notes (GitHub, dotnet/core)
- Announcing .NET 11 Release Candidate 2 (.NET Blog)
- Unicode Technical Standard #18, RL1.6 Line Boundaries (unicode.org)
This article was written with the help of AI and checked against the sources above.
CVE-2026-111497: a Brotli decoder bug forced an out-of-band .NET release (10.0.14) on Oct 25
Microsoft released .NET 10.0.14 on 25 October as an out-of-band security update, and the stated reason is CVE-2026-111497, an out-of-bounds write (CWE-787) in the Brotli decoder bundled with .NET. CVSS 8.1, publicly disclosed, exploitation marked more likely.
Affected and fixed:
- .NET 8 before 8.0.33 → 8.0.33
- .NET 9 before 9.0.22 → 9.0.22
- .NET 10 before 10.0.14 → 10.0.14 (SDK 10.0.114 or 10.0.403)
- .NET Framework is not affected
The same release completes the October 13 fixes for CVE-2026-70197 (SocketAsyncEventArgs) and CVE-2026-102870 (Blazor WebAssembly debugger), which had shipped incomplete, and adds CVE-2026-114148 for Linux source-build tarballs.
Exposure needs a call path that decompresses attacker-controlled Brotli: request decompression with br enabled, HttpClient AutomaticDecompression including Brotli, or BrotliStream/BrotliDecoder on untrusted input. None of that is on by default, and response compression alone does not hit it. Until you update, dropping Brotli from those paths is a workable stopgap.
Framework-dependent apps need the host runtime updated and restarted. Self-contained apps need a rebuild and republish, and the Blazor WebAssembly Server/DevServer packages have to be bumped separately because the shared runtime update does not cover them.
Advisory: announcements#494
Release notes: github.com/dotnet/core/blob/main/release-notes/10…
51 points · 11 comments
Looking at the git diff for the above release notes, there are updates for #dotnet versions 8, 9, 10, AND 11.
Looks like some people's first task tomorrow is to upgrade the runtime for their apps.
github.com/dotnet/core/commit/60871330fce2b3da383…
If you're doing #dotnet things, are using #Brotli, and are on v10, you NEED to install this update!
#Http2Shell #Node2Shell
github.com/dotnet/core/blob/main/release-notes/10…
1 like · 1 repost
.NET 8/9は11月10日でサポート終了、.NET 11は同日GA — AIと走り切った移行メモ
!
📋 この記事の前提
対象は「.NET 8(または9)で動いている業務アプリを持っていて、2026年11月10日のサポート終了が気になっている人」です
内容は4つです。(1) 11月10日に何が起きるか(公式の期限)、(2) .NET 11 / C# 15で実務に関係する変更、(3) 自宅PCでRC2を実際に動かした実測、(4) 会社の.NET 8→10移行をAIと進めた実務メモ
数字は公式ドキュメント(出典つき)と、手元の実測(2026年10月24日・WSL2 / Celeron N5095A)です
言語機能の差分(C# 13〜15)は、先に書いたこちらの記事にまとめています。今回は「期限と移行」に絞ります
用語: LTS/STS=長期/標準サポート(3年/2年)、GA=正式リリース、RC2=リリース候補2、ReadyToRun=起動を速くする事前コンパイル形式、JIT=実行時のコンパイル
結論: 11月10日は「終了」と「開始」が同じ日。移行先は10、11は先取り
先に3つ書きます。
2026年11月10日、.NET 8(LTS)と.NET 9(STS)が同時にサポート終了します。 本丸はLTSの8です。終了後はセキュリティ修正が止まります。同じ日に.NET 11(STS)がGAします
業務アプリの移行先は.NET 10が本命です。 LTSで2028年11月14日までサポートされます。11は新機能が魅力でも、STS(2年)ですし、まず10で足場を固めるのが現実的です。11はRC2が公開済みなので、検証環境で先に触っておくことはできます
移行はAIで「最後まで」走れます。 ただし「何を正解とするか」「どのテストを信じるか」「いつ戻すか」を先に決めておくのが条件です。社内の8→10移行も、この分担で置き換えまで到達しました
期限まであと17日です(執筆時点: 2026年10月24日)。
2026年11月10日に何が起きるか
公式のサポートポリシー(2026年10月13日更新)とリリースノートを並べると、こうなります。
バージョン
種別
リリース
サポート終了
現在の状態
.NET 8
LTS(3年)
2023/11/14
2026/11/10
Maintenance(最新8.0.32)
.NET 9
STS(2年)
2024/11/12
2026/11/10
Maintenance(最新9.0.21)
.NET 10
LTS(3年)
2025/11/11
2028/11/14
Active(最新10.0.13)
.NET 11
STS(2年)
2026/11/10(GA)
2028/11/9(予定)
RC2(10/13公開)
※ .NET 11の終了日は、dotnet/coreの11.0リリースノートに「2026年11月10日〜2028年11月9日」と明記されています(2026年10月24日時点で確認)。ただし2028年11月9日は木曜で、本文で触れた「11月の第2火曜」の数え方なら11月14日(.NET 10と同じ日)になります。GA後にサポートポリシーの表へ反映される際に確定する見込みです。
8と9が同じ日に切れるのは偶然ではありません。.NETは毎年11月にメジャーリリースし、偶数がLTS(3年)、奇数がSTS(2年)です。8は2023年11月+3年、9は2024年11月+2年——どちらも2026年11月の第2火曜(11月10日)に期限が来ます。
.NET Conf 2026は11月10日〜12日(オンライン・無料)で、初日に.NET 11がローンチされます
サポート終了後も「動き続ける」アプリはあります。ただし、その状態は「修正されない脆弱性を抱えて運用する」ことと同義です。業務アプリでこれは、判断として重い
補足: うちの環境では.NET 9を経由せず、8から10へ直接上げました。STSを挟むと検証が2回必要になるうえ、9自体も11月10日で終わるので、経由する意味が薄いからです。段階的にライブラリの変更を追いたい事情があるなら9経由も選択肢ですが、期限が迫っているならLTSへ直接が素直だと思います
.NET 11で、実務に関係する変更
全部を追うとキリがないので、業務アプリの移行で判断に効くものだけ拾います(RC2時点の公式情報です)。
C# 15 — union型が来た
C# 15は、.NET 11 SDKでnet11.0を対象にしたプロジェクトの既定言語バージョンです。追加されるのは union型、コレクション式の引数、closed hierarchies、拡張インデクサー、ラベル付きbreak/continue、古いランタイムでの非仮想staticインターフェースメンバー、メモリ安全性(プレビュー)です。
目玉はunion型です。複数の型のどれか1つを表す型をunionキーワードで宣言でき、switchが全ケースを網羅しているかをコンパイラが確認します。
public record class Pass(string Id);
public record class Review(string Id, double Confidence);
public record class Reject(string Id);
public union GateResult(Pass, Review, Reject);
// switchの腕を1つ消すと warning CS8509(網羅していない)
static string Describe(GateResult r) => r switch
{
Pass p => $"自動採用: {p.Id}",
Review v => $"保留: {v.Id} (p={v.Confidence:0.000})",
Reject j => $"却下: {j.Id}",
};
設備点検の判定を「自動採用 / 保留 / 却下」の3値で返す、というのは前回までの記事(判定モデルの勢力図2026年10月版)で書いた3段ゲートそのものです。それを、stringやobjectではなく、コンパイラに守られた1つの型として書けます。これは現場で使う人がいるだろうな、と思いました。
ひとつ注意があります。網羅性のチェックは警告(CS8509)であってエラーではありません。腕を1つ消してもビルドは通ります。警告をエラー扱いにする設定(TreatWarningsAsErrors)をしているプロジェクトなら止まりますが、そうでなければ「見逃す」余地が残ります。実際に腕を消して試すと、こう出ました。
warning CS8509: The switch expression does not handle all possible values of its input type
(it is not exhaustive). For example, the pattern 'Reject' is not covered.
もうひとつ、地味に効くのがコレクション式の引数です。これまではnew List<string>(capacity)のように書いていた容量や比較器を、コレクション式の中でwith(...)として渡せます。今回のデモでも使っていて、RC2でそのままコンパイルできました。
string[] ids = ["EQ-001", "EQ-042", "EQ-035"];
// 容量をコレクション式の中で指定(実測で動いたコード)
List<string> names = [with(capacity: 8), .. ids];
※ ここまでの断片は形を示すためのもので、これだけではコンパイルできません。コンパイルできるファイル全体は、後ろの「自宅PCでRC2を動かした実測」に載せています。
Runtime Async — asyncの書き方は変えなくていい
.NET 11ではRuntime Asyncが既定になり、net11.0向けではこれまで必要だったプロパティ指定が不要になりました。ランタイムのライブラリ自体もRuntime Asyncでビルドされています。
実務目線でうれしいのは、既存のasync/awaitのコードを書き換えなくていいことです。スタックトレースがきれいになり、オーバーヘッドが減る、という改善が向こうから来ます。移行作業としては「何もしない」で受け取れる類の変更です。
ハードウェア要件の引き上げ — 製造業はここを見てほしい
見落としやすいのですが、.NET 11は最低ハードウェア要件が上がりました。
x86/x64: x86-64-v1 → x86-64-v2(CX16・POPCNT・SSE3・SSSE3・SSE4.1・SSE4.2を保証)
ReadyToRunのターゲット: Windows/Linuxともx86-64-v3(AVX・AVX2・BMI1・BMI2・F16C・FMA・LZCNT・MOVBE)に更新
Arm64: Linuxは変更なし(Raspberry Piのような機器を引き続きサポート)。Windows arm64はLSEが必要です。Appleは変更なし
公式ドキュメントには「.NET 11からは、古いハードウェアでは.NETが起動しなくなる」と書かれています。起動時には次のようなメッセージが出ます。
The current CPU is missing one or more of the baseline instruction sets.
現場のPCをどう判断するか、ざっくり表にするとこうです。
PCの世代
判断
x86-64-v2を満たさない(例: Core 2世代、古いAtomなど)
.NET 11は起動しない可能性。10に留めるか、PC更新とセットで11へ
v2は満たすがAVX2なし(Celeron・Atom系など)
11は動く(今回の実測)。AVX2が無い分、起動時にJITコストがあり得る
AVX2あり(Intel Coreの2013年Haswell以降、AMDはZen世代以降)
11の性能を素直に受けられる
x86-64-v2は、Windows 11と、Windows 10で公式サポートされているx86/x64 CPUの要件と揃えたものです。公式には「Intel/AMDが今もサポートしているチップはすべて含まれ、古いチップは2013年頃にサポートが終わっている」という説明です(これはCPUベンダーのサポート終了時期の話で、v2の境界そのものではありません)。
現場のPCがv2を満たすかは、CPUのフラグで確認するのが確実です。Linuxならgrep -o 'sse4_2|popcnt' /proc/cpuinfo | sort -uで2つ出るか。WindowsならSysinternalsのCoreInfo(coreinfo -f)でSSE4.2とPOPCNTを見ます。
工場には、SSE4.2が無い世代のPCが残っていることがあります。そういう現場では、11に上げる前にハードウェアの棚卸しが必要です。逆に言えば、10に留めておけばこの問題は起きません(10のLTSは2028年11月14日まで)。「11の新機能が欲しい」と「現場のPCを入れ替える」を同じ号令で決めないほうが安全です。
なお、AVX2が無いCPUでも.NET 11自体は動きます。ReadyToRunのターゲットがv3になった分、起動時にJITの追加コストがかかり得る、というのが公式の説明です(今回の実測では、体感できる差は出ませんでした)。
ツール: C# Dev Kit 11とdotnetup
C# Dev Kit 11(VS CodeのC#拡張)は、.NET 11に合わせて3.3から11.0へ。起動時に6つあったマネージドプロセスを、Native AOT(事前コンパイル)の単一実行ファイルにまとめました。公式ブログの数字では、変更なしのビルドが34.4秒→0.88秒(39倍)、Aspire 407プロジェクトで、編集中のファイルが使えるまでが84.1秒→0.47秒(最大180倍。ソリューション全体は3.0秒で28倍)、メモリが2,072MB→316MB(約85%減)。ただし、これはAspireやOrleansという巨大ソリューションでの値です。私の環境では未検証。プロジェクトキャッシュのコミットやC# Doctorも追加され、stableは.NET 11と同時期の見込みです。
dotnetupは10月15日にpublic previewになった、ユーザー領域だけで動く.NETのツールチェーン管理ツールです。管理者権限なしで、複数のSDKを横並びに置けます。公式の流れはdotnetup initで初期化しますが、今回は既存の.NET 10環境に触れないよう、初期化せずにdotnetup dotnet …の転送形式で呼びました(dotnetup env set noneは非対話シェルでは--shell bashが必要で、今回は使っていません)。実際に踏んだ手順はこれです。
1. dotnetup本体を入れる(末尾にPATH追加の案内が出ます)
curl -fsSL aka.ms | bash
2. .NET 11のプレビューSDKを入れる
dotnetup sdk install preview
3. プロジェクトを置く(GateDemo.csprojとProgram.cs)
mkdir demo && cd demo
4. 実行
dotnetup dotnet run
ただし0.3.0-previewのツールなので、業務環境の標準にするのはGA後が無難です。
破壊的変更の一覧は、「種類」で読む
移行の前に見るべきは、公式のバージョン別・破壊的変更一覧です(9・10・11でそれぞれページがあります)。各項目は3種類に分類されています。
Binary incompatible: 新しいランタイムで既存のバイナリが動かない(再コンパイルが必要)
Source incompatible: 新しいSDKで再コンパイルすると、ソースの修正が必要
Behavioral change: コンパイルは通るが、実行時の挙動が変わる
この分類が便利なのは、「ビルドが通ったから大丈夫」が通用しないのが3つ目だと分かることです。たとえば.NET 10の一覧には、既定のトレース伝播がW3C標準に変わる、Unixで終了シグナルの既定ハンドラが提供されなくなる、といった挙動の変更が並びます。業務アプリだと、ログの相関IDやLinuxサービスの停止処理に効いてくる類です。
ほかにも、.NET 10でSystem.Linq.AsyncEnumerableがコアライブラリに入る(source incompatible)という項目があります。System.Linq.Asyncパッケージを併用しているプロジェクトでは、型が衝突し得ます。こういう項目は、一覧とcsprojをAIに突き合わせてもらうと早いです。
自宅PCでRC2を動かした実測
ここからは手元の数字です。環境はWSL2のUbuntu 22.04、CPUはCeleron N5095A(4コア・RAM 2.8GB・AVXなし・SSE4.2まで)、既存のSDKは10.0.401です。開発機としては控えめどころか、かなり弱い部類です。
dotnetupを入れて、RC2のSDKを導入したところ、4分14秒かかりました(SDKは201.3MB)。回線が遅いこともありますが、このマシンだとこのくらいです。初期化はせず、dotnetup dotnet … の形で呼びます。既存の10環境と干渉しません。
結果はこうなりました。
項目
実測値
SDK
11.0.100-rc.2.26504.105
ランタイム
11.0.0-rc.2
net11.0のビルド
12.63秒・0警告0エラー
実行(dll直接起動)
0.21〜0.23秒
net10.0のままSDKだけ11に
ビルド成功(復元21.5秒+ビルド24.3秒)
ビルド時にはNETSDK1057: You are using a preview version of .NETというメッセージも出ます(実測)。プレビュー版であることの注意書きで、エラーではありません。ただしCIのログにこの行が出るのを「警告が出た」と誤解しないように、チームで共有しておくと安全です。
最後の行が地味に大事です。SDKだけ先に11へ上げて、ターゲットフレームワークは10のまま、というビルドが通りました。いきなり全部を上げるのではなく、CIや開発機のSDKだけ先に新しくして、アプリのTFMは後から上げる、という段取りが取れます。
デモはnet11.0のコンソールプロジェクトに、union型とコレクション式の引数を書いただけのものです。csprojはこれだけです。
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net11.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
</Project>
Program.csはこれです(実測で動いたファイルそのまま)。dotnetup dotnet runで実行できます。
// C# 15 / .NET 11 RC2 の動作確認用デモ
// ・union 型(3つの結果を1つの型で受ける)
// ・コレクション式の引数(with(...))
using System;
public record class Pass(string Id);
public record class Review(string Id, double Confidence);
public record class Reject(string Id);
public union GateResult(Pass, Review, Reject);
public static class Program
{
public static void Main()
{
string[] ids = ["EQ-001", "EQ-042", "EQ-035"];
// C# 15: コレクション式の引数で容量を指定
List<string> names = [with(capacity: 8), .. ids];
GateResult[] results =
[
new Pass("EQ-001"),
new Review("EQ-042", 0.973),
new Reject("EQ-035"),
];
foreach (var r in results)
{
Console.WriteLine(Describe(r));
}
Console.WriteLine($"names={names.Count} / runtime={Environment.Version}");
}
// switch は union の全ケースを網羅していることをコンパイラが確認する
static string Describe(GateResult r) => r switch
{
Pass p => $"自動採用: {p.Id}",
Review v => $"保留: {v.Id} (p={v.Confidence:0.000})",
Reject j => $"却下: {j.Id}",
};
}
実行結果(実測のまま):
自動採用: EQ-001
保留: EQ-042 (p=0.973)
却下: EQ-035
names=3 / runtime=11.0.0
会社の.NET 8→10移行を、AIとどう進めたか
ここからは社内の話です。対象のアプリや構成、件数や時間は伏せますが、要点は進め方にあるので、そこを書きます。.NET 8で動いている業務アプリを、サポート終了に合わせて.NET 10へ上げました。
進め方は、大きく4つでした。
対象を小さく切る(全部を一度に上げない)
global.jsonとCIのSDKバージョンを固定する
ビルド→テスト→実行時挙動の差分、の順で確認する
失敗したらログをAIに渡し、修正→再ビルドのループを回す
詰まるのは、ビルド(TFMやAPIの変更)、パッケージ(依存の互換)、テスト(期待値のズレ)のどれかです。どこで詰まっても、やることは同じ。共通するのは、「ビルドが通る=終わりではない」ということでした。
正直に言うと、一発では通りませんでした。直してはビルド、テストを回してはログを見て、の繰り返し。詰まったときの進め方には再現性がありました。効いたのは、エラーを1件ずつ切り出して渡すのではなく、ビルド1回分のログを丸ごと渡すことです。同じ原因から出ているエラーは、まとめて直したほうが往復が減ります。
そして、AIが直した差分は、そのまま次へ進めませんでした。テストが通ること、差分が読めること、実際に動かして挙動が変わっていないこと——この3つを確認してから、次の対象へ移ります。分担表(後述)の「人が握る」には、この確認も含まれます。
やってみての実感です。AIが強いのは「繰り返し」と「ログの読み解き」。ビルドエラーを潰す作業、テストの失敗を分類する作業は、人間がやるより速い。延々と続くビルドエラーの潰し合いは、人間だと集中が切れます。AIは切れない。地味ですが、これが効きました。逆に、AIに決めさせてはいけないのは「何をもって正解とするか」「どのテストを信じるか」「いつ戻すか」です。ここを先に決めずに任せると、通ったように見えて業務ルールが壊れている、という状態になり得ます。
公式のやり方に寄せるなら、GitHub Copilot modernization(旧Upgrade Assistantの後継)があります。Assessment → Planning → Executionの3段階で進み、各段階の成果物を.github/upgrades/配下にMarkdownで残し、変更をコミットしていく形式です。Visual Studioはソリューションを右クリックして「Modernize」、VS Codeは@upgrade、CLIは@modernize-dotnetで起動します。.NET Upgrade Assistantは公式に非推奨(deprecated)になっているので、これから始めるならCopilot側です。
AIに渡すと効くもの
今回のループで効いたのは、エラーメッセージ単体ではなく「セット」で渡すことでした。
ビルドログ(エラーだけでなく、その前後の警告も)
テストの失敗一覧(どのテストが、何を期待して、何が返ったか)
global.jsonとDirectory.Build.props(バージョン指定の全体像)
直前のdiff(何を変えて、何が壊れたか)
逆に、渡しても迷わせるだけだったのは「動きません」の一言です。AIは推測で直し始め、関係のない場所を触り始めます。ログと差分を添えるほど、修正の往復が減りました。
AIに任せる範囲・人が握る範囲
今回の実感を、分担表にしておきます。
工程
AIに任せる
人が握る
依存パッケージの棚卸し
列挙と更新案の作成
ライセンス・供給元の確認
TFM(対象フレームワーク)・プロジェクトファイルの更新
機械的な書き換え
対象範囲の決定
ビルドエラーの修正
ログ→修正→再実行のループ
修正差分のレビュー方針
テストの実行と分類
実行・失敗の切り分け
テストの意味・合否基準
実行時挙動の差分
候補の列挙
業務影響の判断
ロールバック
手順の自動化
戻すかどうかの判断
11月10日に間に合わないとき
全部を10へ上げ切れない、という現場もあると思います。その場合の現実的な順番です。
本番で動いているものから優先する(開発機や社内ツールは後回しにできます)
間に合わないアプリは「サポート切れのまま運用する」と明示的に台帳へ書く(黙って残さない)
切れている間は、ネットワークの露出を減らす・入力の検証を厚くするなど、別の防御で埋める
次の刈り取りを決める(例: 次の設備更新や予算策定に合わせる)
サポート終了は「アプリが止まる日」ではありません。「修正が来なくなる日」です。止まらないから放置、が一番危ないパターンだと思います。
11月10日までのチェックリスト
8/9で動いているアプリを棚卸しする。TFMとCIのSDK指定(global.json)を確認します
移行先を10に固定する。11は検証環境で先取りします(dotnetupが楽でした)
ハードウェアを確認する。 x86-64-v2(SSE4.2)を満たさないPCが現場に残っていないか。残っているなら、その現場は10に留めます(10は2028年11月14日まで)
破壊的変更の一覧をバージョン別に確認する。9・10・11の公式リストから、自社コードに関係する項目だけ抜きます
AIに渡す材料を揃える。ビルドログ、テスト結果、global.json、差分——これがないとAIは「推測」で直し始めます
AIの修正は「テストが通った」で終わらせない。テストが何を保証しているかをレビューします
ロールバック手順を先に確認する。パッケージのバージョン固定、デプロイ単位、戻し先のブランチ
よくある質問
Q. .NET 8から10と11、どっちに上げるべきですか?
A. 業務アプリは10(LTS・2028年11月14日まで)が本命です。11はSTS(2年)で、新機能の検証や先行採用向けです。まず10で安定させ、11は別枠で試すのが現実的だと思います。
Q. .NET 11はプレビューですが、本番で使えますか?
A. RC2はGo-Live(本番利用が可能なプレビュー)ですが、正式GAは11月10日です。業務の標準にするならGA後、しかもSTSである点を踏まえて判断してください。今回の検証はRC2で行いました。
Q. AVXが無い古いPCでも.NET 11は動きますか?
A. 今回のCeleron(SSE4.2まで・AVXなし)ではRC2が動きました。ただし公式の最低要件はx86-64-v2で、SSE4.2やPOPCNTが無いCPUは起動しない可能性があります。ReadyToRunはv3(AVX2)なので、AVX2が無いと起動時にJITの追加コストがかかり得ます。
Q. .NET Upgrade Assistantはもう使えないのですか?
A. 公式には非推奨(deprecated)です。後継はGitHub Copilot modernizationで、Visual Studio 2026、またはVisual Studio 2022 17.14.16以降に含まれます。
Q. 移行でAIはどこまで任せられますか?
A. 「繰り返し」と「ログの読み解き」は任せられます。正解の定義、テストの意味、ロールバックの判断は人が握ってください。今回の8→10移行も、この分担でした。
まとめ
11月10日は、.NET 8と9の終了日であり、.NET 11の開始日です。執筆時点(10月24日)で、期限まで17日。
移行先は10。11は「知っておく・試しておく」段階で、RC2は今すぐ触れます。そして、11はハードウェア要件が上がったので、工場の古いPCが絡む現場なら10が安全です。
移行の実務は、AIでかなり走れます。ただし「何を正解とするか」を先に決めること。これがないと、AIは速いだけのループを回します。
今回のRC2の数字も、AVXなしのCeleronで採った数字です。手元の環境で測った記録として読んでもらえればと思います。
【関連記事】
C# 13→14→15で何が変わった?.NET 10移行前に調べた新機能まとめ
出典
.NET and .NET Core official support policy(2026年10月13日更新)
dotnet/core: releases.md
dotnet/core: .NET 11 release notes
What's new in .NET 11
What's new in the .NET 11 runtime
What's new in the SDK and tooling for .NET 11
What's new in C# 15
.NET Conf 2026 – Save the Date(.NET Blog)
A faster, lighter C# Dev Kit(.NET Blog・2026年10月6日)
Introducing dotnetup(.NET Blog・2026年10月15日)
.NET Upgrade Assistant Overview
GitHub Copilot upgrade overview
.NET breaking changes reference
Stop Hand-Rolling Full Outer Joins in LINQ. .NET 11 Has FullJoin
TL;DR: In .NET 11, a.FullJoin(b, x => x.Key, y => y.Key) returns every matched pair plus the unmatched rows from both sides, as (Outer, Inner) tuples. Join, LeftJoin, RightJoin, and GroupJoin also get overloads with no result selector. .NET 11 is at Release Candidate 2 right now: go-live licensed, but not final yet.
The join you Google every time
You have two lists that should agree and never do. What the system thinks is in stock, and what the stocktake actually counted. You want three answers: what matches, what only the left side has, and what only the right side has.
SQL calls that a full outer join. LINQ never had one, so everybody pastes the same answer:
var left =
from s in system
join c in counted on s.Sku equals c.Sku into matches
from c in matches.DefaultIfEmpty()
select (Record: s, Count: c);
var rightOnly =
from c in counted
where !system.Any(s => s.Sku == c.Sku)
select (Record: (StockRecord?)null, Count: c);
var full = left.Concat(rightOnly);
It works. It is also two queries, a DefaultIfEmpty nobody can explain on the first read, and an Any inside a where, which scans the whole left list once for every row on the right. And the key comparison lives in two places, so the day someone changes one of them, the halves quietly disagree.
Meet FullJoin
.NET 10 already added LeftJoin and RightJoin. .NET 11 closes the set with FullJoin, and it returns tuples, so you do not even need a result selector:
var products = new List<(int Id, string Name, string? Category)>
{
(1, "Laptop", "Electronics"),
(2, "Mouse", "Electronics"),
(3, "Orphan", null),
};
var categories = new List<(string Name, string Description)>
{
("Electronics", "Electronic devices"),
("Furniture", "Office furniture"),
};
foreach (var pair in products.FullJoin(categories, p => p.Category, c => c.Name))
Console.WriteLine($"{pair.Outer.Name ?? "(none)"} | {pair.Inner.Description ?? "(none)"}");
Laptop | Electronic devices
Mouse | Electronic devices
Orphan | (none)
(none) | Office furniture
The tuple elements are named Outer and Inner, and the side with no match is default. For reference types, that means null, which is exactly what you want to pattern match on. Here the elements are themselves tuples, so a missing side is an empty tuple whose Name is null; gotcha 1 explains why that deserves a second look.
The same release gives the old operators a selector-free overload, too. products.Join(categories, p => p.Category, c => c.Name) now yields (Outer, Inner) pairs, so (p, c) => new { p, c } can finally retire.
A realistic one: the stocktake
Here is the reconciliation from the start, done properly. The counted SKUs were typed by a human, so one of them is lowercase.
List<StockRecord> system =
[
new("SKU-100", "USB-C cable", 40),
new("SKU-200", "Desk lamp", 12),
new("SKU-300", "Monitor arm", 5),
];
List<ShelfCount> counted =
[
new("sku-100", 38),
new("SKU-300", 5),
new("SKU-999", 3),
];
var report = system.FullJoin(
counted,
s => s.Sku,
c => c.Sku,
StringComparer.OrdinalIgnoreCase);
foreach (var (record, count) in report)
{
Console.WriteLine((record, count) switch
{
(null, { } c) => $"{c.Sku}: on the shelf, not in the system ({c.Quantity})",
({ } r, null) => $"{r.Sku}: in the system, not on the shelf ({r.Quantity} missing)",
({ } r, { } c) when r.Quantity != c.Quantity => $"{r.Sku}: off by {c.Quantity - r.Quantity}",
({ } r, _) => $"{r.Sku}: OK",
(null, null) => "nothing on either side, which FullJoin never yields",
});
}
record StockRecord(string Sku, string Name, int Quantity);
record ShelfCount(string Sku, int Quantity);
SKU-100: off by -2
SKU-200: in the system, not on the shelf (12 missing)
SKU-300: OK
SKU-999: on the shelf, not in the system (3)
One call, one comparer, and a switch that reads like the spec. The comparer is a plain optional parameter, which the query-syntax join ... equals never let you pass at all. It built with zero warnings, nullable checks included.
Can I use it?
- FullJoin and the tuple-returning overloads are new in .NET 11. The .NET 11 Preview 5 announcement listed "LINQ adds full outer joins", and the release notes cover both additions.
- They exist on Enumerable, Queryable, and AsyncEnumerable, so query providers and async streams get them too.
- .NET 11 RC2 shipped on October 13, 2026 with a go-live support license. It is still a release candidate.
- On the .NET 10 SDK, the call fails with CS1061: 'int[]' does not contain a definition for 'FullJoin'.
I ran every snippet here as a file-based app with dotnet run file.cs on SDK 11.0.100-rc.1, on Windows. The classic two-query version ran on the .NET 10 SDK, with the SKUs in matching case, and paired up the same four rows.
Gotchas
1. Value types hide the missing side. default for an int is 0, not "nothing". A full join of the int arrays [0, 5] and [0, 7] on the values themselves printed (0,0), (5,0), (0,7), and you cannot tell the real match from the two misses. With int?[] the same data printed (0,0), (5,null), (null,7). If your elements are structs or tuples, make them nullable or check a field that is never empty.
2. Null keys never match. Joining ["a", null] with [null, "b"] gave four rows, and the two null keys came out as separate unmatched rows instead of a pair. That is the same rule Join has always had, so this is consistency, not a bug.
3. Duplicate keys multiply. [1, 1, 2] joined with [1, 1, 3] produced four (1,1) rows, then (2,0) and (0,3). Two times two is still four, even in a full join.
4. Unmatched right rows come last. Joining ["x", "a", "y"] with ["b", "a", "c"] gave (x,-), (a,a), (y,-), (-,b), (-,c). Left rows keep their order, and the right-only rows are appended at the end in their own order. If your report needs a different order, sort it.
5. GroupJoin's new overload is not a tuple. The selector-free GroupJoin returns IGrouping<TOuter, TInner>, so the outer element is g.Key. Grouping the products from the first example under their categories gave Electronics: 2 [Laptop, Mouse] and Furniture: 0 [].
Wrap-up
The full outer join was the last SQL join that LINQ made you build by hand. Now it is one call that tells you what matches, what is missing, and what showed up uninvited. Go find that DefaultIfEmpty().Concat(...) in your codebase and see whether it survives .NET 11.
Sources
- What's new in .NET libraries for .NET 11: LINQ join improvements (Microsoft Learn)
- Enumerable.FullJoin method (Microsoft Learn)
- Enumerable.GroupJoin method (Microsoft Learn)
- .NET 11 Preview 5 libraries release notes (GitHub, dotnet/core)
- .NET 11 Preview 5 is now available (.NET Blog)
- Announcing .NET 11 Release Candidate 2 (.NET Blog)
This article was written with the help of AI and checked against the sources above.
1 comment
まぁ、でも .NET もこの間のマンスリーアップデートでクリティカルを含むパッチ提供してるから、情報が出たタイミングの問題な気がする。
github.com/dotnet/core/blob/main/release-notes/10…
12 likes · 1 repost · 921 views
.NET 11 RC2: What Is New and Whether to Upgrade From 8 or 10
.NET 11 RC2, published on 13 October 2026, is the final release candidate and ships with a go-live license, but the practical advice is split by where you start: .NET 8 teams must move before 10 November 2026 and should land on .NET 10, while .NET 10 teams have until November 2028 and can wait for .NET 11 GA.[[1]](#cite-1) .NET 11 is a Standard Term Support release, so it gets two years of patches, from 10 November 2026 to 9 November 2028.[[2]](#cite-2) This post covers what is new in RC2, the support table for .NET 8 to 11, the upgrade steps and the breaking-changes list, and how to decide between shipping on RC2 and waiting.
TL;DR
- RC2 (11.0.0-rc.2, 13 Oct 2026) is production-usable under the go-live license, but Microsoft has not published a GA date on the RC2 post.
- .NET 11 is STS: supported from 10 Nov 2026 to 9 Nov 2028 per the release notes README.
- .NET 8 and .NET 9 both reach end of support on 10 Nov 2026. .NET 10 (LTS) runs to 14 Nov 2028.
- C# 15 adds union types, closed hierarchies, collection expression arguments, extension indexers and labeled break and continue, all still marked preview on the docs page.
- On .NET 8: go to .NET 10 now. On .NET 10: stay put, test on RC2 in CI, move after GA if you want the new features.
What RC2 is and what the go-live license means
The .NET team published the RC2 announcement on 13 October 2026. It is the second release candidate, it includes a go-live support license, and the post says it can be used in production.[[1]](#cite-1) The release notes list the sequence: preview 1 on 10 February, preview 7 on 11 August, RC1 on 8 September, and 11.0.0-rc.2 on 13 October.[[2]](#cite-2)
One thing matters for planning: the support policy page lists RC2 under go-live releases with an end-of-support date of 10 November 2026, the same day .NET 8 and 9 expire.[[3]](#cite-3) So RC2 is not a version you can sit on. If you deploy it, you commit to moving to the GA build when it appears.
The same day, Microsoft shipped the October servicing releases: .NET 10.0.13 (SDK 10.0.402), .NET 9.0.21 (SDK 9.0.319) and .NET 8.0.32 (SDK 8.0.426), each dated 13 October 2026. Their release metadata lists 44, 42 and 40 CVE fixes respectively.[[7]](#cite-7) Patch whatever you run today before you plan the bigger move.
The RC2 post lists tooling support in Visual Studio 2026 Insiders and in Visual Studio Code with the C# Dev Kit. It names no exact GA date.[[1]](#cite-1)
What is new in RC2 that a working team will notice
RC2 is a polish release, not a feature drop. The announcement is organised by area, and most items are small. Here are the ones that change day-to-day work.
Libraries and runtime
The libraries section lists incremental CBOR reading, experimental composite ML-KEM and HPKE APIs, DataAnnotations messages formatted with attribute-specific arguments, and a configurable initial HTTP/2 concurrency.[[1]](#cite-1) The ML-KEM and HPKE items are the post-quantum cryptography work; the post labels them experimental, so do not build a production protocol on them yet. On the runtime side, the only listed change is that file locking on iOS and tvOS is disabled by default.
SDK, MSBuild and NuGet
The SDK now accepts quoted values in file-level directives and allows MSBuild properties inside launch-profile arguments. MSBuild gets faster wildcard matching during project evaluation, which helps repositories with large glob-heavy projects. NuGet lets you set a minimum package publication age from the CLI, a useful guard against grabbing a package version that was published minutes ago, which is the window supply-chain attacks rely on.[[1]](#cite-1)
ASP.NET Core, MAUI, Windows Forms and F#
In ASP.NET Core, the QuickGrid virtualization APIs are no longer experimental. .NET MAUI gains a way to create apps with C# UI, Apple Icon Composer asset support, XAML authoring and Hot Reload work, and updates for .NET for Android and Apple platforms. Windows Forms adds a KioskModeManager component, deferred form reveal, suspension of painting and layout during bulk updates, and new analyzers that guide the Designer. F# gets runtime async support, reraise in computation expressions, fewer allocations in FSharp.Core collections and lower compiler memory use.[[1]](#cite-1)
The RC2 post does not list C# features itself. It points to the C# 15 page, which is where the language changes live.
C# 15 in one table
The Microsoft Learn page for C# 15 lists six feature areas and calls C# 15 the latest preview release.[[4]](#cite-4) Treat that wording as the status: the page last updated on 14 September 2026, before RC2, so check it again before you depend on any of these.
| Feature | What it does | Status note on the docs page |
| --- | --- | --- |
| Collection expression arguments | A with(...) first element passes capacity or a comparer to the collection constructor | Listed under C# 15 |
| Union types | The union keyword declares a value that is one of several case types, with exhaustive switch checking | Some proposal features not yet implemented |
| Closed hierarchies | A closed class can only be derived in its own assembly, so switches over it need no default arm | Listed under C# 15 |
| Extension indexers | Declare an indexer inside an extension block | Listed under C# 15 |
| Labeled break and continue | Name a loop or switch to exit or continue, instead of a flag or goto | Listed under C# 15 |
| Memory safety | Pointer relaxations and an unsafe(expression) form | Needs the preview language version |
Union types are the headline. The docs example declares public union Pet(Cat, Dog, Bird); and the compiler then checks that a switch expression covers all three cases.[[4]](#cite-4) The docs also say the runtime has included UnionAttribute and IUnion since .NET 11 Preview 5. Closed hierarchies solve the same exhaustiveness problem for class trees you control.
Labeled break and continue is the one you will use the first day. The docs show continue outer; and break outer; against a labeled outer for loop, replacing the Boolean flag pattern. A new style rule, IDE0410, flags the flag and goto patterns it replaces.[[4]](#cite-4)
Support windows for .NET 8, 9, 10 and 11
The support policy page states that LTS releases get three years of free support and patches, and STS releases get two years.[[3]](#cite-3) Here are the dates from the policy page and the .NET 11 release notes README.
| Version | Release type | Original release | End of support |
| --- | --- | --- | --- |
| .NET 8 | LTS | 14 Nov 2023 | 10 Nov 2026 |
| .NET 9 | STS | 12 Nov 2024 | 10 Nov 2026 |
| .NET 10 | LTS | 11 Nov 2025 | 14 Nov 2028 |
| .NET 11 | STS | Support starts 10 Nov 2026 (README) | 9 Nov 2028 (README) |
Two details to read carefully. First, .NET 11 does not appear in the policy page support table yet; the dates for it come from the release notes README, which says it will be supported from 10 November 2026 to 9 November 2028.[[2]](#cite-2) Second, .NET 8 loses support in the same week .NET 11 is expected to ship, so the 8 to 10 move is a deadline, not a preference.
On GA timing: the .NET Conf 2026 page lists the conference for 10 to 13 November and says it will celebrate the launch of .NET 11, but it gives no release date.[[5]](#cite-5) The README support start of 10 November lines up with day one of the conference. Treat GA at .NET Conf as expected, not announced.
Upgrade path: what to change in the repo
The mechanics are the same for every move. Pin the SDK, retarget, build with warnings visible, then read the breaking-changes list for the areas you use.
{
"sdk": {
"version": "11.0.100-rc.2.26504.105",
"rollForward": "latestFeature",
"allowPrerelease": true
}
}
That is the RC2 SDK build listed in the dotnet/core release notes (runtime 11.0.0-rc.2).[[2]](#cite-2) Install it from get.dot.net/11 and confirm with dotnet --list-sdks. Then change the project file:
net11.0
false
Leave warnings-as-errors off for the first build so you can see the full list, then turn it back on once the list is clean. To check that global.json is valid JSON before CI does, paste it into the [JSON formatter](/tools/json-formatter).
What the breaking-changes page lists
Microsoft's .NET 11 breaking-changes page groups entries by area and tags each as binary incompatible, source incompatible or a behavioral change. It carries a note that it is a work in progress and not a complete list.[[6]](#cite-6) The areas that most apps touch:
- Core libraries: CborReader and CborWriter enforce a default maximum nesting depth; TAR-reading APIs verify header checksums; CRC32 validation is added when reading ZIP archive entries; Math.Round and MathF.Round return correctly rounded results; DateOnly and TimeOnly TryParse methods throw for invalid input.
- Extensions: IHost.RunAsync and IHost.StopAsync throw when a BackgroundService fails; synchronous access to async-validated options throws.
- JIT: floating-point conversions to small integral types are saturating, and minimum hardware requirements are updated.
- Networking: SslStream server-side AIA certificate downloads are disabled by default.
- SDK and MSBuild: dnx scripts bypass global.json SDK selection; the template engine packages no longer support netstandard2.0; VSTest drops its Newtonsoft.Json dependency.
- .NET MAUI: the minimum Android API level is raised to 24.
- Cryptography: DSA is removed from macOS.
Of these, the BackgroundService change is the one most likely to bite a service: a worker that used to fail quietly now surfaces the failure through the host. That is arguably better behavior, but it will change how your process exits and how your orchestrator restarts it.
Ship on RC2 or wait for GA
The go-live license answers the legal question. It does not answer the engineering one. Use this decision table.
| You run | Deadline | Recommendation |
| --- | --- | --- |
| .NET 8 in production | 10 Nov 2026 | Move to .NET 10 now. It is LTS to Nov 2028 and has a year of fixes behind it. Do not jump to an STS preview to beat a deadline. |
| .NET 9 in production | 10 Nov 2026 | Move to .NET 10 if you want the longest runway, or to .NET 11 GA if you need a C# 15 feature. Either way, plan for November. |
| .NET 10 in production | 14 Nov 2028 | Stay. Add a CI job that builds against RC2 so the breaking-changes list becomes a known quantity before GA. |
| New internal service, short lifespan | None | RC2 is acceptable if you accept moving to GA the week it lands. |
The case against shipping RC2 to customers is simple: an RC build is a moving target until GA, and the policy page gives RC2 the same end-of-support day as .NET 8. The case for it is also real. If you are building something that needs union types and exhaustive switches today, the compiler support is there, with the caveat that parts of the union proposal are unimplemented and the language is still labeled preview.[[4]](#cite-4)
For teams on .NET 10 the better move is cheap: build against net11.0 in a side branch, count the warnings, and read only the breaking-changes entries for APIs you call. If you also run Node or Python services, the same deadline thinking applies there; see our [Node 26 LTS upgrade guide](/blogs/node-26-lts-28-october-2026-upgrade-guide-from-node-24-22) and the [Python 3.15 migration checklist](/blogs/python-3-15-release-october-1-2026-lazy-imports-utf8-default-migration-checklist) for how those release trains compare. The [MCP breaking-changes migration guide](/blogs/mcp-2026-07-28-breaking-changes-migration-guide-stateless-protocol) is a reminder that protocol-level breaks hit harder than language ones.
Frequently asked questions
Can I use .NET 11 RC2 in production?
Yes. The RC2 announcement says it includes a go-live support license and can be used in production.[[1]](#cite-1) The policy page lists RC2 support ending 10 November 2026, so you need to update to the GA build.
When is .NET 11 GA?
No date is stated on the RC2 post. The .NET Conf 2026 page, 10 to 13 November, says it will celebrate the launch of .NET 11, and the release notes README starts support on 10 November. GA at the conference is expected, not confirmed.[[5]](#cite-5)
Is .NET 11 LTS?
No. The README calls it a Standard Term Support release, supported for two years, to 9 November 2028.[[2]](#cite-2) .NET 10 is the current LTS.
When does .NET 8 support end?
10 November 2026, per the support policy page. .NET 9 ends the same day.[[3]](#cite-3)
Is C# 15 final in RC2?
The Microsoft Learn page still calls C# 15 the latest preview release, and some union proposal features are not implemented. Check the page again before relying on a feature.[[4]](#cite-4)
Where is the full list of breaking changes?
learn.microsoft.com/dotnet/core/compatibility/11. Microsoft notes the list is incomplete and still growing.[[6]](#cite-6)
Sources
1. .NET 11 RC 2, .NET Blog, 13 October 2026 (accessed 20 October 2026)
2. dotnet/core: .NET 11.0 release notes README (accessed 20 October 2026)
3. .NET and .NET Core official support policy (accessed 20 October 2026)
4. What is new in C# 15, Microsoft Learn (accessed 20 October 2026)
5. .NET Conf 2026 (accessed 20 October 2026)
6. Breaking changes in .NET 11, Microsoft Learn (accessed 20 October 2026)
7. dotnet/core release notes: releases.json for 8.0, 9.0 and 10.0 (October 2026 servicing, accessed 20 October 2026)
---
Originally published at wowhow.cloud
週刊プログラミングニュース|Deno・Python・OSSと開発ツール【2026-W42/10月9〜15日】
週刊プログラミングニュース|2026年10月9日〜15日 | 2026-W42
最初にDeno、Python、Bun、GitHub Actions、.NET、シークレット保護。その後、OSS・フレームワーク、ライブラリー・データベース、IDE・開発プラットフォームを章ごとに整理します。
対象期間:10月9日00:00〜15日23:59 JST。時刻を確認できた発表はJSTへ換算し、日付のみの資料は公式表記日を採用しています。境界時刻が未確認の候補は確定分と区別しました。
正式版、プレビュー、今後の予定を区別しています。Dockerは修正済み問題の開示、Nodeの10月21日予定分は未提供の修正予告です。
映像は公式情報に基づく解説図で、実機デモではありません。性能値は発表元の測定で、独自の実測ではありません。
日本語音声・日英字幕。
チャプター
00:00 週刊プログラミングニュース
00:28 DenoチームがCloudflareへ
01:50 Python 3.15が正式公開
03:18 Bun 1.4.3に型チェッカー
04:30 Actionsにワークフローロック
05:38 .NETは安定版更新とRC2
07:00 GitHubのシークレット検出拡大
07:59 Next.jsのセキュリティ更新
09:28 Express・Fastify・Micronaut
10:57 フロントエンドの安定版とRC
12:16 Astro・Rolldown・Laravel
13:21 Hydrogenがライブラリーへ
14:14 Pydantic・NumPy・FastAPI
15:59 PgBouncerとpsqlODBC
17:25 SQLite 3.54.0
18:38 Ruby 4.1のプレビュー
19:39 Node.js更新と次のセキュリティ修正
20:51 VS CodeとVisual Studio
22:07 Cursor Originのアプリ連携
22:54 GitLabの緊急修正
23:57 Docker Hubの修正済み問題の開示
25:04 Lambdaの新しい実行モデルと接続維持
26:27 今週の確認リスト
公式出典
deno.com
blog.python.org
bun.com
github.blog
devblogs.microsoft.com
github.blog
devblogs.microsoft.com
github.com/dotnet/core/blob/main/release-notes/10…
nextjs.org
github.com/expressjs/express/releases/tag/v5.3.0
github.com/fastify/fastify/releases
github.com/fastify/fastify/security/advisories/GH…
micronaut.io
micronaut.io
github.com/angular/angular/releases/tag/v22.2.3
github.com/vuejs/core/releases
github.com/vuejs/core/blob/main/CHANGELOG.md
github.com/vuejs/core/blob/minor/CHANGELOG.md
github.com/nuxt/nuxt/releases/tag/v4.6.1
github.com/sveltejs/kit/releases/tag/@sveltejs%2F…
github.com/withastro/astro/releases/tag/astro@7.4…
github.com/rolldown/rolldown/releases/tag/v1.2.14
github.com/laravel/framework/releases/tag/v13.36.0
github.com/laravel/framework/releases
shopify.dev
github.com/pydantic/pydantic/releases/tag/v2.14.0
pydantic.dev
github.com/pydantic/pydantic/releases
github.com/numpy/numpy/releases/tag/v2.5.4
github.com/fastapi/fastapi/releases/tag/0.143.1
github.com/fastapi/fastapi/releases/tag/0.143.2
pgbouncer.org
github.com/pgbouncer/pgbouncer/releases/tag/pgbou…
postgresql.org
sqlite.org
ruby-lang.org
nodejs.org
nodejs.org
nodejs.org
openwall.com
code.visualstudio.com
learn.microsoft.com
cursor.com
docs.gitlab.com
docker.com
aws.amazon.com
developers.cloudflare.com
40 views
Mentions
Stop Pulling a NuGet Package for zstd. .NET 11 Has ZstandardStream TL;DR: .NET 11 puts Zstandard (zstd) right next to GZipStream and BrotliStream. new ZstandardStream(output, CompressionLevel.Optimal) is the whole setup, and HttpClient can decompress zstd responses with one flag. .NET 11 is at Release Candidate 2, which ships with a go-live support license, but it is not GA yet. Everything below was run on the .NET 11 RC 1 SDK. The package nobody wanted to own zstd is everywhere now. Log pipelines, databases, and HTTP all speak it. .NET, meanwhile, made you pick a third-party wrapper, check that its native binary ships for every platform you deploy to, and hope it is still maintained next year. It is the dependency that shows up in every security review and that nobody remembers adding. What .NET 11 adds The new types live in System.IO.Compression: - ZstandardStream - a Stream, used exactly like GZipStream. - ZstandardEncoder and ZstandardDecoder - span-based, no stream needed. - ZstandardDictionary - train a dictionary for lots of small, similar payloads. - ZstandardCompressionOptions - quality, window size, checksums, long-distance matching. On the HTTP side, DecompressionMethods.Zstandard and ZstandardCompressedContent are new too. The minimal version If you have ever written a GZipStream, you already know this API: using System.IO.Compression; byte[] raw = File.ReadAllBytes("app.log"); using var output = new MemoryStream(); using (var zstd = new ZstandardStream(output, CompressionLevel.Optimal)) { zstd.Write(raw); } byte[] compressed = output.ToArray(); using var input = new MemoryStream(compressed); using var unzstd = new ZstandardStream(input, CompressionMode.Decompress); using var back = new MemoryStream(); unzstd.CopyTo(back); Swap GZipStream for ZstandardStream and the rest of your code does not notice. I ran all three built-in compressors over the same payload: 2,000 lines of fake JSON logs, 188,010 bytes, all at CompressionLevel.Optimal. | Format | Compressed size | 200 runs on my machine | |---|---|---| | GZip | 13,499 bytes | 218 ms | | Brotli | 11,936 bytes | 299 ms | | Zstandard | 11,891 bytes | 150 ms | Smallest and fastest in one row. It is a synthetic, very repetitive payload and one run on one machine, so treat the timings as a hint, not a benchmark. The .NET team's own numbers on the Canterbury Corpus are in the Preview 1 release notes linked below. Want more squeeze? ZstandardCompressionOptions takes a Quality from -131072 to 22, and the default is 3. var options = new ZstandardCompressionOptions { Quality = 19, AppendChecksum = true }; using var zstd = new ZstandardStream(output, options); At quality 19 with a checksum, the same log payload came out at 7,535 bytes. The realistic one: thousands of tiny messages Compression is bad at tiny payloads. A 150-byte JSON event has almost no repetition inside it, so there is little to squeeze. The repetition is between messages: the same keys, the same enum values, the same shape, every time. That is what zstd dictionaries are for. You train one on sample messages, then both sides use it: using System.IO.Compression; // training: byte[][] of real messages, e.g. the last 1,000 events // message: one new event as byte[] byte[] samples = [.. training.SelectMany(b => b)]; int[] lengths = [.. training.Select(b => b.Length)]; using ZstandardDictionary dictionary = ZstandardDictionary.Train(samples, lengths, maxDictionarySize: 4096); byte[] buffer = new byte[1024]; ZstandardEncoder.TryCompress( message, buffer, out int written, dictionary, ZstandardCompressionOptions.DefaultWindowLog2); byte[] restored = new byte[message.Length]; ZstandardDecoder.TryDecompress( buffer.AsSpan(0, written), restored, out int read, dictionary); I trained a 4,096-byte dictionary on 1,000 order.created events, then compressed 200 new events it had never seen: | | Average size | |---|---| | Raw JSON | 152.2 bytes | | zstd, no dictionary | 134.7 bytes | | zstd with dictionary | 38.6 bytes | Without a dictionary, zstd barely dented them. With one, the average message went from 152.2 bytes to 38.6. That is the difference between "compression is not worth it for our queue" and "why did we not do this sooner". HttpClient speaks zstd now Automatic decompression is one flag: using var client = new HttpClient(new SocketsHttpHandler { AutomaticDecompression = DecompressionMethods.Zstandard | DecompressionMethods.GZip }); string body = await client.GetStringAsync("https://example.com/api/report"); With that handler, the client sent Accept-Encoding: gzip, zstd. My local test server answered 8,000 bytes of JSON as 33 bytes of zstd, and GetStringAsync handed back all 8,000 characters. Going the other way, ZstandardCompressedContent compresses a request body while it is sent and sets Content-Encoding: zstd for you: var json = new StringContent(payload, Encoding.UTF8, "application/json"); await client.PostAsync(url, new ZstandardCompressedContent(json)); 300 copies of a small JSON object reached my test server as 36 bytes. Make sure the server actually accepts zstd request bodies before you turn this on. Can I use it? - .NET 11 only. The types ship in the shared framework; my test apps had no package references at all. - Status: .NET 11 is at RC 2 with a go-live license; GA is still ahead. Gotchas 1. The int after the dictionary is not quality. In TryCompress(source, dest, out written, dictionary, 3), that 3 is windowLog2, and the call threw ArgumentOutOfRangeException: windowLog2 ('3') must be greater than or equal to '10'. Pass ZstandardCompressionOptions.DefaultWindowLog2 (23), or set quality with ZstandardDictionary.Create(buffer, quality). 2. Both sides need the same dictionary. Decoding a dictionary-compressed message without it did not throw; TryDecompress just returned false. Version your dictionaries and ship the id with the message. 3. HttpClient does not opt in by itself. A default HttpClient sent no Accept-Encoding at all. When my test server sent zstd anyway, the default client got the 33 raw zstd bytes, not the JSON. 4. Preview samples are already stale. The Preview 1 notes use WindowLog; the RC has WindowLog2, MinWindowLog2, and MaxWindowLog2. Trust the API docs over old blog snippets, this one included once GA lands. Wrap-up Zstandard is now just another Stream in the box, with dictionaries and HTTP support on top. Grab the .NET 11 RC SDK, swap one GZipStream for ZstandardStream, and see what your payloads say. Sources - What's new in .NET 11 libraries - Microsoft Learn - .NET 11 Preview 1 libraries release notes - dotnet/core - ZstandardStream class - .NET API reference - dotnet/runtime #119575 - Zstandard compression support - dotnet/runtime #123531 - Zstandard in DecompressionMethods - .NET 11 Release Candidate 2 - .NET Blog This article was written with the help of AI and checked against the sources above.
Stop Trimming \r Off Every Regex Match. .NET 11 Has AnyNewLine TL;DR: In .NET 11, RegexOptions.AnyNewLine makes ^, $, \Z, and . treat \r\n, \r, and the Unicode line breaks as newlines, not just \n. No more \r?$, and no more values that end in an invisible carriage return. .NET 11 is at Release Candidate 2 right now: go-live licensed, but not final yet. The character nobody can see You write a regex that grabs a line. It passes every test, because the test strings were typed in C# with \n. Then a real file arrives from Windows, and every value you capture ends with \r. using System.Text.RegularExpressions; var bug = Regex.Match("foo\r\nbar", @".+$", RegexOptions.Multiline); Console.WriteLine(bug.Value.Length); // 4 Three letters, length 4. The fourth one is \r, and it only shows up later, when "foo" == value is mysteriously false. The reason is old and simple. .NET's regex engine has always treated only \n as a newline. In Multiline mode, $ matches before \n, and . happily matches \r because, as far as the engine knows, it is just another character. So everybody learned the ritual: [^\r\n]*\r?$. It looks like someone sneezed on the keyboard, and as you will see below, it is not even correct. Meet AnyNewLine .NET 11 adds one flag: var fixedMatch = Regex.Match("foo\r\nbar", @".+$", RegexOptions.Multiline | RegexOptions.AnyNewLine); Console.WriteLine(fixedMatch.Value.Length); // 3 With it, ^, $, \Z, and . recognize all of these as line breaks: \r\n, \r, \n, \u0085 (NEL), (line separator), and (paragraph separator). Microsoft's docs say this follows Unicode TR18 RL1.6. The important detail is that \r\n counts as one newline. In "a\r\nb", $ matched at positions 1 and 4: before the \r and at the end, never between \r and \n. ^ matched at 0 and 3, so the second line starts at b, not at a stray \n. A realistic one: a config file from three operating systems Here is a tiny key=value parser. The input mixes Windows, Unix, and classic Mac line endings, because somebody's editor had opinions. using System.Text.RegularExpressions; string config = "host=db.local\r\nport=5432\nuser=app\rtimeout=30\r\n"; foreach (Match m in Config.Line().Matches(config)) Console.WriteLine($"{m.Groups["key"].Value} = [{m.Groups["value"].Value}]"); static partial class Config { [GeneratedRegex(@"^(?<key>\w+)=(?<value>.*)$", RegexOptions.Multiline | RegexOptions.AnyNewLine)] public static partial Regex Line(); } host = [db.local] port = [5432] user = [app] timeout = [30] Four keys, clean values. The source generator accepts the new flag, and so does RegexOptions.Compiled, which returned the same four values. Now the same pattern without AnyNewLine. It found only three keys: host came back as db.local\r, and user swallowed the next line whole, as app\rtimeout=30\r. timeout vanished, because to the old engine a lone \r is not a line break at all. And the ritual? ^(?<key>\w+)=(?<value>[^\r\n]*)\r?$ found 2 of the 4 keys. It fixes \r\n and quietly gives up on a bare \r. Can I use it? - RegexOptions.AnyNewLine is new in .NET 11, with the value 2048. The .NET 11 Preview 3 release notes introduced it, and the RegexOptions docs list it as available in .NET 11 and later. - It works with new Regex(...), the static Regex methods, RegexOptions.Compiled, and [GeneratedRegex]. - .NET 11 RC2 shipped on October 13, 2026 with a go-live support license. It is still a release candidate, and general availability is expected in November. - On the .NET 10 SDK, the code fails to build with CS0117: 'RegexOptions' does not contain a definition for 'AnyNewLine'. I ran every snippet here as a file-based app with dotnet run file.cs on SDK 11.0.100-rc.1, on Windows. Gotchas 1. NonBacktracking and ECMAScript say no. RegexOptions.AnyNewLine | RegexOptions.NonBacktracking threw ArgumentOutOfRangeException at construction, and so did the combination with ECMAScript. If you picked NonBacktracking for its linear-time guarantee, you keep \r? for now. 2. There is no inline letter. Multiline has (?m) and Singleline has (?s), but the docs list no inline option for AnyNewLine. You have to pass the flag in code, so a pattern stored in a config file cannot turn it on by itself. 3. Character classes are not newline-aware. The flag changes ^, $, \Z, and ., and nothing else. [^\n]+ against "foo\r\nbar" still matched 4 characters, \r included, with AnyNewLine on. If you want "anything but a line break", use . and let the flag do its job. 4. \A and \z do not move. They still mean the absolute start and end of the string. With AnyNewLine, done\Z matched "done\r\n", while done\z did not. That is by design, and it is the escape hatch when you really do mean "end of input". 5. Singleline still wins. With Singleline | AnyNewLine, a.+ against "a\r\nb" matched the whole string, newline included. Singleline means "the dot matches everything", and the new flag does not argue with it. 6. The docs disagree about \v and \f. The RegexOptions API reference says AnyNewLine also recognizes vertical tab and form feed. The options article lists only the six sequences above. In the RC1 run, . did not match \v or \f, and ^\w$ found 2 lines in "a\vb", so the API reference matches what the runtime did. If your input can contain form feeds, test it rather than trusting either page. Wrap-up \r has been hiding at the end of captured strings for as long as .NET has had regex. Now one flag tells the engine what a line actually is, on every OS at once. Go search your codebase for \r?$ and see how many of them can retire in .NET 11. Sources - What's new in .NET libraries for .NET 11: AnyNewLine option (Microsoft Learn) - RegexOptions enum (Microsoft Learn) - Regular expression options: AnyNewLine mode (Microsoft Learn) - .NET 11 Preview 3 libraries release notes (GitHub, dotnet/core) - Announcing .NET 11 Release Candidate 2 (.NET Blog) - Unicode Technical Standard #18, RL1.6 Line Boundaries (unicode.org) This article was written with the help of AI and checked against the sources above.
CVE-2026-111497: a Brotli decoder bug forced an out-of-band .NET release (10.0.14) on Oct 25 Microsoft released .NET 10.0.14 on 25 October as an out-of-band security update, and the stated reason is CVE-2026-111497, an out-of-bounds write (CWE-787) in the Brotli decoder bundled with .NET. CVSS 8.1, publicly disclosed, exploitation marked more likely. Affected and fixed: - .NET 8 before 8.0.33 → 8.0.33 - .NET 9 before 9.0.22 → 9.0.22 - .NET 10 before 10.0.14 → 10.0.14 (SDK 10.0.114 or 10.0.403) - .NET Framework is not affected The same release completes the October 13 fixes for CVE-2026-70197 (SocketAsyncEventArgs) and CVE-2026-102870 (Blazor WebAssembly debugger), which had shipped incomplete, and adds CVE-2026-114148 for Linux source-build tarballs. Exposure needs a call path that decompresses attacker-controlled Brotli: request decompression with br enabled, HttpClient AutomaticDecompression including Brotli, or BrotliStream/BrotliDecoder on untrusted input. None of that is on by default, and response compression alone does not hit it. Until you update, dropping Brotli from those paths is a workable stopgap. Framework-dependent apps need the host runtime updated and restarted. Self-contained apps need a rebuild and republish, and the Blazor WebAssembly Server/DevServer packages have to be bumped separately because the shared runtime update does not cover them. Advisory: announcements#494 Release notes: github.com/dotnet/core/blob/main/release-notes/10…
Looking at the git diff for the above release notes, there are updates for #dotnet versions 8, 9, 10, AND 11. Looks like some people's first task tomorrow is to upgrade the runtime for their apps. github.com/dotnet/core/commit/60871330fce2b3da383…
If you're doing #dotnet things, are using #Brotli, and are on v10, you NEED to install this update! #Http2Shell #Node2Shell github.com/dotnet/core/blob/main/release-notes/10…
.NET 8/9は11月10日でサポート終了、.NET 11は同日GA — AIと走り切った移行メモ ! 📋 この記事の前提 対象は「.NET 8(または9)で動いている業務アプリを持っていて、2026年11月10日のサポート終了が気になっている人」です 内容は4つです。(1) 11月10日に何が起きるか(公式の期限)、(2) .NET 11 / C# 15で実務に関係する変更、(3) 自宅PCでRC2を実際に動かした実測、(4) 会社の.NET 8→10移行をAIと進めた実務メモ 数字は公式ドキュメント(出典つき)と、手元の実測(2026年10月24日・WSL2 / Celeron N5095A)です 言語機能の差分(C# 13〜15)は、先に書いたこちらの記事にまとめています。今回は「期限と移行」に絞ります 用語: LTS/STS=長期/標準サポート(3年/2年)、GA=正式リリース、RC2=リリース候補2、ReadyToRun=起動を速くする事前コンパイル形式、JIT=実行時のコンパイル 結論: 11月10日は「終了」と「開始」が同じ日。移行先は10、11は先取り 先に3つ書きます。 2026年11月10日、.NET 8(LTS)と.NET 9(STS)が同時にサポート終了します。 本丸はLTSの8です。終了後はセキュリティ修正が止まります。同じ日に.NET 11(STS)がGAします 業務アプリの移行先は.NET 10が本命です。 LTSで2028年11月14日までサポートされます。11は新機能が魅力でも、STS(2年)ですし、まず10で足場を固めるのが現実的です。11はRC2が公開済みなので、検証環境で先に触っておくことはできます 移行はAIで「最後まで」走れます。 ただし「何を正解とするか」「どのテストを信じるか」「いつ戻すか」を先に決めておくのが条件です。社内の8→10移行も、この分担で置き換えまで到達しました 期限まであと17日です(執筆時点: 2026年10月24日)。 2026年11月10日に何が起きるか 公式のサポートポリシー(2026年10月13日更新)とリリースノートを並べると、こうなります。 バージョン 種別 リリース サポート終了 現在の状態 .NET 8 LTS(3年) 2023/11/14 2026/11/10 Maintenance(最新8.0.32) .NET 9 STS(2年) 2024/11/12 2026/11/10 Maintenance(最新9.0.21) .NET 10 LTS(3年) 2025/11/11 2028/11/14 Active(最新10.0.13) .NET 11 STS(2年) 2026/11/10(GA) 2028/11/9(予定) RC2(10/13公開) ※ .NET 11の終了日は、dotnet/coreの11.0リリースノートに「2026年11月10日〜2028年11月9日」と明記されています(2026年10月24日時点で確認)。ただし2028年11月9日は木曜で、本文で触れた「11月の第2火曜」の数え方なら11月14日(.NET 10と同じ日)になります。GA後にサポートポリシーの表へ反映される際に確定する見込みです。 8と9が同じ日に切れるのは偶然ではありません。.NETは毎年11月にメジャーリリースし、偶数がLTS(3年)、奇数がSTS(2年)です。8は2023年11月+3年、9は2024年11月+2年——どちらも2026年11月の第2火曜(11月10日)に期限が来ます。 .NET Conf 2026は11月10日〜12日(オンライン・無料)で、初日に.NET 11がローンチされます サポート終了後も「動き続ける」アプリはあります。ただし、その状態は「修正されない脆弱性を抱えて運用する」ことと同義です。業務アプリでこれは、判断として重い 補足: うちの環境では.NET 9を経由せず、8から10へ直接上げました。STSを挟むと検証が2回必要になるうえ、9自体も11月10日で終わるので、経由する意味が薄いからです。段階的にライブラリの変更を追いたい事情があるなら9経由も選択肢ですが、期限が迫っているならLTSへ直接が素直だと思います .NET 11で、実務に関係する変更 全部を追うとキリがないので、業務アプリの移行で判断に効くものだけ拾います(RC2時点の公式情報です)。 C# 15 — union型が来た C# 15は、.NET 11 SDKでnet11.0を対象にしたプロジェクトの既定言語バージョンです。追加されるのは union型、コレクション式の引数、closed hierarchies、拡張インデクサー、ラベル付きbreak/continue、古いランタイムでの非仮想staticインターフェースメンバー、メモリ安全性(プレビュー)です。 目玉はunion型です。複数の型のどれか1つを表す型をunionキーワードで宣言でき、switchが全ケースを網羅しているかをコンパイラが確認します。 public record class Pass(string Id); public record class Review(string Id, double Confidence); public record class Reject(string Id); public union GateResult(Pass, Review, Reject); // switchの腕を1つ消すと warning CS8509(網羅していない) static string Describe(GateResult r) => r switch { Pass p => $"自動採用: {p.Id}", Review v => $"保留: {v.Id} (p={v.Confidence:0.000})", Reject j => $"却下: {j.Id}", }; 設備点検の判定を「自動採用 / 保留 / 却下」の3値で返す、というのは前回までの記事(判定モデルの勢力図2026年10月版)で書いた3段ゲートそのものです。それを、stringやobjectではなく、コンパイラに守られた1つの型として書けます。これは現場で使う人がいるだろうな、と思いました。 ひとつ注意があります。網羅性のチェックは警告(CS8509)であってエラーではありません。腕を1つ消してもビルドは通ります。警告をエラー扱いにする設定(TreatWarningsAsErrors)をしているプロジェクトなら止まりますが、そうでなければ「見逃す」余地が残ります。実際に腕を消して試すと、こう出ました。 warning CS8509: The switch expression does not handle all possible values of its input type (it is not exhaustive). For example, the pattern 'Reject' is not covered. もうひとつ、地味に効くのがコレクション式の引数です。これまではnew List<string>(capacity)のように書いていた容量や比較器を、コレクション式の中でwith(...)として渡せます。今回のデモでも使っていて、RC2でそのままコンパイルできました。 string[] ids = ["EQ-001", "EQ-042", "EQ-035"]; // 容量をコレクション式の中で指定(実測で動いたコード) List<string> names = [with(capacity: 8), .. ids]; ※ ここまでの断片は形を示すためのもので、これだけではコンパイルできません。コンパイルできるファイル全体は、後ろの「自宅PCでRC2を動かした実測」に載せています。 Runtime Async — asyncの書き方は変えなくていい .NET 11ではRuntime Asyncが既定になり、net11.0向けではこれまで必要だったプロパティ指定が不要になりました。ランタイムのライブラリ自体もRuntime Asyncでビルドされています。 実務目線でうれしいのは、既存のasync/awaitのコードを書き換えなくていいことです。スタックトレースがきれいになり、オーバーヘッドが減る、という改善が向こうから来ます。移行作業としては「何もしない」で受け取れる類の変更です。 ハードウェア要件の引き上げ — 製造業はここを見てほしい 見落としやすいのですが、.NET 11は最低ハードウェア要件が上がりました。 x86/x64: x86-64-v1 → x86-64-v2(CX16・POPCNT・SSE3・SSSE3・SSE4.1・SSE4.2を保証) ReadyToRunのターゲット: Windows/Linuxともx86-64-v3(AVX・AVX2・BMI1・BMI2・F16C・FMA・LZCNT・MOVBE)に更新 Arm64: Linuxは変更なし(Raspberry Piのような機器を引き続きサポート)。Windows arm64はLSEが必要です。Appleは変更なし 公式ドキュメントには「.NET 11からは、古いハードウェアでは.NETが起動しなくなる」と書かれています。起動時には次のようなメッセージが出ます。 The current CPU is missing one or more of the baseline instruction sets. 現場のPCをどう判断するか、ざっくり表にするとこうです。 PCの世代 判断 x86-64-v2を満たさない(例: Core 2世代、古いAtomなど) .NET 11は起動しない可能性。10に留めるか、PC更新とセットで11へ v2は満たすがAVX2なし(Celeron・Atom系など) 11は動く(今回の実測)。AVX2が無い分、起動時にJITコストがあり得る AVX2あり(Intel Coreの2013年Haswell以降、AMDはZen世代以降) 11の性能を素直に受けられる x86-64-v2は、Windows 11と、Windows 10で公式サポートされているx86/x64 CPUの要件と揃えたものです。公式には「Intel/AMDが今もサポートしているチップはすべて含まれ、古いチップは2013年頃にサポートが終わっている」という説明です(これはCPUベンダーのサポート終了時期の話で、v2の境界そのものではありません)。 現場のPCがv2を満たすかは、CPUのフラグで確認するのが確実です。Linuxならgrep -o 'sse4_2|popcnt' /proc/cpuinfo | sort -uで2つ出るか。WindowsならSysinternalsのCoreInfo(coreinfo -f)でSSE4.2とPOPCNTを見ます。 工場には、SSE4.2が無い世代のPCが残っていることがあります。そういう現場では、11に上げる前にハードウェアの棚卸しが必要です。逆に言えば、10に留めておけばこの問題は起きません(10のLTSは2028年11月14日まで)。「11の新機能が欲しい」と「現場のPCを入れ替える」を同じ号令で決めないほうが安全です。 なお、AVX2が無いCPUでも.NET 11自体は動きます。ReadyToRunのターゲットがv3になった分、起動時にJITの追加コストがかかり得る、というのが公式の説明です(今回の実測では、体感できる差は出ませんでした)。 ツール: C# Dev Kit 11とdotnetup C# Dev Kit 11(VS CodeのC#拡張)は、.NET 11に合わせて3.3から11.0へ。起動時に6つあったマネージドプロセスを、Native AOT(事前コンパイル)の単一実行ファイルにまとめました。公式ブログの数字では、変更なしのビルドが34.4秒→0.88秒(39倍)、Aspire 407プロジェクトで、編集中のファイルが使えるまでが84.1秒→0.47秒(最大180倍。ソリューション全体は3.0秒で28倍)、メモリが2,072MB→316MB(約85%減)。ただし、これはAspireやOrleansという巨大ソリューションでの値です。私の環境では未検証。プロジェクトキャッシュのコミットやC# Doctorも追加され、stableは.NET 11と同時期の見込みです。 dotnetupは10月15日にpublic previewになった、ユーザー領域だけで動く.NETのツールチェーン管理ツールです。管理者権限なしで、複数のSDKを横並びに置けます。公式の流れはdotnetup initで初期化しますが、今回は既存の.NET 10環境に触れないよう、初期化せずにdotnetup dotnet …の転送形式で呼びました(dotnetup env set noneは非対話シェルでは--shell bashが必要で、今回は使っていません)。実際に踏んだ手順はこれです。 1. dotnetup本体を入れる(末尾にPATH追加の案内が出ます) curl -fsSL aka.ms | bash 2. .NET 11のプレビューSDKを入れる dotnetup sdk install preview 3. プロジェクトを置く(GateDemo.csprojとProgram.cs) mkdir demo && cd demo 4. 実行 dotnetup dotnet run ただし0.3.0-previewのツールなので、業務環境の標準にするのはGA後が無難です。 破壊的変更の一覧は、「種類」で読む 移行の前に見るべきは、公式のバージョン別・破壊的変更一覧です(9・10・11でそれぞれページがあります)。各項目は3種類に分類されています。 Binary incompatible: 新しいランタイムで既存のバイナリが動かない(再コンパイルが必要) Source incompatible: 新しいSDKで再コンパイルすると、ソースの修正が必要 Behavioral change: コンパイルは通るが、実行時の挙動が変わる この分類が便利なのは、「ビルドが通ったから大丈夫」が通用しないのが3つ目だと分かることです。たとえば.NET 10の一覧には、既定のトレース伝播がW3C標準に変わる、Unixで終了シグナルの既定ハンドラが提供されなくなる、といった挙動の変更が並びます。業務アプリだと、ログの相関IDやLinuxサービスの停止処理に効いてくる類です。 ほかにも、.NET 10でSystem.Linq.AsyncEnumerableがコアライブラリに入る(source incompatible)という項目があります。System.Linq.Asyncパッケージを併用しているプロジェクトでは、型が衝突し得ます。こういう項目は、一覧とcsprojをAIに突き合わせてもらうと早いです。 自宅PCでRC2を動かした実測 ここからは手元の数字です。環境はWSL2のUbuntu 22.04、CPUはCeleron N5095A(4コア・RAM 2.8GB・AVXなし・SSE4.2まで)、既存のSDKは10.0.401です。開発機としては控えめどころか、かなり弱い部類です。 dotnetupを入れて、RC2のSDKを導入したところ、4分14秒かかりました(SDKは201.3MB)。回線が遅いこともありますが、このマシンだとこのくらいです。初期化はせず、dotnetup dotnet … の形で呼びます。既存の10環境と干渉しません。 結果はこうなりました。 項目 実測値 SDK 11.0.100-rc.2.26504.105 ランタイム 11.0.0-rc.2 net11.0のビルド 12.63秒・0警告0エラー 実行(dll直接起動) 0.21〜0.23秒 net10.0のままSDKだけ11に ビルド成功(復元21.5秒+ビルド24.3秒) ビルド時にはNETSDK1057: You are using a preview version of .NETというメッセージも出ます(実測)。プレビュー版であることの注意書きで、エラーではありません。ただしCIのログにこの行が出るのを「警告が出た」と誤解しないように、チームで共有しておくと安全です。 最後の行が地味に大事です。SDKだけ先に11へ上げて、ターゲットフレームワークは10のまま、というビルドが通りました。いきなり全部を上げるのではなく、CIや開発機のSDKだけ先に新しくして、アプリのTFMは後から上げる、という段取りが取れます。 デモはnet11.0のコンソールプロジェクトに、union型とコレクション式の引数を書いただけのものです。csprojはこれだけです。 <Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <OutputType>Exe</OutputType> <TargetFramework>net11.0</TargetFramework> <ImplicitUsings>enable</ImplicitUsings> <Nullable>enable</Nullable> </PropertyGroup> </Project> Program.csはこれです(実測で動いたファイルそのまま)。dotnetup dotnet runで実行できます。 // C# 15 / .NET 11 RC2 の動作確認用デモ // ・union 型(3つの結果を1つの型で受ける) // ・コレクション式の引数(with(...)) using System; public record class Pass(string Id); public record class Review(string Id, double Confidence); public record class Reject(string Id); public union GateResult(Pass, Review, Reject); public static class Program { public static void Main() { string[] ids = ["EQ-001", "EQ-042", "EQ-035"]; // C# 15: コレクション式の引数で容量を指定 List<string> names = [with(capacity: 8), .. ids]; GateResult[] results = [ new Pass("EQ-001"), new Review("EQ-042", 0.973), new Reject("EQ-035"), ]; foreach (var r in results) { Console.WriteLine(Describe(r)); } Console.WriteLine($"names={names.Count} / runtime={Environment.Version}"); } // switch は union の全ケースを網羅していることをコンパイラが確認する static string Describe(GateResult r) => r switch { Pass p => $"自動採用: {p.Id}", Review v => $"保留: {v.Id} (p={v.Confidence:0.000})", Reject j => $"却下: {j.Id}", }; } 実行結果(実測のまま): 自動採用: EQ-001 保留: EQ-042 (p=0.973) 却下: EQ-035 names=3 / runtime=11.0.0 会社の.NET 8→10移行を、AIとどう進めたか ここからは社内の話です。対象のアプリや構成、件数や時間は伏せますが、要点は進め方にあるので、そこを書きます。.NET 8で動いている業務アプリを、サポート終了に合わせて.NET 10へ上げました。 進め方は、大きく4つでした。 対象を小さく切る(全部を一度に上げない) global.jsonとCIのSDKバージョンを固定する ビルド→テスト→実行時挙動の差分、の順で確認する 失敗したらログをAIに渡し、修正→再ビルドのループを回す 詰まるのは、ビルド(TFMやAPIの変更)、パッケージ(依存の互換)、テスト(期待値のズレ)のどれかです。どこで詰まっても、やることは同じ。共通するのは、「ビルドが通る=終わりではない」ということでした。 正直に言うと、一発では通りませんでした。直してはビルド、テストを回してはログを見て、の繰り返し。詰まったときの進め方には再現性がありました。効いたのは、エラーを1件ずつ切り出して渡すのではなく、ビルド1回分のログを丸ごと渡すことです。同じ原因から出ているエラーは、まとめて直したほうが往復が減ります。 そして、AIが直した差分は、そのまま次へ進めませんでした。テストが通ること、差分が読めること、実際に動かして挙動が変わっていないこと——この3つを確認してから、次の対象へ移ります。分担表(後述)の「人が握る」には、この確認も含まれます。 やってみての実感です。AIが強いのは「繰り返し」と「ログの読み解き」。ビルドエラーを潰す作業、テストの失敗を分類する作業は、人間がやるより速い。延々と続くビルドエラーの潰し合いは、人間だと集中が切れます。AIは切れない。地味ですが、これが効きました。逆に、AIに決めさせてはいけないのは「何をもって正解とするか」「どのテストを信じるか」「いつ戻すか」です。ここを先に決めずに任せると、通ったように見えて業務ルールが壊れている、という状態になり得ます。 公式のやり方に寄せるなら、GitHub Copilot modernization(旧Upgrade Assistantの後継)があります。Assessment → Planning → Executionの3段階で進み、各段階の成果物を.github/upgrades/配下にMarkdownで残し、変更をコミットしていく形式です。Visual Studioはソリューションを右クリックして「Modernize」、VS Codeは@upgrade、CLIは@modernize-dotnetで起動します。.NET Upgrade Assistantは公式に非推奨(deprecated)になっているので、これから始めるならCopilot側です。 AIに渡すと効くもの 今回のループで効いたのは、エラーメッセージ単体ではなく「セット」で渡すことでした。 ビルドログ(エラーだけでなく、その前後の警告も) テストの失敗一覧(どのテストが、何を期待して、何が返ったか) global.jsonとDirectory.Build.props(バージョン指定の全体像) 直前のdiff(何を変えて、何が壊れたか) 逆に、渡しても迷わせるだけだったのは「動きません」の一言です。AIは推測で直し始め、関係のない場所を触り始めます。ログと差分を添えるほど、修正の往復が減りました。 AIに任せる範囲・人が握る範囲 今回の実感を、分担表にしておきます。 工程 AIに任せる 人が握る 依存パッケージの棚卸し 列挙と更新案の作成 ライセンス・供給元の確認 TFM(対象フレームワーク)・プロジェクトファイルの更新 機械的な書き換え 対象範囲の決定 ビルドエラーの修正 ログ→修正→再実行のループ 修正差分のレビュー方針 テストの実行と分類 実行・失敗の切り分け テストの意味・合否基準 実行時挙動の差分 候補の列挙 業務影響の判断 ロールバック 手順の自動化 戻すかどうかの判断 11月10日に間に合わないとき 全部を10へ上げ切れない、という現場もあると思います。その場合の現実的な順番です。 本番で動いているものから優先する(開発機や社内ツールは後回しにできます) 間に合わないアプリは「サポート切れのまま運用する」と明示的に台帳へ書く(黙って残さない) 切れている間は、ネットワークの露出を減らす・入力の検証を厚くするなど、別の防御で埋める 次の刈り取りを決める(例: 次の設備更新や予算策定に合わせる) サポート終了は「アプリが止まる日」ではありません。「修正が来なくなる日」です。止まらないから放置、が一番危ないパターンだと思います。 11月10日までのチェックリスト 8/9で動いているアプリを棚卸しする。TFMとCIのSDK指定(global.json)を確認します 移行先を10に固定する。11は検証環境で先取りします(dotnetupが楽でした) ハードウェアを確認する。 x86-64-v2(SSE4.2)を満たさないPCが現場に残っていないか。残っているなら、その現場は10に留めます(10は2028年11月14日まで) 破壊的変更の一覧をバージョン別に確認する。9・10・11の公式リストから、自社コードに関係する項目だけ抜きます AIに渡す材料を揃える。ビルドログ、テスト結果、global.json、差分——これがないとAIは「推測」で直し始めます AIの修正は「テストが通った」で終わらせない。テストが何を保証しているかをレビューします ロールバック手順を先に確認する。パッケージのバージョン固定、デプロイ単位、戻し先のブランチ よくある質問 Q. .NET 8から10と11、どっちに上げるべきですか? A. 業務アプリは10(LTS・2028年11月14日まで)が本命です。11はSTS(2年)で、新機能の検証や先行採用向けです。まず10で安定させ、11は別枠で試すのが現実的だと思います。 Q. .NET 11はプレビューですが、本番で使えますか? A. RC2はGo-Live(本番利用が可能なプレビュー)ですが、正式GAは11月10日です。業務の標準にするならGA後、しかもSTSである点を踏まえて判断してください。今回の検証はRC2で行いました。 Q. AVXが無い古いPCでも.NET 11は動きますか? A. 今回のCeleron(SSE4.2まで・AVXなし)ではRC2が動きました。ただし公式の最低要件はx86-64-v2で、SSE4.2やPOPCNTが無いCPUは起動しない可能性があります。ReadyToRunはv3(AVX2)なので、AVX2が無いと起動時にJITの追加コストがかかり得ます。 Q. .NET Upgrade Assistantはもう使えないのですか? A. 公式には非推奨(deprecated)です。後継はGitHub Copilot modernizationで、Visual Studio 2026、またはVisual Studio 2022 17.14.16以降に含まれます。 Q. 移行でAIはどこまで任せられますか? A. 「繰り返し」と「ログの読み解き」は任せられます。正解の定義、テストの意味、ロールバックの判断は人が握ってください。今回の8→10移行も、この分担でした。 まとめ 11月10日は、.NET 8と9の終了日であり、.NET 11の開始日です。執筆時点(10月24日)で、期限まで17日。 移行先は10。11は「知っておく・試しておく」段階で、RC2は今すぐ触れます。そして、11はハードウェア要件が上がったので、工場の古いPCが絡む現場なら10が安全です。 移行の実務は、AIでかなり走れます。ただし「何を正解とするか」を先に決めること。これがないと、AIは速いだけのループを回します。 今回のRC2の数字も、AVXなしのCeleronで採った数字です。手元の環境で測った記録として読んでもらえればと思います。 【関連記事】 C# 13→14→15で何が変わった?.NET 10移行前に調べた新機能まとめ 出典 .NET and .NET Core official support policy(2026年10月13日更新) dotnet/core: releases.md dotnet/core: .NET 11 release notes What's new in .NET 11 What's new in the .NET 11 runtime What's new in the SDK and tooling for .NET 11 What's new in C# 15 .NET Conf 2026 – Save the Date(.NET Blog) A faster, lighter C# Dev Kit(.NET Blog・2026年10月6日) Introducing dotnetup(.NET Blog・2026年10月15日) .NET Upgrade Assistant Overview GitHub Copilot upgrade overview .NET breaking changes reference
Stop Hand-Rolling Full Outer Joins in LINQ. .NET 11 Has FullJoin TL;DR: In .NET 11, a.FullJoin(b, x => x.Key, y => y.Key) returns every matched pair plus the unmatched rows from both sides, as (Outer, Inner) tuples. Join, LeftJoin, RightJoin, and GroupJoin also get overloads with no result selector. .NET 11 is at Release Candidate 2 right now: go-live licensed, but not final yet. The join you Google every time You have two lists that should agree and never do. What the system thinks is in stock, and what the stocktake actually counted. You want three answers: what matches, what only the left side has, and what only the right side has. SQL calls that a full outer join. LINQ never had one, so everybody pastes the same answer: var left = from s in system join c in counted on s.Sku equals c.Sku into matches from c in matches.DefaultIfEmpty() select (Record: s, Count: c); var rightOnly = from c in counted where !system.Any(s => s.Sku == c.Sku) select (Record: (StockRecord?)null, Count: c); var full = left.Concat(rightOnly); It works. It is also two queries, a DefaultIfEmpty nobody can explain on the first read, and an Any inside a where, which scans the whole left list once for every row on the right. And the key comparison lives in two places, so the day someone changes one of them, the halves quietly disagree. Meet FullJoin .NET 10 already added LeftJoin and RightJoin. .NET 11 closes the set with FullJoin, and it returns tuples, so you do not even need a result selector: var products = new List<(int Id, string Name, string? Category)> { (1, "Laptop", "Electronics"), (2, "Mouse", "Electronics"), (3, "Orphan", null), }; var categories = new List<(string Name, string Description)> { ("Electronics", "Electronic devices"), ("Furniture", "Office furniture"), }; foreach (var pair in products.FullJoin(categories, p => p.Category, c => c.Name)) Console.WriteLine($"{pair.Outer.Name ?? "(none)"} | {pair.Inner.Description ?? "(none)"}"); Laptop | Electronic devices Mouse | Electronic devices Orphan | (none) (none) | Office furniture The tuple elements are named Outer and Inner, and the side with no match is default. For reference types, that means null, which is exactly what you want to pattern match on. Here the elements are themselves tuples, so a missing side is an empty tuple whose Name is null; gotcha 1 explains why that deserves a second look. The same release gives the old operators a selector-free overload, too. products.Join(categories, p => p.Category, c => c.Name) now yields (Outer, Inner) pairs, so (p, c) => new { p, c } can finally retire. A realistic one: the stocktake Here is the reconciliation from the start, done properly. The counted SKUs were typed by a human, so one of them is lowercase. List<StockRecord> system = [ new("SKU-100", "USB-C cable", 40), new("SKU-200", "Desk lamp", 12), new("SKU-300", "Monitor arm", 5), ]; List<ShelfCount> counted = [ new("sku-100", 38), new("SKU-300", 5), new("SKU-999", 3), ]; var report = system.FullJoin( counted, s => s.Sku, c => c.Sku, StringComparer.OrdinalIgnoreCase); foreach (var (record, count) in report) { Console.WriteLine((record, count) switch { (null, { } c) => $"{c.Sku}: on the shelf, not in the system ({c.Quantity})", ({ } r, null) => $"{r.Sku}: in the system, not on the shelf ({r.Quantity} missing)", ({ } r, { } c) when r.Quantity != c.Quantity => $"{r.Sku}: off by {c.Quantity - r.Quantity}", ({ } r, _) => $"{r.Sku}: OK", (null, null) => "nothing on either side, which FullJoin never yields", }); } record StockRecord(string Sku, string Name, int Quantity); record ShelfCount(string Sku, int Quantity); SKU-100: off by -2 SKU-200: in the system, not on the shelf (12 missing) SKU-300: OK SKU-999: on the shelf, not in the system (3) One call, one comparer, and a switch that reads like the spec. The comparer is a plain optional parameter, which the query-syntax join ... equals never let you pass at all. It built with zero warnings, nullable checks included. Can I use it? - FullJoin and the tuple-returning overloads are new in .NET 11. The .NET 11 Preview 5 announcement listed "LINQ adds full outer joins", and the release notes cover both additions. - They exist on Enumerable, Queryable, and AsyncEnumerable, so query providers and async streams get them too. - .NET 11 RC2 shipped on October 13, 2026 with a go-live support license. It is still a release candidate. - On the .NET 10 SDK, the call fails with CS1061: 'int[]' does not contain a definition for 'FullJoin'. I ran every snippet here as a file-based app with dotnet run file.cs on SDK 11.0.100-rc.1, on Windows. The classic two-query version ran on the .NET 10 SDK, with the SKUs in matching case, and paired up the same four rows. Gotchas 1. Value types hide the missing side. default for an int is 0, not "nothing". A full join of the int arrays [0, 5] and [0, 7] on the values themselves printed (0,0), (5,0), (0,7), and you cannot tell the real match from the two misses. With int?[] the same data printed (0,0), (5,null), (null,7). If your elements are structs or tuples, make them nullable or check a field that is never empty. 2. Null keys never match. Joining ["a", null] with [null, "b"] gave four rows, and the two null keys came out as separate unmatched rows instead of a pair. That is the same rule Join has always had, so this is consistency, not a bug. 3. Duplicate keys multiply. [1, 1, 2] joined with [1, 1, 3] produced four (1,1) rows, then (2,0) and (0,3). Two times two is still four, even in a full join. 4. Unmatched right rows come last. Joining ["x", "a", "y"] with ["b", "a", "c"] gave (x,-), (a,a), (y,-), (-,b), (-,c). Left rows keep their order, and the right-only rows are appended at the end in their own order. If your report needs a different order, sort it. 5. GroupJoin's new overload is not a tuple. The selector-free GroupJoin returns IGrouping<TOuter, TInner>, so the outer element is g.Key. Grouping the products from the first example under their categories gave Electronics: 2 [Laptop, Mouse] and Furniture: 0 []. Wrap-up The full outer join was the last SQL join that LINQ made you build by hand. Now it is one call that tells you what matches, what is missing, and what showed up uninvited. Go find that DefaultIfEmpty().Concat(...) in your codebase and see whether it survives .NET 11. Sources - What's new in .NET libraries for .NET 11: LINQ join improvements (Microsoft Learn) - Enumerable.FullJoin method (Microsoft Learn) - Enumerable.GroupJoin method (Microsoft Learn) - .NET 11 Preview 5 libraries release notes (GitHub, dotnet/core) - .NET 11 Preview 5 is now available (.NET Blog) - Announcing .NET 11 Release Candidate 2 (.NET Blog) This article was written with the help of AI and checked against the sources above.
まぁ、でも .NET もこの間のマンスリーアップデートでクリティカルを含むパッチ提供してるから、情報が出たタイミングの問題な気がする。 github.com/dotnet/core/blob/main/release-notes/10…
.NET 11 RC2: What Is New and Whether to Upgrade From 8 or 10 .NET 11 RC2, published on 13 October 2026, is the final release candidate and ships with a go-live license, but the practical advice is split by where you start: .NET 8 teams must move before 10 November 2026 and should land on .NET 10, while .NET 10 teams have until November 2028 and can wait for .NET 11 GA.[[1]](#cite-1) .NET 11 is a Standard Term Support release, so it gets two years of patches, from 10 November 2026 to 9 November 2028.[[2]](#cite-2) This post covers what is new in RC2, the support table for .NET 8 to 11, the upgrade steps and the breaking-changes list, and how to decide between shipping on RC2 and waiting. TL;DR - RC2 (11.0.0-rc.2, 13 Oct 2026) is production-usable under the go-live license, but Microsoft has not published a GA date on the RC2 post. - .NET 11 is STS: supported from 10 Nov 2026 to 9 Nov 2028 per the release notes README. - .NET 8 and .NET 9 both reach end of support on 10 Nov 2026. .NET 10 (LTS) runs to 14 Nov 2028. - C# 15 adds union types, closed hierarchies, collection expression arguments, extension indexers and labeled break and continue, all still marked preview on the docs page. - On .NET 8: go to .NET 10 now. On .NET 10: stay put, test on RC2 in CI, move after GA if you want the new features. What RC2 is and what the go-live license means The .NET team published the RC2 announcement on 13 October 2026. It is the second release candidate, it includes a go-live support license, and the post says it can be used in production.[[1]](#cite-1) The release notes list the sequence: preview 1 on 10 February, preview 7 on 11 August, RC1 on 8 September, and 11.0.0-rc.2 on 13 October.[[2]](#cite-2) One thing matters for planning: the support policy page lists RC2 under go-live releases with an end-of-support date of 10 November 2026, the same day .NET 8 and 9 expire.[[3]](#cite-3) So RC2 is not a version you can sit on. If you deploy it, you commit to moving to the GA build when it appears. The same day, Microsoft shipped the October servicing releases: .NET 10.0.13 (SDK 10.0.402), .NET 9.0.21 (SDK 9.0.319) and .NET 8.0.32 (SDK 8.0.426), each dated 13 October 2026. Their release metadata lists 44, 42 and 40 CVE fixes respectively.[[7]](#cite-7) Patch whatever you run today before you plan the bigger move. The RC2 post lists tooling support in Visual Studio 2026 Insiders and in Visual Studio Code with the C# Dev Kit. It names no exact GA date.[[1]](#cite-1) What is new in RC2 that a working team will notice RC2 is a polish release, not a feature drop. The announcement is organised by area, and most items are small. Here are the ones that change day-to-day work. Libraries and runtime The libraries section lists incremental CBOR reading, experimental composite ML-KEM and HPKE APIs, DataAnnotations messages formatted with attribute-specific arguments, and a configurable initial HTTP/2 concurrency.[[1]](#cite-1) The ML-KEM and HPKE items are the post-quantum cryptography work; the post labels them experimental, so do not build a production protocol on them yet. On the runtime side, the only listed change is that file locking on iOS and tvOS is disabled by default. SDK, MSBuild and NuGet The SDK now accepts quoted values in file-level directives and allows MSBuild properties inside launch-profile arguments. MSBuild gets faster wildcard matching during project evaluation, which helps repositories with large glob-heavy projects. NuGet lets you set a minimum package publication age from the CLI, a useful guard against grabbing a package version that was published minutes ago, which is the window supply-chain attacks rely on.[[1]](#cite-1) ASP.NET Core, MAUI, Windows Forms and F# In ASP.NET Core, the QuickGrid virtualization APIs are no longer experimental. .NET MAUI gains a way to create apps with C# UI, Apple Icon Composer asset support, XAML authoring and Hot Reload work, and updates for .NET for Android and Apple platforms. Windows Forms adds a KioskModeManager component, deferred form reveal, suspension of painting and layout during bulk updates, and new analyzers that guide the Designer. F# gets runtime async support, reraise in computation expressions, fewer allocations in FSharp.Core collections and lower compiler memory use.[[1]](#cite-1) The RC2 post does not list C# features itself. It points to the C# 15 page, which is where the language changes live. C# 15 in one table The Microsoft Learn page for C# 15 lists six feature areas and calls C# 15 the latest preview release.[[4]](#cite-4) Treat that wording as the status: the page last updated on 14 September 2026, before RC2, so check it again before you depend on any of these. | Feature | What it does | Status note on the docs page | | --- | --- | --- | | Collection expression arguments | A with(...) first element passes capacity or a comparer to the collection constructor | Listed under C# 15 | | Union types | The union keyword declares a value that is one of several case types, with exhaustive switch checking | Some proposal features not yet implemented | | Closed hierarchies | A closed class can only be derived in its own assembly, so switches over it need no default arm | Listed under C# 15 | | Extension indexers | Declare an indexer inside an extension block | Listed under C# 15 | | Labeled break and continue | Name a loop or switch to exit or continue, instead of a flag or goto | Listed under C# 15 | | Memory safety | Pointer relaxations and an unsafe(expression) form | Needs the preview language version | Union types are the headline. The docs example declares public union Pet(Cat, Dog, Bird); and the compiler then checks that a switch expression covers all three cases.[[4]](#cite-4) The docs also say the runtime has included UnionAttribute and IUnion since .NET 11 Preview 5. Closed hierarchies solve the same exhaustiveness problem for class trees you control. Labeled break and continue is the one you will use the first day. The docs show continue outer; and break outer; against a labeled outer for loop, replacing the Boolean flag pattern. A new style rule, IDE0410, flags the flag and goto patterns it replaces.[[4]](#cite-4) Support windows for .NET 8, 9, 10 and 11 The support policy page states that LTS releases get three years of free support and patches, and STS releases get two years.[[3]](#cite-3) Here are the dates from the policy page and the .NET 11 release notes README. | Version | Release type | Original release | End of support | | --- | --- | --- | --- | | .NET 8 | LTS | 14 Nov 2023 | 10 Nov 2026 | | .NET 9 | STS | 12 Nov 2024 | 10 Nov 2026 | | .NET 10 | LTS | 11 Nov 2025 | 14 Nov 2028 | | .NET 11 | STS | Support starts 10 Nov 2026 (README) | 9 Nov 2028 (README) | Two details to read carefully. First, .NET 11 does not appear in the policy page support table yet; the dates for it come from the release notes README, which says it will be supported from 10 November 2026 to 9 November 2028.[[2]](#cite-2) Second, .NET 8 loses support in the same week .NET 11 is expected to ship, so the 8 to 10 move is a deadline, not a preference. On GA timing: the .NET Conf 2026 page lists the conference for 10 to 13 November and says it will celebrate the launch of .NET 11, but it gives no release date.[[5]](#cite-5) The README support start of 10 November lines up with day one of the conference. Treat GA at .NET Conf as expected, not announced. Upgrade path: what to change in the repo The mechanics are the same for every move. Pin the SDK, retarget, build with warnings visible, then read the breaking-changes list for the areas you use. { "sdk": { "version": "11.0.100-rc.2.26504.105", "rollForward": "latestFeature", "allowPrerelease": true } } That is the RC2 SDK build listed in the dotnet/core release notes (runtime 11.0.0-rc.2).[[2]](#cite-2) Install it from get.dot.net/11 and confirm with dotnet --list-sdks. Then change the project file: net11.0 false Leave warnings-as-errors off for the first build so you can see the full list, then turn it back on once the list is clean. To check that global.json is valid JSON before CI does, paste it into the [JSON formatter](/tools/json-formatter). What the breaking-changes page lists Microsoft's .NET 11 breaking-changes page groups entries by area and tags each as binary incompatible, source incompatible or a behavioral change. It carries a note that it is a work in progress and not a complete list.[[6]](#cite-6) The areas that most apps touch: - Core libraries: CborReader and CborWriter enforce a default maximum nesting depth; TAR-reading APIs verify header checksums; CRC32 validation is added when reading ZIP archive entries; Math.Round and MathF.Round return correctly rounded results; DateOnly and TimeOnly TryParse methods throw for invalid input. - Extensions: IHost.RunAsync and IHost.StopAsync throw when a BackgroundService fails; synchronous access to async-validated options throws. - JIT: floating-point conversions to small integral types are saturating, and minimum hardware requirements are updated. - Networking: SslStream server-side AIA certificate downloads are disabled by default. - SDK and MSBuild: dnx scripts bypass global.json SDK selection; the template engine packages no longer support netstandard2.0; VSTest drops its Newtonsoft.Json dependency. - .NET MAUI: the minimum Android API level is raised to 24. - Cryptography: DSA is removed from macOS. Of these, the BackgroundService change is the one most likely to bite a service: a worker that used to fail quietly now surfaces the failure through the host. That is arguably better behavior, but it will change how your process exits and how your orchestrator restarts it. Ship on RC2 or wait for GA The go-live license answers the legal question. It does not answer the engineering one. Use this decision table. | You run | Deadline | Recommendation | | --- | --- | --- | | .NET 8 in production | 10 Nov 2026 | Move to .NET 10 now. It is LTS to Nov 2028 and has a year of fixes behind it. Do not jump to an STS preview to beat a deadline. | | .NET 9 in production | 10 Nov 2026 | Move to .NET 10 if you want the longest runway, or to .NET 11 GA if you need a C# 15 feature. Either way, plan for November. | | .NET 10 in production | 14 Nov 2028 | Stay. Add a CI job that builds against RC2 so the breaking-changes list becomes a known quantity before GA. | | New internal service, short lifespan | None | RC2 is acceptable if you accept moving to GA the week it lands. | The case against shipping RC2 to customers is simple: an RC build is a moving target until GA, and the policy page gives RC2 the same end-of-support day as .NET 8. The case for it is also real. If you are building something that needs union types and exhaustive switches today, the compiler support is there, with the caveat that parts of the union proposal are unimplemented and the language is still labeled preview.[[4]](#cite-4) For teams on .NET 10 the better move is cheap: build against net11.0 in a side branch, count the warnings, and read only the breaking-changes entries for APIs you call. If you also run Node or Python services, the same deadline thinking applies there; see our [Node 26 LTS upgrade guide](/blogs/node-26-lts-28-october-2026-upgrade-guide-from-node-24-22) and the [Python 3.15 migration checklist](/blogs/python-3-15-release-october-1-2026-lazy-imports-utf8-default-migration-checklist) for how those release trains compare. The [MCP breaking-changes migration guide](/blogs/mcp-2026-07-28-breaking-changes-migration-guide-stateless-protocol) is a reminder that protocol-level breaks hit harder than language ones. Frequently asked questions Can I use .NET 11 RC2 in production? Yes. The RC2 announcement says it includes a go-live support license and can be used in production.[[1]](#cite-1) The policy page lists RC2 support ending 10 November 2026, so you need to update to the GA build. When is .NET 11 GA? No date is stated on the RC2 post. The .NET Conf 2026 page, 10 to 13 November, says it will celebrate the launch of .NET 11, and the release notes README starts support on 10 November. GA at the conference is expected, not confirmed.[[5]](#cite-5) Is .NET 11 LTS? No. The README calls it a Standard Term Support release, supported for two years, to 9 November 2028.[[2]](#cite-2) .NET 10 is the current LTS. When does .NET 8 support end? 10 November 2026, per the support policy page. .NET 9 ends the same day.[[3]](#cite-3) Is C# 15 final in RC2? The Microsoft Learn page still calls C# 15 the latest preview release, and some union proposal features are not implemented. Check the page again before relying on a feature.[[4]](#cite-4) Where is the full list of breaking changes? learn.microsoft.com/dotnet/core/compatibility/11. Microsoft notes the list is incomplete and still growing.[[6]](#cite-6) Sources 1. .NET 11 RC 2, .NET Blog, 13 October 2026 (accessed 20 October 2026) 2. dotnet/core: .NET 11.0 release notes README (accessed 20 October 2026) 3. .NET and .NET Core official support policy (accessed 20 October 2026) 4. What is new in C# 15, Microsoft Learn (accessed 20 October 2026) 5. .NET Conf 2026 (accessed 20 October 2026) 6. Breaking changes in .NET 11, Microsoft Learn (accessed 20 October 2026) 7. dotnet/core release notes: releases.json for 8.0, 9.0 and 10.0 (October 2026 servicing, accessed 20 October 2026) --- Originally published at wowhow.cloud
週刊プログラミングニュース|Deno・Python・OSSと開発ツール【2026-W42/10月9〜15日】 週刊プログラミングニュース|2026年10月9日〜15日 | 2026-W42 最初にDeno、Python、Bun、GitHub Actions、.NET、シークレット保護。その後、OSS・フレームワーク、ライブラリー・データベース、IDE・開発プラットフォームを章ごとに整理します。 対象期間:10月9日00:00〜15日23:59 JST。時刻を確認できた発表はJSTへ換算し、日付のみの資料は公式表記日を採用しています。境界時刻が未確認の候補は確定分と区別しました。 正式版、プレビュー、今後の予定を区別しています。Dockerは修正済み問題の開示、Nodeの10月21日予定分は未提供の修正予告です。 映像は公式情報に基づく解説図で、実機デモではありません。性能値は発表元の測定で、独自の実測ではありません。 日本語音声・日英字幕。 チャプター 00:00 週刊プログラミングニュース 00:28 DenoチームがCloudflareへ 01:50 Python 3.15が正式公開 03:18 Bun 1.4.3に型チェッカー 04:30 Actionsにワークフローロック 05:38 .NETは安定版更新とRC2 07:00 GitHubのシークレット検出拡大 07:59 Next.jsのセキュリティ更新 09:28 Express・Fastify・Micronaut 10:57 フロントエンドの安定版とRC 12:16 Astro・Rolldown・Laravel 13:21 Hydrogenがライブラリーへ 14:14 Pydantic・NumPy・FastAPI 15:59 PgBouncerとpsqlODBC 17:25 SQLite 3.54.0 18:38 Ruby 4.1のプレビュー 19:39 Node.js更新と次のセキュリティ修正 20:51 VS CodeとVisual Studio 22:07 Cursor Originのアプリ連携 22:54 GitLabの緊急修正 23:57 Docker Hubの修正済み問題の開示 25:04 Lambdaの新しい実行モデルと接続維持 26:27 今週の確認リスト 公式出典 deno.com blog.python.org bun.com github.blog devblogs.microsoft.com github.blog devblogs.microsoft.com github.com/dotnet/core/blob/main/release-notes/10… nextjs.org github.com/expressjs/express/releases/tag/v5.3.0 github.com/fastify/fastify/releases github.com/fastify/fastify/security/advisories/GH… micronaut.io micronaut.io github.com/angular/angular/releases/tag/v22.2.3 github.com/vuejs/core/releases github.com/vuejs/core/blob/main/CHANGELOG.md github.com/vuejs/core/blob/minor/CHANGELOG.md github.com/nuxt/nuxt/releases/tag/v4.6.1 github.com/sveltejs/kit/releases/tag/@sveltejs%2F… github.com/withastro/astro/releases/tag/astro@7.4… github.com/rolldown/rolldown/releases/tag/v1.2.14 github.com/laravel/framework/releases/tag/v13.36.0 github.com/laravel/framework/releases shopify.dev github.com/pydantic/pydantic/releases/tag/v2.14.0 pydantic.dev github.com/pydantic/pydantic/releases github.com/numpy/numpy/releases/tag/v2.5.4 github.com/fastapi/fastapi/releases/tag/0.143.1 github.com/fastapi/fastapi/releases/tag/0.143.2 pgbouncer.org github.com/pgbouncer/pgbouncer/releases/tag/pgbou… postgresql.org sqlite.org ruby-lang.org nodejs.org nodejs.org nodejs.org openwall.com code.visualstudio.com learn.microsoft.com cursor.com docs.gitlab.com docker.com aws.amazon.com developers.cloudflare.com