diff --git a/docs/core/whats-new/dotnet-10/libraries.md b/docs/core/whats-new/dotnet-10/libraries.md index d160657de7993..0c9ac5d159867 100644 --- a/docs/core/whats-new/dotnet-10/libraries.md +++ b/docs/core/whats-new/dotnet-10/libraries.md @@ -2,14 +2,14 @@ title: What's new in .NET libraries for .NET 10 description: Learn about the updates to the .NET libraries for .NET 10. titleSuffix: "" -ms.date: 07/16/2025 +ms.date: 08/12/2025 ms.topic: whats-new ai-usage: ai-assisted --- # What's new in .NET libraries for .NET 10 -This article describes new features in the .NET libraries for .NET 10. It's been updated for Preview 6. +This article describes new features in the .NET libraries for .NET 10. It's been updated for Preview 7. ## Cryptography @@ -113,6 +113,73 @@ private static bool ValidateMLDsaSignature(ReadOnlySpan data, ReadOnlySpan The PQC algorithms are available on systems where the system cryptographic libraries are OpenSSL 3.5 (or newer) or Windows CNG with PQC support. Also, the new classes are all marked as [`[Experimental]`](../../../fundamentals/syslib-diagnostics/experimental-overview.md) under diagnostic `SYSLIB5006` until development is complete. +#### ML-DSA + +The class includes ease-of-use features that simplify common code patterns: + +```diff +private static byte[] SignData(string privateKeyPath, ReadOnlySpan data) +{ + using (MLDsa signingKey = MLDsa.ImportFromPem(File.ReadAllBytes(privateKeyPath))) + { +- byte[] signature = new byte[signingKey.Algorithm.SignatureSizeInBytes]; +- signingKey.SignData(data, signature); ++ return signingKey.SignData(data); +- return signature; + } +} +``` + +Additionally, this release added support for HashML-DSA, which is called "PreHash" to help distinguish it from "pure" ML-DSA. As the underlying specification interacts with the Object Identifier (OID) value, the SignPreHash and VerifyPreHash methods on this `[Experimental]` type take the dotted-decimal OID as a string. This might evolve as more scenarios using HashML-DSA become well-defined. + +```csharp +private static byte[] SignPreHashSha3_256(MLDsa signingKey, ReadOnlySpan data) +{ + const string Sha3_256Oid = "2.16.840.1.101.3.4.2.8"; + return signingKey.SignPreHash(SHA3_256.HashData(data), Sha3_256Oid); +} +``` + +#### Composite ML-DSA + +.NET 10 introduces new types to support [ietf-lamps-pq-composite-sigs](https://datatracker.ietf.org/doc/draft-ietf-lamps-pq-composite-sigs/) (currently at draft 7), including `CompositeMLDsa` and `CompositeMLDsaAlgorithm` types with implementation of the primitive methods for RSA variants. + +```csharp +var algorithm = CompositeMLDsaAlgorithm.MLDsa65WithRSA4096Pss; +using var privateKey = CompositeMLDsa.GenerateKey(algorithm); + +byte[] data = [42]; +byte[] signature = privateKey.SignData(data); + +using var publicKey = CompositeMLDsa.ImportCompositeMLDsaPublicKey(algorithm, privateKey.ExportCompositeMLDsaPublicKey()); +Console.WriteLine(publicKey.VerifyData(data, signature)); // True + +signature[0] ^= 1; // Tamper with signature +Console.WriteLine(publicKey.VerifyData(data, signature)); // False +``` + +### AES KeyWrap with Padding (IETF RFC 5649) + +AES-KWP is an algorithm that is occasionally used in constructions like Cryptographic Message Syntax (CMS) EnvelopedData, where content is encrypted once, but the decryption key needs to be distributed to multiple parties, each one in a distinct secret form. + +.NET now supports the AES-KWP algorithm via instance methods on the class: + +```csharp +private static byte[] DecryptContent(ReadOnlySpan kek, ReadOnlySpan encryptedKey, ReadOnlySpan ciphertext) +{ + using (Aes aes = Aes.Create()) + { + aes.SetKey(kek); + + Span dek = stackalloc byte[256 / 8]; + int length = aes.DecryptKeyWrapPadded(encryptedKey, dek); + + aes.SetKey(dek.Slice(0, length)); + return aes.DecryptCbc(ciphertext); + } +} +``` + ## Globalization and date/time - [New method overloads in ISOWeek for DateOnly type](#new-method-overloads-in-isoweek-for-dateonly-type) @@ -183,6 +250,7 @@ This new API is already used in and improves the p - [Allow specifying ReferenceHandler in `JsonSourceGenerationOptions`](#allow-specifying-referencehandler-in-jsonsourcegenerationoptions) - [Option to disallow duplicate JSON properties](#option-to-disallow-duplicate-json-properties) - [Strict JSON serialization options](#strict-json-serialization-options) +- [PipeReader support for JSON serializer](#pipereader-support-for-json-serializer) ### Allow specifying ReferenceHandler in `JsonSourceGenerationOptions` @@ -224,6 +292,37 @@ These options are read-compatible with now supports , complementing the existing support. Previously, deserializing from a `PipeReader` required converting it to a , but the new overloads eliminate that step by integrating `PipeReader` directly into the serializer. As a bonus, not having to convert from what you're already holding can yield some efficiency benefits. + +This shows the basic usage: + +:::code language="csharp" source="snippets/csharp/PipeReaderBasic.cs"::: + +Here is an example of a producer that produces tokens in chunks and a consumer that receives and displays them: + +:::code language="csharp" source="snippets/csharp/PipeReaderChunks.cs"::: + +All of this is serialized as JSON in the (formatted here for readability): + +```json +[ + { + "Message": "The quick brown fox", + "Timestamp": "2025-08-01T18:37:27.2930151-07:00" + }, + { + "Message": " jumps over", + "Timestamp": "2025-08-01T18:37:27.8594502-07:00" + }, + { + "Message": " the lazy dog.", + "Timestamp": "2025-08-01T18:37:28.3753669-07:00" + } +] +``` + ## System.Numerics - [More left-handed matrix transformation methods](#more-left-handed-matrix-transformation-methods) @@ -328,3 +427,85 @@ A community contribution improved the performance of to launch a process in a separate process group. This allows you to send isolated signals to child processes that could otherwise take down the parent without proper handling. Sending signals is convenient to avoid forceful termination. + +:::code language="csharp" source="snippets/csharp/ProcessGroup.cs"::: + +## WebSocket enhancements + +### WebSocketStream + +.NET 10 introduces `WebSocketStream` , a new API designed to simplify some of the most common—and previously cumbersome— scenarios in .NET. + +Traditional `WebSocket` APIs are low-level and require significant boilerplate: handling buffering and framing, reconstructing messages, managing encoding/decoding, and writing custom wrappers to integrate with streams, channels, or other transport abstractions. These complexities make it difficult to use WebSockets as a transport, especially for apps with streaming or text-based protocols, or event-driven handlers. + +`WebSocketStream` addresses these pain points by providing a -based abstraction over a WebSocket. This enables seamless integration with existing APIs for reading, writing, and parsing data, whether binary or text, and reduces the need for manual plumbing. + +`WebSocketStream` enables high-level, familiar APIs for common WebSocket consumption and production patterns. These APIs reduce friction and make advanced scenarios easier to implement. + +#### Common usage patterns + +Here are a few examples of how `WebSocketStream` simplifies typical `WebSocket` workflows: + +##### Streaming text protocol (for example, STOMP) + +:::code language="csharp" source="snippets/csharp/WebSocketStreamText.cs"::: + +##### Streaming binary protocol (for example, AMQP) + +:::code language="csharp" source="snippets/csharp/WebSocketStreamBinary.cs"::: + +##### Read a single message as a stream (for example, JSON deserialization) + +:::code language="csharp" source="snippets/csharp/WebSocketStreamRead.cs"::: + +##### Write a single message as a stream (for example, binary serialization) + +:::code language="csharp" source="snippets/csharp/WebSocketStreamWrite.cs"::: + +## TLS enhancements + +### TLS 1.3 for macOS (client) + +.NET 10 adds client-side TLS 1.3 support on macOS by integrating Apple's Network.framework into and . Historically, macOS used Secure Transport which doesn't support TLS 1.3; opting into Network.framework enables TLS 1.3. + +#### Scope and behavior + +- macOS only, client-side in this release. +- Opt-in. Existing apps continue to use the current stack unless enabled. +- When enabled, older TLS versions (TLS 1.0 and 1.1) might no longer be available via Network.framework. + +#### How to enable + +Use an AppContext switch in code: + +```csharp +// Opt in to Network.framework-backed TLS on Apple platforms +AppContext.SetSwitch("System.Net.Security.UseNetworkFramework", true); + +using var client = new HttpClient(); +var html = await client.GetStringAsync("https://example.com"); +``` + +Or use an environment variable: + +```bash +# Opt-in via environment variable (set for the process or machine as appropriate) +DOTNET_SYSTEM_NET_SECURITY_USENETWORKFRAMEWORK=1 +# or +DOTNET_SYSTEM_NET_SECURITY_USENETWORKFRAMEWORK=true +``` + +#### Notes + +- TLS 1.3 applies to and APIs built on it (for example, /). +- Cipher suites are controlled by macOS via Network.framework. +- Underlying stream behavior might differ when Network.framework is enabled (for example, buffering, read/write completion, cancellation semantics). +- Semantics might differ for zero-byte reads. Avoid relying on zero-length reads for detecting data availability. +- Certain internationalized domain names (IDN) hostnames might be rejected by Network.framework. Prefer ASCII/Punycode (A-label) hostnames or validate names against macOS/Network.framework constraints. +- If your app relies on specific edge-case behavior, validate it under Network.framework. diff --git a/docs/core/whats-new/dotnet-10/overview.md b/docs/core/whats-new/dotnet-10/overview.md index 2b70efb375c74..02d2f1a8b4592 100644 --- a/docs/core/whats-new/dotnet-10/overview.md +++ b/docs/core/whats-new/dotnet-10/overview.md @@ -2,14 +2,14 @@ title: What's new in .NET 10 description: Learn about the new features introduced in .NET 10 for the runtime, libraries, and SDK. Also find links to what's new in other areas, such as ASP.NET Core. titleSuffix: "" -ms.date: 07/16/2025 +ms.date: 08/12/2025 ms.topic: whats-new ai-usage: ai-assisted --- # What's new in .NET 10 -Learn about the new features in .NET 10 and find links to further documentation. This page has been updated for Preview 6. +Learn about the new features in .NET 10 and find links to further documentation. This page has been updated for Preview 7. .NET 10, the successor to [.NET 9](../dotnet-9/overview.md), is [supported for three years](https://dotnet.microsoft.com/platform/support/policy/dotnet-core) as a long-term support (LTS) release. You can [download .NET 10 here](https://get.dot.net/10). @@ -23,14 +23,14 @@ For more information, see [What's new in the .NET 10 runtime](runtime.md). ## .NET libraries -The .NET 10 libraries introduce new APIs in cryptography, globalization, numerics, serialization, collections, and diagnostics, and when working with ZIP files. New JSON serialization options include disallowing duplicate properties and strict serialization settings. Post-quantum cryptography support has been expanded with Windows Cryptography API: Next Generation (CNG) support. +The .NET 10 libraries introduce new APIs in cryptography, globalization, numerics, serialization, collections, and diagnostics, and when working with ZIP files. New JSON serialization options include disallowing duplicate properties, strict serialization settings, and `PipeReader` support for improved efficiency. Post-quantum cryptography support has been expanded with Windows Cryptography API: Next Generation (CNG) support, enhanced ML-DSA with simplified APIs and HashML-DSA support, plus Composite ML-DSA. Additional cryptography enhancements include AES KeyWrap with Padding support. New networking capabilities include `WebSocketStream` for simplified `WebSocket` usage and TLS 1.3 support for macOS clients. Process management gains Windows process group support for better signal isolation. For more information, see [What's new in the .NET 10 libraries](libraries.md). For details on JSON serialization, see [System.Text.Json overview](/dotnet/standard/serialization/system-text-json/overview). ## .NET SDK -The .NET 10 SDK includes support for [Microsoft.Testing.Platform](../../testing/microsoft-testing-platform-intro.md) in `dotnet test`, standardizes CLI command order, and updates the CLI to generate native tab-completion scripts for popular shells. For containers, console apps can natively create container images, and a new property lets you explicitly set the format of container images. The SDK also supports platform-specific .NET tools, one-shot tool execution with `dotnet tool exec`, the new `dnx` tool execution script, CLI introspection with `--cli-schema`, and enhanced file-based apps with publish support and native AOT. +The .NET 10 SDK includes support for [Microsoft.Testing.Platform](../../testing/microsoft-testing-platform-intro.md) in `dotnet test`, standardizes CLI command order, and updates the CLI to generate native tab-completion scripts for popular shells. For containers, console apps can natively create container images, and a new property lets you explicitly set the format of container images. The SDK also supports platform-specific .NET tools with enhanced compatibility via the `any` RuntimeIdentifier, one-shot tool execution with `dotnet tool exec`, the new `dnx` tool execution script, CLI introspection with `--cli-schema`, and enhanced file-based apps with publish support and native AOT. For more information, see [What's new in the SDK for .NET 10](sdk.md). For details on .NET tools, see [Manage .NET tools](/dotnet/core/tools/global-tools). diff --git a/docs/core/whats-new/dotnet-10/runtime.md b/docs/core/whats-new/dotnet-10/runtime.md index 36e804b2ae8f9..174f9937fd37e 100644 --- a/docs/core/whats-new/dotnet-10/runtime.md +++ b/docs/core/whats-new/dotnet-10/runtime.md @@ -2,13 +2,13 @@ title: What's new in .NET 10 runtime description: Learn about the new features introduced in the .NET 10 runtime. titleSuffix: "" -ms.date: 07/16/2025 +ms.date: 08/12/2025 ms.topic: whats-new ai-usage: ai-assisted --- # What's new in the .NET 10 runtime -This article describes new features and performance improvements in the .NET runtime for .NET 10. It has been updated for Preview 6. +This article describes new features and performance improvements in the .NET runtime for .NET 10. It's been updated for Preview 7. ## JIT compiler improvements @@ -48,7 +48,7 @@ private static void Main() } ``` -On x64, we pass the members of `Point` to `Consume` in separate registers, and since physical promotion kicked in for the local `p`, we don't allocate anything on the stack first: +On x64, the members of `Point` are passed to `Consume` in separate registers, and since physical promotion kicked in for the local `p`, nothing is allocated on the stack first: ```asm Program:Main() (FullOpts): @@ -57,7 +57,7 @@ Program:Main() (FullOpts): tail.jmp [Program:Consume(Program+Point)] ``` -Now, suppose we changed the type of the members of `Point` to `int` instead of `long`. Because an `int` is four bytes wide, and registers are eight bytes wide on x64, the calling convention requires us to pass the members of `Point` in one register. Previously, the JIT compiler would first store the values to memory, and then load the eight-byte chunk into a register. With the .NET 10 improvements, the JIT compiler can now place the promoted members of struct arguments into shared registers directly: +Now, suppose the type of the members of `Point` was changed to `int` instead of `long`. Because an `int` is four bytes wide, and registers are eight bytes wide on x64, the calling convention requires the members of `Point` to be passed in one register. Previously, the JIT compiler would first store the values to memory, and then load the eight-byte chunk into a register. With the .NET 10 improvements, the JIT compiler can now place the promoted members of struct arguments into shared registers directly: ```asm Program:Main() (FullOpts): diff --git a/docs/core/whats-new/dotnet-10/sdk.md b/docs/core/whats-new/dotnet-10/sdk.md index 4e9670b714c64..6eb8d37e46818 100644 --- a/docs/core/whats-new/dotnet-10/sdk.md +++ b/docs/core/whats-new/dotnet-10/sdk.md @@ -2,14 +2,14 @@ title: What's new in the SDK and tooling for .NET 10 description: Learn about the new .NET SDK features introduced in .NET 10. titleSuffix: "" -ms.date: 07/16/2025 +ms.date: 08/12/2025 ms.topic: whats-new ai-usage: ai-assisted --- # What's new in the SDK and tooling for .NET 10 -This article describes new features and enhancements in the .NET SDK for .NET 10. It has been updated for Preview 6. +This article describes new features and enhancements in the .NET SDK for .NET 10. It's been updated for Preview 7. ## .NET tools enhancements @@ -59,6 +59,27 @@ The actual implementation of the `dnx` command is in the `dotnet` CLI itself, al For more information about managing .NET tools, see [Manage .NET tools](../../tools/global-tools.md). +### Use the `any` RuntimeIdentifier with platform-specific .NET tools + +The [platform-specific .NET tools](#platform-specific-net-tools) feature is great for making sure tools are optimized for specific platforms that you target ahead-of-time. However, there are times where you won't know all of the platforms that you'd like to target, or sometimes .NET itself will learn how to support a new platform, and you'd like your tool to be runnable there too. + +To make your tool work this way, add the `any` runtime identifier to your project file: + +```xml + + + linux-x64; + linux-arm64; + macos-arm64; + win-x64; + win-arm64; + any + + +``` + +This RuntimeIdentifier is at the 'root' of the platform-compatibility checking, and since it declares support for _any_ platform, the tool that gets packaged will be the most compatible kind of tool - a framework-dependent, platform-agnostic .NET DLL, which requires a compatible .NET Runtime to execute. When you perform a `dotnet pack` to create your tool, you'll see a new package for the `any` RuntimeIdentifier appear alongside the other platform-specific packages and the top-level manifest package. + ### CLI introspection with `--cli-schema` A new `--cli-schema` option is available on all CLI commands. When used, it outputs a JSON representation of the CLI command tree for the invoked command or subcommand. This is useful for tool authors, shell integration, and advanced scripting. diff --git a/docs/core/whats-new/dotnet-10/snippets/csharp/PipeReaderBasic.cs b/docs/core/whats-new/dotnet-10/snippets/csharp/PipeReaderBasic.cs new file mode 100644 index 0000000000000..2087bc145425d --- /dev/null +++ b/docs/core/whats-new/dotnet-10/snippets/csharp/PipeReaderBasic.cs @@ -0,0 +1,19 @@ +using System; +using System.IO.Pipelines; +using System.Text.Json; +using System.Threading.Tasks; + +var pipe = new Pipe(); + +// Serialize to writer +await JsonSerializer.SerializeAsync(pipe.Writer, new Person("Alice")); +await pipe.Writer.CompleteAsync(); + +// Deserialize from reader +var result = await JsonSerializer.DeserializeAsync(pipe.Reader); +await pipe.Reader.CompleteAsync(); + +Console.WriteLine($"Your name is {result.Name}."); +// Output: Your name is Alice. + +record Person(string Name); \ No newline at end of file diff --git a/docs/core/whats-new/dotnet-10/snippets/csharp/PipeReaderChunks.cs b/docs/core/whats-new/dotnet-10/snippets/csharp/PipeReaderChunks.cs new file mode 100644 index 0000000000000..8c043ddcb591b --- /dev/null +++ b/docs/core/whats-new/dotnet-10/snippets/csharp/PipeReaderChunks.cs @@ -0,0 +1,51 @@ +using System; +using System.Collections.Generic; +using System.IO.Pipelines; +using System.Text.Json; +using System.Threading.Tasks; + +var pipe = new Pipe(); + +// Producer writes to the pipe in chunks. +var producerTask = Task.Run(async () => +{ + async static IAsyncEnumerable GenerateResponse() + { + yield return new Chunk("The quick brown fox", DateTime.Now); + await Task.Delay(500); + yield return new Chunk(" jumps over", DateTime.Now); + await Task.Delay(500); + yield return new Chunk(" the lazy dog.", DateTime.Now); + } + + await JsonSerializer.SerializeAsync>(pipe.Writer, GenerateResponse()); + await pipe.Writer.CompleteAsync(); +}); + +// Consumer reads from the pipe and outputs to console. +var consumerTask = Task.Run(async () => +{ + var thinkingString = "..."; + var clearThinkingString = new string("\b\b\b"); + var lastTimestamp = DateTime.MinValue; + + // Read response to end. + Console.Write(thinkingString); + await foreach (var chunk in JsonSerializer.DeserializeAsyncEnumerable(pipe.Reader)) + { + Console.Write(clearThinkingString); + Console.Write(chunk.Message); + Console.Write(thinkingString); + lastTimestamp = DateTime.Now; + } + + Console.Write(clearThinkingString); + Console.WriteLine($" Last message sent at {lastTimestamp}."); + + await pipe.Reader.CompleteAsync(); +}); + +await producerTask; +await consumerTask; + +record Chunk(string Message, DateTime Timestamp); \ No newline at end of file diff --git a/docs/core/whats-new/dotnet-10/snippets/csharp/ProcessGroup.cs b/docs/core/whats-new/dotnet-10/snippets/csharp/ProcessGroup.cs new file mode 100644 index 0000000000000..fce205adc4386 --- /dev/null +++ b/docs/core/whats-new/dotnet-10/snippets/csharp/ProcessGroup.cs @@ -0,0 +1,73 @@ +using System; +using System.Diagnostics; +using System.IO; +using System.Runtime.InteropServices; +using System.Threading; + +class Program +{ + static void Main(string[] args) + { + bool isChildProcess = args.Length > 0 && args[0] == "child"; + if (!isChildProcess) + { + var psi = new ProcessStartInfo + { + FileName = Environment.ProcessPath, + Arguments = "child", + CreateNewProcessGroup = true, + }; + + using Process process = Process.Start(psi)!; + Thread.Sleep(5_000); + + GenerateConsoleCtrlEvent(CTRL_C_EVENT, (uint)process.Id); + process.WaitForExit(); + + Console.WriteLine("Child process terminated gracefully, continue with the parent process logic if needed."); + } + else + { + // If you need to send a CTRL+C, the child process needs to re-enable CTRL+C handling, if you own the code, you can call SetConsoleCtrlHandler(NULL, FALSE). + // see https://learn.microsoft.com/windows/win32/api/processthreadsapi/nf-processthreadsapi-createprocessw#remarks + SetConsoleCtrlHandler((IntPtr)null, false); + + Console.WriteLine("Greetings from the child process! I need to be gracefully terminated, send me a signal!"); + + bool stop = false; + + var registration = PosixSignalRegistration.Create(PosixSignal.SIGINT, ctx => + { + stop = true; + ctx.Cancel = true; + Console.WriteLine("Received CTRL+C, stopping..."); + }); + + StreamWriter sw = File.AppendText("log.txt"); + int i = 0; + while (!stop) + { + Thread.Sleep(1000); + sw.WriteLine($"{++i}"); + Console.WriteLine($"Logging {i}..."); + } + + // Clean up + sw.Dispose(); + registration.Dispose(); + + Console.WriteLine("Thanks for not killing me!"); + } + } + + private const int CTRL_C_EVENT = 0; + private const int CTRL_BREAK_EVENT = 1; + + [DllImport("kernel32.dll", SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + private static extern bool SetConsoleCtrlHandler(IntPtr handler, [MarshalAs(UnmanagedType.Bool)] bool Add); + + [DllImport("kernel32.dll", SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + private static extern bool GenerateConsoleCtrlEvent(uint dwCtrlEvent, uint dwProcessGroupId); +} \ No newline at end of file diff --git a/docs/core/whats-new/dotnet-10/snippets/csharp/WebSocketStreamBinary.cs b/docs/core/whats-new/dotnet-10/snippets/csharp/WebSocketStreamBinary.cs new file mode 100644 index 0000000000000..286c1b253ac22 --- /dev/null +++ b/docs/core/whats-new/dotnet-10/snippets/csharp/WebSocketStreamBinary.cs @@ -0,0 +1,16 @@ +using System; +using System.IO; +using System.Net.WebSockets; +using System.Threading; +using System.Threading.Tasks; + +// Streaming binary protocol (for example, AMQP). +Stream transportStream = WebSocketStream.Create( + connectedWebSocket, + WebSocketMessageType.Binary, + closeTimeout: TimeSpan.FromSeconds(10)); +await message.SerializeToStreamAsync(transportStream, cancellationToken); +var receivePayload = new byte[payloadLength]; +await transportStream.ReadExactlyAsync(receivePayload, cancellationToken); +transportStream.Dispose(); +// `Dispose` automatically handles closing handshake. \ No newline at end of file diff --git a/docs/core/whats-new/dotnet-10/snippets/csharp/WebSocketStreamRead.cs b/docs/core/whats-new/dotnet-10/snippets/csharp/WebSocketStreamRead.cs new file mode 100644 index 0000000000000..5ecce13b17819 --- /dev/null +++ b/docs/core/whats-new/dotnet-10/snippets/csharp/WebSocketStreamRead.cs @@ -0,0 +1,8 @@ +using System.IO; +using System.Net.WebSockets; +using System.Text.Json; + +// Reading a single message as a stream (for example, JSON deserialization). +using Stream messageStream = WebSocketStream.CreateReadableMessageStream(connectedWebSocket, WebSocketMessageType.Text); +// JsonSerializer.DeserializeAsync reads until the end of stream. +var appMessage = await JsonSerializer.DeserializeAsync(messageStream); \ No newline at end of file diff --git a/docs/core/whats-new/dotnet-10/snippets/csharp/WebSocketStreamText.cs b/docs/core/whats-new/dotnet-10/snippets/csharp/WebSocketStreamText.cs new file mode 100644 index 0000000000000..6295107f6af9b --- /dev/null +++ b/docs/core/whats-new/dotnet-10/snippets/csharp/WebSocketStreamText.cs @@ -0,0 +1,15 @@ +using System.IO; +using System.Net.WebSockets; +using System.Threading; +using System.Threading.Tasks; + +// Streaming text protocol (for example, STOMP). +using Stream transportStream = WebSocketStream.Create( + connectedWebSocket, + WebSocketMessageType.Text, + ownsWebSocket: true); +// Integration with Stream-based APIs. +// Don't close the stream, as it's also used for writing. +using var transportReader = new StreamReader(transportStream, leaveOpen: true); +var line = await transportReader.ReadLineAsync(cancellationToken); // Automatic UTF-8 and new line handling. +transportStream.Dispose(); // Automatic closing handshake handling on `Dispose`. \ No newline at end of file diff --git a/docs/core/whats-new/dotnet-10/snippets/csharp/WebSocketStreamWrite.cs b/docs/core/whats-new/dotnet-10/snippets/csharp/WebSocketStreamWrite.cs new file mode 100644 index 0000000000000..58ad0b9b090f1 --- /dev/null +++ b/docs/core/whats-new/dotnet-10/snippets/csharp/WebSocketStreamWrite.cs @@ -0,0 +1,15 @@ +using System; +using System.IO; +using System.Net.WebSockets; +using System.Threading; +using System.Threading.Tasks; + +// Writing a single message as a stream (for example, binary serialization). +public async Task SendMessageAsync(AppMessage message, CancellationToken cancellationToken) +{ + using Stream messageStream = WebSocketStream.CreateWritableMessageStream(_connectedWebSocket, WebSocketMessageType.Binary); + foreach (ReadOnlyMemory chunk in message.SplitToChunks()) + { + await messageStream.WriteAsync(chunk, cancellationToken); + } +} // EOM sent on messageStream.Dispose(). \ No newline at end of file diff --git a/docs/core/whats-new/dotnet-10/snippets/csharp/snippets.csproj b/docs/core/whats-new/dotnet-10/snippets/csharp/snippets.csproj new file mode 100644 index 0000000000000..6f0d5c788bb02 --- /dev/null +++ b/docs/core/whats-new/dotnet-10/snippets/csharp/snippets.csproj @@ -0,0 +1,26 @@ + + + + net8.0 + Library + enable + enable + preview + false + + + + + + + + + + + + + + + + + \ No newline at end of file