From 17d020b91bff9b30e23d025c9ecf7c4ac75c3c1a Mon Sep 17 00:00:00 2001 From: Genevieve Warren <24882762+gewarren@users.noreply.github.com> Date: Tue, 10 Sep 2024 14:12:32 -0700 Subject: [PATCH] What's new for .NET 9 RC 1 (#42550) --- docs/core/whats-new/dotnet-9/libraries.md | 147 +++++++++++++----- docs/core/whats-new/dotnet-9/overview.md | 18 ++- docs/core/whats-new/dotnet-9/runtime.md | 2 +- docs/core/whats-new/dotnet-9/sdk.md | 36 +++-- .../snippets/dotnet-9/csharp/Collections.cs | 2 +- .../snippets/dotnet-9/csharp/Compression.cs | 25 +++ .../snippets/dotnet-9/csharp/Diagnostics.cs | 62 +++++++- .../snippets/dotnet-9/csharp/Networking.cs | 18 ++- .../snippets/dotnet-9/csharp/Program.cs | 3 +- .../snippets/dotnet-9/csharp/Project.csproj | 1 + .../snippets/dotnet-9/csharp/Runtime.cs | 2 +- .../snippets/dotnet-9/csharp/Serialization.cs | 2 +- .../snippets/dotnet-9/csharp/TarEntry.cs | 43 +++++ 13 files changed, 301 insertions(+), 60 deletions(-) create mode 100644 docs/core/whats-new/snippets/dotnet-9/csharp/Compression.cs create mode 100644 docs/core/whats-new/snippets/dotnet-9/csharp/TarEntry.cs diff --git a/docs/core/whats-new/dotnet-9/libraries.md b/docs/core/whats-new/dotnet-9/libraries.md index 12e984fc3f0d5..13e8f3e65665d 100644 --- a/docs/core/whats-new/dotnet-9/libraries.md +++ b/docs/core/whats-new/dotnet-9/libraries.md @@ -2,12 +2,13 @@ title: What's new in .NET libraries for .NET 9 description: Learn about the new .NET libraries features introduced in .NET 9. titleSuffix: "" -ms.date: 08/09/2024 +ms.date: 09/09/2024 ms.topic: whats-new --- + # What's new in .NET libraries for .NET 9 -This article describes new features in the .NET libraries for .NET 9. It's been updated for .NET 9 Preview 7. +This article describes new features in the .NET libraries for .NET 9. It's been updated for .NET 9 RC 1. ## Base64Url @@ -22,7 +23,7 @@ string encoded = Base64Url.EncodeToString(bytes); ## BinaryFormatter -.NET 9 removes from the .NET runtime. The APIs are still present, but their implementations always throw an exception, regardless of project type. For more information about the removal and your options if you're affected, see [BinaryFormatter migration guide](../../../standard/serialization/binaryformatter-migration-guide/index.md). +.NET 9 removes from the .NET runtime. The APIs are still present, but their implementations always throw an exception, regardless of project type. For more information about the removal and your options if you're affected, see [BinaryFormatter migration guide](../../../standard/serialization/binaryformatter-migration-guide/index.md). ## Collections @@ -37,7 +38,7 @@ The collection types in .NET gain the following updates for .NET 9: In high-performance code, spans are often used to avoid allocating strings unnecessarily, and lookup tables with types like and are frequently used as caches. However, there has been no safe, built-in mechanism for doing lookups on these collection types with spans. With the new `allows ref struct` feature in C# 13 and new features on these collection types in .NET 9, it's now possible to perform these kinds of lookups. -The following example demonstrates using `Dictionary.GetAlternateLookup()` . +The following example demonstrates using [Dictionary.GetAlternateLookup](xref:System.Collections.Generic.CollectionExtensions.GetAlternateLookup%60%603(System.Collections.Generic.Dictionary{%60%600,%60%601})). :::code language="csharp" source="../snippets/dotnet-9/csharp/Collections.cs" id="AlternateLookup"::: @@ -131,13 +132,13 @@ Those methods all used content-sniffing to figure out if the input was something - It's a protocol deviation. - It's a source of security issues. -.NET 9 introduces a new `X509CertificateLoader` class, which has a "one method, one purpose" design. In its initial version, it only supports two of the five formats that the constructor supported. Those are the two formats that worked on all operation systems. +.NET 9 introduces a new class, which has a "one method, one purpose" design. In its initial version, it only supports two of the five formats that the constructor supported. Those are the two formats that worked on all operation systems. ### OpenSSL providers support .NET 8 introduced the OpenSSL-specific APIs and . They enable interacting with OpenSSL [`ENGINE` components](https://github.com/openssl/openssl/blob/master/README-ENGINES.md) and use hardware security modules (HSM), for example. -.NET 9 introduces `SafeEvpPKeyHandle.OpenKeyFromProvider`, which enables using [OpenSSL providers](https://docs.openssl.org/master/man7/provider/) and interacting with providers such as `tpm2` or `pkcs11`. +.NET 9 introduces , which enables using [OpenSSL providers](https://docs.openssl.org/master/man7/provider/) and interacting with providers such as `tpm2` or `pkcs11`. Some distros have [removed `ENGINE` support](https://github.com/dotnet/runtime/issues/104775) since it is now deprecated. @@ -161,7 +162,7 @@ There are some performance improvements during the TLS handshake as well as impr Windows 11 has added new APIs to help secure Windows keys with [virtualization-based security (VBS)](https://techcommunity.microsoft.com/t5/windows-it-pro-blog/advancing-key-protection-in-windows-using-vbs/ba-p/4050988). With this new capability, keys can be protected from admin-level key theft attacks with negligible effect on performance, reliability, or scale. -.NET 9 has added matching `CngKeyCreationOptions` flags. The following three flags were added: +.NET 9 has added matching flags. The following three flags were added: - `CngKeyCreationOptions.PreferVbs` matching `NCRYPT_PREFER_VBS_FLAG` - `CngKeyCreationOptions.RequireVbs` matching `NCRYPT_REQUIRE_VBS_FLAG` @@ -202,6 +203,7 @@ The constructor resolution for event source provider, but prior to .NET 9, you had to specify the full meter name. In .NET 9, you can listen to all meters by using the wildcard character `*`, which allows you to capture metrics from every meter in a process. Additionally, it adds support for listening by meter prefix, so you can listen to all meters whose names start with a specified prefix. For example, specifying `MyMeter*` enables listening to all meters with names that begin with `MyMeter`. + +:::code language="csharp" source="../snippets/dotnet-9/csharp/Diagnostics.cs" id="Wildcard"::: + +The `MyEventListener` class is defined as follows. + +:::code language="csharp" source="../snippets/dotnet-9/csharp/Diagnostics.cs" id="EventListener"::: + +When you execute the code, the output is as follows: + +```txt +CounterRateValuePublished + sessionId: 7cd94a65-0d0d-460e-9141-016bf390d522 + meterName: MyCompany.MyMeter + meterVersion: + instrumentName: MyCounter + unit: + tags: + rate: 0 + value: 1 + instrumentId: 1 +CounterRateValuePublished + sessionId: 7cd94a65-0d0d-460e-9141-016bf390d522 + meterName: MyCompany.MyMeter + meterVersion: + instrumentName: MyCounter + unit: + tags: + rate: 0 + value: 1 + instrumentId: 1 +``` + +You can also use the wildcard character to listen to metrics with monitoring tools like [dotnet-counters](../../diagnostics/dotnet-counters.md). + ## LINQ New methods and have been introduced. These methods make it possible to aggregate state by key without needing to allocate intermediate groupings via . @@ -310,6 +349,8 @@ This new capability has an optimized implementation that takes advantage of the - [SocketsHttpHandler is default in HttpClientFactory](#socketshttphandler-is-default-in-httpclientfactory) - [System.Net.ServerSentEvents](#systemnetserversentevents) - [TLS resume with client certificates on Linux](#tls-resume-with-client-certificates-on-linux) +- [WebSocket keep-alive ping and timeout](#websocket-keep-alive-ping-and-timeout) +- [HttpClientFactory no longer logs header values by default](#httpclientfactory-no-longer-logs-header-values-by-default) ### SocketsHttpHandler is default in HttpClientFactory @@ -325,9 +366,32 @@ The following code demonstrates using the new class. ### TLS resume with client certificates on Linux -*TLS resume* is a feature of the TLS protocol that allows resuming previously established sessions to a server. Doing so avoids a few roundtrips and saves computational resources during TLS handshake. +_TLS resume_ is a feature of the TLS protocol that allows resuming previously established sessions to a server. Doing so avoids a few roundtrips and saves computational resources during TLS handshake. + +_TLS resume_ has already been supported on Linux for SslStream connections without client certificates. .NET 9 adds support for TLS resume of mutually authenticated TLS connections, which are common in server-to-server scenarios. The feature is enabled automatically. + +### WebSocket keep-alive ping and timeout + +New APIs on and let you opt in to sending pings and aborting the connection if the peer doesn't respond in time. -*TLS resume* has already been supported on Linux for SslStream connections without client certificates. .NET 9 adds support for TLS resume of mutually authenticated TLS connections, which are common in server-to-server scenarios. The feature is enabled automatically. +Until now, you could specify a to keep the connection from staying idle, but there was no built-in mechanism to enforce that the peer is responding. + +The following example pings the server every 5 seconds and aborts the connection if it doesn't respond within a second. + +:::code language="csharp" source="../snippets/dotnet-9/csharp/Networking.cs" id="KeepAliveTimeout"::: + +### HttpClientFactory no longer logs header values by default + + events logged by `HttpClientFactory` no longer include header values by default. You can opt in to logging values for specific headers via the helper method. + +The following example redacts all headers, except for the user agent. + +```csharp +services.AddHttpClient("myClient") + .RedactLoggedHeaders(name => name != "User-Agent"); +``` + +For more information, see [HttpClientFactory logging redacts header values by default](../../compatibility/networking/9.0/redact-headers.md). ## Reflection @@ -336,7 +400,7 @@ The following code demonstrates using the new class. ### Persisted assemblies -In .NET Core versions and .NET 5-8, support for building an assembly and emitting reflection metadata for dynamically created types was limited to a runnable . The lack of support for *saving* an assembly was often a blocker for customers migrating from .NET Framework to .NET. .NET 9 adds a new type, , that you can use to save an emitted assembly. +In .NET Core versions and .NET 5-8, support for building an assembly and emitting reflection metadata for dynamically created types was limited to a runnable . The lack of support for _saving_ an assembly was often a blocker for customers migrating from .NET Framework to .NET. .NET 9 adds a new type, , that you can use to save an emitted assembly. To create a `PersistedAssemblyBuilder` instance, call its constructor and pass the assembly name, the core assembly, `System.Private.CoreLib`, to reference base runtime types, and optional custom attributes. After you emit all members to the assembly, call the method to create an assembly with default settings. If you want to set the entry point or other options, you can call and use the metadata it returns to save the assembly. The following code shows an example of creating a persisted assembly and setting the entry point. @@ -359,7 +423,7 @@ The new `TypeName` class provides: - `IsByRef` and `IsPointer` for working with pointers and managed references. - `GetElementType()` for working with pointers, references, and arrays. - `IsNested` and `DeclaringType` for working with nested types. - - `AssemblyName`, which exposes the assembly name information via the new class. In contrast to `AssemblyName`, the new type is *immutable*, and parsing culture names doesn't create instances of `CultureInfo`. + - `AssemblyName`, which exposes the assembly name information via the new class. In contrast to `AssemblyName`, the new type is _immutable_, and parsing culture names doesn't create instances of `CultureInfo`. Both `TypeName` and `AssemblyNameInfo` types are immutable and don't provide a way to check for equality (they don't implement `IEquatable`). Comparing assembly names is simple, but different scenarios need to compare only a subset of exposed information (`Name`, `Version`, `CultureName`, and `PublicKeyOrToken`). @@ -382,7 +446,7 @@ The following partial method will be source generated with all the code necessar :::code language="csharp" source="../snippets/dotnet-9/csharp/RegularExpressions.cs" id="GeneratedRegexMethod"::: -C# 13 supports partial *properties* in addition to partial methods, so starting in .NET 9 you can also use `[GeneratedRegex(...)]` on a property. +C# 13 supports partial _properties_ in addition to partial methods, so starting in .NET 9 you can also use `[GeneratedRegex(...)]` on a property. The following partial property is the property equivalent of the previous example. @@ -440,24 +504,18 @@ The generated schema is: ```json { - "type": [ - "object", - "null" - ], - "properties": { - "Title": { - "type": "string" - }, - "Author": { - "type": [ - "string", - "null" - ] - }, - "PublishYear": { - "type": "integer" + "type": ["object", "null"], + "properties": { + "Title": { + "type": "string" + }, + "Author": { + "type": ["string", "null"] + }, + "PublishYear": { + "type": "integer" + } } - } } ``` @@ -469,7 +527,7 @@ The following code shows how to set the option (the `Book` type definition is sh :::code language="csharp" source="../snippets/dotnet-9/csharp/Serialization.cs" id="RespectNullable"::: -You can also enable this setting globally using the `System.Text.Json.JsonSerializerOptions.RespectNullableAnnotations` feature switch in your project file (for example, *.csproj* file): +You can also enable this setting globally using the `System.Text.Json.JsonSerializerOptions.RespectNullableAnnotations` feature switch in your project file (for example, _.csproj_ file): ```xml @@ -491,7 +549,7 @@ The `MyPoco` type is defined as follows: :::code language="csharp" source="../snippets/dotnet-9/csharp/Serialization.cs" id="Poco"::: -You can also enable this setting globally using the `System.Text.Json.JsonSerializerOptions.RespectRequiredConstructorParameters` feature switch in your project file (for example, *.csproj* file): +You can also enable this setting globally using the `System.Text.Json.JsonSerializerOptions.RespectRequiredConstructorParameters` feature switch in your project file (for example, _.csproj_ file): ```xml @@ -594,7 +652,7 @@ Now, a call like `string.Join(", ", "a", "b", "c")` is made without allocating a In .NET 8, a set of `Split` and `SplitAny` methods were introduced for `ReadOnlySpan`. Rather than returning a new `string[]`, these methods accept a destination `Span` into which the bounding indices for each component are written. This makes the operation fully allocation-free. These methods are appropriate to use when the number of ranges is both known and small. -In .NET 9, new overloads of `Split` and `SplitAny` have been added to allow incrementally parsing a `ReadOnlySpan` with an *a priori* unknown number of segments. The new methods enable enumerating through each segment, which is similarly represented as a `Range` that can be used to slice into the original span. +In .NET 9, new overloads of `Split` and `SplitAny` have been added to allow incrementally parsing a `ReadOnlySpan` with an _a priori_ unknown number of segments. The new methods enable enumerating through each segment, which is similarly represented as a `Range` that can be used to slice into the original span. ```csharp public static bool ListContainsItem(ReadOnlySpan span, string item) @@ -611,22 +669,39 @@ public static bool ListContainsItem(ReadOnlySpan span, string item) } ``` +## System.Formats + +The position or offset of the data in the enclosing stream for a object is now a public property. `TarEntry.DataOffset` returns the position in the entry's archive stream where the entry's first data byte is located. The entry's data is encapsulated in a substream that you can access via , which hides the real position of the data relative to the archive stream. That's enough for most users, but if you need more flexibility and want to know the real starting position of the data in the archive stream, the new `TarEntry.DataOffset` API makes it easy to support features like concurrent access with very large TAR files. + +:::code language="csharp" source="../snippets/dotnet-9/csharp/TarEntry.cs" id="DataOffset"::: + ## System.Guid - creates a `Guid` filled mostly with [cryptographically secure random data](https://www.rfc-editor.org/rfc/rfc9562#section-6.9), following the UUID Version 4 specification in RFC 9562. That same RFC also defines other versions, including Version 7, which "features a time-ordered value field derived from the widely implemented and well-known Unix Epoch timestamp source". In other words, much of the data is still random, but some of it is reserved for data based on a timestamp, which enables these values to have a natural sort order. In .NET 9, you can create a `Guid` according to Version 7 via the new `Guid.CreateVersion7()` and `Guid.CreateVersion7(DateTimeOffset timestamp)` methods. You can also use the new `Version` property to retrieve a `Guid` object's version field. + creates a `Guid` filled mostly with [cryptographically secure random data](https://www.rfc-editor.org/rfc/rfc9562#section-6.9), following the UUID Version 4 specification in RFC 9562. That same RFC also defines other versions, including Version 7, which "features a time-ordered value field derived from the widely implemented and well-known Unix Epoch timestamp source". In other words, much of the data is still random, but some of it is reserved for data based on a timestamp, which enables these values to have a natural sort order. In .NET 9, you can create a `Guid` according to Version 7 via the new and methods. You can also use the new property to retrieve a `Guid` object's version field. ## System.IO -- [Compression](#compression) +- [Compression with zlib-ng](#compression-with-zlib-ng) +- [ZLib and Brotli compression options](#zlib-and-brotli-compression-options) - [XPS documents from XPS virtual printer](#xps-documents-from-xps-virtual-printer) -### Compression +### Compression with zlib-ng features like , , , and are all based primarily on the zlib library. Starting in .NET 9, these features instead all use [zlib-ng](https://github.com/zlib-ng/zlib-ng), a library that yields more consistent and efficient processing across a wider array of operating systems and hardware. +### ZLib and Brotli compression options + +`ZLibCompressionOptions` and `BrotliCompressionOptions` are new types for setting algorithm-specific compression [level](xref:System.IO.Compression.CompressionLevel) and strategy (`Default`, `Filtered`, `HuffmanOnly`, `RunLengthEncoding`, or `Fixed`). These types are aimed at users who want more fine-tuned settings than the only existing option, . + +The new compression option types might be expanded in the future. + +The following code snippet shows some example usage: + +:::code language="csharp" source="../snippets/dotnet-9/csharp/Compression.cs" id="CompressStream"::: + ### XPS documents from XPS virtual printer -XPS documents coming from a V4 XPS virtual printer previously couldn't be opened using the library, due to lack of support for handling *.piece* files. This gap has been addressed in .NET 9. +XPS documents coming from a V4 XPS virtual printer previously couldn't be opened using the library, due to lack of support for handling _.piece_ files. This gap has been addressed in .NET 9. ## System.Numerics diff --git a/docs/core/whats-new/dotnet-9/overview.md b/docs/core/whats-new/dotnet-9/overview.md index f9cc6e096aadc..ec722083d484a 100644 --- a/docs/core/whats-new/dotnet-9/overview.md +++ b/docs/core/whats-new/dotnet-9/overview.md @@ -2,9 +2,10 @@ title: What's new in .NET 9 description: Learn about the new .NET features introduced in .NET 9 for the runtime, libraries, and SDK. Also find links to what's new in other areas, such as ASP.NET Core. titleSuffix: "" -ms.date: 08/15/2024 +ms.date: 09/10/2024 ms.topic: whats-new --- + # What's new in .NET 9 Learn about the new features in .NET 9 and find links to further documentation. @@ -17,7 +18,7 @@ New for .NET 9, the engineering team posts .NET 9 preview updates on [GitHub Dis The .NET 9 runtime includes a new attribute model for feature switches with trimming support. The new attributes make it possible to define [feature switches](https://github.com/dotnet/designs/blob/main/accepted/2020/feature-switch.md) that libraries can use to toggle areas of functionality. -Garbage collection includes a *dynamic adaptation to application size* feature that's used by default instead of Server GC. +Garbage collection includes a _dynamic adaptation to application size_ feature that's used by default instead of Server GC. The runtime also includes numerous performance improvements, including loop optimizations, inlining, and Arm64 vectorization and code generation. @@ -29,11 +30,11 @@ For more information, see [What's new in the .NET 9 runtime](runtime.md). In LINQ, the new methods and make it possible to aggregate state by key without needing to allocate intermediate groupings via . -For collection types, the type includes a new method that you can use to *update* the priority of an item in the queue. +For collection types, the type includes a new method that you can use to _update_ the priority of an item in the queue. For cryptography, .NET 9 adds a new one-shot hash method on the type. It also adds new classes that use the KMAC algorithm. -For reflection, the new type lets you *save* an emitted assembly. This new class also includes PDB support, meaning you can emit symbol info and use it to debug a generated assembly. +For reflection, the new type lets you _save_ an emitted assembly. This new class also includes PDB support, meaning you can emit symbol info and use it to debug a generated assembly. The class includes new `From*` methods that let you create a `TimeSpan` object from an `int` (instead of a `double`). These methods help to avoid errors caused by inherent imprecision in floating-point calculations. @@ -41,7 +42,14 @@ For more information, see [What's new in the .NET 9 libraries](libraries.md). ## .NET SDK -The .NET 9 SDK introduces *workload sets*, where all of your workloads stay at a single, specific version until explicitly updated. Unit testing has better MSBuild integration that allows you to run tests in parallel. For tools, a new option for [`dotnet tool install`](../../tools/dotnet-tool-install.md) lets users (instead of tool authors) decide whether a tool is allowed to run on a newer .NET runtime version than the version the tool targets. NuGet security audits run on both direct and transitive package references, by default. The terminal logger is now enabled by default and also has improved usability. For example, the total count of failures and warnings is now summarized at the end of a build. New MSBuild script analyzers are available. The SDK can detect and adjust for version mismatches between the .NET SDK and MSBuild. +The .NET 9 SDK introduces _workload sets_, where all of your workloads stay at a single, specific version until explicitly updated. For tools, a new option for [`dotnet tool install`](../../tools/dotnet-tool-install.md) lets users (instead of tool authors) decide whether a tool is allowed to run on a newer .NET runtime version than the version the tool targets. In addition: + +- Unit testing has better MSBuild integration that allows you to run tests in parallel. +- NuGet security audits run on both direct and transitive package references, by default. +- The terminal logger is enabled by default and also has improved usability. For example, the total count of failures and warnings is now summarized at the end of a build. +- New MSBuild script analyzers ("build checks") are available. +- The SDK can detect and adjust for version mismatches between the .NET SDK and MSBuild. +- The `dotnet workload history` command shows you the history of workload installations and modifications for the current .NET SDK installation. For more information, see [What's new in the SDK for .NET 9](sdk.md). diff --git a/docs/core/whats-new/dotnet-9/runtime.md b/docs/core/whats-new/dotnet-9/runtime.md index 794bb95c7295e..728a04e57de8b 100644 --- a/docs/core/whats-new/dotnet-9/runtime.md +++ b/docs/core/whats-new/dotnet-9/runtime.md @@ -2,7 +2,7 @@ title: What's new in .NET 9 runtime description: Learn about the new .NET features introduced in the .NET 9 runtime. titleSuffix: "" -ms.date: 08/08/2024 +ms.date: 09/09/2024 ms.topic: whats-new --- # What's new in the .NET 9 runtime diff --git a/docs/core/whats-new/dotnet-9/sdk.md b/docs/core/whats-new/dotnet-9/sdk.md index b9774009a9d3a..06f91e1620b6b 100644 --- a/docs/core/whats-new/dotnet-9/sdk.md +++ b/docs/core/whats-new/dotnet-9/sdk.md @@ -2,12 +2,13 @@ title: What's new in the SDK for .NET 9 description: Learn about the new .NET SDK features introduced in .NET 9, including for unit testing, terminal logger, tool roll-forward, and build script analyzers. titleSuffix: "" -ms.date: 08/15/2024 +ms.date: 09/09/2024 ms.topic: whats-new --- + # What's new in the SDK for .NET 9 -This article describes new features in the .NET SDK for .NET 9. It's been updated for .NET 9 Preview 7. +This article describes new features in the .NET SDK for .NET 9. It's been updated for .NET RC 1. ## Unit testing @@ -19,15 +20,15 @@ In .NET 9, `dotnet test` is more fully integrated with MSBuild. Because MSBuild ### Terminal logger test display -Test result reporting for [`dotnet test`](../../tools/dotnet-test.md) is now supported directly in the MSBuild terminal logger. You get more fully featured test reporting both *while* tests are running (displays the running test name) and *after* tests are completed (any test errors are rendered in a better way). +Test result reporting for [`dotnet test`](../../tools/dotnet-test.md) is now supported directly in the MSBuild terminal logger. You get more fully featured test reporting both _while_ tests are running (displays the running test name) and _after_ tests are completed (any test errors are rendered in a better way). For more information about the terminal logger, see [dotnet build options](../../tools/dotnet-build.md#options). ## .NET tool roll-forward -[.NET tools](../../tools/global-tools.md) are framework-dependent apps that you can install globally or locally, then run using the .NET SDK and installed .NET runtimes. These tools, like all .NET apps, target a specific major version of .NET. By default, apps don't run on *newer* versions of .NET. Tool authors have been able to opt in to running their tools on newer versions of the .NET runtime by setting the `RollForward` MSBuild property. However, not all tools do so. +[.NET tools](../../tools/global-tools.md) are framework-dependent apps that you can install globally or locally, then run using the .NET SDK and installed .NET runtimes. These tools, like all .NET apps, target a specific major version of .NET. By default, apps don't run on _newer_ versions of .NET. Tool authors have been able to opt in to running their tools on newer versions of the .NET runtime by setting the `RollForward` MSBuild property. However, not all tools do so. -A new option for [`dotnet tool install`](../../tools/dotnet-tool-install.md) lets *users* decide how .NET tools should be run. When you install a tool via `dotnet tool install`, or when you run tool via [`dotnet tool run `](../../tools/dotnet-tool-run.md), you can specify a new flag called `--allow-roll-forward`. This option configures the tool with roll-forward mode `Major`. This mode allows the tool to run on a newer major version of .NET if the matching .NET version is not available. This feature helps early adopters use .NET tools without tool authors having to change any code. +A new option for [`dotnet tool install`](../../tools/dotnet-tool-install.md) lets _users_ decide how .NET tools should be run. When you install a tool via `dotnet tool install`, or when you run tool via [`dotnet tool run `](../../tools/dotnet-tool-run.md), you can specify a new flag called `--allow-roll-forward`. This option configures the tool with roll-forward mode `Major`. This mode allows the tool to run on a newer major version of .NET if the matching .NET version is not available. This feature helps early adopters use .NET tools without tool authors having to change any code. ## Terminal logger @@ -77,7 +78,7 @@ Consider the following project file that emits a warning when the project is bui ``` -When you run `dotnet build -tl` on the .NET 8 SDK, the output is as shown following this paragraph. Each line of the multi-line warning is a separate line with a full error message prefix in the output, which is hard to read. Also, the final build summary says that there *were* warnings, but not *how many* there were. The missing information can make it hard to determine if a particular build is better or worse than previous builds. +When you run `dotnet build -tl` on the .NET 8 SDK, the output is as shown following this paragraph. Each line of the multi-line warning is a separate line with a full error message prefix in the output, which is hard to read. Also, the final build summary says that there _were_ warnings, but not _how many_ there were. The missing information can make it hard to determine if a particular build is better or worse than previous builds. ```terminal $ dotnet build -tl @@ -112,7 +113,7 @@ If you have feedback about the terminal logger, you can provide it in the [MSBui ## NuGet security audits -Starting in .NET 8, `dotnet restore` [audits NuGet package references for known vulnerabilities](../../tools/dotnet-restore.md#audit-for-security-vulnerabilities). In .NET 9, the default mode has changed from auditing only *direct* package references to auditing both *direct* and *transitive* package references. +Starting in .NET 8, `dotnet restore` [audits NuGet package references for known vulnerabilities](../../tools/dotnet-restore.md#audit-for-security-vulnerabilities). In .NET 9, the default mode has changed from auditing only _direct_ package references to auditing both _direct_ and _transitive_ package references. ## MSBuild script analyzers ("BuildChecks") @@ -128,7 +129,7 @@ For more information, see the [design documentation](https://github.com/dotnet/m Many users install the .NET SDK and Visual Studio at different cadences. While this flexibility is desirable, it can lead to problems for tooling that needs to interop between the two environments. One example of this kind of tooling is Roslyn Analyzers. Analyzer authors have to code for specific versions of Roslyn, but which versions are available and which is used by a given build is sometimes unclear. -This kind of version mismatch between the .NET SDK and MSBuild is referred to as a *torn SDK*. When you're in this state, you might see errors like this: +This kind of version mismatch between the .NET SDK and MSBuild is referred to as a _torn SDK_. When you're in this state, you might see errors like this: > CSC : warning CS9057: The analyzer assembly '..\dotnet\sdk\8.0.200\Sdks\Microsoft.NET.Sdk.Razor\source-generators\Microsoft.CodeAnalysis.Razor.Compiler.SourceGenerators.dll' references version '4.9.0.0' of the compiler, which is newer than the currently running version '4.8.0.0'. @@ -136,7 +137,7 @@ This kind of version mismatch between the .NET SDK and MSBuild is referred to as ## Workload sets -*Workload sets* is an SDK feature intended to give users more control over the workloads they install and the cadence of change of those workloads. In previous versions, workloads were periodically updated as new versions of individual workloads were released onto any configured NuGet feeds. Now, *all* of your workloads stay at a specific, single version until you make an explicit update gesture. +_Workload sets_ is an SDK feature intended to give users more control over the workloads they install and the cadence of change of those workloads. In previous versions, workloads were periodically updated as new versions of individual workloads were released onto any configured NuGet feeds. Now, _all_ of your workloads stay at a specific, single version until you make an explicit update gesture. You can see what mode your SDK installation is in by running `dotnet workload --info`: @@ -162,6 +163,23 @@ If you need to change back for any reason, you can run the same command with `ma For more information, see [.NET SDK workload sets](../../tools/dotnet-workload-sets.md). +## Workload history + +.NET SDK workloads are an integral part of .NET MAUI and Blazor WebAssembly. In their default configuration, you can update workloads independently as .NET tooling authors release new versions. In addition, .NET SDK installations done through Visual Studio install a parallel set of versions. Without taking care, the workload installation status of a given .NET SDK installation can drift over time, but there hasn't been a way to visualize this drift. + +To address this, .NET 9 adds a new `dotnet workload history` command to the .NET SDK. `dotnet workload history` prints out a table of the history of workload installations and modifications for the current .NET SDK installation. The table shows the date of the installation or modification, the command that was run, the workloads that were installed or modified, and the relevant versions for the command. This output can help you understand the drift in workload installations over time, and help you make informed decisions about which workloads versions to set your installation to. You can think of it as `git reflog` for workloads. + +```dotnetcli +> dotnet workload history + +Id Date Command Workloads Global.json Version Workload Version +----------------------------------------------------------------------------------------------------------------------------------------------- +1 1/1/0001 12:00:00 AM +00:00 InitialState android, ios, maccatalyst, maui-windows 9.0.100-manifests.6d3c8f5d +2 9/4/2024 8:15:33 PM -05:00 install android, aspire, ios, maccatalyst, maui-windows 9.0.100-rc.1.24453.3 +``` + +In this example, the SDK was initially installed with the `android`, `ios`, `maccatalyst`, and `maui-windows` workloads. Then, the `dotnet workload install aspire --version 9.0.100-rc.1.24453.3` command was used to install the `aspire` workload and switch to [workload sets mode](../../tools/dotnet-workload-sets.md). To return to the previous state, you can use the ID from the first column in the history table, for example, `dotnet workload update --from-history 1`. + ## Containers - [Publishing support for insecure registries](#publishing-support-for-insecure-registries) diff --git a/docs/core/whats-new/snippets/dotnet-9/csharp/Collections.cs b/docs/core/whats-new/snippets/dotnet-9/csharp/Collections.cs index ac49b9977b9fc..75769bf172996 100644 --- a/docs/core/whats-new/snippets/dotnet-9/csharp/Collections.cs +++ b/docs/core/whats-new/snippets/dotnet-9/csharp/Collections.cs @@ -62,7 +62,7 @@ private static Dictionary CountWords(ReadOnlySpan input) { Dictionary wordCounts = new(StringComparer.OrdinalIgnoreCase); Dictionary.AlternateLookup> spanLookup = - wordCounts.GetAlternateLookup>(); + wordCounts.GetAlternateLookup>(); foreach (Range wordRange in Regex.EnumerateSplits(input, @"\b\w+\b")) { diff --git a/docs/core/whats-new/snippets/dotnet-9/csharp/Compression.cs b/docs/core/whats-new/snippets/dotnet-9/csharp/Compression.cs new file mode 100644 index 0000000000000..cbaf231d1778b --- /dev/null +++ b/docs/core/whats-new/snippets/dotnet-9/csharp/Compression.cs @@ -0,0 +1,25 @@ +using System.IO; +using System.IO.Compression; + +namespace Project; +internal class Compression +{ + // + private MemoryStream CompressStream(Stream uncompressedStream) + { + MemoryStream compressorOutput = new(); + using ZLibStream compressionStream = new( + compressorOutput, + new ZLibCompressionOptions() + { + CompressionLevel = 6, + CompressionStrategy = ZLibCompressionStrategy.HuffmanOnly + } + ); + uncompressedStream.CopyTo(compressionStream); + compressionStream.Flush(); + + return compressorOutput; + } + // +} diff --git a/docs/core/whats-new/snippets/dotnet-9/csharp/Diagnostics.cs b/docs/core/whats-new/snippets/dotnet-9/csharp/Diagnostics.cs index af780d855e3b2..d6bab1b0370ab 100644 --- a/docs/core/whats-new/snippets/dotnet-9/csharp/Diagnostics.cs +++ b/docs/core/whats-new/snippets/dotnet-9/csharp/Diagnostics.cs @@ -1,5 +1,8 @@ -using System.Diagnostics; +using System; +using System.Collections.Generic; +using System.Diagnostics; using System.Diagnostics.Metrics; +using System.Diagnostics.Tracing; internal class Diagnostics { @@ -14,13 +17,64 @@ public static void RunIt() // // - Meter meter = new("MeasurementLibrary.Sound"); - Gauge gauge = meter.CreateGauge( + Meter soundMeter = new("MeasurementLibrary.Sound"); + Gauge gauge = soundMeter.CreateGauge( name: "NoiseLevel", unit: "dB", // Decibels. description: "Background Noise Level" ); - gauge.Record(10, new TagList() { { "Room1", "dB" } } ); + gauge.Record(10, new TagList() { { "Room1", "dB" } }); // + + // + // The complete meter name is "MyCompany.MyMeter". + var meter = new Meter("MyCompany.MyMeter"); + // Create a counter and allow publishing values. + meter.CreateObservableCounter("MyCounter", () => 1); + + // Create the listener to use the wildcard character + // to listen to all meters using prefix names. + MyEventListener listener = new MyEventListener(); + // + } +} + +// +internal class MyEventListener : EventListener +{ + protected override void OnEventSourceCreated(EventSource eventSource) + { + Console.WriteLine(eventSource.Name); + if (eventSource.Name == "System.Diagnostics.Metrics") + { + // Listen to all meters with names starting with "MyCompany". + // If using "*", allow listening to all meters. + EnableEvents( + eventSource, + EventLevel.Informational, + (EventKeywords)0x3, + new Dictionary() { { "Metrics", "MyCompany*" } } + ); + } + } + + protected override void OnEventWritten(EventWrittenEventArgs eventData) + { + // Ignore other events. + if (eventData.EventSource.Name != "System.Diagnostics.Metrics" || + eventData.EventName == "CollectionStart" || + eventData.EventName == "CollectionStop" || + eventData.EventName == "InstrumentPublished" + ) + return; + + Console.WriteLine(eventData.EventName); + + if (eventData.Payload is not null) + { + for (int i = 0; i < eventData.Payload.Count; i++) + Console.WriteLine($"\t{eventData.PayloadNames![i]}: {eventData.Payload[i]}"); + } } } +// diff --git a/docs/core/whats-new/snippets/dotnet-9/csharp/Networking.cs b/docs/core/whats-new/snippets/dotnet-9/csharp/Networking.cs index 1e242e48baa65..3b023e4294a27 100644 --- a/docs/core/whats-new/snippets/dotnet-9/csharp/Networking.cs +++ b/docs/core/whats-new/snippets/dotnet-9/csharp/Networking.cs @@ -1,10 +1,13 @@ using System; using System.IO; +using System.Net.Http; using System.Net.ServerSentEvents; +using System.Net.WebSockets; +using System.Threading; internal class Networking { - public async static void RunIt() + public static async void RunIt() { // Stream responseStream = new MemoryStream(); @@ -13,5 +16,18 @@ public async static void RunIt() Console.WriteLine(e.Data); } // + + var uri = new Uri("http://localhost:5000"); + var httpClient = new HttpClient(); + var cancellationToken = new CancellationToken(); + + // + using var cws = new ClientWebSocket(); + cws.Options.HttpVersionPolicy = HttpVersionPolicy.RequestVersionOrHigher; + cws.Options.KeepAliveInterval = TimeSpan.FromSeconds(5); + cws.Options.KeepAliveTimeout = TimeSpan.FromSeconds(1); + + await cws.ConnectAsync(uri, httpClient, cancellationToken); + // } } diff --git a/docs/core/whats-new/snippets/dotnet-9/csharp/Program.cs b/docs/core/whats-new/snippets/dotnet-9/csharp/Program.cs index 36e7048304053..7b9921056172e 100644 --- a/docs/core/whats-new/snippets/dotnet-9/csharp/Program.cs +++ b/docs/core/whats-new/snippets/dotnet-9/csharp/Program.cs @@ -5,4 +5,5 @@ //TimeSpan.RunIt(); //Channels.RunIt(); //RegularExpressions.RunIt(); -Collections.RunIt(); +//Collections.RunIt(); +Diagnostics.RunIt(); diff --git a/docs/core/whats-new/snippets/dotnet-9/csharp/Project.csproj b/docs/core/whats-new/snippets/dotnet-9/csharp/Project.csproj index ac1ab131ad26e..a22789e316a8c 100644 --- a/docs/core/whats-new/snippets/dotnet-9/csharp/Project.csproj +++ b/docs/core/whats-new/snippets/dotnet-9/csharp/Project.csproj @@ -8,6 +8,7 @@ + diff --git a/docs/core/whats-new/snippets/dotnet-9/csharp/Runtime.cs b/docs/core/whats-new/snippets/dotnet-9/csharp/Runtime.cs index 9da000f07457b..442fcdacdbc09 100644 --- a/docs/core/whats-new/snippets/dotnet-9/csharp/Runtime.cs +++ b/docs/core/whats-new/snippets/dotnet-9/csharp/Runtime.cs @@ -53,7 +53,7 @@ static void Advance(double dt, Body[] bodies) static byte Test1() { Vector128 v = Vector128.Zero; - byte size = 1; + const byte size = 1; v = Sse2.ShiftRightLogical128BitLane(v, size); return Sse41.Extract(v, 0); } diff --git a/docs/core/whats-new/snippets/dotnet-9/csharp/Serialization.cs b/docs/core/whats-new/snippets/dotnet-9/csharp/Serialization.cs index a9a411760588b..797a528132409 100644 --- a/docs/core/whats-new/snippets/dotnet-9/csharp/Serialization.cs +++ b/docs/core/whats-new/snippets/dotnet-9/csharp/Serialization.cs @@ -95,7 +95,7 @@ public static void RunIt3() // public class Book { - public string Title { get; set; } + public required string Title { get; set; } public string? Author { get; set; } public int PublishYear { get; set; } } diff --git a/docs/core/whats-new/snippets/dotnet-9/csharp/TarEntry.cs b/docs/core/whats-new/snippets/dotnet-9/csharp/TarEntry.cs new file mode 100644 index 0000000000000..bef37338bd5c1 --- /dev/null +++ b/docs/core/whats-new/snippets/dotnet-9/csharp/TarEntry.cs @@ -0,0 +1,43 @@ +using System.Formats.Tar; +using System.IO; +using System.Threading; +using System.Threading.Tasks; +using Azure.Storage.Blobs; + +namespace Project; +internal class TarEntry +{ + public static async Task RunItAsync() + { + Azure.Storage.Blobs.Models.BlobOpenReadOptions options = new(true); + CancellationToken cancellationToken = new(); + string connectionString = ""; + string blobContainerName = ""; + string blobName = ""; + + // + // Create stream for tar ball data in Azure Blob Storage. + BlobClient blobClient = new(connectionString, blobContainerName, blobName); + Stream blobClientStream = await blobClient.OpenReadAsync(options, cancellationToken); + + // Create TarReader for the stream and get a TarEntry. + TarReader tarReader = new(blobClientStream); + System.Formats.Tar.TarEntry? tarEntry = await tarReader.GetNextEntryAsync(); + + if (tarEntry is null) + return; + + // Get position of TarEntry data in blob stream. + long entryOffsetInBlobStream = tarEntry.DataOffset; + long entryLength = tarEntry.Length; + + // Create a separate stream. + Stream newBlobClientStream = await blobClient.OpenReadAsync(options, cancellationToken); + newBlobClientStream.Seek(entryOffsetInBlobStream, SeekOrigin.Begin); + + // Read tar ball content from separate BlobClient stream. + byte[] bytes = new byte[entryLength]; + await newBlobClientStream.ReadExactlyAsync(bytes, 0, (int)entryLength); + // + } +}