-
Notifications
You must be signed in to change notification settings - Fork 4.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
What's new in .NET 8 Preview 6 [WIP] #8437
Comments
System.Security: SHA-3 SupportIn .NET 8 Preview 6, support for the SHA-3 hashing primitives is now available on platforms that offer SHA-3. This is currently Linux with OpenSSL 1.1.1 or later, and Windows 11 Build 25324 or later. APIs where SHA-2 is available now offer a SHA-3 compliment. This includes Using the SHA-3 APIs is similar to SHA-2, with the addition of an // Hashing example
// Check if SHA-3-256 is supported on the current platform.
if (SHA3_256.IsSupported)
{
byte[] hash = SHA3_256.HashData(dataToHash);
}
else
{
// Determine application behavior if SHA-3 is not available.
}
// Signing Example
// Check if SHA-3-256 is supported on the current platform.
if (SHA3_256.IsSupported)
{
using ECDsa ec = ECDsa.Create(ECCurve.NamedCuves.nistP256);
byte[] signature = ec.SignData(dataToBeSigned, HashAlgorithmName.SHA3_256);
}
else
{
// Determine application behavior if SHA-3 is not available.
} Additionally, SHA-3 includes two extendable-output functions (XOFs), SHAKE128 and SHAKE256. These are available as if (Shake128.IsSupported)
{
using Shake128 shake = new Shake128();
shake.AppendData("Hello .NET!"u8);
byte[] digest = shake.GetHashAndReset(outputLength: 32);
// Also available as a one-shot:
digest = Shake128.HashData("Hello .NET!"u8, outputLength: 32);
}
else
{
// Determine application behavior if SHAKE is not available.
} SHA-3 support is currently aimed at supporting cryptographic primitives. Higher level constructions and protocols are not expected to fully support SHA-3 initially. This includes, but is not limited to, X.509 certificates, SHA-3 was standardized by NIST as FIPS 202 as an alternative to, not successor, to SHA-2. Developers and organizations should decide when or even if adopting SHA-3 is appropriate for them. |
|
Expanding LoggerMessageAttribute Constructor Overloads for Enhanced FunctionalityThe LoggerMessageAttribute previously offered only two constructors: public LoggerMessageAttribute()
public LoggerMessageAttribute(int eventide, LogLevel level, string message) a parameter-less constructor and another constructor that required specifying all the parameters (int eventide, LogLevel level, string message). This limitation meant that users had to either provide all the parameters, even if they didn't need them, using the second constructor, or use the parameter-less constructor and then supply the necessary parameters through attribute properties, such as public LoggerMessageAttribute(LogLevel level, string message);
public LoggerMessageAttribute(LogLevel level);
public LoggerMessageAttribute(string message); Example [LoggerMessage(Level = LogLevel.Warning, Message = "{p1} should be valid")]
public partial void LogWaraning(string p1); Note: In the upcoming preview, for constructors that do not require an event Id, the system will automatically generate the event Id, eliminating the need for users to manually provide it. |
Metric APIs changeInstrumentRecorderIn Preview 5, we introduced a class called Example const string CounterName = "MyCounter";
var now = DateTimeOffset.Now;
var timeProvider = new FakeTimeProvider(now);
using var meter = new Meter(Guid.NewGuid().ToString());
var counter = meter.CreateCounter<long>(CounterName);
using var collector = new MetricCollector<long>(counter, timeProvider);
Assert.Empty(collector.GetMeasurementSnapshot());
Assert.Null(collector.LastMeasurement);
counter. Add(3);
// verify the update was recorded
Assert.Equal(counter, collector.Instrument);
Assert.NotNull(collector.LastMeasurement);
Assert.Single(collector.GetMeasurementSnapshot());
Assert.Same(collector.GetMeasurementSnapshot().Last(), collector.LastMeasurement);
Assert.Equal(3, collector.LastMeasurement.Value);
Assert.Empty(collector.LastMeasurement.Tags);
Assert.Equal(now, collector.LastMeasurement.Timestamp); AddMetrics extension method namespaceTo ensure consistency with the other extension methods in - namespace Microsoft.Extensions.Diagnostics.Metrics
+ namespace Microsoft.Extensions.DependencyInjection
{
public static class MetricsServiceExtensions
{
public static IServiceCollection AddMetrics(this IServiceCollection services)
}
} |
Introducing the options validation source generatorTo reduce startup overhead and improve validation feature set, we've introduced the source code generator that implements the validation logic. Examplepublic class FirstModelNoNamespace
{
[Required]
[MinLength(5)]
public string P1 { get; set; } = string. Empty;
[Microsoft.Extensions.Options.ValidateObjectMembers(typeof(SecondValidatorNoNamespace))]
public SecondModelNoNamespace? P2 { get; set; }
[Microsoft.Extensions.Options.ValidateObjectMembers]
public ThirdModelNoNamespace? P3 { get; set; }
}
public class SecondModelNoNamespace
{
[Required]
[MinLength(5)]
public string P4 { get; set; } = string. Empty;
}
public class ThirdModelNoNamespace
{
[Required]
[MinLength(5)]
public string P5 { get; set; } = string.Empty;
}
[OptionsValidator]
public partial class FirstValidatorNoNamespace : IValidateOptions<FirstModelNoNamespace>
{
}
[OptionsValidator]
public partial class SecondValidatorNoNamespace : IValidateOptions<SecondModelNoNamespace>
{
} This will generate code like the following: partial class FirstValidatorNoNamespace
{
/// <summary>
/// Validates a specific named options instance (or all when <paramref name="name"/> is <see langword="null" />).
/// </summary>
/// <param name="name">The name of the options instance being validated.</param>
/// <param name="options">The options instance.</param>
/// <returns>Validation result.</returns>
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Options.SourceGeneration", "42.42.42.42")]
public global::Microsoft.Extensions.Options.ValidateOptionsResult Validate(string? name, global::FirstModelNoNamespace options)
{
var baseName = (string.IsNullOrEmpty(name) ? "FirstModelNoNamespace" : name) + ".";
var builder = new global::Microsoft.Extensions.Options.ValidateOptionsResultBuilder();
var context = new global::System.ComponentModel.DataAnnotations.ValidationContext(options);
var validationResults = new global::System.Collections.Generic.List<global::System.ComponentModel.DataAnnotations.ValidationResult>();
var validationAttributes = new global::System.Collections.Generic.List<global::System.ComponentModel.DataAnnotations.ValidationAttribute>(2);
context.MemberName = "P1";
context.DisplayName = baseName + "P1";
validationAttributes.Add(global::__OptionValidationStaticInstances.__Attributes.A1);
validationAttributes.Add(global::__OptionValidationStaticInstances.__Attributes.A2);
if (!global::System.ComponentModel.DataAnnotations.Validator.TryValidateValue(options.P1!, context, validationResults, validationAttributes))
{
builder.AddResults(validationResults);
}
if (options.P2 is not null)
{
builder.AddResult(global::__OptionValidationStaticInstances.__Validators.V1.Validate(baseName + "P2", options.P2));
}
if (options.P3 is not null)
{
builder.AddResult(global::__ThirdModelNoNamespaceValidator__.Validate(baseName + "P3", options.P3));
}
return builder. Build();
}
}
partial class SecondValidatorNoNamespace
{
/// <summary>
/// Validates a specific named options instance (or all when <paramref name="name"/> is <see langword="null" />).
/// </summary>
/// <param name="name">The name of the options instance being validated.</param>
/// <param name="options">The options instance.</param>
/// <returns>Validation result.</returns>
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Options.SourceGeneration", "42.42.42.42")]
public global::Microsoft.Extensions.Options.ValidateOptionsResult Validate(string? name, global::SecondModelNoNamespace options)
{
var baseName = (string.IsNullOrEmpty(name) ? "SecondModelNoNamespace" : name) + ".";
var builder = new global::Microsoft.Extensions.Options.ValidateOptionsResultBuilder();
var context = new global::System.ComponentModel.DataAnnotations.ValidationContext(options);
var validationResults = new global::System.Collections.Generic.List<global::System.ComponentModel.DataAnnotations.ValidationResult>();
var validationAttributes = new global::System.Collections.Generic.List<global::System.ComponentModel.DataAnnotations.ValidationAttribute>(2);
context.MemberName = "P4";
context.DisplayName = baseName + "P4";
validationAttributes.Add(global::__OptionValidationStaticInstances.__Attributes.A1);
validationAttributes.Add(global::__OptionValidationStaticInstances.__Attributes.A2);
if (!global::System.ComponentModel.DataAnnotations.Validator.TryValidateValue(options.P4!, context, validationResults, validationAttributes))
{
builder.AddResults(validationResults);
}
return builder. Build();
}
} If the app is using dependency injection, it can easily inject the validation there. var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllersWithViews();
builder.Services.Configure<FirstModelNoNamespace>(builder.Configuration.GetSection(...));
builder.Services.AddSingleton<IValidateOptions<FirstModelNoNamespace>, FirstValidatorNoNamespace>();
builder.Services.AddSingleton<IValidateOptions<SecondModelNoNamespace>, SecondValidatorNoNamespace>(); |
Interop: Source Generated COM InteropIn .NET 8 Preview 6, we have shipped a new source generator that supports interoperating with COM interfaces using the source generated interop story that we started with using System.Runtime.InteropServices;
using System.Runtime.InteropServices.Marshalling;
[GeneratedComInterface]
[Guid("5401c312-ab23-4dd3-aa40-3cb4b3a4683e")]
interface IComInterface
{
void DoWork();
}
internal class MyNativeLib
{
[LibraryImport(nameof(MyNativeLib))]
public static partial void GetComInterface(out IComInterface comInterface);
} The source generator also supports the new using System.Runtime.InteropServices;
using System.Runtime.InteropServices.Marshalling;
MyNativeLib.GetComInterface(out IComInterface comInterface);
comInterface.RegisterCallbacks(new MyCallbacks());
comInterface.DoWork();
[GeneratedComInterface]
[Guid("5401c312-ab23-4dd3-aa40-3cb4b3a4683e")]
interface IComInterface
{
void DoWork();
void RegisterCallbacks(ICallbacks callbacks);
}
[GeneratedComInterface]
[Guid("88470b56-aabc-46af-bf97-8106a1aa3cf9")]
interface ICallbacks
{
void Callback();
}
internal class MyNativeLib
{
[LibraryImport(nameof(MyNativeLib))]
public static partial void GetComInterface(out IComInterface comInterface);
}
[GeneratedComClass]
internal class MyCallbacks : ICallbacks
{
public void Callback()
{
Console.WriteLine("Callback called");
}
} Methods on interfaces with the If your C# code will only be use a The source generator uses the new How to UseLike LimitationsCurrently the COM source generator has the following limitations. We do not expect to address these limitations in .NET 8, but we may in a future version of .NET.
Usage in .NETWe have already moved the .NET support for distributed transactions through the System.Transactions library to use the new source-generated interop! We used this experience to help refine the analyzers and code-fixers to provide a good migration experience. |
Stream-based ZipFile CreateFromDirectory and ExtractToDirectory method overloadsWe added new overloads of Symmetrically, we added the These APIs can avoid having to use the disk as an intermediate step. This can be useful in scenarios where disk space is constrained, like for example, in cloud-based environments:
APIsnamespace System.IO.Compression;
public static partial class ZipFile
{
public static void CreateFromDirectory(string sourceDirectoryName, Stream destination);
public static void CreateFromDirectory(string sourceDirectoryName, Stream destination, CompressionLevel compressionLevel, bool includeBaseDirectory);
public static void CreateFromDirectory(string sourceDirectoryName, Stream destination, CompressionLevel compressionLevel, bool includeBaseDirectory, Encoding? entryNameEncoding);
public static void ExtractToDirectory(Stream source, string destinationDirectoryName) { }
public static void ExtractToDirectory(Stream source, string destinationDirectoryName, bool overwriteFiles) { }
public static void ExtractToDirectory(Stream source, string destinationDirectoryName, Encoding? entryNameEncoding) { }
public static void ExtractToDirectory(Stream source, string destinationDirectoryName, Encoding? entryNameEncoding, bool overwriteFiles) { }
} UsageHere are a couple of examples using the overloads that take all the arguments: Stream destinationStream = GetStreamFromSomewhere();
ZipFile.CreateFromDirectory(
sourceDirectoryName: "/home/username/sourcedirectory/",
destination: destinationStream,
compressionLevel: CompressionLevel.Optimal,
includeBaseDirectory: true,
entryNameEncoding: Encoding.UTF8); Stream sourceStream = GetStreamFromSomewhere();
ZipFile.ExtractToDirectory(
source: sourceStream,
destinationDirectoryName: "/home/username/destinationdirectory/",
entryNameEncoding: Encoding.UTF8,
overwriteFiles: true); |
Configuration binding source generator improvementsIn Preview 3, we introduced a new source generator to provide AOT and trim-friendly configuration in ASP.NET Core. The generator is an alternative to the pre-exising reflection-based implementation. When it was first shipped, community feedback revealed some important bugs that largely prevented usage. Since then, we've made several improvements and the generator is ready for folks to give it another go using Preview 6. An example app that uses configuration binding and is published with AOT goes from having two (2) AOT analysis warnings during compilation to having none. The app would fail when executed, but now it works. This sample adds configuration binding to the template SDK project for building minimal API apps for AOT. dotnet new api --aot For the upcoming Preview 7, we've made changes to improve size. The sample app drops by ~839.68 kB from 10.19 MB to 9.34 MB when published with AOT. No source code changes are needed to use the generator. It's enabled by default in AOT'd web apps. For other project types it is off by default, but you can control it by adding the following property to your project. <PropertyGroup>
<EnableConfigurationBindingGenerator>true</EnableConfigurationBindingGenerator>
</PropertyGroup> Please try the source generator in AOT apps that perform configuration, and report any issues at https://github.com/dotnet/runtime/issues. You can find known issues here. |
CodeGenCommunity PRs (Many thanks to JIT community contributors!)
Vector3 Foo1() => new Vector3 { X = 1, Y = 2, Z = 3 };
Vector3 Foo2() => new Vector3(1, 2, 3);
AVX-512Since we enabled Vector512 and AVX512 support in Preview 4, our community members continued to add new features.
Arm64
|
Support for targeting iOS-like platforms with NativeAOT as an opt-in featureIn This is available as an opt-in feature intended for app deployment, while Current stateThe current state has been tested with:
These sample applications show the following preliminary results compared to
The
By fixing the identified issues 1-3) we have estimated that We would like to point out that conclusions regarding
How to build and run a .NET MAUI application with NativeAOT on an iOS device with .NET CLIInstallationdotnet workload install maui Creating a sample applicationdotnet new maui -n HelloMaui Choosing NativeAOT over MonoThe MSBuild properties These two properties are the only notable difference compared to when deploying with <PropertyGroup>
<PublishAot>true</PublishAot>
<PublishAotUsingRuntimePack>true</PublishAotUsingRuntimePack>
</PropertyGroup> Which means that every time the app is deployed via Running your applicationdotnet publish -f net8.0-ios -c Release -r ios-arm64 /t:Run iOS and NativeAOT compatibilityNot all features in iOS are compatible with NativeAOT. Similarly, not all libraries commonly used in iOS are compatible with NativeAOT. .NET 8 represents the start of work to enable NativeAOT for iOS, your feedback will help guide our efforts during .NET 8 previews and beyond, to ensure we focus on the places where the benefits of NativeAOT can have the largest impact. The following list includes some limitations when targeting iOS-like platforms that have been encountered so far (and thus might not be the final list):
NOTE: The previous list is an extension to the limitations applicable for all platforms with Final noteWe would like to invite everyone to try out this new feature and file any discovered issues to help us improve the user experience further. Please note that Mono AOT which offers more compatibility with dynamic code is still going to be the primary deployment option and we’re not trying to remove dynamic code - just offer an option to people who don’t need dynamic code compatibility and don’t want the app size/memory/startup implications caused by it. |
Support for HTTPS proxyWhile How to Usefor Unix |
System.Text.Json improvementsPreview 6 sees the inclusion of a number of improvements and reliability fixes for the System.Text.Json source generator aimed for making the Native AOT experience on par with the reflection-based serializer:
|
SDK: Containers performance and compatibilityOver the course of the preview 6 time frame we invested in improving the performance of the Container pushes to remote registries, the reliability of the push operation, and support for a larger range of registries. At the same time, Tom Desyn of Red Hat was busy improving our support for a range of technologies prevalent in that ecosystem, like Podman, the Quay.io registry, and more. This version should see much improved performance for container pushes, especially to Azure registries. This is because of our improved support for pushing layers in one operation. In addition, for registries that don't support atomic uploads, we've implemented a more reliable and re-try-able chunking upload mechanism. As a side effect of these changes, we also expanded our support matrix for registries. Harbor and Artifactory join the list of known-working registries, and Tom's work enabled Quay.io and Podman pushes as well. We've also made a few changes to the generated image defaults for .NET 8 - we now default to using the new Rootless capability of the Microsoft .NET containers, helping your applications stay secure-by-default. You can change this at any time by setting your own |
What's new in .NET 8 Preview 6
This issue is for teams to highlight work for the community that will release in .NET 8 Preview 6
To add content, use a new conversation entry. The entry should include the team name and feature title as the first line shown in the template below.
Required
Optional
Below are three additional items to consider. These will help the .NET 8 blog team and the community throughout the release.
Index of .NET 8 releases
Preview 1: #8133
Preview 2: #8134
Preview 3: #8135
Preview 4: #8234
Preview 5: #8436
Preview 6: #8437
Preview 7: #8438
RC 1: #8439
RC 2: #8440
The text was updated successfully, but these errors were encountered: