Breaking changes for migration from version 3.1 to 5.0

If you're migrating from version 3.1 of .NET Core, ASP.NET Core, or EF Core to version 5.0 of .NET, ASP.NET Core, or EF Core, the breaking changes listed in this article may affect your app.

ASP.NET Core

Authorization: Resource in endpoint routing is HttpContext

When using endpoint routing in ASP.NET Core 3.1, the resource used for authorization is the endpoint. This approach was insufficient for gaining access to the route data (RouteData). Previously in MVC, an HttpContext resource was passed in, which allows access to both the endpoint (Endpoint) and the route data. This change ensures that the resource passed to authorization is always the HttpContext.

Version introduced

5.0 Preview 7

Old behavior

When using endpoint routing and the authorization middleware (AuthorizationMiddleware) or [Authorize] attributes, the resource passed to authorization is the matching endpoint.

New behavior

Endpoint routing passes the HttpContext to authorization.

Reason for change

You can get to the endpoint from the HttpContext. However, there was no way to get from the endpoint to things like the route data. There was a loss in functionality from non-endpoint routing.

If your app uses the endpoint resource, call GetEndpoint on the HttpContext to continue accessing the endpoint.

In ASP.NET Core 5.0 Preview 8 and later, you can revert to the old behavior with SetSwitch. For example:

AppContext.SetSwitch(
    "Microsoft.AspNetCore.Authorization.SuppressUseHttpContextAsAuthorizationResource",
    isEnabled: true);

Category

ASP.NET Core

Affected APIs

None


Azure: Microsoft-prefixed Azure integration packages removed

The following Microsoft.* packages that provide integration between ASP.NET Core and Azure SDKs aren't included in ASP.NET Core 5.0:

For discussion on this issue, see dotnet/aspnetcore#19570.

Version introduced

5.0 Preview 1

Old behavior

The Microsoft.* packages integrated Azure services with the Configuration and Data Protection APIs.

New behavior

New Azure.* packages integrate Azure services with the Configuration and Data Protection APIs.

Reason for change

The change was made because the Microsoft.* packages were:

  • Using outdated versions of the Azure SDK. Simple updates weren't possible because the new versions of the Azure SDK included breaking changes.
  • Tied to the .NET Core release schedule. Transferring ownership of the packages to the Azure SDK team enables package updates as the Azure SDK is updated.

In ASP.NET Core 2.1 or later projects, replace the old Microsoft.* with the new Azure.* packages.

Old New
Microsoft.AspNetCore.DataProtection.AzureKeyVault Azure.Extensions.AspNetCore.DataProtection.Keys
Microsoft.AspNetCore.DataProtection.AzureStorage Azure.Extensions.AspNetCore.DataProtection.Blobs
Microsoft.Extensions.Configuration.AzureKeyVault Azure.Extensions.AspNetCore.Configuration.Secrets

The new packages use a new version of the Azure SDK that includes breaking changes. The general usage patterns are unchanged. Some overloads and options may differ to adapt to changes in the underlying Azure SDK APIs.

The old packages will:

  • Be supported by the ASP.NET Core team for the lifetime of .NET Core 2.1 and 3.1.
  • Not be included in .NET 5.

When upgrading your project to .NET 5, transition to the Azure.* packages to maintain support.

Category

ASP.NET Core

Affected APIs


Blazor: Insignificant whitespace trimmed from components at compile time

Starting with ASP.NET Core 5.0 Preview 7, the Razor compiler omits insignificant whitespace in Blazor components (.razor files) at compile time. For discussion, see issue dotnet/aspnetcore#23568.

Version introduced

5.0 Preview 7

Old behavior

In 3.x versions of Blazor Server and Blazor WebAssembly, whitespace is honored in a component's source code. Whitespace-only text nodes render in the browser's Document Object Model (DOM) even when there's no visual effect.

Consider the following Blazor component code:

<ul>
    @foreach (var item in Items)
    {
        <li>
            @item.Text
        </li>
    }
</ul>

The preceding example renders two whitespace nodes:

  • Outside of the @foreach code block.
  • Around the <li> element.
  • Around the @item.Text output.

A list containing 100 items results in 402 whitespace nodes. That's over half of all nodes rendered, even though none of the whitespace nodes visually affect the rendered output.

When rendering static HTML for components, whitespace inside a tag wasn't preserved. For example, view the source of the following component:

<foo        bar="baz"     />

Whitespace isn't preserved. The pre-rendered output is:

<foo bar="baz" />

New behavior

Unless the @preservewhitespace directive is used with value true, whitespace nodes are removed if they:

  • Are leading or trailing within an element.
  • Are leading or trailing within a RenderFragment parameter. For example, child content being passed to another component.
  • Precede or follow a C# code block such as @if and @foreach.

Reason for change

A goal for Blazor in ASP.NET Core 5.0 is to improve the performance of rendering and diffing. Insignificant whitespace tree nodes consumed up to 40 percent of the rendering time in benchmarks.

In most cases, the visual layout of the rendered component is unaffected. However, the whitespace removal might affect the rendered output when using a CSS rule like white-space: pre. To disable this performance optimization and preserve the whitespace, take one of the following actions:

  • Add the @preservewhitespace true directive at the top of the .razor file to apply the preference to a specific component.
  • Add the @preservewhitespace true directive inside an _Imports.razor file to apply the preference to an entire subdirectory or the entire project.

In most cases, no action is required, as applications will typically continue to behave normally (but faster). If the whitespace stripping causes any problems for a particular component, use @preservewhitespace true in that component to disable this optimization.

Category

ASP.NET Core

Affected APIs

None


Blazor: Target framework of NuGet packages changed

Blazor 3.2 WebAssembly projects were compiled to target .NET Standard 2.1 (<TargetFramework>netstandard2.1</TargetFramework>). In ASP.NET Core 5.0, both Blazor Server and Blazor WebAssembly projects target .NET 5.0 (<TargetFramework>net5.0</TargetFramework>). To better align with the target framework change, the following Blazor packages no longer target .NET Standard 2.1:

For discussion, see GitHub issue dotnet/aspnetcore#23424.

Version introduced

5.0 Preview 7

Old behavior

In Blazor 3.1 and 3.2, packages target .NET Standard 2.1 and .NET Core 3.1.

New behavior

In ASP.NET Core 5.0, packages target .NET 5.0.

Reason for change

The change was made to better align with .NET target framework requirements.

Blazor 3.2 WebAssembly projects should target .NET 5.0 as part of updating their package references to 5.x.x. Libraries that reference one of these packages can either target .NET 5.0 or multi-target depending on their requirements.

Category

ASP.NET Core

Affected APIs

None


Extensions: Package reference changes affecting some NuGet packages

With the migration of some Microsoft.Extensions.* NuGet packages from the dotnet/extensions repository to dotnet/runtime, as described in aspnet/Announcements#411, packaging changes are being applied to some of the migrated packages. For discussion on this issue, see dotnet/aspnetcore#21033.

Version introduced

5.0 Preview 4

Old behavior

Some Microsoft.Extensions.* packages included package references for APIs on which your app relied.

New behavior

Your app may have to add Microsoft.Extensions.* package dependencies.

Reason for change

Packaging policies were updated to better align with the dotnet/runtime repository. Under the new policy, unused package references are removed from .nupkg files during packaging.

Consumers of the affected packages should add a direct dependency on the removed package dependency in their project if APIs from removed package dependency are used. The following table lists the affected packages and the corresponding changes.

Package name Change description
Microsoft.Extensions.Configuration.Binder Removed reference to Microsoft.Extensions.Configuration
Microsoft.Extensions.Configuration.Json Removed reference to System.Threading.Tasks.Extensions
Microsoft.Extensions.Hosting.Abstractions Removed reference to Microsoft.Extensions.Logging.Abstractions
Microsoft.Extensions.Logging Removed reference to Microsoft.Extensions.Configuration.Binder
Microsoft.Extensions.Logging.Console Removed reference to Microsoft.Extensions.Configuration.Abstractions
Microsoft.Extensions.Logging.EventLog Removed reference to System.Diagnostics.EventLog for the .NET Framework 4.6.1 target framework moniker
Microsoft.Extensions.Logging.EventSource Removed reference to System.Threading.Tasks.Extensions
Microsoft.Extensions.Options Removed reference to System.ComponentModel.Annotations

For example, the package reference to Microsoft.Extensions.Configuration was removed from Microsoft.Extensions.Configuration.Binder. No API from the dependency was used in the package. Users of Microsoft.Extensions.Configuration.Binder who depend on APIs from Microsoft.Extensions.Configuration should add a direct reference to it in their project.

Category

ASP.NET Core

Affected APIs

None


HTTP: HttpClient instances created by IHttpClientFactory log integer status codes

HttpClient instances created by IHttpClientFactory log HTTP status codes as integers instead of with status code names.

Version introduced

5.0 Preview 1

Old behavior

Logging uses the textual descriptions of HTTP status codes. Consider the following log messages:

Received HTTP response after 56.0044ms - OK
End processing HTTP request after 70.0862ms - OK

New behavior

Logging uses the integer values of HTTP status codes. Consider the following log messages:

Received HTTP response after 56.0044ms - 200
End processing HTTP request after 70.0862ms - 200

Reason for change

The original behavior of this logging is inconsistent with other parts of ASP.NET Core that have always used integer values. The inconsistency makes logs difficult to query via structured logging systems such as Elasticsearch. For more context, see dotnet/extensions#1549.

Using integer values is more flexible than text because it allows queries on ranges of values.

Adding another log value to capture the integer status code was considered. Unfortunately, doing so would introduce another inconsistency with the rest of ASP.NET Core. HttpClient logging and HTTP server/hosting logging use the same StatusCode key name already.

The best option is to update logging queries to use the integer values of status codes. This option may cause some difficulty writing queries across multiple ASP.NET Core versions. However, using integers for this purpose is much more flexible for querying logs.

If you need to force compatibility with the old behavior and use textual status codes, replace the IHttpClientFactory logging with your own:

  1. Copy the .NET Core 3.1 versions of the following classes into your project:

  2. Rename the classes to avoid conflicts with public types in the Microsoft.Extensions.Http NuGet package.

  3. Replace the built-in implementation of LoggingHttpMessageHandlerBuilderFilter with your own in the project's Startup.ConfigureServices method. For example:

    public void ConfigureServices(IServiceCollection services)
    {
        // Other service registrations go first. Code omitted for brevity.
    
        // Place the following after all AddHttpClient registrations.
        var descriptors = services.Where(
            s => s.ServiceType == typeof(IHttpMessageHandlerBuilderFilter));
        foreach (var descriptor in descriptors)
        {
            services.Remove(descriptor);
        }
    
        services.AddSingleton<IHttpMessageHandlerBuilderFilter,
                              MyLoggingHttpMessageHandlerBuilderFilter>();
    }
    

Category

ASP.NET Core

Affected APIs

System.Net.Http.HttpClient


HTTP: Kestrel and IIS BadHttpRequestException types marked obsolete and replaced

Microsoft.AspNetCore.Server.Kestrel.BadHttpRequestException and Microsoft.AspNetCore.Server.IIS.BadHttpRequestException have been marked obsolete and changed to derive from Microsoft.AspNetCore.Http.BadHttpRequestException. The Kestrel and IIS servers still throw their old exception types for backwards compatibility. The obsolete types will be removed in a future release.

For discussion, see dotnet/aspnetcore#20614.

Version introduced

5.0 Preview 4

Old behavior

Microsoft.AspNetCore.Server.Kestrel.BadHttpRequestException and Microsoft.AspNetCore.Server.IIS.BadHttpRequestException derived from System.IO.IOException.

New behavior

Microsoft.AspNetCore.Server.Kestrel.BadHttpRequestException and Microsoft.AspNetCore.Server.IIS.BadHttpRequestException are obsolete. The types also derive from Microsoft.AspNetCore.Http.BadHttpRequestException, which derives from System.IO.IOException.

Reason for change

The change was made to:

  • Consolidate duplicate types.
  • Unify behavior across server implementations.

An app can now catch the base exception Microsoft.AspNetCore.Http.BadHttpRequestException when using either Kestrel or IIS.

Replace usages of Microsoft.AspNetCore.Server.Kestrel.BadHttpRequestException and Microsoft.AspNetCore.Server.IIS.BadHttpRequestException with Microsoft.AspNetCore.Http.BadHttpRequestException.

Category

ASP.NET Core

Affected APIs


HttpSys: Client certificate renegotiation disabled by default

The option to renegotiate a connection and request a client certificate has been disabled by default. For discussion, see issue dotnet/aspnetcore#23181.

Version introduced

ASP.NET Core 5.0

Old behavior

The connection can be renegotiated to request a client certificate.

New behavior

Client certificates can only be requested during the initial connection handshake. For more information, see pull request dotnet/aspnetcore#23162.

Reason for change

Renegotiation caused a number of performance and deadlock issues. It's also not supported in HTTP/2. For additional context from when the option to control this behavior was introduced in ASP.NET Core 3.1, see issue dotnet/aspnetcore#14806.

Apps that require client certificates should use netsh.exe to set the clientcertnegotiation option to enabled. For more information, see netsh http commands.

If you want client certificates enabled for only some parts of your app, see the guidance at Optional client certificates.

If you need the old renegotiate behavior, set HttpSysOptions.ClientCertificateMethod to the old value ClientCertificateMethod.AllowRenegotiate. This isn't recommended for the reasons outlined above and in the linked guidance.

Category

ASP.NET Core

Affected APIs


IIS: UrlRewrite middleware query strings are preserved

An IIS UrlRewrite middleware defect prevented the query string from being preserved in rewrite rules. That defect has been fixed to maintain consistency with the IIS UrlRewrite Module's behavior.

For discussion, see issue dotnet/aspnetcore#22972.

Version introduced

ASP.NET Core 5.0 Preview 7

Old behavior

Consider the following rewrite rule:

<rule name="MyRule" stopProcessing="true">
  <match url="^about" />
  <action type="Redirect" url="/contact" redirectType="Temporary" appendQueryString="true" />
</rule>

The preceding rule doesn't append the query string. A URI like /about?id=1 redirects to /contact instead of /contact?id=1. The appendQueryString attribute defaults to true as well.

New behavior

The query string is preserved. The URI from the example in Old behavior would be /contact?id=1.

Reason for change

The old behavior didn't match the IIS UrlRewrite Module's behavior. To support porting between the middleware and module, the goal is to maintain consistent behaviors.

If the behavior of removing the query string is preferred, set the action element to appendQueryString="false".

Category

ASP.NET Core

Affected APIs

IISUrlRewriteOptionsExtensions.AddIISUrlRewrite


Kestrel: Configuration changes at run time detected by default

Kestrel now reacts to changes made to the Kestrel section of the project's IConfiguration instance (for example, appsettings.json) at run time. To learn more about how to configure Kestrel using appsettings.json, see the appsettings.json example in Endpoint configuration.

Kestrel will bind, unbind, and rebind endpoints as necessary to react to these configuration changes.

For discussion, see issue dotnet/aspnetcore#22807.

Version introduced

5.0 Preview 7

Old behavior

Before ASP.NET Core 5.0 Preview 6, Kestrel didn't support changing configuration at run time.

In ASP.NET Core 5.0 Preview 6, you could opt into the now-default behavior of reacting to configuration changes at run time. Opting in required binding Kestrel's configuration manually:

using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Hosting;

public class Program
{
    public static void Main(string[] args) =>
        CreateHostBuilder(args).Build().Run();

    public static IHostBuilder CreateHostBuilder(string[] args) =>
        Host.CreateDefaultBuilder(args)
            .ConfigureWebHostDefaults(webBuilder =>
            {
                webBuilder.UseKestrel((builderContext, kestrelOptions) =>
                {
                    kestrelOptions.Configure(
                        builderContext.Configuration.GetSection("Kestrel"), reloadOnChange: true);
                });

                webBuilder.UseStartup<Startup>();
            });
}

New behavior

Kestrel reacts to configuration changes at run time by default. To support that change, ConfigureWebHostDefaults calls KestrelServerOptions.Configure(IConfiguration, bool) with reloadOnChange: true by default.

Reason for change

The change was made to support endpoint reconfiguration at run time without completely restarting the server. Unlike with a full server restart, unchanged endpoints aren't unbound even temporarily.

  • For most scenarios in which Kestrel's default configuration section doesn't change at run time, this change has no impact and no action is needed.

  • For scenarios in which Kestrel's default configuration section does change at run time and Kestrel should react to it, this is now the default behavior.

  • For scenarios in which Kestrel's default configuration section changes at run time and Kestrel shouldn't react to it, you can opt out as follows:

    using Microsoft.AspNetCore.Hosting;
    using Microsoft.Extensions.Hosting;
    
    public class Program
    {
        public static void Main(string[] args) =>
            CreateHostBuilder(args).Build().Run();
    
        public static IHostBuilder CreateHostBuilder(string[] args) =>
            Host.CreateDefaultBuilder(args)
                .ConfigureWebHostDefaults(webBuilder =>
                {
                    webBuilder.UseKestrel((builderContext, kestrelOptions) =>
                    {
                        kestrelOptions.Configure(
                            builderContext.Configuration.GetSection("Kestrel"), reloadOnChange: false);
                    });
    
                    webBuilder.UseStartup<Startup>();
                });
    }
    

Notes:

This change doesn't modify the behavior of the KestrelServerOptions.Configure(IConfiguration) overload, which still defaults to the reloadOnChange: false behavior.

It's also important to make sure the configuration source supports reloading. For JSON sources, reloading is configured by calling AddJsonFile(path, reloadOnChange: true). Reloading is already configured by default for appsettings.json and appsettings.{Environment}.json.

Category

ASP.NET Core

Affected APIs

GenericHostBuilderExtensions.ConfigureWebHostDefaults


Kestrel: Default supported TLS protocol versions changed

Kestrel now uses the system default TLS protocol versions rather than restricting connections to the TLS 1.1 and TLS 1.2 protocols like it did previously.

This change allows:

  • TLS 1.3 to be used by default in environments that support it.
  • TLS 1.0 to be used in some environments (such as Windows Server 2016 by default), which is usually not desirable.

For discussion, see issue dotnet/aspnetcore#22563.

Version introduced

5.0 Preview 6

Old behavior

Kestrel required that connections use TLS 1.1 or TLS 1.2 by default.

New behavior

Kestrel allows the operating system to choose the best protocol to use and to block insecure protocols. HttpsConnectionAdapterOptions.SslProtocols now defaults to SslProtocols.None instead of SslProtocols.Tls12 | SslProtocols.Tls11.

Reason for change

The change was made to support TLS 1.3 and future TLS versions by default as they become available.

Unless your app has a specific reason not to, you should use the new defaults. Verify your system is configured to allow only secure protocols.

To disable older protocols, take one of the following actions:

  • Disable older protocols, such as TLS 1.0, system-wide with the Windows instructions. It's currently enabled by default on all Windows versions.

  • Manually select which protocols you want to support in code as follows:

    using System.Security.Authentication;
    using Microsoft.AspNetCore.Hosting;
    using Microsoft.Extensions.Hosting;
    
    public class Program
    {
        public static void Main(string[] args) =>
            CreateHostBuilder(args).Build().Run();
    
        public static IHostBuilder CreateHostBuilder(string[] args) =>
            Host.CreateDefaultBuilder(args)
                .ConfigureWebHostDefaults(webBuilder =>
                {
                    webBuilder.UseKestrel(kestrelOptions =>
                    {
                        kestrelOptions.ConfigureHttpsDefaults(httpsOptions =>
                        {
                            httpsOptions.SslProtocols = SslProtocols.Tls12 | SslProtocols.Tls13;
                        });
                    });
    
                    webBuilder.UseStartup<Startup>();
                });
    }
    

Unfortunately, there's no API to exclude specific protocols.

Category

ASP.NET Core

Affected APIs

HttpsConnectionAdapterOptions.SslProtocols


Kestrel: HTTP/2 disabled over TLS on incompatible Windows versions

To enable HTTP/2 over Transport Layer Security (TLS) on Windows, two requirements need to be met:

  • Application-Layer Protocol Negotiation (ALPN) support, which is available starting with Windows 8.1 and Windows Server 2012 R2.
  • A set of ciphers compatible with HTTP/2, which is available starting with Windows 10 and Windows Server 2016.

As such, Kestrel's behavior when HTTP/2 over TLS is configured has changed to:

  • Downgrade to Http1 and log a message at the Information level when ListenOptions.HttpProtocols is set to Http1AndHttp2. Http1AndHttp2 is the default value for ListenOptions.HttpProtocols.
  • Throw a NotSupportedException when ListenOptions.HttpProtocols is set to Http2.

For discussion, see issue dotnet/aspnetcore#23068.

Version introduced

ASP.NET Core 5.0 Preview 7

Old behavior

The following table outlines the behavior when HTTP/2 over TLS is configured.

Protocols Windows 7,
Windows Server 2008 R2,
or earlier
Windows 8,
Windows Server 2012
Windows 8.1,
Windows Server 2012 R2
Windows 10,
Windows Server 2016,
or newer
Http2 Throw NotSupportedException Error during TLS handshake Error during TLS handshake * No error
Http1AndHttp2 Downgrade to Http1 Downgrade to Http1 Error during TLS handshake * No error

* Configure compatible cipher suites to enable these scenarios.

New behavior

The following table outlines the behavior when HTTP/2 over TLS is configured.

Protocols Windows 7,
Windows Server 2008 R2,
or earlier
Windows 8,
Windows Server 2012
Windows 8.1,
Windows Server 2012 R2
Windows 10,
Windows Server 2016,
or newer
Http2 Throw NotSupportedException Throw NotSupportedException Throw NotSupportedException ** No error
Http1AndHttp2 Downgrade to Http1 Downgrade to Http1 Downgrade to Http1 ** No error

** Configure compatible cipher suites and set the app context switch Microsoft.AspNetCore.Server.Kestrel.EnableWindows81Http2 to true to enable these scenarios.

Reason for change

This change ensures compatibility errors for HTTP/2 over TLS on older Windows versions are surfaced as early and as clearly as possible.

Ensure HTTP/2 over TLS is disabled on incompatible Windows versions. Windows 8.1 and Windows Server 2012 R2 are incompatible since they lack the necessary ciphers by default. However, it's possible to update the Computer Configuration settings to use HTTP/2 compatible ciphers. For more information, see TLS cipher suites in Windows 8.1. Once configured, HTTP/2 over TLS on Kestrel must be enabled by setting the app context switch Microsoft.AspNetCore.Server.Kestrel.EnableWindows81Http2. For example:

AppContext.SetSwitch("Microsoft.AspNetCore.Server.Kestrel.EnableWindows81Http2", true);

No underlying support has changed. For example, HTTP/2 over TLS has never worked on Windows 8 or Windows Server 2012. This change modifies how errors in these unsupported scenarios are presented.

Category

ASP.NET Core

Affected APIs

None


Kestrel: Libuv transport marked as obsolete

Earlier versions of ASP.NET Core used Libuv as an implementation detail of how asynchronous input and output was performed. In ASP.NET Core 2.0, an alternative, Socket-based transport was developed. In ASP.NET Core 2.1, Kestrel switched to using the Socket-based transport by default. Libuv support was maintained for compatibility reasons.

At this point, use of the Socket-based transport is far more common than the Libuv transport. Consequently, Libuv support is marked as obsolete in .NET 5.0 and will be removed entirely in .NET 6.0.

As part of this change, Libuv support for new operating system platforms (like Windows ARM64) won't be added in the .NET 5.0 timeframe.

For discussion on blocking issues that require the use of the Libuv transport, see the GitHub issue at dotnet/aspnetcore#23409.

Version introduced

5.0 Preview 8

Old behavior

The Libuv APIs aren't marked as obsolete.

New behavior

The Libuv APIs are marked as obsolete.

Reason for change

The Socket-based transport is the default. There aren't any compelling reasons to continue using the Libuv transport.

Discontinue use of the Libuv package and extension methods.

Category

ASP.NET Core

Affected APIs


Localization: "Pubternal" APIs removed

To better maintain the public API surface of ASP.NET Core, some "pubternal" localization APIs were removed. A "pubternal" API has a public access modifier and is defined in a namespace that implies an internal intent.

For discussion, see dotnet/aspnetcore#22291.

Version introduced

5.0 Preview 6

Old behavior

The following APIs were public:

  • Microsoft.Extensions.Localization.Internal.AssemblyWrapper
  • Microsoft.Extensions.Localization.Internal.IResourceStringProvider
  • Microsoft.Extensions.Localization.ResourceManagerStringLocalizer constructor overloads accepting either of the following parameter types:
    • AssemblyWrapper
    • IResourceStringProvider

New behavior

The following list outlines the changes:

  • Microsoft.Extensions.Localization.Internal.AssemblyWrapper became Microsoft.Extensions.Localization.AssemblyWrapper and is now internal.
  • Microsoft.Extensions.Localization.Internal.IResourceStringProvider became Microsoft.Extensions.Localization.Internal.IResourceStringProvider and is now internal.
  • Microsoft.Extensions.Localization.ResourceManagerStringLocalizer constructor overloads accepting either of the following parameter types are now internal:
    • AssemblyWrapper
    • IResourceStringProvider

Reason for change

Explained more thoroughly at aspnet/Announcements#377, "pubternal" types were removed from the public API surface. These changes adapt more classes to that design decision. The classes in question were intended as extension points for the team's internal testing.

Although it's unlikely, some apps may intentionally or accidentally depend upon the "pubternal" types. See the New behavior sections to determine how to migrate away from the types.

If you've identified a scenario which the public API allowed before this change but doesn't now, file an issue at dotnet/aspnetcore.

Category

ASP.NET Core

Affected APIs


Localization: Obsolete constructor removed in request localization middleware

The RequestLocalizationMiddleware constructor that lacks an ILoggerFactory parameter was marked as obsolete in this commit. In ASP.NET Core 5.0, the obsolete constructor was removed. For discussion, see dotnet/aspnetcore#23785.

Version introduced

5.0 Preview 8

Old behavior

The obsolete RequestLocalizationMiddleware.ctor(RequestDelegate, IOptions<RequestLocalizationOptions>) constructor exists.

New behavior

The obsolete RequestLocalizationMiddleware.ctor(RequestDelegate, IOptions<RequestLocalizationOptions>) constructor doesn't exist.

Reason for change

This change ensures that the request localization middleware always has access to a logger.

When manually constructing an instance of RequestLocalizationMiddleware, pass an ILoggerFactory instance in the constructor. If a valid ILoggerFactory instance isn't available in that context, consider passing the middleware constructor a NullLoggerFactory instance.

Category

ASP.NET Core

Affected APIs

RequestLocalizationMiddleware.ctor(RequestDelegate, IOptions<RequestLocalizationOptions>)


Localization: ResourceManagerWithCultureStringLocalizer class and WithCulture interface member removed

The ResourceManagerWithCultureStringLocalizer class and WithCulture method were removed in .NET 5.0 Preview 1.

For context, see aspnet/Announcements#346 and dotnet/aspnetcore#3324. For discussion on this change, see dotnet/aspnetcore#7756.

Version introduced

5.0 Preview 1

Old behavior

The ResourceManagerWithCultureStringLocalizer class and the ResourceManagerStringLocalizer.WithCulture method are obsolete in .NET Core 3.0 Preview 3 and later.

New behavior

The ResourceManagerWithCultureStringLocalizer class and the ResourceManagerStringLocalizer.WithCulture method have been removed in .NET 5.0 Preview 1. For an inventory of the changes made, see the pull request at dotnet/extensions#2562.

Reason for change

The ResourceManagerWithCultureStringLocalizer class and ResourceManagerStringLocalizer.WithCulture method were often sources of confusion for users of localization. The confusion was especially high when creating a custom IStringLocalizer implementation. This class and method give consumers the impression that an IStringLocalizer instance is expected to be "per-language, per-resource". In reality, the instance should only be "per-resource". At run time, the CultureInfo.CurrentUICulture property determines the language to be used.

Stop using the ResourceManagerWithCultureStringLocalizer class and the ResourceManagerStringLocalizer.WithCulture method.

Category

ASP.NET Core

Affected APIs


The HTTP cookie standard allows only specific characters in cookie names and values. To support disallowed characters, ASP.NET Core:

  • Encodes when creating a response cookie.
  • Decodes when reading a request cookie.

In ASP.NET Core 5.0, this encoding behavior changed in response to a security concern.

For discussion, see GitHub issue dotnet/aspnetcore#23578.

Version introduced

5.0 Preview 8

Old behavior

Response cookie names are encoded. Request cookie names are decoded.

New behavior

Encoding and decoding of cookie names was removed. For prior supported versions of ASP.NET Core, the team plans to mitigate the decoding issue in-place. Additionally, calling IResponseCookies.Append with an invalid cookie name throws an exception of type ArgumentException. Encoding and decoding of cookie values remains unchanged.

Reason for change

An issue was discovered in multiple web frameworks. The encoding and decoding could allow an attacker to bypass a security feature called cookie prefixes by spoofing reserved prefixes like __Host- with encoded values like __%48ost-. The attack requires a secondary exploit to inject the spoofed cookies, such as a cross-site scripting (XSS) vulnerability, in the website. These prefixes aren't used by default in ASP.NET Core or Microsoft.Owin libraries or templates.

If you're moving projects to ASP.NET Core 5.0 or later, ensure that their cookie names conform to the token specification requirements: ASCII characters excluding controls and separators "(" | ")" | "<" | ">" | "@" | "," | ";" | ":" | "\" | <"> | "/" | "[" | "]" | "?" | "=" | "{" | "}" | SP | HT. The use of non-ASCII characters in cookie names or other HTTP headers may cause an exception from the server or be improperly round-tripped by the client.

Category

ASP.NET Core

Affected APIs


Security: IdentityModel NuGet package versions updated

The following packages were updated to version 6.6.0:

Version introduced

5.0 Preview 7

Old behavior

The package version used is 5.5.0.

New behavior

For details about changes between package versions, see the 6.6.0 release notes.

Reason for change

The packages were updated to take advantage of improvements in the underlying libraries.

The package updates don't introduce public API changes to ASP.NET Core. However, it's possible there are breaking changes in the packages themselves.

Category

ASP.NET Core

Affected APIs

None


SignalR: MessagePack Hub Protocol moved to MessagePack 2.x package

The ASP.NET Core SignalR MessagePack Hub Protocol uses the MessagePack NuGet package for MessagePack serialization. ASP.NET Core 5.0 upgrades the package from 1.x to the latest 2.x package version.

For discussion on this issue, see dotnet/aspnetcore#18692.

Version introduced

5.0 Preview 1

Old behavior

ASP.NET Core SignalR used the MessagePack 1.x package to serialize and deserialize MessagePack messages.

New behavior

ASP.NET Core SignalR uses the MessagePack 2.x package to serialize and deserialize MessagePack messages.

Reason for change

The latest improvements in the MessagePack 2.x package add useful functionality.

This breaking change applies when:

  • Setting or configuring values on MessagePackHubProtocolOptions.
  • Using the MessagePack APIs directly and using the ASP.NET Core SignalR MessagePack Hub Protocol in the same project. The newer version will be loaded instead of the previous version.

For migration guidance from the package authors, see Migrating from MessagePack v1.x to MessagePack v2.x. Some aspects of message serialization and deserialization are affected. Specifically, there are behavioral changes to how DateTime values are serialized.

Category

ASP.NET Core

Affected APIs

Microsoft.AspNetCore.SignalR.MessagePackHubProtocolOptions


SignalR: MessagePack Hub Protocol options type changed

The ASP.NET Core SignalR MessagePack Hub Protocol options type has changed from IList<MessagePack.IFormatterResolver> to the MessagePack library's MessagePackSerializerOptions type.

For discussion on this change, see dotnet/aspnetcore#20506.

Version introduced

5.0 Preview 4

Old behavior

You can add to the options as shown in the following example:

services.AddSignalR()
    .AddMessagePackProtocol(options =>
    {
        options.FormatterResolvers.Add(MessagePack.Resolvers.StandardResolver.Instance);
    });

And replace the options as follows:

services.AddSignalR()
    .AddMessagePackProtocol(options =>
    {
        options.FormatterResolvers = new List<MessagePack.IFormatterResolver>()
        {
            MessagePack.Resolvers.StandardResolver.Instance
        };
    });

New behavior

You can add to the options as shown in the following example:

services.AddSignalR()
    .AddMessagePackProtocol(options =>
    {
        options.SerializerOptions =
            options.SerializeOptions.WithResolver(MessagePack.Resolvers.StandardResolver.Instance);
    });

And replace the options as follows:

services.AddSignalR()
    .AddMessagePackProtocol(options =>
    {
        options.SerializerOptions = MessagePackSerializerOptions
                .Standard
                .WithResolver(MessagePack.Resolvers.StandardResolver.Instance)
                .WithSecurity(MessagePackSecurity.UntrustedData);
    });

Reason for change

This change is part of moving to MessagePack v2.x, which was announced in aspnet/Announcements#404. The v2.x library has added an options API that's easier to use and provides more features than the list of MessagePack.IFormatterResolver that was exposed before.

This breaking change affects anyone who is configuring values on MessagePackHubProtocolOptions. If you're using the ASP.NET Core SignalR MessagePack Hub Protocol and modifying the options, update your usage to use the new options API as shown above.

Category

ASP.NET Core

Affected APIs

Microsoft.AspNetCore.SignalR.MessagePackHubProtocolOptions


SignalR: UseSignalR and UseConnections methods removed

In ASP.NET Core 3.0, SignalR adopted endpoint routing. As part of that change, the UseSignalR, UseConnections, and some related methods were marked as obsolete. In ASP.NET Core 5.0, those obsolete methods were removed. For the full list of methods, see Affected APIs.

For discussion on this issue, see dotnet/aspnetcore#20082.

Version introduced

5.0 Preview 3

Old behavior

SignalR hubs and connection handlers could be registered in the middleware pipeline using the UseSignalR or UseConnections methods.

New behavior

SignalR hubs and connection handlers should be registered within UseEndpoints using the MapHub and MapConnectionHandler extension methods on IEndpointRouteBuilder.

Reason for change

The old methods had custom routing logic that didn't interact with other routing components in ASP.NET Core. In ASP.NET Core 3.0, a new general-purpose routing system, called endpoint routing, was introduced. Endpoint routing enabled SignalR to interact with other routing components. Switching to this model allows users to realize the full benefits of endpoint routing. Consequently, the old methods have been removed.

Remove code that calls UseSignalR or UseConnections from your project's Startup.Configure method. Replace it with calls to MapHub or MapConnectionHandler, respectively, within the body of a call to UseEndpoints. For example:

Old code:

app.UseSignalR(routes =>
{
    routes.MapHub<SomeHub>("/path");
});

New code:

app.UseEndpoints(endpoints =>
{
    endpoints.MapHub<SomeHub>("/path");
});

In general, your previous MapHub and MapConnectionHandler calls can be transferred directly from the body of UseSignalR and UseConnections to UseEndpoints with little-to-no change needed.

Category

ASP.NET Core

Affected APIs


Static files: CSV content type changed to standards-compliant

In ASP.NET Core 5.0, the default Content-Type response header value that the Static File Middleware uses for .csv files has changed to the standards-compliant value text/csv.

For discussion on this issue, see dotnet/aspnetcore#17385.

Version introduced

5.0 Preview 1

Old behavior

The Content-Type header value application/octet-stream was used.

New behavior

The Content-Type header value text/csv is used.

Reason for change

Compliance with the RFC 7111 standard.

If this change impacts your app, you can customize the file extension-to-MIME type mapping. To revert to the application/octet-stream MIME type, modify the UseStaticFiles method call in Startup.Configure. For example:

var provider = new FileExtensionContentTypeProvider();
provider.Mappings[".csv"] = MediaTypeNames.Application.Octet;

app.UseStaticFiles(new StaticFileOptions
{
    ContentTypeProvider = provider
});

For more information on customizing the mapping, see FileExtensionContentTypeProvider.

Category

ASP.NET Core

Affected APIs

Microsoft.AspNetCore.StaticFiles.FileExtensionContentTypeProvider


Core .NET libraries

Complexity of LINQ OrderBy.First{OrDefault} increased

The implementation of OrderBy.First<TSource>(IEnumerable<TSource>, Func<TSource,Boolean>) and OrderBy.FirstOrDefault<TSource>(IEnumerable<TSource>, Func<TSource,Boolean>) has changed, resulting in increased complexity for the operation.

Change description

In .NET Core 1.x - 3.x, calling OrderBy or OrderByDescending followed by First<TSource>(IEnumerable<TSource>, Func<TSource,Boolean>) or FirstOrDefault<TSource>(IEnumerable<TSource>, Func<TSource,Boolean>) operates with O(N) complexity. Since only the first (or default) element is required, only one enumeration is required to find it. However, the predicate that's supplied to First<TSource>(IEnumerable<TSource>, Func<TSource,Boolean>) or FirstOrDefault<TSource>(IEnumerable<TSource>, Func<TSource,Boolean>) is invoked exactly N times, where N is the length of the sequence.

In .NET 5.0 and later versions, a change was made such that calling OrderBy or OrderByDescending followed by First<TSource>(IEnumerable<TSource>, Func<TSource,Boolean>) or FirstOrDefault<TSource>(IEnumerable<TSource>, Func<TSource,Boolean>) operates with O(N log N) complexity instead of O(N) complexity. However, the predicate that's supplied to First<TSource>(IEnumerable<TSource>, Func<TSource,Boolean>) or FirstOrDefault<TSource>(IEnumerable<TSource>, Func<TSource,Boolean>) may be invoked less than N times, which is more important for overall performance.

Note

This change matches the implementation and complexity of the operation in .NET Framework.

Reason for change

The benefit of invoking the predicate fewer times outweighs a lower overall complexity, so the implementation that was introduced in .NET Core 1.0 was reverted. For more information, see this dotnet/runtime issue.

Version introduced

5.0

No action is required on the developer's part.

Category

Core .NET libraries

Affected APIs


IntPtr and UIntPtr implement IFormattable

IntPtr and UIntPtr now implement IFormattable. Functions that check for IFormattable support may now return different results for these types, because they may pass in a format specifier and a culture.

Change description

In previous versions of .NET, IntPtr and UIntPtr do not implement IFormattable. Functions that check for IFormattable may fall back to just calling IntPtr.ToString or UIntPtr.ToString, which means that format specifiers and cultures are not respected.

In .NET 5.0 and later versions, IntPtr and UIntPtr implement IFormattable. Functions that check for IFormattable support may now return different results for these types, because they may pass in a format specifier and a culture.

This change impacts scenarios like interpolated strings and Console.WriteLine, among others.

Reason for change

IntPtr and UIntPtr now have language support in C# through the nint and nuint keywords. The backing types were updated to provide near parity (where possible) with functionality exposed by other primitive types, such as System.Int32.

Version introduced

5.0 Preview 4

If you don't want a format specifier or custom culture to be used when displaying values of these types, you can call the IntPtr.ToString() and UIntPtr.ToString() overloads of ToString().

Category

Core .NET libraries

Affected APIs

Not detectable via API analysis.


PrincipalPermissionAttribute is obsolete as error

The PrincipalPermissionAttribute constructor is obsolete and produces a compile-time error. You cannot instantiate this attribute or apply it to a method.

Change description

On .NET Framework and .NET Core, you can annotate methods with the PrincipalPermissionAttribute attribute. For example:

[PrincipalPermission(SecurityAction.Demand, Role = "Administrators")]
public void MyMethod()
{
    // Code that should only run when the current user is an administrator.
}

Starting in .NET 5.0, you cannot apply the PrincipalPermissionAttribute attribute to a method. The constructor for the attribute is obsolete and produces a compile-time error. Unlike other obsoletion warnings, you can't suppress the error.

Reason for change

The PrincipalPermissionAttribute type, like other types that subclass SecurityAttribute, is part of .NET's Code Access Security (CAS) infrastructure. In .NET Framework 2.x - 4.x, the runtime enforces PrincipalPermissionAttribute annotations on method entry, even if the application is running under a full-trust scenario. .NET Core and .NET 5.0 and later don't support CAS attributes, and the runtime ignores them.

This difference in behavior from .NET Framework to .NET Core and .NET 5.0 can result in a "fail open" scenario, where access should have been blocked but instead has been allowed. To prevent the "fail open" scenario, you can no longer apply the attribute in code that targets .NET 5.0 or later.

Version introduced

5.0 Preview 7

If you encounter the obsoletion error, you must take action.

  • If you're applying the attribute to an ASP.NET MVC action method:

    Consider using ASP.NET's built-in authorization infrastructure. The following code demonstrates how to annotate a controller with an AuthorizeAttribute attribute. The ASP.NET runtime will authorize the user before performing the action.

    using Microsoft.AspNetCore.Authorization;
    
    namespace MySampleApp
    {
        [Authorize(Roles = "Administrator")]
        public class AdministrationController : Controller
        {
            public ActionResult MyAction()
            {
                // This code won't run unless the current user
                // is in the 'Administrator' role.
            }
        }
    }
    

    For more information, see Role-based authorization in ASP.NET Core and Introduction to authorization in ASP.NET Core.

  • If you're applying the attribute to library code outside the context of a web app:

    Perform the checks manually at the beginning of your method. This can be done by using the IPrincipal.IsInRole(String) method.

    using System.Threading;
    
    void DoSomething()
    {
        if (Thread.CurrentPrincipal == null
            || !Thread.CurrentPrincipal.IsInRole("Administrators"))
        {
            throw new Exception("User is anonymous or isn't an admin.");
        }
    
        // Code that should run only when user is an administrator.
    }
    

Category

  • Core .NET libraries
  • Security

Affected APIs


BinaryFormatter serialization methods are obsolete and prohibited in ASP.NET apps

Serialize and Deserialize methods on BinaryFormatter, Formatter, and IFormatter are now obsolete. Additionally, BinaryFormatter serialization is prohibited by default for ASP.NET apps.

Change description

Due to security vulnerabilities in BinaryFormatter, the following methods are now obsolete. Additionally, in ASP.NET 5.0 and later apps, they will throw a NotSupportedException, unless the web app has re-enabled BinaryFormatter functionality.

These methods are marked obsolete as part of an effort to wind down usage of BinaryFormatter within the .NET ecosystem.

The following serialization methods are also now obsolete, but have no behavioral changes:

Version introduced

5.0 Preview 8

  • Stop using BinaryFormatter in your code. Instead, consider using JsonSerializer or XmlSerializer. For more information, see BinaryFormatter security guide.

  • You can temporarily suppress the BinaryFormatter compile-time warning, which is SYSLIB0011. We recommend that you thoroughly assess your code for risks before choosing this option. The easiest way to suppress the warnings is to surround the individual call site with #pragma directives.

    // Now read the purchase order back from disk
    using (var readStream = new FileStream("myfile.bin", FileMode.Open))
    {
        var formatter = new BinaryFormatter();
    #pragma warning disable SYSLIB0011
        return (PurchaseOrder)formatter.Deserialize(readStream);
    #pragma warning restore SYSLIB0011
    }
    

    You can also suppress the warning in the project file.

    <PropertyGroup>
      <OutputType>Exe</OutputType>
      <TargetFramework>net5.0</TargetFramework>
      <!-- Disable "BinaryFormatter is obsolete" warnings for entire project -->
      <NoWarn>$(NoWarn);SYSLIB0011</NoWarn>
    </PropertyGroup>
    

    If you suppress the warning in the project file, the warning is suppressed for all code files in the project. Suppressing SYSLIB0011 does not suppress warnings caused by using other obsolete APIs.

  • To continue using BinaryFormatter in ASP.NET apps, you can re-enable it in the project file. However, it's strongly recommended not to do this. For more information, see BinaryFormatter security guide.

    <PropertyGroup>
      <TargetFramework>net5.0</TargetFramework>
      <!-- Warning: Setting the following switch is *NOT* recommended in web apps. -->
      <EnableUnsafeBinaryFormatterSerialization>true</EnableUnsafeBinaryFormatterSerialization>
    </PropertyGroup>
    

For more information about recommended actions, see Resolving BinaryFormatter obsoletion and disablement errors.

Category

  • Core .NET libraries
  • ASP.NET

Affected APIs


UTF-7 code paths are obsolete

The UTF-7 encoding is no longer in wide use among applications, and many specs now forbid its use in interchange. It's also occasionally used as an attack vector in applications that don't anticipate encountering UTF-7-encoded data. Microsoft warns against use of System.Text.UTF7Encoding because it doesn't provide error detection.

Consequently, the Encoding.UTF7 property and UTF7Encoding constructors are now obsolete. Additionally, Encoding.GetEncoding and Encoding.GetEncodings no longer allow you to specify UTF-7.

Change description

Previously, you could create an instance of the UTF-7 encoding by using the Encoding.GetEncoding APIs. For example:

Encoding enc1 = Encoding.GetEncoding("utf-7"); // By name.
Encoding enc2 = Encoding.GetEncoding(65000); // By code page.

Additionally, an instance that represents the UTF-7 encoding was enumerated by the Encoding.GetEncodings() method, which enumerates all the Encoding instances registered on the system.

Starting in .NET 5.0, the Encoding.UTF7 property and UTF7Encoding constructors are obsolete and produce warning SYSLIB0001 (or MSLIB0001 in preview versions). However, to reduce the number of warnings that callers receive when using the UTF7Encoding class, the UTF7Encoding type itself is not marked obsolete.

// The next line generates warning SYSLIB0001.
UTF7Encoding enc = new UTF7Encoding();
// The next line does not generate a warning.
byte[] bytes = enc.GetBytes("Hello world!");

Additionally, the Encoding.GetEncoding methods treat the encoding name utf-7 and the code page 65000 as unknown. Treating the encoding as unknown causes the method to throw an ArgumentException.

// Throws ArgumentException, same as calling Encoding.GetEncoding("unknown").
Encoding enc = Encoding.GetEncoding("utf-7");

Finally, the Encoding.GetEncodings() method doesn't include the UTF-7 encoding in the EncodingInfo array that it returns. The encoding is excluded because it cannot be instantiated.

foreach (EncodingInfo encInfo in Encoding.GetEncodings())
{
    // The next line would throw if GetEncodings included UTF-7.
    Encoding enc = Encoding.GetEncoding(encInfo.Name);
}

Reason for change

Many applications call Encoding.GetEncoding("encoding-name") with an encoding name value that's provided by an untrusted source. For example, a web client or server might take the charset portion of the Content-Type header and pass the value directly to Encoding.GetEncoding without validating it. This could allow a malicious endpoint to specify Content-Type: ...; charset=utf-7, which could cause the receiving application to misbehave.

Additionally, disabling UTF-7 code paths allows optimizing compilers, such as those used by Blazor, to remove these code paths entirely from the resulting application. As a result, the compiled applications run more efficiently and take less disk space.

Version introduced

5.0 Preview 8

In most cases, you don't need to take any action. However, for apps that have previously activated UTF-7-related code paths, consider the guidance that follows.

  • If your app calls Encoding.GetEncoding with unknown encoding names provided by an untrusted source:

    Instead, compare the encoding names against a configurable allow list. The configurable allow list should at minimum include the industry-standard "utf-8". Depending on your clients and regulatory requirements, you may also need to allow region-specific encodings, such as "GB18030".

    If you don't implement an allow list, Encoding.GetEncoding will return any Encoding that's built into the system or that's registered via a custom EncodingProvider. Audit your service's requirements to validate that this is the desired behavior. UTF-7 continues to be disabled by default unless your application re-enables the compatibility switch mentioned later in this article.

  • If you're using Encoding.UTF7 or UTF7Encoding within your own protocol or file format:

    Switch to using Encoding.UTF8 or UTF8Encoding. UTF-8 is an industry standard and is widely supported across languages, operating systems, and runtimes. Using UTF-8 eases future maintenance of your code and makes it more interoperable with the rest of the ecosystem.

  • If you're comparing an Encoding instance against Encoding.UTF7:

    Instead, consider performing a check against the well-known UTF-7 code page, which is 65000. By comparing against the code page, you avoid the warning and also handle some edge cases, such as if somebody called new UTF7Encoding() or subclassed the type.

    void DoSomething(Encoding enc)
    {
        // Don't perform the check this way.
        // It produces a warning and misses some edge cases.
        if (enc == Encoding.UTF7)
        {
            // Encoding is UTF-7.
        }
    
        // Instead, perform the check this way.
        if (enc != null && enc.CodePage == 65000)
        {
            // Encoding is UTF-7.
        }
    }
    
  • If you must use Encoding.UTF7 or UTF7Encoding:

    You can suppress the SYSLIB0001 warning in code or within your project's .csproj file.

    #pragma warning disable SYSLIB0001 // Disable the warning.
    Encoding enc = Encoding.UTF7;
    #pragma warning restore SYSLIB0001 // Re-enable the warning.
    
    <Project Sdk="Microsoft.NET.Sdk">
      <PropertyGroup>
       <TargetFramework>net5.0</TargetFramework>
       <!-- NoWarn below suppresses SYSLIB0001 project-wide -->
       <NoWarn>$(NoWarn);SYSLIB0001</NoWarn>
      </PropertyGroup>
    </Project>
    

    Note

    Suppressing SYSLIB0001 only disables the Encoding.UTF7 and UTF7Encoding obsoletion warnings. It doesn't disable any other warnings or change the behavior of APIs like Encoding.GetEncoding.

  • If you must support Encoding.GetEncoding("utf-7", ...):

    You can re-enable support for this via a compatibility switch. This compatibility switch can be specified in the application's .csproj file or in a run-time configuration file, as shown in the following examples.

    In the application's .csproj file:

    <Project Sdk="Microsoft.NET.Sdk">
      <PropertyGroup>
       <TargetFramework>net5.0</TargetFramework>
       <!-- Re-enable support for UTF-7 -->
       <EnableUnsafeUTF7Encoding>true</EnableUnsafeUTF7Encoding>
      </PropertyGroup>
    </Project>
    

    In the application's runtimeconfig.template.json file:

    {
      "configProperties": {
        "System.Text.Encoding.EnableUnsafeUTF7Encoding": true
      }
    }
    

    Tip

    If you re-enable support for UTF-7, you should perform a security review of code that calls Encoding.GetEncoding.

Category

  • Core .NET libraries
  • Security

Affected APIs


Vector<T> always throws NotSupportedException for unsupported types

System.Numerics.Vector<T> now always throws a NotSupportedException for unsupported type parameters.

Change description

Previously, members of Vector<T> would not always throw a NotSupportedException when T was an unsupported type. The exception wasn't always thrown because of code paths that supported hardware acceleration. For example, Vector<bool> + Vector<bool> returned default instead of throwing an exception on platforms that have no hardware acceleration, such as ARM32. For unsupported types, Vector<T> members exhibited inconsistent behavior across different platforms and hardware configurations.

Starting in .NET 5.0, Vector<T> members always throw a NotSupportedException on all hardware configurations when T is not a supported type.

Unsupported types

The supported types for the type parameter of Vector<T> are:

  • byte
  • sbyte
  • short
  • ushort
  • int
  • uint
  • long
  • ulong
  • float
  • double

The supported types have not changed, however, they may change in the future.

Version introduced

5.0 Preview 8

Don't use an unsupported type for the type parameter of Vector<T>.

Category

Core .NET libraries

Affected APIs


Default ActivityIdFormat is W3C

The default identifier format for activity (Activity.DefaultIdFormat) is now ActivityIdFormat.W3C.

Change description

The W3C activity ID format was introduced in .NET Core 3.0 as an alternative to the hierarchical ID format. However, to preserve compatibility, the W3C format wasn't made the default until .NET 5.0. The default was changed in .NET 5.0 because the W3C format has been ratified and gained traction across multiple language implementations.

If your app targets a platform other than .NET 5.0 or later, it will experience the old behavior, where Hierarchical is the default format. This default applies to platforms net45+, netstandard1.1+, and netcoreapp (1.x, 2.x, and 3.x). In .NET 5.0 and later, Activity.DefaultIdFormat is set to ActivityIdFormat.W3C.

Version introduced

5.0 Preview 7

If your application is agnostic to the identifier that's used for distributed tracing, no action is needed. Libraries such as ASP.NET Core and HttpClient can consume or propagate both versions of the ActivityIdFormat.

If you require interoperability with existing systems, or current systems rely on the format of the identifier, you can preserve the old behavior by setting DefaultIdFormat to ActivityIdFormat.Hierarchical. Alternatively, you can set an AppContext switch in one of three ways:

  • In the project file.

    <ItemGroup>
      <RuntimeHostConfigurationOption Include="System.Diagnostics.DefaultActivityIdFormatIsHierarchial" Value="true" />
    </ItemGroup>
    
  • In the runtimeconfig.json file.

    {
        "runtimeOptions": {
            "configProperties": {
                "System.Diagnostics.DefaultActivityIdFormatIsHierarchial": true
            }
        }
    }
    
  • Through an environment variable.

    Set DOTNET_SYSTEM_DIAGNOSTICS_DEFAULTACTIVITYIDFORMATISHIERARCHIAL to true or 1.

Category

Core .NET libraries

Affected APIs


Behavior change for Vector2.Lerp and Vector4.Lerp

The implementation of Vector2.Lerp(Vector2, Vector2, Single) and Vector4.Lerp(Vector4, Vector4, Single) changed to correctly account for a floating-point rounding error.

Change description

Previously, Vector2.Lerp(Vector2, Vector2, Single) and Vector4.Lerp(Vector4, Vector4, Single) were implemented as value1 + (value2 - value1) * amount. However, due to a floating-point rounding error, this algorithm doesn't always return value2 when amount is 1.0f.

In .NET 5.0 and later, the implementation uses the same algorithm as Vector3.Lerp(Vector3, Vector3, Single), which is (value1 * (1.0f - amount)) + (value2 * amount). This algorithm correctly accounts for the rounding error. Now, when amount is 1.0f, the result is precisely value2. The updated algorithm also allows the algorithm to be freely optimized using MathF.FusedMultiplyAdd when it's available.

Version introduced

5.0 Preview 7

No action is necessary. However, if you want to maintain the old behavior, you can implement your own Lerp function that uses the previous algorithm of value1 + (value2 - value1) * amount.

Category

Core .NET libraries

Affected APIs


SSE and SSE2 CompareGreaterThan methods properly handle NaN inputs

The following System.Runtime.Intrinsics.X86.Sse and System.Runtime.Intrinsics.X86.Sse2 methods have been fixed to properly handle NaN inputs and match the hardware behavior of the equivalent methods in the System.Runtime.Intrinsics.X86.Avx class:

  • CompareGreaterThan
  • CompareGreaterThanOrEqual
  • CompareNotGreaterThan
  • CompareNotGreaterThanOrEqual
  • CompareScalarGreaterThan
  • CompareScalarGreaterThanOrEqual
  • CompareScalarNotGreaterThan
  • CompareScalarNotGreaterThanOrEqual

Change description

Previously, NaN inputs to the listed Sse and Sse2 methods returned an incorrect result. The result also differed from the result generated by the corresponding method in the Avx class.

Starting in .NET 5.0, these methods correctly handle NaN inputs and return the same results as the corresponding methods in the Avx class.

The Streaming SIMD Extensions (SSE) and Streaming SIMD Extensions 2 (SSE2) industry standard architectures (ISAs) don't provide direct hardware support for these comparison methods, so they're implemented in software. Previously, the methods were improperly implemented, and they incorrectly handled NaN inputs. For code ported from native, the incorrect behavior may introduce bugs. For a 256-bit code path, the methods can also produce different results to the equivalent methods in the Avx class.

As an example of how the methods were previously incorrect, you can implement CompareNotGreaterThan(x,y) as CompareLessThanOrEqual(x,y) for regular integers. However, for NaN inputs, that logic computes the wrong result. Instead, using CompareNotLessThan(y,x) compares the numbers correctly and takes NaN inputs into consideration.

Version introduced

5.0 Preview 4

  • If the previous behavior was a bug, no change is required.

  • If the previous behavior was desired, you can maintain that behavior by changing the relevant invocation as follows:

    • CompareGreaterThan(x,y) -> CompareNotLessThanOrEqual(x,y)
    • CompareGreaterThanOrEqual(x,y) -> CompareNotLessThan(x,y)
    • CompareNotGreaterThan(x,y) -> CompareLessThanOrEqual(x,y)
    • CompareNotGreaterThanOrEqual(x,y) -> CompareLessThan(x,y)
    • CompareScalarGreaterThan(x,y) -> CompareScalarNotLessThanOrEqual(x,y)
    • CompareScalarGreaterThanOrEqual(x,y) -> CompareScalarNotLessThan(x,y)
    • CompareScalarNotGreaterThan(x,y) -> CompareScalarLessThanOrEqual(x,y)
    • CompareScalarNotGreaterThanOrEqual(x,y) -> CompareScalarLessThan(x,y)

Category

Core .NET libraries

Affected APIs


CounterSet.CreateCounterSetInstance now throws InvalidOperationException if instance already exists

Starting in .NET 5.0, CounterSet.CreateCounterSetInstance(String) throws an InvalidOperationException instead of an ArgumentException if the counter set already exists.

Change description

In .NET Framework and .NET Core 1.0 to 3.1, you can create an instance of the counter set by calling CreateCounterSetInstance. However, if the counter set already exists, the method throws an ArgumentException exception.

In .NET 5.0 and later versions, when you call CreateCounterSetInstance and the counter set exists, an InvalidOperationException exception is thrown.

Version introduced

5.0 Preview 5

If you catch ArgumentException exceptions in your app when calling CreateCounterSetInstance, consider also catching InvalidOperationException exceptions.

Note

Catching ArgumentException exceptions is not recommended.

Category

Core .NET libraries

Affected APIs


Microsoft.DotNet.PlatformAbstractions package removed

No new versions of the Microsoft.DotNet.PlatformAbstractions NuGet package will be produced.

Change description

Previously, new versions of the Microsoft.DotNet.PlatformAbstractions library were produced alongside new versions of .NET Core. Going forward, no new functionality will be added to the library, and no new major versions will be released. However, existing versions of the library will continue to work and be serviced.

The Microsoft.DotNet.PlatformAbstractions library overlaps with APIs that are already established in the System.* namespaces. Also, some Microsoft.DotNet.PlatformAbstractions APIs weren't designed with the same level of scrutiny and long-term supportability as the rest of the System.* APIs. For example, Microsoft.DotNet.PlatformAbstractions uses the Platform enumeration to describe the current operating system platform. This enumeration design was explicitly rejected when the RuntimeInformation.IsOSPlatform(OSPlatform) API was designed, to allow for new platforms and future flexibility.

The scenarios enabled by the Microsoft.DotNet.PlatformAbstractions library are now possible without it. Existing versions will continue to work, even in .NET 5.0 and later, and will be serviced along with previous versions of .NET Core. However, new functionality won't be added to the library. Instead, new functionality will be added to other libraries and APIs.

Version introduced

5.0 Preview 6

Category

Core .NET libraries

Affected APIs

  • Microsoft.DotNet.PlatformAbstractions.ApplicationEnvironment.ApplicationBasePath
  • Microsoft.DotNet.PlatformAbstractions.HashCodeCombiner
  • Microsoft.DotNet.PlatformAbstractions.RuntimeEnvironment.GetRuntimeIdentifier()
  • Microsoft.DotNet.PlatformAbstractions.RuntimeEnvironment.OperatingSystem
  • Microsoft.DotNet.PlatformAbstractions.RuntimeEnvironment.OperatingSystemPlatform
  • Microsoft.DotNet.PlatformAbstractions.RuntimeEnvironment.OperatingSystemVersion
  • Microsoft.DotNet.PlatformAbstractions.RuntimeEnvironment.RuntimeArchitecture

Globalization

StringInfo and TextElementEnumerator are now UAX29-compliant

Prior to this change, System.Globalization.StringInfo and System.Globalization.TextElementEnumerator didn't properly handle all grapheme clusters. Some graphemes were split into their constituent components instead of being kept together. Now, StringInfo and TextElementEnumerator process grapheme clusters according to the latest version of the Unicode Standard.

In addition, the Microsoft.VisualBasic.Strings.StrReverse method, which reverses the characters in a string in Visual Basic, now also follows the Unicode standard for grapheme clusters.

Change description

A grapheme or extended grapheme cluster is a single user-perceived character that may be made up of multiple Unicode code points. For example, the string containing the Thai character "kam" (กำ) consists of the following two characters:

  • ก (= '\u0e01') THAI CHARACTER KO KAI
  • ำ (= '\u0e33') THAI CHARACTER SARA AM

When displayed to the user, the operating system combines the two characters to form the single display character (or grapheme) "kam" or กำ. Emoji can also consist of multiple characters that are combined for display in a similar way.

Tip

The .NET documentation sometimes uses the term "text element" when referring to a grapheme.

The StringInfo and TextElementEnumerator classes inspect strings and return information about the graphemes they contain. In .NET Framework (all versions) and .NET Core 3.x and earlier, these two classes use custom logic that handles some combining classes but doesn't fully comply with the Unicode Standard. For example, the StringInfo and TextElementEnumerator classes incorrectly split the single Thai character "kam" back into its constituent components instead of keeping them together. These classes also incorrectly split the emoji character "🤷🏽‍♀️" into four clusters (person shrugging, skin tone modifier, gender modifier, and an invisible combiner) instead of keeping them together as a single grapheme cluster.

Starting with .NET 5, the StringInfo and TextElementEnumerator classes implement the Unicode standard as defined by Unicode Standard Annex #29, rev. 35, sec. 3. In particular, they now return extended grapheme clusters for all combining classes.

Consider the following C# code:

using System.Globalization;

static void Main(string[] args)
{
    PrintGraphemes("กำ");
    PrintGraphemes("🤷🏽‍♀️");
}

static void PrintGraphemes(string str)
{
    Console.WriteLine($"Printing graphemes of \"{str}\"...");
    int i = 0;

    TextElementEnumerator enumerator = StringInfo.GetTextElementEnumerator(str);
    while (enumerator.MoveNext())
    {
        Console.WriteLine($"Grapheme {++i}: \"{enumerator.Current}\"");
    }

    Console.WriteLine($"({i} grapheme(s) total.)");
    Console.WriteLine();
}

In .NET Framework and .NET Core 3.x and earlier versions, the graphemes are split up, and the console output is as follows:

Printing graphemes of "กำ"...
Grapheme 1: "ก"
Grapheme 2: "ำ"
(2 grapheme(s) total.)

Printing graphemes of "🤷🏽‍♀️"...
Grapheme 1: "🤷"
Grapheme 2: "🏽"
Grapheme 3: "‍"
Grapheme 4: "♀️"
(4 grapheme(s) total.)

In .NET 5 and later versions, the graphemes are kept together, and the console output is as follows:

Printing graphemes of "กำ"...
Grapheme 1: "กำ"
(1 grapheme(s) total.)

Printing graphemes of "🤷🏽‍♀️"...
Grapheme 1: "🤷🏽‍♀️"
(1 grapheme(s) total.)

In addition, starting in .NET 5, the Microsoft.VisualBasic.Strings.StrReverse method, which reverses the characters in a string in Visual Basic, now also follows the Unicode standard for grapheme clusters.

These changes are part of a wider set of Unicode and UTF-8 improvements in .NET, including an extended grapheme cluster enumeration API to complement the Unicode scalar-value enumeration APIs that were introduced with the System.Text.Rune type in .NET Core 3.0.

Version introduced

.NET 5.0 Preview 1

You don't need to take any action. Your apps will automatically behave in a more standards-compliant manner in a variety of globalization-related scenarios.

Category

Globalization

Affected APIs


Globalization APIs use ICU libraries on Windows

.NET 5.0 and later versions use International Components for Unicode (ICU) libraries for globalization functionality when running on Windows 10 May 2019 Update or later.

Change description

Previously, .NET libraries used National Language Support (NLS) APIs for globalization functionality. For example, NLS functions were used to get culture data, such as date and time format patterns, compare strings, and perform string casing in the appropriate culture.

Starting in .NET 5.0, if an app is running on Windows 10 May 2019 Update or later, .NET libraries use ICU globalization APIs. Windows 10 May 2019 Update and later versions ship with the ICU native library. If the .NET runtime can't load ICU, it uses NLS instead.

This change was introduced for two reasons:

  • Apps have the same globalization behavior across platforms, including Linux, macOS, and Windows.
  • Apps can control globalization behavior by using custom ICU libraries.

Version introduced

.NET 5.0 Preview 4

No action is required on the part of the developer. However, if you wish to continue using NLS globalization APIs, you can set a run-time switch to revert to that behavior. For more information about the available switches, see the .NET globalization and ICU article.

Category

Globalization

Affected APIs


Interop

No A/W suffix probing on non-Windows platforms

The .NET runtimes no longer add an A or W suffix to function export names during probing for P/Invokes on non-Windows platforms.

Version introduced

5.0 Preview 4

Change description

Windows has a convention of adding an A or W suffix to Windows SDK function names, which correspond to the Windows code page and Unicode versions, respectively.

In previous versions of .NET, both the CoreCLR and Mono runtimes add an A or W suffix to the export name during export discovery for P/Invokes on all platforms.

In .NET 5.0 and later versions, an A or W suffix is added to the export name during export discovery on Windows only. On Unix platforms, the suffix is not added. The semantics of both runtimes on the Windows platform remain unchanged.

Reason for change

This change was made to simplify cross-platform probing. It's overhead that shouldn't be incurred, given that non-Windows platforms don't contain this semantic.

To mitigate the change, you can manually add the desired suffix on non-Windows platforms. For example:

[DllImport(...)]
extern static void SetWindowTextW();

Category

Interop

Affected APIs


Built-in support for WinRT is removed from .NET

Built-in support for consumption of Windows runtime (WinRT) APIs in .NET is removed.

Version introduced

5.0 Preview 6

Change description

Previously, CoreCLR could consume Windows metadata (WinMD) files to active and consume WinRT types. Starting in .NET 5.0, CoreCLR can no longer consume WinMD files directly.

If you attempt to reference an unsupported assembly, you'll get a FileNotFoundException. If you activate a WinRT class, you'll get a PlatformNotSupportedException.

This breaking change was made for the following reasons:

  • So WinRT can be developed and improved separately from the .NET runtime.
  • For symmetry with interop systems provided for other operating systems, such as iOS and Android.
  • To take advantage of other .NET features, such as C# features, intermediate language (IL) linking, and ahead-of-time (AOT) compilation.
  • To simplify the .NET runtime codebase.

Category

Interop

Affected APIs


Serialization

BinaryFormatter.Deserialize rewraps some exceptions in SerializationException

The BinaryFormatter.Deserialize method now rewraps some exception objects inside a SerializationException before propagating the exception back to the caller.

Change description

Previously, the BinaryFormatter.Deserialize method allowed some arbitrary exceptions, such as ArgumentNullException, to propagate up the stack to its callers.

In .NET 5.0 and later, the BinaryFormatter.Deserialize method more aggressively catches exceptions that occur due to invalid deserialization operations and wraps them in a SerializationException.

Version introduced

5.0 Preview 1

In most cases, you don't need to take any action. However, if your call site depends on a particular exception being thrown, you can unwrap the exception from the outer SerializationException, as shown in the following example.

Stream inputStream = GetInputStream();
var formatter = new BinaryFormatter();

try
{
    object deserialized = formatter.Deserialize(inputStream);
}
catch (MyException myEx)
{
    // Handle 'myEx' here in case it was thrown directly.
}
catch (SerializationException serEx) when (serEx.InnerException is MyException myEx)
{
    // Handle 'myEx' here in case it was wrapped in SerializationException.
}

Category

Serialization

Affected APIs


Windows Forms

Removed status bar controls

Starting in .NET 5.0, some Windows Forms controls are no longer available.

Change description

Starting with .NET 5.0, some of the status bar-related Windows Forms controls are no longer available. Replacement controls that have better design and support were introduced in .NET Framework 2.0. The deprecated controls were previously removed from designer toolboxes but were still available to be used. Now, they have been completely removed.

The following types are no longer available:

  • StatusBar
  • StatusBarDrawItemEventArgs
  • StatusBarDrawItemEventHandler
  • StatusBarPanel
  • StatusBarPanelAutoSize
  • StatusBarPanelBorderStyle
  • StatusBarPanelClickEventArgs
  • StatusBarPanelClickEventHandler
  • StatusBarPanelStyle

Version introduced

5.0 Preview 1

Move to the replacement APIs for these controls and their scenarios:

Old Control (API) Recommended Replacement
StatusBar StatusStrip
StatusBarPanel ToolStripStatusLabel

Category

Windows Forms

Affected APIs


WinForms methods now throw ArgumentException

Some Windows Forms methods now throw an ArgumentException for invalid arguments, where previously they did not.

Change description

Previously, passing arguments of an unexpected or incorrect type to certain Windows Forms methods would result in an indeterminate state. Starting in .NET 5.0, these methods now throw an ArgumentException when passed invalid arguments.

Throwing an ArgumentException conforms to the behavior of the .NET runtime. It also improves the debugging experience by clearly communicating which argument is invalid.

Version introduced

.NET 5.0

  • Update the code to prevent passing invalid arguments.
  • If necessary, handle an ArgumentException when calling the method.

Category

Windows Forms

Affected APIs

The following table lists the affected methods and parameters:

Method Parameter name Condition Version added
System.Windows.Forms.TabControl.GetToolTipText(Object) item Argument is not of type TabPage. Preview 1
System.Windows.Forms.DataFormats.GetFormat(String) format Argument is null, String.Empty, or white space. Preview 5
InputLanguageChangedEventArgs(CultureInfo, Byte) culture Unable to retrieve an InputLanguage for the specified culture. Preview 7

WinForms methods now throw ArgumentNullException

Some Windows Forms methods now throw an ArgumentNullException for null arguments, where previously they threw a NullReferenceException.

Change description

Previously, certain Windows Forms methods threw a NullReferenceException if passed an argument that was null. Starting in .NET 5.0, these methods now throw an ArgumentNullException for null arguments, instead.

Throwing an ArgumentNullException conforms to the behavior of the .NET runtime. It also improves the debugging experience by clearly communicating that an argument is null and which argument it is.

Version introduced

.NET 5.0

If you call any of these methods and your code currently catches a NullReferenceException for null arguments, catch an ArgumentNullException instead. In addition, consider updating the code to prevent passing null arguments to the listed methods.

Category

Windows Forms

Affected APIs

The following table lists the affected methods and parameters:

Method Parameter name Version added
Control.ControlCollection(Control) owner Preview 1
TabControl.GetToolTipText(Object) item Preview 1
TableLayoutControlCollection(TableLayoutPanel) container Preview 1
ToolStripRenderer.OnRenderArrow(ToolStripArrowRenderEventArgs) e Preview 1
ToolStripRenderer.OnRenderItemCheck(ToolStripItemImageRenderEventArgs) e Preview 1
ToolStripRenderer.OnRenderItemImage(ToolStripItemImageRenderEventArgs) e Preview 1
ToolStripRenderer.OnRenderItemText(ToolStripItemTextRenderEventArgs) e Preview 1
ToolStripRenderer.OnRenderStatusStripSizingGrip(ToolStripRenderEventArgs) > e Preview 1
DataGridViewComboBoxEditingControl.ApplyCellStyleToEditingControl(DataGridViewCellStyle) dataGridViewCellStyle Preview 2
RichTextBox.LoadFile(Stream, RichTextBoxStreamType) data Preview 2
ListBox.IntegerCollection(ListBox) owner Preview 5
ListBox.IntegerCollection.CopyTo(Array, Int32) destination Preview 5
ListViewGroup.ISerializable.GetObjectData(SerializationInfo, StreamingContext) info Preview 5
VisualStyleRenderer(String, Int32, Int32) className Preview 5
ListBox.ObjectCollection(ListBox) owner Preview 6
ListBox.ObjectCollection(ListBox, Object[]) owner, value Preview 6
ListBox.ObjectCollection(ListBox, ListBox+ObjectCollection) owner, value Preview 6
ListBox.ObjectCollection.AddRange(Object[]) items Preview 6
ListBox.ObjectCollection.AddRange(ListBox+ObjectCollection) value Preview 6
ListBox.ObjectCollection.CopyTo(Object[], Int32) destination Preview 6
ListBox.ObjectCollection.ICollection.CopyTo(Array, Int32) destination Preview 6
ListView.SelectedIndexCollection(ListView) owner Preview 7
TreeNodeCollection.Find(String, Boolean) key is null or empty Preview 8

WinForms properties now throw ArgumentOutOfRangeException

Some Windows Forms properties now throw an ArgumentOutOfRangeException for invalid arguments, where previously they did not.

Change description

Previously, these properties threw various exceptions, such as NullReferenceException, IndexOutOfRangeException, or ArgumentException, when passed out-of-range arguments. Starting in .NET 5.0, these properties now throw an ArgumentOutOfRangeException when passed arguments that are out of range.

Throwing an ArgumentOutOfRangeException conforms to the behavior of the .NET runtime. It also improves the debugging experience by clearly communicating which argument is invalid.

Version introduced

.NET 5.0

Category

Windows Forms

Affected APIs

The following table lists the affected properties and parameters:

Property Parameter name Version added
ListBox.IntegerCollection.Item[Int32] index 5.0 Preview 5
TreeNode.ImageIndex value 5.0 Preview 6
TreeNode.SelectedImageIndex value 5.0 Preview 6