Skip to content
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 6 Preview 7 #6444

Closed
leecow opened this issue Jul 8, 2021 · 25 comments
Closed

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

leecow opened this issue Jul 8, 2021 · 25 comments
Milestone

Comments

@leecow
Copy link
Member

leecow commented Jul 8, 2021

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: #5853
Preview 2: #5889
Preview 3: #5890
Preview 4: #6098
Preview 5: #6099
Preview 6: #6325

@leecow leecow added this to the 6.0 milestone Jul 8, 2021
@JunTaoLuo
Copy link

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

Doc issue: dotnet/docs#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
Copy link
Member

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
Copy link
Member

tannergooding commented Jul 15, 2021

NativeMemory

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

@steveharter
Copy link
Member

steveharter commented Jul 21, 2021

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
Copy link
Member

@tannergooding should there be something about dotnet/runtime#55776 here? although it's a little special.

@tannergooding
Copy link
Member

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

@danmoseley
Copy link
Member

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

@tarekgh
Copy link
Member

tarekgh commented Jul 26, 2021

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
Copy link
Member

tarekgh commented Jul 26, 2021

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
Copy link
Member

When mentioning the improvement to Globalization Invariant Mode above it might be an opportunity to note the breaking change dotnet/docs#24849

@tarekgh
Copy link
Member

tarekgh commented Jul 26, 2021

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

@danmoseley
Copy link
Member

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

@janvorli
Copy link
Member

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
Copy link
Member

bartonjs commented Jul 28, 2021

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
Copy link
Member

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

As discussed in dotnet/runtime#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
Copy link
Member

.NET 6 C# project templates use latest language idioms

dotnet/templating#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:

  • Top-level statements
  • Global using directives (via SDK driven defaults)
  • File-scoped namespaces
  • Target-typed new expressions
  • Nullable reference types
  • async Main

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
Copy link
Contributor

buyaa-n commented Aug 3, 2021

Added APIs for exposing nullability information from reflection

Runtime Issue: dotnet/runtime#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
Copy link
Member

tannergooding commented Aug 5, 2021

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
Copy link

layomia commented Aug 6, 2021

System.Text.Json

Utf8JsonWriter "write raw" APIs (dotnet/runtime#1784)

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

  • I have a blob which I think represents JSON content and which I want to envelope, and I need to make sure the envelope & its inner contents remain well-formed
  • I have a deliberate sequence of bytes I want to write out on the wire, and I know what I'm doing

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 (dotnet/runtime#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
Copy link
Member

JulieLeeMSFT commented Aug 10, 2021

CodeGen

Community PRs

From @SingleAccretion:

Dynamic PGO dotnet/runtime#43618

LSRA dotnet/runtime#43318

Loop Optimization

Structs

Optimizations

@danmoseley
Copy link
Member

Gosh @SingleAccretion did a lot!

@filipnavara
Copy link
Member

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

@kunalspathak
Copy link
Member

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

@rbhanda rbhanda pinned this issue Aug 10, 2021
@JulieLeeMSFT
Copy link
Member

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

Thanks for catching it. Deleted the line.

@leecow leecow changed the title What's new in .NET 6 Preview 7 [WIP] What's new in .NET 6 Preview 7 Aug 11, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

16 participants