dotnet / core

.NET news, announcements, release notes, and more!
https://dot.net
MIT License
20.72k stars 4.86k forks source link

What's new in .NET 6 Preview 7 #6444

Closed leecow closed 2 years ago

leecow commented 3 years ago

What's new in .NET 6 Preview 7

This issue is for teams to highlight work for the community that will release .NET 6 Preview 7.

To add content, use a new conversation entry. The entry should include the team name and feature title as the first line as shown in the template below.

## Team Name: Feature title

[link to the tracking issue or epic item for the work]

Tell the story of the feature and anything the community should pay particular attention 
to be successful using the feature.

Preview 1: https://github.com/dotnet/core/issues/5853 Preview 2: https://github.com/dotnet/core/issues/5889 Preview 3: https://github.com/dotnet/core/issues/5890 Preview 4: https://github.com/dotnet/core/issues/6098 Preview 5: https://github.com/dotnet/core/issues/6099 Preview 6: https://github.com/dotnet/core/issues/6325

JunTaoLuo commented 3 years ago

.NET SDK: C# Implicit Namespace Imports for C# Projects

Doc issue: https://github.com/dotnet/docs/issues/25066

We added support for implicit namespace imports for C# projects to reduce the amount of boilerplate in project templates by reducing the using statements needed in each C# file.

For example, an empty web app looked like this:

using System;
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.Hosting;

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

if (app.Environment.IsDevelopment())
{
    app.UseDeveloperExceptionPage();
}
app.MapGet("/", () => "Hello World!");
app.Run();

but now it's simplified to:

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

if (app.Environment.IsDevelopment())
{
    app.UseDeveloperExceptionPage();
}
app.MapGet("/", () => "Hello World!");
app.Run();
eerhardt commented 2 years ago

ZipFile Respects Unix File Permissions

We added support to System.IO.Compression.ZipFile to capture Unix file permissions during create and set the file permissions when extracting zip archives on Unix. This allows for executable files to be "round tripped" through a zip archive, and it means you no longer have to modify the file permissions to make files executable anymore after extracting a zip archive. It also respects the read/write permissions for user, group, and other as well.

If a zip archive doesn't contain file permissions (because it was created on Windows, or using a tool which didn't capture the permissions, like an earlier version of .NET) extracted files get the default file permissions, just like any other newly created file. This behavior didn't change.

The Unix file permissions work with other zip archive tools as well, including:

tannergooding commented 2 years ago

NativeMemory

We added new native memory allocation APIs exposed via System.Runtime.InteropServices.NativeMemory: https://github.com/dotnet/runtime/pull/54006. These APIs represent equivalents to the malloc, free, realloc, and calloc C APIs and also includes APIs for doing aligned allocations.

steveharter commented 2 years ago

Serialization notifications and property ordering for System.Text.Json

These features make it much easier to customize the behavior of JSON objects. Previously, this functionality required implementing a custom converter.

Notifications for (de)serialization are useful for defaulting values and validation. To use them, implement one or more of the interfaces IJsonOnDeserialized, IJsonOnDeserializing, IJsonOnSerialized or IJsonOnSerializing from the System.Text.Json.Serialization namespace.

Here's an example that validates during both JsonSerializer.Serialize() and JsonSerializer.Deserialize() to ensure a FirstName property is not null.

  public class Person : IJsonOnDeserialized, IJsonOnSerializing
  {
      public string FirstName{ get; set; }

      void IJsonOnDeserialized.OnDeserialized() => Validate(); // Call after deserialization
      void IJsonOnSerializing.OnSerializing() => Validate(); // Call before serialization

      private void Validate()
      {
          if (FirstName is null)
          {
              throw new InvalidOperationException("The 'FirstName' property cannot be 'null'.");
          }
      }
  }

The order properties are serialized in can now be controlled by applying the System.Text.Json.Serialization.JsonPropertyOrderAttribute with an integer specifying the order. Smaller numbers are serialized first; properties that have no attribute have a default ordering value of 0. Previously, the order was determined by reflection order which is not deterministic.

Here's an example that specifies JSON should be serialized in the order Id, City, FirstName, LastName:

public class Person
{
    public string City { get; set; } // No order defined (has the default ordering value of 0)

    [JsonPropertyOrder(1)] // Serialize after other properties that have default ordering
    public string FirstName { get; set; }

    [JsonPropertyOrder(2)] // Serialize after FirstName
    public string LastName { get; set; }

    [JsonPropertyOrder(-1)] // Serialize before other properties that have default ordering
    public int Id { get; set; }
}
danmoseley commented 2 years ago

@tannergooding should there be something about https://github.com/dotnet/runtime/pull/55776 here? although it's a little special.

tannergooding commented 2 years ago

Yes. I will be adding it and working on the related blog post when I get back from vacation.

danmoseley commented 2 years ago

Oops, didn't realize you were on vacation. 😺

tarekgh commented 2 years ago

Introducing System.Diagnostics Propagators

Developers will be able to control how distributed tracing information is propagated

Introduced DistributedContextPropagator abstract class which determines if and how distributed context information is encoded and decoded as it traverses the network. The encoding can be transported over any network protocol that supports string key-value pairs. For example, when using HTTP, each key value pair is an HTTP header. DistributedContextPropagator inject values into and extracts values from carriers as string key/value pairs.

DistributedContextPropagator propagator = DistributedContextPropagator.Current;
propagator.Inject(activity, carrier, (object theCarrier, string fieldName, string value) =>
{
   // Extract the context from the activity then inject it to the carrier.
});

Configure propagation with different propagator

// Set the current propagation behavior to not transmit any distributed context information in outbound network messages.
DistributedContextPropagator.Current = DistributedContextPropagator.CreateNoOutputPropagator();

CC @noahfalk @shirhatti

tarekgh commented 2 years ago

Full Case Mapping Support in Globalization Invariant Mode

Doc

.NET Core has been supporting the Globalization Invariant Mode for a while. This mode used to support only ASCII range characters case mapping in operations like String.ToUpper, String.ToLower, and strings comparing and searching with Ignore Casing option. Now the full Unicode characters set case mapping fully supported in the Globalization Invariant Mode.

danmoseley commented 2 years ago

When mentioning the improvement to Globalization Invariant Mode above it might be an opportunity to note the breaking change https://github.com/dotnet/docs/issues/24849

tarekgh commented 2 years ago

@danmoseley the doc I linked is the published breaking change doc.

danmoseley commented 2 years ago

@tarekgh ah my bad, I had forgotten they were both in the same breaking change doc.

janvorli commented 2 years ago

W^X (write xor execute) support for all platforms and architectures

The runtime now has a mode in which it doesn't create any memory pages that are writeable and executable at the same time. All executable memory is mapped as read-execute only. This was already supported on macOS ARM64 before preview 7, because it is not allowed to create memory mappings that are writeable and executable at the same time on that platform at all. Preview 7 adds that for all the remaining platform. On these platforms, executable code generation / modification is done via separate read-write memory mappings. This is true for both JITted code and runtime generated helpers. These mappings are created at virtual memory addresses that are different from the executable code address and exist only for a very brief period of time when the writing is performed. For example, JIT now generates code into a scratch buffer that is copied into the executable memory using a single memory copy function call after the whole method is jitted. And the writeable mapping lifetime spans only the time of the memory copy.

This new feature is disabled by default and it can be enabled by setting the environment variable DOTNET_EnableWriteXorExecute (or COMPlus_EnableWriteXorExecute) to 1. The reason is that there is a slightly worse startup performance (around 10% in the ASP.Net benchmark tests with R2R enabled) caused by this feature and we want to look into improving that (post .NET 6) before we enable it by default. However, the steady state performance was measured to be the same with and without the feature enabled. So for applications where the startup performance doesn't matter much, it might be interesting to try to enable it now.

bartonjs commented 2 years ago

Simplified call patterns for cryptographic operations

The .NET encryption and decryption routines were designed around streaming, with no real concession for when the payload is already in memory. The new Encrypt- and Decrypt- methods on SymmetricAlgorithm accelerate the already-in-memory case, and are intended to provide clarity to the caller and the code reviewer. Additionally, they support reading from and writing to spans.

Previous versions:

private static byte[] Decrypt(byte[] key, byte[] iv, byte[] ciphertext)
{
    using (Aes aes = Aes.Create())
    {
        aes.Key = key;
        aes.IV = iv;

        // These are the defaults, but let's set them anyways.
        aes.Padding = PaddingMode.PKCS7;
        aes.Mode = CipherMode.CBC;

        using (MemoryStream destination = new MemoryStream())
        using (ICryptoTransform transform = aes.CreateDecryptor())
        using (CryptoStream cryptoStream = new CryptoStream(destination, transform, CryptoStreamMode.Write))
        {
            cryptoStream.Write(ciphertext, 0, ciphertext.Length);
            cryptoStream.FlushFinalBlock();
            return destination.ToArray();
        }
    }
}

With the new simplified methods:

private static byte[] Decrypt(byte[] key, byte[] iv, byte[] ciphertext)
{
    using (Aes aes = Aes.Create())
    {
        aes.Key = key;

        return aes.DecryptCbc(ciphertext, iv);
    }
}

With the new Encrypt- and Decrypt-methods only the key property is used from the SymmetricAlgorithm instance. The new DecryptCbc method does support choosing the padding algorithm, but PKCS#7 is used with CBC so often that it's a default argument. If you like the clarity, just specify it:

private static byte[] Decrypt(byte[] key, byte[] iv, byte[] ciphertext)
{
    using (Aes aes = Aes.Create())
    {
        aes.Key = key;

        return aes.DecryptCbc(ciphertext, iv, PaddingMode.PKCS7);
    }
}
steveharter commented 2 years ago

System.Text.Json.Nodes.JsonNode support for dynamic is removed.

As discussed in https://github.com/dotnet/runtime/issues/53195, the "dynamic" feature in C# is considered somewhat stale and adding a dependency to a new JsonNode API (which was added in Preview 4) is not considered a good practice.

If a newer, more modern version of "dynamic" is introduced, this support will be reconsidered.

See also the breaking change issue .

Preview 4-6 behavior

The "dynamic" keyword could be used to get and set properties on the new JsonObject class such as:

dynamic obj = JsonNode.Parse("{\"A\":42}");
int i = (int)obj.A;

Preview 7 behavior

The property name must be specified as a string, and "dynamic" not used:

JsonNode obj = JsonNode.Parse("{\"A\":42}");
int i = (int)obj["A"];
vlada-shubina commented 2 years ago

.NET 6 C# project templates use latest language idioms

https://github.com/dotnet/templating/issues/3359

We updated .NET SDK templates to use latest C# language idioms when creating the project targeting .NET 6 framework. The following language features will be used or enabled by default in the SDK-included project templates as applicable:

console template prior to .NET 6

using System;

namespace Company.ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Hello, World!");
        }
    }
}

.NET 6 console template

// See https://aka.ms/new-console-template for more information
Console.WriteLine("Hello, World!");

The users may encounter difficulty if the following scenarios which used to work before: • Use a .NET 6 template and then change the target framework to previous version • Use a .NET 6 template and then add different TFM to multi-target

More information on this breaking change is available here.

buyaa-n commented 2 years ago

Added APIs for exposing nullability information from reflection

Runtime Issue: https://github.com/dotnet/runtime/issues/29723

API shape

namespace System.Reflection
{
    public sealed class NullabilityInfoContext
    {
        public NullabilityInfo Create(ParameterInfo parameterInfo);
        public NullabilityInfo Create(PropertyInfo propertyInfo);
        public NullabilityInfo Create(EventInfo eventInfo);
        public NullabilityInfo Create(FieldInfo parameterInfo);
    }

    public sealed class NullabilityInfo
    {
        public Type Type { get; }
        // ReadState and WriteState could be different in case the member has nullability attribute affecting them
        // For example for a non null property [MaybeNull] attribute makes ReadState  nullable
        public NullabilityState ReadState { get; } 
        // For example for a nulllable property [DisallowNull] attribute makes WriteState not null
        public NullabilityState WriteState { get; }
        // if the member is an array, then the nullability of the array elements null otherwise
        public NullabilityInfo? ElementType { get; }
       // if the member has generic argument(s) then nullability of each argument, else empty array
        public NullabilityInfo[] GenericTypeArguments { get; }
    }

    public enum NullabilityState
    {
        Unknown, // Nullability not enabled
        NotNull, // Not nullable value or reference type
        Nullable // Nulable value or reference type
    }
}

Usage

Getting top-level nullability information

private NullabilityInfoContext _nullabilityContext = new NullabilityInfoContext();

private void DeserializePropertyValue(PropertyInfo p, object instance, object? value)
{
    if (value == null)
    {
        var nullabilityInfo = _nullabilityContext.Create(p);
        if (nullabilityInfo.WriteState != NullabilityState.Nullable)
            throw new MySerializerException($"Property '{p.GetType().Name}.{p.Name}'' cannot be set to null.");
    }

    p.SetValue(instance, value);
}

Getting nested nullability information

class Data
{
    public string?[] ArrayField;
    public Tuple<string?, object> TupleField;
}
private void Print()
{
    Type type = typeof(Data);
    FieldInfo arrayField = type.GetField("ArrayField");
    FieldInfo tupleField = type.GetField("TupleField");

    NullabilityInfoContext context = new ();

    NullabilityInfo arrayInfo = context.Create(arrayField);
    Console.WriteLine(arrayInfo.ReadState );       // NotNull
    Console.WriteLine(arrayInfo.Element.State);    // Nullable

    NullabilityInfo tupleInfo = context.Create(tupleField);
    Console.WriteLine(tupleInfo.ReadState);                      // NotNull
    Console.WriteLine(tupleInfo.GenericTypeArguments [0].State); // Nullable
    Console.WriteLine(tupleInfo.GenericTypeArguments [1].State); // NotNull
}
tannergooding commented 2 years ago

Generic Math

We are previewing (as in they will not ship as "stable" with .NET 6) several new interfaces which enable operators to be used with generics:

public static TSelf Sum<TSelf>(IEnumerable<TSelf> values)
    where TSelf : INumber<TSelf>
{
    TSelf result = TSelf.Zero;

    foreach (var value in values)
    {
        result += value;
    }

    return result;
}

This is powered by a new feature which allows static abstract members to be declared in interfaces. This enables interfaces to expose operators and other static methods, such as Parse or Create, and for those to be implemented by a derived type. Please see our associated blog post for more details!

layomia commented 2 years ago

System.Text.Json

Utf8JsonWriter "write raw" APIs (https://github.com/dotnet/runtime/issues/1784)

There are scenarios where customers want to integrate existing, "raw" JSON payloads when writing new JSON payloads with Utf8JsonWriter.

Sample code:

JsonWriterOptions writerOptions = new() { WriteIndented = true, };

using MemoryStream ms = new();
using UtfJsonWriter writer = new(ms, writerOptions);

writer.WriteStartObject();
writer.WriteString("dataType", "CalculationResults");

writer.WriteStartArray("data");

foreach (CalculationResult result in results)
{
    writer.WriteStartObject();
    writer.WriteString("measurement", result.Measurement);

    writer.WritePropertyName("value");
    // Write raw JSON numeric value
    byte[] formattedValue = FormatNumberValue(result.Value);
    writer.WriteRawValue(formattedValue, skipValidation: true);

    writer.WriteEndObject();
}

writer.WriteEndArray();
writer.WriteEndObject();

New synchronous stream overloads on JsonSerializer (https://github.com/dotnet/runtime/issues/1574)

In many scenarios, users need to serialize and deserialize JSON data to/from a stream in a synchronous manner. We've added new APIs on JsonSerializer to support this. Overloads that take source-generated content are also provided.

using MemoryStream ms = GetMyStream();
MyPoco poco = JsonSerializer.Deserialize<MyPoco>(ms);
JulieLeeMSFT commented 2 years ago

CodeGen

Community PRs

From @SingleAccretion:

Dynamic PGO https://github.com/dotnet/runtime/issues/43618

LSRA https://github.com/dotnet/runtime/issues/43318

Loop Optimization

Structs

Optimizations

danmoseley commented 2 years ago

Gosh @SingleAccretion did a lot!

filipnavara commented 2 years ago

One of them is listed twice but still hell of a job!

kunalspathak commented 2 years ago

@EgorBo - Feel free to add some performance improvement graphs for dynamic PGO/inline changes.

JulieLeeMSFT commented 2 years ago

One of them is listed twice but still hell of a job!

Thanks for catching it. Deleted the line.