Closed leecow closed 1 year ago
https://github.com/dotnet/runtime/issues/20342
In .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 SHA3_256
, SHA3_384
, and SHA3_512
for hashing; HMACSHA3_256
, HMACSHA3_384
, and HMACSHA3_512
for HMAC; HashAlgorithmName.SHA3_256
, HashAlgorithmName.SHA3_384
, and HashAlgorithmName.SHA3_512
for hashing where the algorithm is configurable; and RSAEncryptionPadding.OaepSHA3_256
, RSAEncryptionPadding.OaepSHA3_384
, and RSAEncryptionPadding.OaepSHA3_512
for RSA OAEP encryption.
Using the SHA-3 APIs is similar to SHA-2, with the addition of an IsSupported
property to determine if the platform offers SHA-3.
// 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 Shake128
and Shake256
.
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, SignedXml
, and COSE. Support for SHA-3 may expand in the future depending on platform support and standards which adopt SHA-3.
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.
HybridGlobalization
mode on WASMWASM app can use a new globalization mode that brings lighter ICU bundle and leverages Web API instead. In Hybrid mode, globalization data is partially pulled from ICU bundle and partially from calls into JS. It serves all the locales supported by WASM.
HybridGlobalization
:This option is most suitable for applications that cannot work in InvariantGlobalization
mode and use localization data from more than one ICU shard (EFIGS, CJK, no-CJK) - so currently using either:
<BlazorWebAssemblyLoadAllGlobalizationData>true</BlazorWebAssemblyLoadAllGlobalizationData>
in Blazor WebAssembly or
<WasmIncludeFullIcuData>true</WasmIncludeFullIcuData>
in WASM Browser.
Apps that were loading no-CJK or CJK shard using the custom ICU file loading method:
<WasmIcuDataFileName>icudt_no_CJK.dat</WasmIcuDataFileName>
might also be interested, because hybrid file is smaller than no-CJK shard by 26% and smaller than CJK by 15%.
HybridGlobalization
:Set MsBuild property: <HybridGlobalization>true</HybridGlobalization>
. It will load icudt_hybrid.dat
file that is 46% smaller than originally loaded icudt.dat
.
Due to limitations of Web API, not all Globalization APIs are supported in Hybrid mode. Some of the supported APIs changed their behavior. To make sure your application is will not be affected, read the section Behavioral differences for WASM.
APIs that obtain the result by calling into JS have worse performance than the non-hybrid version. These APIs are listed in the documentation. The APIs that are not on the "Affected public APIs" lists perform the same as in non-hybrid mode.
The 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 [LoggerMessage(Message = "A record is being skipped: {recordId}")]
.
To address this issue, additional constructor overloads have been introduced, offering greater flexibility in specifying the required parameters with reduced code. The new constructor overloads include options such as specifying only the LogLevel and message, only the LogLevel, or only the message. These enhancements make it easier for users to define LoggerMessageAttributes while minimizing unnecessary code.
public LoggerMessageAttribute(LogLevel level, string message);
public LoggerMessageAttribute(LogLevel level);
public LoggerMessageAttribute(string message);
[LoggerMessage(Level = LogLevel.Warning, Message = "{p1} should be valid")]
public partial void LogWaraning(string p1);
https://github.com/dotnet/runtime/issues/87254
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.
In Preview 5, we introduced a class called InstrumentRecorder
, primarily designed as a helper class for test scenarios. However, in Preview 6, we made significant improvements to this class and relocated it to a different library called Microsoft.Extensions.Telemetry.Testing. Along with the relocation, we also renamed the class to MetricCollector.
These enhancements in Preview 6 now allow the MetricCollector
class to record metric measurements along with timestamps. Additionally, the class offers the flexibility to use any desired time provider for accurate timestamp generation.
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);
To ensure consistency with the other extension methods in IServiceCollection
, the namespace of the AddMetrics
method has been modified from Microsoft.Extensions.Diagnostics.Metrics
to Microsoft.Extensions.DependencyInjection
. This change allows for a more unified and streamlined approach when using the IServiceCollection
extension methods.
- namespace Microsoft.Extensions.Diagnostics.Metrics
+ namespace Microsoft.Extensions.DependencyInjection
{
public static class MetricsServiceExtensions
{
public static IServiceCollection AddMetrics(this IServiceCollection services)
}
}
To reduce startup overhead and improve validation feature set, we've introduced the source code generator that implements the validation logic.
public 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>();
In .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 LibraryImportAttribute
. In .NET 8 Preview 6, you can use the System.Runtime.InteropServices.Marshalling.GeneratedComInterfaceAttribute
to mark an interface as a COM interface for the source generator. The source generator will then generate code to enable calling from C# code to unmanaged code, as well as code to enable calling from unmanaged code into C#. This source generator integrates with LibraryImportAttribute
, and you can use types with the GeneratedComInterfaceAttribute
as parameters and return types in LibraryImportAttribute
-attributed methods.
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 System.Runtime.InteropServices.Marshalling.GeneratedComClassAttribute
to enable you to pass your types that implement interfaces with the System.Runtime.InteropServices.Marshalling.GeneratedComInterfaceAttribute
-attributed interfaces to unmanaged code. The source generator will generate the code necessary to expose a COM object that implements the interfaces and forwards calls to the managed implementation.
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 GeneratedComInterfaceAttribute
support all the same types as LibraryImportAttribute
, and LibraryImportAttribute
gains support for GeneratedComInterface
-attributed types and GeneratedComClass
-attributed types in this release.
If your C# code will only be use a GeneratedComInterfaceAttribute
-attributed interface to either wrap a COM object from unmanaged code or wrap a managed object from C# to expose to unmanaged code, you can use the options in the GeneratedComInterfaceAttribute.Options
property to customize which code will be generated. This option will enable you to not need to write marshallers for scenarios that you know will not be used.
The source generator uses the new System.Runtime.InteropServices.Marshalling.StrategyBasedComWrappers
type to create and manage the COM object wrappers and the managed object wrappers. This new type handles providing the expected .NET user experience for COM interop, while providing customization points for advanced users. If your application has its own mechanism for defining types from COM or if you need to support scenarios that source-generated COM does not currently support, you can consider using the new StrategyBasedComWrappers
type to add the missing features for your scenario and get the same .NET user experience for your COM types.
Like LibraryImportAttribute
, the COM source generator provides an easy onboarding experience through analyzers and code fixers. Next to each interface that has the System.Runtime.InteropServices.ComImportAttribute
, a lightbulb will offer an option to convert to source generated interop. This fix will change the interface to use the GeneratedComInterfaceAttribute
. Next to every class that implements an interface with the GeneratedComInterfaceAttribute
, a lightbulb will offer an option to add the GeneratedComClassAttribute
to the type. Once your types are converted, you can move your DllImport
methods to use LibraryImportAttribute
with the existing code fixer there. With these two lightbulbs, it is easy to convert your existing COM interop code to use the new source generated interop. There are also more analyzers to help catch places where you may be mixing source-generated and runtime-based COM interop that may require additional work.
Currently 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.
IDispatch
-based interfaces.
IDispatch
interface.IInspectable
-based interfaces.
StrategyBasedComWrappers
type and custom strategy implementations.new
keyword to activate a COM CoClass.
LibraryImportAttribute
to P/Invoke to the CoCreateInstance
API to activate the CoClass.We 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.
We added new overloads of ZipFile.CreateFromDirectory
that allow users to collect all the files included in a directory and zip them, then store the resulting zip file into the provided stream.
Symmetrically, we added the ZipFile.ExtractToDirectory
overloads that allow users to provide a stream containing a zipped file and extract its contents into the filesystem.
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:
CreateFromDirectory
does not have to write the zipped result into disk.ExtractToDirectory
does not require the zipped file to be located in disk.namespace 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) { }
}
Here 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);
In 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.
@huoyaoyuan made use of ECMA-335 III.4.6 to optimize type checks involving Nullable<T>
, PR#87241. @huoyaoyuan also made a fix for parameter ordering and value in gtNewColonNode
in PR#87366.
@jasper-d optimized new Vector3 { X = val1, Y = val2, Z = val3 }
pattern to emit the same codegen as Vector3 { val1,val2, val3}
by folding cost WithElement
to CNS_VEC
, PR#84543.
Vector3 Foo1() => new Vector3 { X = 1, Y = 2, Z = 3 };
Vector3 Foo2() => new Vector3(1, 2, 3);
@pedrobsaila fused numeric <
and ==
comparisons to <=
and >
and ==
to >=
and reduced the number of instructions in #PR78786.
@SingleAccretion has been working on internal JIT cleanup to greatly simplify the Internal Representation (IR). He removed GT_ASG
IR that we had for many years, and it shows about 1 ~ 1.5% of throughput improvements, PR#85871.
Since we enabled Vector512 and AVX512 support in Preview 4, our community members continued to add new features.
@DeepakRajendrakumaran accelerated Vector256/512.Shuffle() using AVX512 VBMI that improves performance by reducing data movement from memory to registers and rearranging data in the two memory addresses, PR#87083.
@Ruihan-Yin enabled EVEX feature that embeds broadcast and optimized Vector256.Add() when vector base type is TYP_FLOAT
, PR#84821.
cinv
) and Conditional Negate (cneg
) instead of Conditional Select(csel
) that reduced code size and speed on Arm64 platform, PR#84926. @SwapnilGaikwad also combined two vector table lookups into one on Arm64 that shows 10-50% of speed-up on microbenchmarks such as FormatterInt32
, PR#87126.
We changed register allocation to iterate over the registers of interest instead of over all the registers, which improved the JIT’s own throughput for various scenarios by up to 5% on Arm64 and around 2% on x64.
https://github.com/dotnet/runtime/issues/80905
In .NET 8 Preview 6
, we are shipping NativeAOT
with support for targeting iOS-like platforms. This includes building and running .NET iOS
and .NET MAUI
applications with NativeAOT
on: ios
, iossimulator
, maccatalyst
, tvos
or tvossimulator
systems.
The motivation of this work is to enable our customers to explore the possibility of achieving better performance and size savings when targeting such platforms with NativeAOT
.
This is available as an opt-in feature intended for app deployment, while Mono
is still used as the default runtime choice for app development and deployment.
This milestone has been reached with a great collaboration between our community members: @filipnavara @AustinWise and @am11 who contributed with their work, and joint effort of NativeAOT
, Mono
and Xamarin
teams.
The current state has been tested with:
.NET iOS app
(dotnet new ios
) .NET MAUI iOS app
(dotnet new maui
) These sample applications show the following preliminary results compared to Mono
:
.NET iOS app | Mono-p6 | NativeAOT-p6 | diff (%) |
---|---|---|---|
Size on disk (Mb) | 11,61 | 6,99 | -40% |
.ipa (Mb) | 4,37 | 2,69 | -39% |
.NET MAUI iOS app | Mono-p6 | NativeAOT-p6 | diff (%) | NativeAOT-fix | diff (%) |
---|---|---|---|---|---|
Size on disk (Mb) | 40,24 | 50,13 | 25% | 27,58 | -31,46% |
.ipa (Mb) | 14,68 | 16,59 | 13% | 10,23 | -30,32% |
The .NET 8 Preview 6
results (marked as *-p6
) show that the .NET iOS app
has significant improvements compared to Mono
where the compressed app bundle (.ipa) is up to ~39%
smaller showing great potential, while the .NET MAUI iOS app
shows worse results producing ~13%
larger output. However, we have identified the root cause of the size regression with the .NET MAUI
app and we are currently working on the following list of issues to address the size regression:
By fixing the identified issues 1-3) we have estimated that NativeAOT
can reach great results with the .NET MAUI
app, which is shown in the column NativeAOT-fix
, where the size of the application bundle is ~30%
smaller compared to Mono
. Fixing the issue 4) would potentially improve the performance even further, but at this stage we cannot estimate the exact numbers. More information about .NET MAUI
performance with NativeAOT
is being tracked in: https://github.com/dotnet/runtime/issues/80907
We would like to point out that conclusions regarding NativeAOT
performance on iOS-like platforms should not be drawn out from the presented numbers in the table nor from the .NET 8 Preview 6
release in general. Especially since this is still work-in-progress and only the first step towards making the feature ready for .NET 9
official release. Therefore, we are actively working on improvements and identifying all the work that will try to bring full NativeAOT
experience to our customers towards achieving great performance and size savings, which is being tracked in the following list of issues (and their subtasks):
Xamarin
integration improvementsdotnet workload install maui
dotnet new maui -n HelloMaui
The MSBuild properties PublishAot=true
and PublishAotUsingRuntimePack=true
(temporary, see below) enable NativeAOT
deployments.
These two properties are the only notable difference compared to when deploying with Mono
.
You need to add them in a PropertyGroup
of the project file of your application:
<PropertyGroup>
<PublishAot>true</PublishAot>
<PublishAotUsingRuntimePack>true</PublishAotUsingRuntimePack>
</PropertyGroup>
Which means that every time the app is deployed via dotnet publish
it will be deployed by using NativeAOT
.
dotnet publish -f net8.0-ios -c Release -r ios-arm64 /t:Run
Not 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):
NativeAOT
is only enabled during app deployment - dotnet publish
Linq.Expressions
library functionality is not fully supported yetPublishAotUsingRuntimePack=true
MSBuild property is a temporary workaround required for targeting iOS-like platforms with NativeAOT
Mono
NOTE: The previous list is an extension to the limitations applicable for all platforms with NativeAOT
: https://github.com/dotnet/runtime/blob/main/src/coreclr/nativeaot/docs/limitations.md
We 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.
https://github.com/dotnet/runtime/issues/31113
While HttpClient
supported various proxy types for a while, they all allows man-in-the-middle to see what site the client is connecting to. (even for HTTPS URIs) HTTPS proxy allows to create encrypted channel between client and the proxy so all the subsequent request can be handled with full privacy.
for Unix
export all_proxy=https://x.x.x.x:3218
and set all_proxy=https://x.x.x.x:3218
for Windows. This can be also controlled programmatically via WebProxy class.
Preview 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:
JsonNumberHandling
support https://github.com/dotnet/runtime/pull/87484JsonSerializerContext
declarations within arbitrary type kinds https://github.com/dotnet/runtime/pull/87829JsonStringEnumConverter<TEnum>
https://github.com/dotnet/runtime/pull/87224This new converter complements the existing JsonStringEnumConverter
class, which is not supported in Native AOT. Users wishing to target Native AOT users should annotate their enum types using the new converter like so:
[JsonConverter(typeof(JsonStringEnumConverter<MyEnum>))]
public enum MyEnum { Value1, Value2, Value3 }
[JsonSerializable(typeof(MyEnum))]
public partial class MyContext : JsonSerializerContext { }
JsonConverter.Type
https://github.com/dotnet/runtime/pull/87382The new property allows users to look up the type of a non-generic JsonConverter
instance:
Dictionary<Type, JsonConverter> CreateDictionary(IEnumerable<JsonConverter> converters)
=> converters.Where(converter => converter.Type != null).ToDictionary(converter => converter.Type!);
It should be noted that the property is nullable since it returns null
for JsonConverterFactory
instances and typeof(T)
for JsonConverter<T>
instances.
Over 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 ContainerUser
. We also changed the default container tag to be latest
- keeping in sync with other tooling in the containers space, and making it easier to use in your inner development loops.
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: https://github.com/dotnet/core/issues/8133 Preview 2: https://github.com/dotnet/core/issues/8134 Preview 3: https://github.com/dotnet/core/issues/8135 Preview 4: https://github.com/dotnet/core/issues/8234 Preview 5: https://github.com/dotnet/core/issues/8436 Preview 6: https://github.com/dotnet/core/issues/8437 Preview 7: https://github.com/dotnet/core/issues/8438 RC 1: https://github.com/dotnet/core/issues/8439 RC 2: https://github.com/dotnet/core/issues/8440