Uploaded by Code Buoyant

dotnet-core-compatibility

advertisement
Tell us about your PDF experience.
Breaking changes in .NET
Article • 05/24/2023
Use this reference section to find breaking changes that might apply to you if you're
upgrading your app to a newer version of .NET. You can navigate the table of contents
either by .NET version or by technology area.
If you're looking for breaking changes from .NET Framework to .NET, see Breaking
changes for migration from .NET Framework to .NET (Core).
GitHub issues and announcements
You can also view individual issues that detail the breaking changes introduced in .NET
in the following GitHub repositories:
For .NET Core and .NET 5+, the dotnet/docs
For ASP.NET Core, the aspnet/Announcements
For Entity Framework Core, the dotnet/efcore
repository.
repository.
repository.
See also
API removal in .NET
.NET runtime compatibility
Migrate from .NET Framework to .NET Core
Breaking changes for migration from .NET Framework to .NET Core
Breaking changes in .NET 8
Article • 08/29/2023
If you're migrating an app to .NET 8, the breaking changes listed here might affect you.
Changes are grouped by technology area, such as ASP.NET Core or Windows Forms.
This article categorizes each breaking change as binary incompatible or source
incompatible, or as a behavioral change:
Binary incompatible - When run against the new runtime or component, existing
binaries may encounter a breaking change in behavior, such as failure to load or
execute, and if so, require recompilation.
Source incompatible - When recompiled using the new SDK or component or to
target the new runtime, existing source code may require source changes to
compile successfully.
Behavioral change - Existing code and binaries may behave differently at run time.
If the new behavior is undesirable, existing code would need to be updated and
recompiled.
7 Note
This article is a work in progress. It's not a complete list of breaking changes in .NET
8. To query breaking changes that are still pending publication, see Issues of
.NET
.
ASP.NET Core
Title
Type of change
Introduced
ConcurrencyLimiterMiddleware is obsolete
Source incompatible
Preview 4
Custom converters for serialization removed
Behavioral change
Preview 2
ISystemClock is obsolete
Source incompatible
Preview 5
Rate-limiting middleware requires AddRateLimiter
Behavioral change
Preview 5
Security token events return a JSonWebToken
Preview 7
TrimMode defaults to full for Web SDK projects
Preview 7
Containers
Title
Type of change
Introduced
'ca-certificates' and 'krb5-libs' packages
removed from Alpine images
Binary incompatible
Preview 7
Debian container images upgraded to Debian
12
Binary incompatible/behavioral
change
Preview 1
Default ASP.NET Core port changed to 8080
Behavioral change
Preview 1
'libintl' package removed from Alpine images
Behavioral change
Preview 5
Multi-platform container tags are Linux-only
Behavioral change
Preview 3
New 'app' user in Linux images
Behavioral change
Preview 1
Core .NET libraries
Title
Type of change
Introduced
Activity operation name when null
Behavioral change
Preview 1
AnonymousPipeServerStream.Dispose behavior
Behavioral change
Preview 1
API obsoletions with custom diagnostic IDs
Source incompatible
Preview 1,
4
Backslash mapping in Unix file paths
Behavioral change
Preview 1
Base64.DecodeFromUtf8 methods ignore whitespace
Behavioral change
Preview 5
Boolean-backed enum type support removed
Behavioral change
Preview 1
FileStream writes when pipe is closed
Behavioral change
Preview 1
GC.GetGeneration might return Int32.MaxValue
Behavioral change
Preview 4
GetFolderPath behavior on Unix
Behavioral change
Preview 1
IndexOfAnyValues renamed to SearchValues
Source/binary
Preview 5
incompatible
ITypeDescriptorContext nullable annotations
Source incompatible
Preview 1
Legacy Console.ReadKey removed
Behavioral change
Preview 1
Method builders generate parameters with
HasDefaultValue set to false
Behavioral change
Preview 5
Title
Type of change
Introduced
Removed overloads of ToFrozenDictionary/ToFrozenSet
Source/binary
incompatible
Preview 7
Title
Type of change
Introduced
AesGcm authentication tag size on macOS
Behavioral change
Preview 1
RSA.EncryptValue and RSA.DecryptValue obsolete
Source incompatible
Preview 1
Cryptography
Deployment
Title
Type of change
Introduced
Host determines RID-specific assets
Binary incompatible/behavioral change
Preview 5
StripSymbols defaults to true
Behavioral change
Preview 4
Entity Framework Core
Breaking changes in EF Core 8
Extensions
Title
Type of change
Introduced
ActivatorUtilities.CreateInstance behaves consistently
Behavioral
change
Preview 1
ActivatorUtilities.CreateInstance requires non-null provider
Behavioral
change
Preview 1
ConfigurationBinder throws for mismatched value
Behavioral
change
Preview 1
ConfigurationManager package no longer references
System.Security.Permissions
Source
incompatible
Preview 3
DirectoryServices package no longer references
System.Security.Permissions
Source
incompatible
Preview 3
Title
Type of change
Introduced
Empty keys added to dictionary by configuration binder
Behavioral
change
Preview 5
HostApplicationBuilderSettings.Args respected by
Behavioral
Preview 2
HostApplicationBuilder ctor
change
Globalization
Title
Type of change
Introduced
Date and time converters honor culture argument
Behavioral change
Preview 4
TwoDigitYearMax default is 2049
Behavioral change
Preview 1
Interop
Title
Type of change
Introduced
CreateObjectFlags.Unwrap only unwraps on target instance
Behavioral change
Preview 5
IDispatchImplAttribute API is removed
Binary incompatible
Preview 6
SafeHandle types must have public constructor
Source incompatible
Preview 5
Reflection
Title
Type of change
Introduced
IntPtr no longer used for function pointer types
Behavioral change
Preview 2
SDK
Title
Type of change
Introduced
.NET tool roll-forward behavior
Behavioral change
Preview 5
CLI console output uses UTF-8
Behavioral change/Source and
Preview 1
binary incompatible
Console encoding not UTF-8 after completion
Behavioral change/Binary
incompatible
Preview 3
Title
Type of change
Introduced
Containers default to use the 'latest' tag
Behavioral change
Preview 6
'dotnet pack' uses Release configuration
Behavioral change/Source
incompatible
Preview 1
'dotnet publish' uses Release configuration
Behavioral change/Source
Preview 1
incompatible
MSBuild respects DOTNET_CLI_UI_LANGUAGE
Behavioral change
Preview 5
Runtime-specific apps not self-contained
Source/binary incompatible
Preview 5
'dotnet restore' produces security vulnerability
warnings
Behavioral change
Preview 4
Trimming may not be used with .NET Standard
or .NET Framework
Behavioral change
RC 1
Serialization
Title
Type of change
Introduced
BinaryFormatter disabled for most projects
Behavioral change
Preview 4
Windows Forms
Title
Type of change
Introduced
Anchor layout changes
Behavioral change
Preview 1
DefaultValueAttribute removed from some properties
Behavioral change
Preview 2
ExceptionCollection ctor throws ArgumentException
Behavioral change
Preview 1
Forms scale according to AutoScaleMode
Behavioral change
Preview 1
ImageList.ColorDepth default is Depth32Bit
Behavioral change
Preview 1
TableLayoutStyleCollection throws ArgumentException
Behavioral change
Preview 1
Top-level forms scale minimum and maximum size to DPI
Behavioral change
Preview 1
WFDEV002 obsoletion is now an error
Source incompatible
Preview 1
See also
What's new in .NET 8
ConcurrencyLimiterMiddleware is
obsolete
Article • 05/04/2023
ConcurrencyLimiterMiddleware and its associated methods and types have been marked
as obsolete.
If you require rate-limiting capabilities, switch to the newer and more capable ratelimiting middleware that was introduced in .NET 7 (for example,
RateLimiterApplicationBuilderExtensions.UseRateLimiter). The .NET 7 rate-limiting API
includes a concurrency limiter and several other rate-limiting algorithms that you can
apply to your application.
Version introduced
ASP.NET Core 8.0 Preview 4
Previous behavior
Developers could use ConcurrencyLimiterMiddleware to control concurrency by adding
a policy to dependency injection (DI) and enabling the middleware:
C#
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddStackPolicy<options => {
options.MaxConcurrentRequests = 2;
options.RequestQueueLimit = 25;
});
var app = builder.Build();
app.UseConcurrencyLimiter();
// Map endpoints.
app.Run();
New behavior
If you use the Affected APIs in your code, you'll get warning CS0618 at compile time.
Type of breaking change
This change affects source compatibility.
Reason for change
ConcurrencyLimiterMiddleware is infrequently used and undocumented. The newer
rate-limiting API has more extensive functionality.
Recommended action
If you're using the older ConcurrencyLimiterMiddleware, we recommend moving to the
newer rate-limiting middleware. Here's an example usage of the newer API,
RateLimiterApplicationBuilderExtensions.UseRateLimiter:
C#
using Microsoft.AspNetCore.RateLimiting;
using System.Threading.RateLimiting;
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.UseRateLimiter(new RateLimiterOptions()
.AddConcurrencyLimiter("only-one-at-a-time-stacked", (options) =>
{
options.PermitLimit = 2;
options.QueueLimit = 25;
options.QueueProcessingOrder = QueueProcessingOrder.NewestFirst;
}));
app.MapGet("/", async () =>
{
await Task.Delay(10000);
return "Hello World";
}).RequireRateLimiting("only-one-at-a-time-stacked");
app.Run();
Affected APIs
Microsoft.AspNetCore.Builder.ConcurrencyLimiterExtensions.UseConcurrencyLimite
r(IApplicationBuilder)
Microsoft.AspNetCore.ConcurrencyLimiter.ConcurrencyLimiterMiddleware
System.Threading.RateLimiting.ConcurrencyLimiterOptions
See also
Rate limiting middleware in ASP.NET Core
Custom converters for serialization
removed
Article • 05/27/2023
ProblemDetails and ValidationProblemDetails previously used custom converters to
support JSON serialization due to a lack of built-in support for the IgnoreNullValues
option. Now that this option is supported by the System.Text.Json APIs, we've removed
the custom converters from the framework in favor of the serialization provided by the
framework.
As a result of this change, the properties in the ProblemDetails and
ValidationProblemDetails types no longer assume lowercase type names. Developers
must specify a JsonNamingPolicy to get the correct behavior.
Version introduced
ASP.NET Core 8.0 Preview 2
Previous behavior
Previously, you could add JsonStringEnumConverter to the serialization options as a
custom converter, and deserialization resulted in a 400 status for
ValidationProblemDetails.
C#
string content = "{\"status\":400,\"detail\":\"HTTP egress is not
enabled.\"}";
using MemoryStream stream = new();
using StreamWriter writer = new(stream);
writer.Write(content);
writer.Flush();
stream.Position = 0;
JsonSerializerOptions options = new();
options.Converters.Add(new JsonStringEnumConverter());
ValidationProblemDetails? details = await
JsonSerializer.DeserializeAsync<ValidationProblemDetails>(stream, options);
Console.WriteLine(details.Status); // 400
New behavior
Starting in .NET 8, the same code results in a null status for ValidationProblemDetails.
C#
string content = "{\"status\":400,\"detail\":\"HTTP egress is not
enabled.\"}";
using MemoryStream stream = new();
using StreamWriter writer = new(stream);
writer.Write(content);
writer.Flush();
stream.Position = 0;
JsonSerializerOptions options = new();
options.Converters.Add(new JsonStringEnumConverter());
ValidationProblemDetails? details = await
JsonSerializer.DeserializeAsync<ValidationProblemDetails>(stream, options);
Console.WriteLine(details.Status); // null
Type of breaking change
This change is a behavioral change.
Reason for change
Now that JsonSerializerOptions.IgnoreNullValues is supported by the System.Text.Json
APIs, we've removed the custom converters in favor of the serialization provided by the
framework.
Recommended action
Provide a JsonSerializerOptions with the correct details.
C#
JsonSerializerOptions options = new()
{
PropertyNameCaseInsensitive = true
};
ValidationProblemDetails? details = await
JsonSerializer.DeserializeAsync<ValidationProblemDetails>(stream, options);
Affected APIs
Microsoft.AspNetCore.Mvc.ProblemDetails
Microsoft.AspNetCore.Mvc.ValidationProblemDetails
ISystemClock is obsolete
Article • 06/01/2023
Microsoft.AspNetCore.Authentication.ISystemClock has been used by ASP.NET Core's
authentication and identity components since version 1.0 to enable unit testing of timerelated functionality, like expiration checking. .NET 8 includes a suitable abstraction,
System.TimeProvider, that provides the same functionality and much more. We're taking
this opportunity to obsolete ISystemClock and replace it with TimeProvider throughout
the ASP.NET Core libraries.
Version introduced
ASP.NET Core 8.0 Preview 5
Previous behavior
ISystemClock was injected into the constructors of the authentication and identity
components by dependency injection (DI) and could be overridden for testing.
The default SystemClock implementation truncated to the nearest second for easier
formatting.
New behavior
ISystemClock, SystemClock, and the authentication handler constructors that have an
ISystemClock parameter have been marked obsolete. Using these APIs in code will
generate a warning at compile time.
ISystemClock remains in the dependency injection container but is no longer used. It
may be removed from the container in a future version.
TimeProvider is now a settable property on the Options classes for the authentication
and identity components. It can be set directly or by registering a provider in the
dependency injection container.
TimeProvider does not truncate to the nearest second. Consumers are expected to
correctly format the time as needed.
Type of breaking change
This change affects source compatibility.
Reason for change
This change was made to unify time abstraction across the stack for easier testing.
Recommended action
If you have components that derive from
Microsoft.AspNetCore.Authentication.AuthenticationHandler<TOptions> or
Microsoft.AspNetCore.Identity.SecurityStampValidator<TUser>, remove the
ISystemClock constructor parameter and call the new base constructor accordingly.
diff
- public
BasicAuthenticationHandler(IOptionsMonitor<AuthenticationSchemeOptions>
options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock)
: base(options, logger, encoder, clock)
+ public
BasicAuthenticationHandler(IOptionsMonitor<AuthenticationSchemeOptions>
options, ILoggerFactory logger, UrlEncoder encoder)
+
: base(options, logger, encoder)
Similarly, derived implementations that reference the Clock property on these types
should reference the new TimeProvider property instead.
diff
- var currentUtc = Clock.UtcNow;
+ var currentUtc = TimeProvider.GetUtcNow();
You can set TimeProvider for testing on the options or via DI.
Affected APIs
Microsoft.AspNetCore.Authentication.ISystemClock
Microsoft.AspNetCore.Authentication.SystemClock
Microsoft.AspNetCore.Authentication.AuthenticationHandler<TOptions>
Microsoft.AspNetCore.Authentication.AuthenticationHandler<TOptions>.Clock
CookieAuthenticationHandler(IOptionsMonitor<CookieAuthenticationOptions>,
ILoggerFactory, UrlEncoder, ISystemClock)
FacebookHandler(IOptionsMonitor<FacebookOptions>, ILoggerFactory,
UrlEncoder, ISystemClock)
GoogleHandler(IOptionsMonitor<GoogleOptions>, ILoggerFactory, UrlEncoder,
ISystemClock)
JwtBearerHandler(IOptionsMonitor<JwtBearerOptions>, ILoggerFactory,
UrlEncoder, ISystemClock)
MicrosoftAccountHandler(IOptionsMonitor<MicrosoftAccountOptions>,
ILoggerFactory, UrlEncoder, ISystemClock)
NegotiateHandler(IOptionsMonitor<NegotiateOptions>, ILoggerFactory,
UrlEncoder, ISystemClock)
OAuthHandler<TOptions>(IOptionsMonitor<TOptions>, ILoggerFactory,
UrlEncoder, ISystemClock)
OpenIdConnectHandler(IOptionsMonitor<OpenIdConnectOptions>,
ILoggerFactory, HtmlEncoder, UrlEncoder, ISystemClock)
Microsoft.AspNetCore.Authentication.PolicySchemeHandler.PolicySchemeHandler(I
OptionsMonitor<PolicySchemeOptions>, ILoggerFactory, UrlEncoder,
ISystemClock)
Microsoft.AspNetCore.Authentication.RemoteAuthenticationHandler<TOptions>
SignInAuthenticationHandler<TOptions>(IOptionsMonitor<TOptions>,
ILoggerFactory, UrlEncoder, ISystemClock)
SignOutAuthenticationHandler<TOptions>(IOptionsMonitor<TOptions>,
ILoggerFactory, UrlEncoder, ISystemClock)
TwitterHandler(IOptionsMonitor<TwitterOptions>, ILoggerFactory, UrlEncoder,
ISystemClock)
WsFederationHandler(IOptionsMonitor<WsFederationOptions>, ILoggerFactory,
UrlEncoder, ISystemClock)
SecurityStampValidator<TUser>(IOptions<SecurityStampValidatorOptions>,
SignInManager<TUser>, ISystemClock, ILoggerFactory)
Microsoft.AspNetCore.Identity.SecurityStampValidator<TUser>.Clock
TwoFactorSecurityStampValidator<TUser>
(IOptions<SecurityStampValidatorOptions>, SignInManager<TUser>,
ISystemClock, ILoggerFactory)
Rate-limiting middleware requires
AddRateLimiter
Article • 06/01/2023
ASP.NET Core rate-limiting middleware has been updated with extra functionality. The
middleware now requires services registered with AddRateLimiter.
Version introduced
ASP.NET Core 8.0 Preview 5
Previous behavior
Previously, rate limiting could be used without AddRateLimiter. For example, the
middleware could be configured by calling Configure<RateLimiterOptions>(o => { }) :
C#
var builder = WebApplication.CreateBuilder(args);
builder.Services.Configure<RateLimiterOptions>(o => o
.AddFixedWindowLimiter(policyName: "fixed", options =>
{
// configuration
}));
var app = builder.Build();
app.UseRateLimiter();
app.MapGet("/", () => Results.Ok($"Hello
world")).RequireRateLimiting("fixed");
app.Run();
New behavior
If AddRateLimiter is not called on app startup, ASP.NET Core throws an informative
error:
Unable to find the required services. Please add all the required services by calling
'IServiceCollection.AddRateLimiter' in the application startup code.
Type of breaking change
This change is a behavioral change.
Reason for change
Rate-limiting middleware requires services that are only registered by calling
AddRateLimiter.
Recommended action
Ensure that UseRateLimiter is called at application startup.
For example, update Configure<RateLimiterOptions>(o => { }) to use UseRateLimiter:
C#
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddRateLimiter(o => o
.AddFixedWindowLimiter(policyName: "fixed", options =>
{
// configuration
}));
var app = builder.Build();
app.UseRateLimiter();
app.MapGet("/", () => Results.Ok($"Hello
world")).RequireRateLimiting("fixed");
app.Run();
Affected APIs
Microsoft.AspNetCore.Builder.RateLimiterApplicationBuilderExtensions.UseRateLim
iter
Security token events return a
JSonWebToken
Article • 08/03/2023
The JwtBearerEvents, WsFederationEvents, and OpenIdConnectEvents events are
authentication events fired respectively by the JwtBearer, WsFederation, and
OpenIdConnect authentication handlers. For example, the OnTokenValidated event is
fired when a security token is validated. These events are fired with a context (for
example, TokenValidatedContext) that exposes a TokenValidatedContext.SecurityToken
property of abstract type SecurityToken. The default real implementation of
TokenValidatedContext.SecurityToken changed from JwtSecurityToken to JsonWebToken.
Version introduced
ASP.NET Core 8.0 Preview 7
Previous behavior
Previously, the affected SecurityToken properties were implemented by
JwtSecurityToken, which derives from SecurityToken, JwtSecurityToken is the previous
generation of JSON Web Token (JWT) implementation. The JwtSecurityToken tokens
were produced by SecurityTokenValidators.
New behavior
Starting in ASP.NET Core 8.0, the Microsoft.IdentityModel.JsonWebTokens class, which
also derives from SecurityToken, implements the SecurityToken properties, by default.
Microsoft.IdentityModel.JsonWebTokens tokens are produced by more optimized
TokenHandler handlers.
Type of breaking change
This change is a behavioral change.
Reason for change
This change was made because JsonWebToken (and its associated
JsonWebTokenHandler) bring the following benefits:
30% performance improvement.
Improved reliability by using a "last known good" metadata (such as
OpenIdConnectMetadata ).
Async processing.
Recommended action
For most users, this change shouldn't be a problem as the type of the properties
(SecurityToken) hasn't changed, and you weren't supposed to look at the real type.
However, if you were down-casting one of the affected SecurityToken properties to
JwtSecurityToken (for example, to get the claims), you have two options:
Down-cast the property to JSonWebToken :
C#
service.Configure<JwtBearerOptions>
(JwtBearerDefaults.AuthenticationScheme, options => {
options.Events.TokenValidated = (context) => {
// Replace your cast to JwtSecurityToken.
JSonWebToken token = context.SecurityToken as JSonWebToken;
// Do something ...
};
});
Set one of the UseSecurityTokenValidators Boolean properties on the
corresponding options (JwtBearerOptions, WsFederationOptions, or
OpenIdConnectOptions) to true . By setting the property to true , the
authentication handlers will keep using JwtTokenValidators and will keep
producing JwtSecurityToken tokens.
C#
service.Configure<JwtBearerOptions>
(JwtBearerDefaults.AuthenticationScheme, options => {
options.UseSecurityTokenValidators = true;
options.Events.TokenValidated = (context) => {
// As you were doing before
JwtSecurityToken token = context.SecurityToken as
JwtSecurityToken;
// Do something ...
};
});
Affected APIs
Microsoft.AspNetCore.Authentication.WsFederation.SecurityTokenValidatedContext
.SecurityToken
Microsoft.AspNetCore.Authentication.JwtBearer.TokenValidatedContext.SecurityTok
en
Microsoft.AspNetCore.Authentication.OpenIdConnect.TokenValidatedContext.Secur
ityToken
AuthorizationCodeReceivedContext.SecurityToken
TrimMode defaults to full for Web SDK
projects
Article • 08/03/2023
Trimming now trims all assemblies in applications that target the Web SDK, by default.
This change only affects apps that are published with PublishTrimmed=true , and it only
breaks apps that had existing trim warnings.
Version introduced
ASP.NET Core 8.0 Preview 7
Previous behavior
Previously, TrimMode=partial was set by default for all projects that targeted the Web
SDK.
New behavior
Starting in .NET 8, all the assemblies in the app are trimmed, by default. Apps that
previously worked with PublishTrimmed=true and TrimMode=partial might not work in
.NET 8 Preview 7 and later versions. However, only apps with trim warnings are affected.
If your app has no trim warnings, the change in behavior should not cause any adverse
effects.
Type of breaking change
This change can affect source compatibility.
Reason for change
This change helps to decrease app size without users having to explicitly opt in. It also
aligns with user expectations that the entire app is trimmed unless noted otherwise.
Recommended action
The best resolution is to resolve all the trim warnings in your application. For
information about resolving the warnings in your own libraries, see Introduction to trim
warnings.
To revert to the previous behavior, set the TrimMode property to partial .
XML
<TrimMode>partial</TrimMode>
Affected APIs
None.
'ca-certificates' and 'krb5-libs' packages
removed from Alpine images
Article • 08/03/2023
The krb5-libs package is no longer installed in .NET Alpine container images. This
package enables Kerberos secure networking. Kerberos is installed by default in Debian
and Ubuntu, so .NET Debian and Ubuntu images aren't affected by this change.
In addition, the ca-certificates package is no longer installed in .NET Alpine container
images. The Alpine base image includes the ca-certificates-bundle package, which
provides the same baseline content that's needed for most .NET apps. It's expected that
very few (if any) .NET apps will be affected by this change.
Previous behavior
Prior to .NET 8, the ca-certificates and krb5-libs packages were included in .NET's
Alpine container images.
New behavior
.NET no longer includes the ca-certificates and krb5-libs packages in its Alpine
container images.
Version introduced
.NET 8 Preview 7
Type of change
This change can affect binary compatibility.
Reason for change
The packages were removed to reduce the image size.
For krb5-libs , the Kerberos secure networking scenario was considered not
popular enough to warrant installing this package by default. The removal of this
package reduces .NET 8 Alpine images by ~2.7 MB.
For ca-certificates , the removal of this package reduces .NET 8 Alpine images by
0.6MB.
Recommended action
If you require the affected packages for your scenario, install them yourself using the
following Dockerfile instruction.
Dockerfile
RUN apk add --upgrade krb5-libs
RUN apk add --upgrade ca-certificates
Affected APIs
System.DirectoryServices.Protocols
System.Net.Http.HttpClient
Debian container images upgraded to
Debian 12
Article • 07/14/2023
The default Linux distro for .NET container images is Debian. In .NET 8, the Debian
version has been upgraded to Debian 12 (Bookworm). As a result of upgraded package
versions, this new version of Debian may have changes in it that break your application.
The most notable change is the upgrade of Open SSL from 1.1 to 3.0.
Previous behavior
In .NET 6 and 7, the default Debian version was Debian 11 (Bullseye).
Notable package versions:
libicu : 67
libssl : 1.1
New behavior
In .NET 8, the default Debian version is Debian 12 (Bookworm).
Notable package versions:
libicu : 72
libssl : 3
Version introduced
.NET 8 Preview 1
Type of change
This change can affect binary compatibility and is also a behavioral change.
Reason for change
According to the container support policy , with each version of .NET, the default
version of Debian that's targeted is the latest stable version.
Recommended action
If necessary, update your application to work with the newer package versions.
Affected APIs
None.
See also
Debian release notes
Default ASP.NET Core port changed to
8080
Article • 08/10/2023
The default ASP.NET Core port configured in .NET container images has been updated
from port 80 to 8080.
We also added the new ASPNETCORE_HTTP_PORTS environment variable instead of
ASPNETCORE_URLS .
Previous behavior
Prior to .NET 8, you could run a container expecting port 80 to be the default port and
be able to access the running app.
For example, running the following command allowed you to access the app locally at
port 9999, which is mapped to port 80 in the container:
docker run --rm -it -p 9999:80 <my-app>
New behavior
Starting with .NET 8, if you map to port 80 in the container without explicitly setting the
ASP.NET Core port used in the container, any attempt to connect to that mapped port
will fail.
For example, if you run the following command, you'd be unable to connect to the
application locally using port 9999.
docker run --rm -it -p 9999:80 <my-app>
Instead, change the command to use port 8080 within the container:
docker run --rm -it -p 9999:8080 <my-app>
Version introduced
.NET 8 Preview 1
Type of change
This change is a behavioral change.
Reason for change
The change to the port number was made because of the need to provide a good
usability experience when switching to a non-root user. Running as a non-root user
requires the use of a non-privileged port in some environments. Since port 80, the
previous default port, is a privileged port, the default was updated to port 8080, which is
a non-privileged port.
Recommended action
There are two ways to respond to this breaking change:
Recommended: Explicitly set the ASPNETCORE_HTTP_PORTS , ASPNETCORE_HTTPS_PORTS ,
and ASPNETCORE_URLS environment variables to the desired port. Example: docker
run --rm -it -p 9999:80 -e ASPNETCORE_HTTP_PORTS=80 <my-app>
Update existing commands and configuration that rely on the expected default
port of port 80 to reference port 8080 instead. Example: docker run --rm -it -p
9999:8080 <my-app>
Affected APIs
None.
See also
New non-root 'app' user in Linux images
Containerize a .NET app
Blog: Secure your .NET cloud apps with rootless Linux containers
Bog: Running non-root .NET containers with Kubernetes
'libintl' package removed from Alpine
images
Article • 07/14/2023
The libintl package is no longer included in .NET's Alpine container images.
Previous behavior
Prior to .NET 8, the libintl package was included in .NET's Alpine container images.
New behavior
.NET no longer includes the libintl package in its Alpine container images.
If your application has its own dependency on libintl , you might see the following
error when running with .NET 8 in an Alpine container:
Error loading shared library libintl.so.8: No such file or directory
Version introduced
.NET 8 Preview 5
Type of change
This change is a behavioral change.
Reason for change
It was determined that .NET has no dependency on the libintl package. Only packages
that .NET requires are included on top of the base Alpine container image.
Recommended action
Verify the functionality of your application when upgrading to .NET 8. If your application
has a dependency on the libintl package, you can include it in the image by adding
the following instruction to your Dockerfile:
RUN apk add --no-cache libintl
Affected APIs
None.
See also
dotnet/docker issue 4627
Multi-platform container tags are Linuxonly
Article • 07/14/2023
The .NET 8 multi-platform container tags have been updated to be Linux-only. This
means that the latest , <major>.<minor> , and <major>.<minor>.<patch> tags are Linuxonly going forward.
Multi-platform tags, also known as multi-arch or manifest list tags, are dynamic tags
that cause the appropriate image to be retrieved based on the context of the host
system. For example, if you pull an image with a multi-platform tag from a Linux Arm64
machine, you'll get an Arm64 image (if the tag supports that).
Previous behavior
Previously, you could reference a tag like 7.0 and be able to retrieve a Windows-based
container image.
New behavior
Starting in .NET 8, the 8.0 tag will only retrieve a Linux-based image.
Version introduced
.NET 8 Preview 3
Type of change
This change is a behavioral change.
Reason for change
This change was made because of usability issues related to the platform-matching
algorithm for containerd when used in conjunction with Windows desktop OS versions.
This change aligns .NET's Windows container images with the method of tagging that's
used for the actual base Windows Server container images.
Recommended action
Update your tag usage to indicate which Windows version you're targeting. Instead of
using an image name like mcr.microsoft.com/dotnet/aspnet:8.0 , you'll now need to use
something like one of the following:
mcr.microsoft.com/dotnet/aspnet:8.0-nanoserver-1809
mcr.microsoft.com/dotnet/aspnet:8.0-nanoserver-ltsc2022
mcr.microsoft.com/dotnet/aspnet:8.0-windowsservercore-ltsc2019
mcr.microsoft.com/dotnet/aspnet:8.0-windowsservercore-ltsc2022
Select an image name based on whether you're using Nano Server or Windows Server
Core and which version of that OS. You can find a full list of all supported tags on .NET's
Docker Hub page
.
7 Note
In the example image names, the 8.0 tag prefix is used, which is the value that will
exist once .NET 8 ships. While .NET 8 is still in preview, you can replace 8.0 with
8.0-preview .
Affected APIs
None.
See also
dotnet/dotnet-docker issue 4492
New 'app' user in Linux images
Article • 07/14/2023
The .NET Linux container images include a new non-root user named app . You can opt
into this new user to provide security benefits. The name of this user may conflict with
an existing user defined by an application's Dockerfile.
Previous behavior
Prior to .NET 8, the Linux container images did not include any additional users beyond
what was included by default in the base Linux container image (for example, Debian,
Alpine, and Ubuntu).
New behavior
Starting in .NET 8, Linux container images define a user named `app`` that can be optedinto for additional security benefits. However, the name of this user may conflict with an
existing user that was defined by the application's Dockerfile. If the application's
Dockerfile attempts to create a user with the same name, an error might occur saying
that the user already exists.
Version introduced
.NET 8 Preview 1
Type of change
This change is a behavioral change.
Reason for change
The new user was introduced to improve usability for securing containers.
Recommended action
If your application's Dockerfile attempts to create a new user with the same name as the
existing app user, there are two options:
Update the Dockerfile to change the name of the user so that it no longer conflicts.
Remove the user creation logic and migrate to use the built-in app user instead.
Affected APIs
None.
See also
Blog post: Rootless Linux containers
Activity operation name when null
Article • 01/27/2023
Starting in .NET 8, if you create an Activity object using null for the operation name,
the operation name will be stored as an empty string ( "" ) instead of null .
Previous behavior
Previously, if you created an Activity object using a null operation name, the operation
name inside the activity was stored as null .
C#
new Activity(operationName: null).OperationName // Value is null.
New behavior
Starting in .NET 8, if you create an Activity object using a null operation name, the
operation name is stored as an empty string.
C#
new Activity(operationName: null).OperationName // Value is "".
Version introduced
.NET 8 Preview 1
Type of breaking change
This change is a behavioral change.
Reason for change
A null operation name in an Activity object can have an undesirable effect on backend
trace collectors, which usually assume non-null operation names. To avoid crashes, trace
collectors have to special case null operation names inside an Activity object. This
change removes the special case requirement.
Recommended action
This change is unlikely to cause breaks as using null when creating Activity objects is
rare. If for any reason your code depended on the null value for the operation name,
adjust the code to either not use null or expect that the operation name will be stored
as an empty string when you specify null .
Affected APIs
Activity constructor
System.Diagnostics.Activity.OperationName
System.Diagnostics.ActivitySource.CreateActivity
System.Diagnostics.ActivitySource.StartActivity
AnonymousPipeServerStream.Dispose
behavior for
HandleInheritability.Inheritable
Article • 02/08/2023
To avoid resource leaks, your code should call the
AnonymousPipeServerStream.DisposeLocalCopyOfClientHandle() method after passing
the client handle to the child process. The behavior of
AnonymousPipeServerStream.Dispose has been improved to lower the chance of similar
leaks for users who don't call DisposeLocalCopyOfClientHandle().
Previous behavior
Previously, the client handle owned by the AnonymousPipeServerStream instance wasn't
disposed by AnonymousPipeServerStream.Dispose unless the handle wasn't exposed at all.
New behavior
Starting in .NET 8, the client handle owned by a server that was created for out-of-proc
communication is disposed by AnonymousPipeServerStream.Dispose if it's not exposed by
using the AnonymousPipeServerStream.ClientSafePipeHandle property. (You create a
server for out-of-proc communication by passing HandleInheritability.Inheritable to the
AnonymousPipeServerStream(PipeDirection, HandleInheritability) constructor.)
Version introduced
.NET 8 Preview 1
Type of breaking change
This change is a behavioral change.
Reason for change
This change was introduced to avoid a common resource leak.
Recommended action
If a server was created for out-of-proc communication, don't reuse the client handle
that's exposed as a string via the GetClientHandleAsString() method after the server
instance has been disposed.
Affected APIs
System.IO.Pipes.AnonymousPipeServerStream (specifically,
AnonymousPipeServerStream.Dispose() )
API obsoletions with non-default
diagnostic IDs (.NET 8)
Article • 06/08/2023
Some APIs have been marked as obsolete, starting in .NET 8. This breaking change is
specific to APIs that have been marked as obsolete with a custom diagnostic ID.
Suppressing the default obsoletion diagnostic ID, which is CS0618 for the C# compiler,
does not suppress the warnings that the compiler generates when these APIs are used.
Change description
In previous .NET versions, these APIs can be used without any build warning. In .NET 8
and later versions, use of these APIs produces a compile-time warning or error with a
custom diagnostic ID. The use of custom diagnostic IDs allows you to suppress the
obsoletion warnings individually instead of blanket-suppressing all obsoletion warnings.
The following table lists the custom diagnostic IDs and their corresponding warning
messages for obsoleted APIs.
Diagnostic
ID
Description
Severity
SYSLIB0048
RSA.EncryptValue(Byte[]) and RSA.DecryptValue(Byte[]) are obsolete. Use
RSA.Encrypt and RSA.Decrypt instead.
Warning
SYSLIB0050
Formatter-based serialization is obsolete and should not be used.
Warning
SYSLIB0051
APIs that support obsolete formatter-based serialization are obsolete.
Warning
They should not be called or extended by application code.
SYSLIB0053
AesGcm should indicate the required tag size for encryption and
decryption. Use a constructor that accepts the tag size.
Version introduced
.NET 8
Type of breaking change
These obsoletions can affect source compatibility.
Warning
Recommended action
Follow the specific guidance provided for the each diagnostic ID using the URL link
provided on the warning.
Warnings or errors for these obsoletions can't be suppressed using the standard
diagnostic ID for obsolete types or members; use the custom SYSLIBxxxx
diagnostic ID value instead.
Affected APIs
SYSLIB0048
System.Security.Cryptography.RSA.EncryptValue(Byte[])
System.Security.Cryptography.RSA.DecryptValue(Byte[])
System.Security.Cryptography.RSACryptoServiceProvider.EncryptValue(Byte[])
System.Security.Cryptography.RSACryptoServiceProvider.DecryptValue(Byte[])
SYSLIB0050
System.Runtime.Serialization.FormatterConverter
System.Runtime.Serialization.FormatterServices
System.Runtime.Serialization.IFormatterConverter
System.Runtime.Serialization.IObjectReference
System.Runtime.Serialization.ISafeSerializationData
System.Runtime.Serialization.ISerializationSurrogate
System.Runtime.Serialization.ISurrogateSelector
System.Runtime.Serialization.ObjectIDGenerator
System.Runtime.Serialization.ObjectManager
System.Runtime.Serialization.SafeSerializationEventArgs
System.Runtime.Serialization.SerializationObjectManager
System.Runtime.Serialization.StreamingContextStates
System.Runtime.Serialization.SurrogateSelector
System.Runtime.Serialization.Formatters.FormatterAssemblyStyle
System.Runtime.Serialization.Formatters.FormatterTypeStyle
System.Runtime.Serialization.Formatters.IFieldInfo
System.Runtime.Serialization.Formatters.TypeFilterLevel
System.Type.IsSerializable
System.Reflection.FieldAttributes.NotSerialized
System.Reflection.FieldInfo.IsNotSerialized
System.Reflection.TypeAttributes.Serializable
System.Runtime.Serialization.ISerializable.GetObjectData(SerializationInfo,
StreamingContext)
SerializationInfo(Type, IFormatterConverter, Boolean)
SerializationInfo(Type, IFormatterConverter)
StreamingContext(StreamingContextStates, Object)
StreamingContext(StreamingContextStates)
SYSLIB0051
The SYSLIB0051 API obsoletions are organized here by namespace.
Microsoft.CSharp.RuntimeBinder namespace
Microsoft.CSharp.RuntimeBinder.RuntimeBinderException.RuntimeBinderException
(SerializationInfo, StreamingContext)
Microsoft.CSharp.RuntimeBinder.RuntimeBinderInternalCompilerException.Runtim
eBinderInternalCompilerException(SerializationInfo, StreamingContext)
Microsoft.VisualBasic.FileIO namespace
Microsoft.VisualBasic.FileIO.MalformedLineException.MalformedLineException(Seri
alizationInfo, StreamingContext)
Microsoft.VisualBasic.FileIO.MalformedLineException.GetObjectData(SerializationIn
fo, StreamingContext)
System namespace
System.AccessViolationException.AccessViolationException(SerializationInfo,
StreamingContext)
System.AggregateException.AggregateException(SerializationInfo,
StreamingContext)
System.AggregateException.GetObjectData(SerializationInfo, StreamingContext)
System.AppDomainUnloadedException.AppDomainUnloadedException(Serializatio
nInfo, StreamingContext)
System.ApplicationException.ApplicationException(SerializationInfo,
StreamingContext)
System.ArgumentException.ArgumentException(SerializationInfo,
StreamingContext)
System.ArgumentException.GetObjectData(SerializationInfo, StreamingContext)
System.ArgumentNullException.ArgumentNullException(SerializationInfo,
StreamingContext)
System.ArgumentOutOfRangeException.ArgumentOutOfRangeException(Serializati
onInfo, StreamingContext)
System.ArgumentOutOfRangeException.GetObjectData(SerializationInfo,
StreamingContext)
System.ArithmeticException.ArithmeticException(SerializationInfo,
StreamingContext)
System.ArrayTypeMismatchException.ArrayTypeMismatchException(SerializationInf
o, StreamingContext)
System.BadImageFormatException.BadImageFormatException(SerializationInfo,
StreamingContext)
System.BadImageFormatException.GetObjectData(SerializationInfo,
StreamingContext)
System.CannotUnloadAppDomainException.CannotUnloadAppDomainException(S
erializationInfo, StreamingContext)
System.ContextMarshalException.ContextMarshalException(SerializationInfo,
StreamingContext)
System.DBNull.GetObjectData(SerializationInfo, StreamingContext)
System.Delegate.GetObjectData(SerializationInfo, StreamingContext)
System.DivideByZeroException.DivideByZeroException(SerializationInfo,
StreamingContext)
System.DuplicateWaitObjectException.DuplicateWaitObjectException(SerializationI
nfo, StreamingContext)
System.EntryPointNotFoundException.EntryPointNotFoundException(SerializationIn
fo, StreamingContext)
System.Exception.Exception(SerializationInfo, StreamingContext)
System.Exception.GetObjectData(SerializationInfo, StreamingContext)
System.FieldAccessException.FieldAccessException(SerializationInfo,
StreamingContext)
System.FormatException.FormatException(SerializationInfo, StreamingContext)
System.InvalidCastException.InvalidCastException(SerializationInfo,
StreamingContext)
System.InvalidOperationException.InvalidOperationException(SerializationInfo,
StreamingContext)
System.InvalidTimeZoneException.InvalidTimeZoneException(SerializationInfo,
StreamingContext)
System.MemberAccessException.MemberAccessException(SerializationInfo,
StreamingContext)
System.MethodAccessException.MethodAccessException(SerializationInfo,
StreamingContext)
System.MissingFieldException.MissingFieldException(SerializationInfo,
StreamingContext)
System.MissingMemberException.MissingMemberException(SerializationInfo,
StreamingContext)
System.MissingMemberException.GetObjectData(SerializationInfo,
StreamingContext)
System.MissingMethodException.MissingMethodException(SerializationInfo,
StreamingContext)
System.MulticastDelegate.GetObjectData(SerializationInfo, StreamingContext)
System.NotImplementedException.NotImplementedException(SerializationInfo,
StreamingContext)
System.NotSupportedException.NotSupportedException(SerializationInfo,
StreamingContext)
System.NullReferenceException.NullReferenceException(SerializationInfo,
StreamingContext)
System.ObjectDisposedException.ObjectDisposedException(SerializationInfo,
StreamingContext)
System.ObjectDisposedException.GetObjectData(SerializationInfo,
StreamingContext)
System.OperatingSystem.GetObjectData(SerializationInfo, StreamingContext)
System.OperationCanceledException.OperationCanceledException(SerializationInfo
, StreamingContext)
System.OutOfMemoryException.OutOfMemoryException(SerializationInfo,
StreamingContext)
System.OverflowException.OverflowException(SerializationInfo, StreamingContext)
System.PlatformNotSupportedException.PlatformNotSupportedException(Serializat
ionInfo, StreamingContext)
System.RankException.RankException(SerializationInfo, StreamingContext)
System.RuntimeFieldHandle.GetObjectData(SerializationInfo, StreamingContext)
System.RuntimeMethodHandle.GetObjectData(SerializationInfo, StreamingContext)
System.RuntimeTypeHandle.GetObjectData(SerializationInfo, StreamingContext)
System.SystemException.SystemException(SerializationInfo, StreamingContext)
System.TimeoutException.TimeoutException(SerializationInfo, StreamingContext)
System.TimeZoneNotFoundException.TimeZoneNotFoundException(SerializationInf
o, StreamingContext)
System.TypeAccessException.TypeAccessException(SerializationInfo,
StreamingContext)
System.TypeInitializationException.GetObjectData(SerializationInfo,
StreamingContext)
System.TypeLoadException.TypeLoadException(SerializationInfo, StreamingContext)
System.TypeLoadException.GetObjectData(SerializationInfo, StreamingContext)
System.TypeUnloadedException.TypeUnloadedException(SerializationInfo,
StreamingContext)
System.UnauthorizedAccessException.UnauthorizedAccessException(SerializationIn
fo, StreamingContext)
System.WeakReference.WeakReference(SerializationInfo, StreamingContext)
System.WeakReference.GetObjectData(SerializationInfo, StreamingContext)
System.UriFormatException.UriFormatException(SerializationInfo,
StreamingContext)
System.Collections namespace
System.Collections.Comparer.GetObjectData(SerializationInfo, StreamingContext)
System.Collections.Generic namespace
System.Collections.Generic.LinkedList<T>.LinkedList<T>(SerializationInfo,
StreamingContext)
System.Collections.Generic.LinkedList<T>.GetObjectData(SerializationInfo,
StreamingContext)
System.Collections.Generic.SortedSet<T>.SortedSet<T>(SerializationInfo,
StreamingContext)
System.Collections.Generic.Dictionary<TKey,TValue>.Dictionary<TKey,TValue>
(SerializationInfo, StreamingContext)
System.Collections.Generic.Dictionary<TKey,TValue>.GetObjectData(SerializationInf
o, StreamingContext)
System.Collections.Generic.HashSet<T>.HashSet<T>(SerializationInfo,
StreamingContext)
System.Collections.Generic.HashSet<T>.GetObjectData(SerializationInfo,
StreamingContext)
System.Collections.Generic.KeyNotFoundException.KeyNotFoundException(Serializ
ationInfo, StreamingContext)
System.Collections.Specialized namespace
System.Collections.Specialized.NameObjectCollectionBase.NameObjectCollectionB
ase(SerializationInfo, StreamingContext)
System.Collections.Specialized.NameObjectCollectionBase.GetObjectData(Serializat
ionInfo, StreamingContext)
System.Collections.Specialized.NameValueCollection.NameValueCollection(Serializa
tionInfo, StreamingContext)
System.Collections.Specialized.OrderedDictionary.OrderedDictionary(SerializationIn
fo, StreamingContext)
System.Collections.Specialized.OrderedDictionary.GetObjectData(SerializationInfo,
StreamingContext)
System.ComponentModel namespace
System.ComponentModel.InvalidAsynchronousStateException.InvalidAsynchronous
StateException(SerializationInfo, StreamingContext)
System.ComponentModel.InvalidEnumArgumentException.InvalidEnumArgumentE
xception(SerializationInfo, StreamingContext)
System.ComponentModel.LicenseException.LicenseException(SerializationInfo,
StreamingContext)
System.ComponentModel.LicenseException.GetObjectData(SerializationInfo,
StreamingContext)
System.ComponentModel.WarningException.WarningException(SerializationInfo,
StreamingContext)
System.ComponentModel.WarningException.GetObjectData(SerializationInfo,
StreamingContext)
System.ComponentModel.Win32Exception.Win32Exception(SerializationInfo,
StreamingContext)
System.ComponentModel.Win32Exception.GetObjectData(SerializationInfo,
StreamingContext)
System.ComponentModel.Composition namespace
System.ComponentModel.Composition.CompositionContractMismatchException.C
ompositionContractMismatchException(SerializationInfo, StreamingContext)
System.ComponentModel.Composition.ImportCardinalityMismatchException.Impor
tCardinalityMismatchException(SerializationInfo, StreamingContext)
System.ComponentModel.Composition.Primitives namespace
System.ComponentModel.Composition.Primitives.ComposablePartException.Comp
osablePartException(SerializationInfo, StreamingContext)
System.ComponentModel.Composition.Primitives.ComposablePartException.GetOb
jectData(SerializationInfo, StreamingContext)
System.ComponentModel.DataAnnotations namespace
System.ComponentModel.DataAnnotations.ValidationException.ValidationExceptio
n(SerializationInfo, StreamingContext)
System.ComponentModel.Design namespace
System.ComponentModel.Design.CheckoutException.CheckoutException(Serializati
onInfo, StreamingContext)
System.Configuration namespace
System.Configuration.ConfigurationErrorsException.ConfigurationErrorsException(S
erializationInfo, StreamingContext)
System.Configuration.ConfigurationErrorsException.GetObjectData(SerializationInf
o, StreamingContext)
System.Configuration.ConfigurationException.ConfigurationException(SerializationI
nfo, StreamingContext)
System.Configuration.ConfigurationException.GetObjectData(SerializationInfo,
StreamingContext)
System.Configuration.ConfigurationSectionCollection.GetObjectData(SerializationIn
fo, StreamingContext)
System.Configuration.ConfigurationSectionGroupCollection.GetObjectData(Serializ
ationInfo, StreamingContext)
System.Configuration.PropertyInformationCollection.GetObjectData(SerializationInf
o, StreamingContext)
System.Configuration.SettingsPropertyIsReadOnlyException.SettingsPropertyIsRead
OnlyException(SerializationInfo, StreamingContext)
System.Configuration.SettingsPropertyNotFoundException.SettingsPropertyNotFou
ndException(SerializationInfo, StreamingContext)
System.Configuration.SettingsPropertyWrongTypeException.SettingsPropertyWron
gTypeException(SerializationInfo, StreamingContext)
System.Configuration.Provider.ProviderException.ProviderException(SerializationInf
o, StreamingContext)
System.Configuration.SettingsPropertyIsReadOnlyException.SettingsPropertyIsRead
OnlyException(SerializationInfo, StreamingContext)
System.Configuration.SettingsPropertyNotFoundException.SettingsPropertyNotFou
ndException(SerializationInfo, StreamingContext)
System.Configuration.SettingsPropertyWrongTypeException.SettingsPropertyWron
gTypeException(SerializationInfo, StreamingContext)
System.Data namespace
System.Data.ConstraintException.ConstraintException(SerializationInfo,
StreamingContext)
System.Data.DataException.DataException(SerializationInfo, StreamingContext)
System.Data.DataSet.GetObjectData(SerializationInfo, StreamingContext)
System.Data.DataTable.GetObjectData(SerializationInfo, StreamingContext)
System.Data.DBConcurrencyException.GetObjectData(SerializationInfo,
StreamingContext)
System.Data.DeletedRowInaccessibleException.DeletedRowInaccessibleException(S
erializationInfo, StreamingContext)
System.Data.DuplicateNameException.DuplicateNameException(SerializationInfo,
StreamingContext)
System.Data.EvaluateException.EvaluateException(SerializationInfo,
StreamingContext)
System.Data.InRowChangingEventException.InRowChangingEventException(Serializ
ationInfo, StreamingContext)
System.Data.InvalidConstraintException.InvalidConstraintException(SerializationInfo
, StreamingContext)
System.Data.InvalidExpressionException.InvalidExpressionException(SerializationInf
o, StreamingContext)
System.Data.MissingPrimaryKeyException.MissingPrimaryKeyException(Serializatio
nInfo, StreamingContext)
System.Data.NoNullAllowedException.NoNullAllowedException(SerializationInfo,
StreamingContext)
System.Data.PropertyCollection.PropertyCollection(SerializationInfo,
StreamingContext)
System.Data.ReadOnlyException.ReadOnlyException(SerializationInfo,
StreamingContext)
System.Data.RowNotInTableException.RowNotInTableException(SerializationInfo,
StreamingContext)
System.Data.StrongTypingException.StrongTypingException(SerializationInfo,
StreamingContext)
System.Data.SyntaxErrorException.SyntaxErrorException(SerializationInfo,
StreamingContext)
System.Data.TypedTableBase<T>.TypedTableBase<T>(SerializationInfo,
StreamingContext)
System.Data.VersionNotFoundException.VersionNotFoundException(SerializationInf
o, StreamingContext)
System.Data.Common namespace
System.Data.Common.DbException.DbException(SerializationInfo,
StreamingContext)
System.Data.Odbc namespace
System.Data.Odbc.OdbcException.GetObjectData(SerializationInfo,
StreamingContext)
System.Data.OleDb namespace
System.Data.OleDb.OleDbException.GetObjectData(SerializationInfo,
StreamingContext)
System.Data.SqlTypes namespace
System.Data.SqlTypes.SqlTypeException.SqlTypeException(SerializationInfo,
StreamingContext)
System.Diagnostics.Eventing.Reader namespace
System.Diagnostics.Eventing.Reader.EventLogException.EventLogException(Serializ
ationInfo, StreamingContext)
System.Diagnostics.Eventing.Reader.EventLogException.GetObjectData(Serializatio
nInfo, StreamingContext)
System.Diagnostics.Eventing.Reader.EventLogInvalidDataException.EventLogInvalid
DataException(SerializationInfo, StreamingContext)
System.Diagnostics.Eventing.Reader.EventLogNotFoundException.EventLogNotFou
ndException(SerializationInfo, StreamingContext)
System.Diagnostics.Eventing.Reader.EventLogProviderDisabledException.EventLogP
roviderDisabledException(SerializationInfo, StreamingContext)
System.Diagnostics.Eventing.Reader.EventLogReadingException.EventLogReadingE
xception(SerializationInfo, StreamingContext)
System.Diagnostics.Tracing namespace
System.Diagnostics.Tracing.EventSourceException.EventSourceException(Serializati
onInfo, StreamingContext)
System.DirectoryServices namespace
System.DirectoryServices.DirectoryServicesCOMException.DirectoryServicesCOMEx
ception(SerializationInfo, StreamingContext)
System.DirectoryServices.DirectoryServicesCOMException.GetObjectData(Serializati
onInfo, StreamingContext)
System.DirectoryServices.AccountManagement namespace
System.DirectoryServices.AccountManagement.MultipleMatchesException.Multiple
MatchesException(SerializationInfo, StreamingContext)
System.DirectoryServices.AccountManagement.NoMatchingPrincipalException.No
MatchingPrincipalException(SerializationInfo, StreamingContext)
System.DirectoryServices.AccountManagement.PasswordException.PasswordExcept
ion(SerializationInfo, StreamingContext)
System.DirectoryServices.AccountManagement.PrincipalException.PrincipalExceptio
n(SerializationInfo, StreamingContext)
System.DirectoryServices.AccountManagement.PrincipalExistsException.PrincipalExi
stsException(SerializationInfo, StreamingContext)
System.DirectoryServices.AccountManagement.PrincipalOperationException.Princip
alOperationException(SerializationInfo, StreamingContext)
System.DirectoryServices.AccountManagement.PrincipalOperationException.GetOb
jectData(SerializationInfo, StreamingContext)
System.DirectoryServices.AccountManagement.PrincipalServerDownException.Princ
ipalServerDownException(SerializationInfo, StreamingContext)
System.DirectoryServices.AccountManagement.PrincipalServerDownException.Get
ObjectData(SerializationInfo, StreamingContext)
System.DirectoryServices.ActiveDirectory namespace
System.DirectoryServices.ActiveDirectory.ActiveDirectoryObjectExistsException.Acti
veDirectoryObjectExistsException(SerializationInfo, StreamingContext)
System.DirectoryServices.ActiveDirectory.ActiveDirectoryObjectNotFoundException
.ActiveDirectoryObjectNotFoundException(SerializationInfo, StreamingContext)
System.DirectoryServices.ActiveDirectory.ActiveDirectoryObjectNotFoundException
.GetObjectData(SerializationInfo, StreamingContext)
System.DirectoryServices.ActiveDirectory.ActiveDirectoryOperationException.Active
DirectoryOperationException(SerializationInfo, StreamingContext)
System.DirectoryServices.ActiveDirectory.ActiveDirectoryOperationException.GetOb
jectData(SerializationInfo, StreamingContext)
System.DirectoryServices.ActiveDirectory.ActiveDirectoryServerDownException.Acti
veDirectoryServerDownException(SerializationInfo, StreamingContext)
System.DirectoryServices.ActiveDirectory.ActiveDirectoryServerDownException.Get
ObjectData(SerializationInfo, StreamingContext)
System.DirectoryServices.ActiveDirectory.ForestTrustCollisionException.ForestTrustC
ollisionException(SerializationInfo, StreamingContext)
System.DirectoryServices.ActiveDirectory.ForestTrustCollisionException.GetObjectD
ata(SerializationInfo, StreamingContext)
System.DirectoryServices.ActiveDirectory.SyncFromAllServersOperationException.Sy
ncFromAllServersOperationException(SerializationInfo, StreamingContext)
System.DirectoryServices.ActiveDirectory.SyncFromAllServersOperationException.G
etObjectData(SerializationInfo, StreamingContext)
System.DirectoryServices.Protocols namespace
System.DirectoryServices.Protocols.BerConversionException.BerConversionExceptio
n(SerializationInfo, StreamingContext)
System.DirectoryServices.Protocols.DirectoryException.DirectoryException(Serializat
ionInfo, StreamingContext)
System.DirectoryServices.Protocols.DirectoryOperationException.DirectoryOperatio
nException(SerializationInfo, StreamingContext)
System.DirectoryServices.Protocols.DirectoryOperationException.GetObjectData(Se
rializationInfo, StreamingContext)
System.DirectoryServices.Protocols.LdapException.LdapException(SerializationInfo,
StreamingContext)
System.DirectoryServices.Protocols.LdapException.GetObjectData(SerializationInfo,
StreamingContext)
System.DirectoryServices.Protocols.TlsOperationException.TlsOperationException(S
erializationInfo, StreamingContext)
System.Formats.Asn1 namespace
System.Formats.Asn1.AsnContentException.AsnContentException(SerializationInfo,
StreamingContext)
System.Formats.Cbor namespace
System.Formats.Cbor.CborContentException.CborContentException(SerializationInf
o, StreamingContext)
System.Globalization namespace
System.Globalization.CultureNotFoundException.CultureNotFoundException(Seriali
zationInfo, StreamingContext)
System.Globalization.CultureNotFoundException.GetObjectData(SerializationInfo,
StreamingContext)
System.IO namespace
System.IO.DirectoryNotFoundException.DirectoryNotFoundException(SerializationI
nfo, StreamingContext)
System.IO.DriveNotFoundException.DriveNotFoundException(SerializationInfo,
StreamingContext)
System.IO.EndOfStreamException.EndOfStreamException(SerializationInfo,
StreamingContext)
System.IO.FileFormatException.FileFormatException(SerializationInfo,
StreamingContext)
System.IO.FileFormatException.GetObjectData(SerializationInfo, StreamingContext)
System.IO.FileLoadException.FileLoadException(SerializationInfo,
StreamingContext)
System.IO.FileLoadException.GetObjectData(SerializationInfo, StreamingContext)
System.IO.FileNotFoundException.FileNotFoundException(SerializationInfo,
StreamingContext)
System.IO.FileNotFoundException.GetObjectData(SerializationInfo,
StreamingContext)
System.IO.FileSystemInfo.FileSystemInfo(SerializationInfo, StreamingContext)
System.IO.FileSystemInfo.GetObjectData(SerializationInfo, StreamingContext)
System.IO.InternalBufferOverflowException.InternalBufferOverflowException(Seriali
zationInfo, StreamingContext)
System.IO.IOException.IOException(SerializationInfo, StreamingContext)
System.IO.IsolatedStorage.IsolatedStorageException.IsolatedStorageException(Seri
alizationInfo, StreamingContext)
System.IO.PathTooLongException.PathTooLongException(SerializationInfo,
StreamingContext)
System.Management namespace
System.Management.ManagementBaseObject.ManagementBaseObject(Serializatio
nInfo, StreamingContext)
System.Management.ManagementClass.ManagementClass(SerializationInfo,
StreamingContext)
System.Management.ManagementException.ManagementException(SerializationIn
fo, StreamingContext)
System.Management.ManagementException.GetObjectData(SerializationInfo,
StreamingContext)
System.Management.ManagementNamedValueCollection.ManagementNamedValu
eCollection(SerializationInfo, StreamingContext)
System.Media namespace
System.Media.SoundPlayer.SoundPlayer(SerializationInfo, StreamingContext)
System.Net namespace
System.Net.CookieException.CookieException(SerializationInfo, StreamingContext)
System.Net.CookieException.GetObjectData(SerializationInfo, StreamingContext)
System.Net.FileWebRequest.FileWebRequest(SerializationInfo, StreamingContext)
System.Net.FileWebResponse.FileWebResponse(SerializationInfo,
StreamingContext)
System.Net.HttpListenerException.HttpListenerException(SerializationInfo,
StreamingContext)
System.Net.HttpWebRequest.HttpWebRequest(SerializationInfo, StreamingContext)
System.Net.HttpWebResponse.HttpWebResponse(SerializationInfo,
StreamingContext)
System.Net.ProtocolViolationException.ProtocolViolationException(SerializationInfo
, StreamingContext)
System.Net.ProtocolViolationException.GetObjectData(SerializationInfo,
StreamingContext)
System.Net.WebException.WebException(SerializationInfo, StreamingContext)
System.Net.WebException.GetObjectData(SerializationInfo, StreamingContext)
System.Net.WebHeaderCollection.WebHeaderCollection(SerializationInfo,
StreamingContext)
System.Net.WebHeaderCollection.GetObjectData(SerializationInfo,
StreamingContext)
System.Net.WebRequest.WebRequest(SerializationInfo, StreamingContext)
System.Net.WebResponse.WebResponse(SerializationInfo, StreamingContext)
System.Net.Mail namespace
System.Net.Mail.SmtpException.SmtpException(SerializationInfo,
StreamingContext)
System.Net.Mail.SmtpException.GetObjectData(SerializationInfo,
StreamingContext)
System.Net.Mail.SmtpFailedRecipientException.SmtpFailedRecipientException(Serial
izationInfo, StreamingContext)
System.Net.Mail.SmtpFailedRecipientException.GetObjectData(SerializationInfo,
StreamingContext)
System.Net.Mail.SmtpFailedRecipientsException.SmtpFailedRecipientsException(Ser
ializationInfo, StreamingContext)
System.Net.Mail.SmtpFailedRecipientsException.GetObjectData(SerializationInfo,
StreamingContext)
System.Net.NetworkInformation namespace
System.Net.NetworkInformation.NetworkInformationException.NetworkInformatio
nException(SerializationInfo, StreamingContext)
System.Net.NetworkInformation.PingException.PingException(SerializationInfo,
StreamingContext)
System.Net.Sockets namespace
System.Net.Sockets.SocketException.SocketException(SerializationInfo,
StreamingContext)
System.Reflection namespace
System.Reflection.Assembly.GetObjectData(SerializationInfo, StreamingContext)
System.Reflection.AssemblyName.GetObjectData(SerializationInfo,
StreamingContext)
System.Reflection.CustomAttributeFormatException.CustomAttributeFormatExcepti
on(SerializationInfo, StreamingContext)
System.Reflection.InvalidFilterCriteriaException.InvalidFilterCriteriaException(Serializ
ationInfo, StreamingContext)
System.Reflection.Module.GetObjectData(SerializationInfo, StreamingContext)
System.Reflection.ParameterInfo.GetRealObject(StreamingContext)
System.Reflection.ReflectionTypeLoadException.GetObjectData(SerializationInfo,
StreamingContext)
System.Reflection.StrongNameKeyPair.StrongNameKeyPair(SerializationInfo,
StreamingContext)
System.Reflection.TargetException.TargetException(SerializationInfo,
StreamingContext)
System.Reflection.Metadata namespace
System.Reflection.Metadata.ImageFormatLimitationException.ImageFormatLimitati
onException(SerializationInfo, StreamingContext)
System.Resources namespace
System.Resources.MissingManifestResourceException.MissingManifestResourceExc
eption(SerializationInfo, StreamingContext)
System.Resources.MissingSatelliteAssemblyException.MissingSatelliteAssemblyExce
ption(SerializationInfo, StreamingContext)
System.Runtime.CompilerServices namespace
System.Runtime.CompilerServices.RuntimeWrappedException.GetObjectData(Serial
izationInfo, StreamingContext)
System.Runtime.CompilerServices.SwitchExpressionException.GetObjectData(Seriali
zationInfo, StreamingContext)
System.Runtime.InteropServices namespace
System.Runtime.InteropServices.ExternalException.ExternalException(SerializationIn
fo, StreamingContext)
System.Runtime.Serialization.SerializationException.SerializationException(Serializat
ionInfo, StreamingContext)
System.Runtime.Serialization namespace
System.Runtime.Serialization.InvalidDataContractException.InvalidDataContractExc
eption(SerializationInfo, StreamingContext)
System.Security namespace
System.Security.HostProtectionException.HostProtectionException(SerializationInfo,
StreamingContext)
System.Security.SecurityException.SecurityException(SerializationInfo,
StreamingContext)
System.Security.SecurityException.GetObjectData(SerializationInfo,
StreamingContext)
System.Security.VerificationException.VerificationException(SerializationInfo,
StreamingContext)
System.Security.AccessControl namespace
System.Security.AccessControl.PrivilegeNotHeldException.GetObjectData(Serializati
onInfo, StreamingContext)
System.Security.Authentication namespace
System.Security.Authentication.InvalidCredentialException.InvalidCredentialExcepti
on(SerializationInfo, StreamingContext)
System.Security.Authentication.ExtendedProtection.ExtendedProtectionPolicy.Exten
dedProtectionPolicy(SerializationInfo, StreamingContext)
System.Security.Claims namespace
System.Security.Claims.ClaimsIdentity.ClaimsIdentity(SerializationInfo)
System.Security.Claims.ClaimsIdentity.ClaimsIdentity(SerializationInfo,
StreamingContext)
System.Security.Claims.ClaimsPrincipal.ClaimsPrincipal(SerializationInfo,
StreamingContext)
System.Security.Cryptography namespace
System.Security.Cryptography.CryptographicException.CryptographicException(Seri
alizationInfo, StreamingContext)
System.Security.Cryptography.CryptographicUnexpectedOperationException.Crypt
ographicUnexpectedOperationException(SerializationInfo, StreamingContext)
System.Security.Cryptography.X509Certificates.X509Certificate.X509Certificate(Seri
alizationInfo, StreamingContext)
System.Security.Cryptography.X509Certificates.X509Certificate2.X509Certificate2(Se
rializationInfo, StreamingContext)
System.Security.Policy namespace
System.Security.Policy.Hash.GetObjectData(SerializationInfo, StreamingContext)
System.Security.Policy.PolicyException.PolicyException(SerializationInfo,
StreamingContext)
System.Security.Principal namespace
System.Security.Principal.IdentityNotMappedException.GetObjectData(Serialization
Info, StreamingContext)
System.Security.Principal.WindowsIdentity.WindowsIdentity(SerializationInfo,
StreamingContext)
System.Text.Json namespace
System.Text.Json.JsonException.JsonException(SerializationInfo, StreamingContext)
System.Text.Json.JsonException.GetObjectData(SerializationInfo, StreamingContext)
System.Text.RegularExpressions namespace
System.Text.RegularExpressions.RegexMatchTimeoutException.RegexMatchTimeout
Exception(SerializationInfo, StreamingContext)
System.Text.RegularExpressions.RegexParseException.GetObjectData(SerializationIn
fo, StreamingContext)
System.Threading namespace
System.Threading.CompressedStack.GetObjectData(SerializationInfo,
StreamingContext)
System.Threading.ThreadInterruptedException.ThreadInterruptedException(Serializ
ationInfo, StreamingContext)
System.Threading.ThreadStateException.ThreadStateException(SerializationInfo,
StreamingContext)
System.Threading.BarrierPostPhaseException.BarrierPostPhaseException(Serializatio
nInfo, StreamingContext)
System.Threading.AbandonedMutexException.AbandonedMutexException(Serializa
tionInfo, StreamingContext)
System.Threading.ExecutionContext.GetObjectData(SerializationInfo,
StreamingContext)
System.Threading.LockRecursionException.LockRecursionException(SerializationInf
o, StreamingContext)
System.Threading.SemaphoreFullException.SemaphoreFullException(SerializationIn
fo, StreamingContext)
System.Threading.SynchronizationLockException.SynchronizationLockException(Ser
ializationInfo, StreamingContext)
System.Threading.WaitHandleCannotBeOpenedException.WaitHandleCannotBeOp
enedException(SerializationInfo, StreamingContext)
System.Threading.Channels namespace
System.Threading.Channels.ChannelClosedException.ChannelClosedException(Seria
lizationInfo, StreamingContext)
System.Threading.Tasks namespace
System.Threading.Tasks.TaskCanceledException.TaskCanceledException(Serializatio
nInfo, StreamingContext)
System.Threading.Tasks.TaskSchedulerException.TaskSchedulerException(Serializati
onInfo, StreamingContext)
System.Transactions namespace
System.Transactions.TransactionAbortedException.TransactionAbortedException(Se
rializationInfo, StreamingContext)
System.Transactions.TransactionException.TransactionException(SerializationInfo,
StreamingContext)
System.Transactions.TransactionInDoubtException.TransactionInDoubtException(Se
rializationInfo, StreamingContext)
System.Transactions.TransactionManagerCommunicationException.TransactionMan
agerCommunicationException(SerializationInfo, StreamingContext)
System.Transactions.TransactionPromotionException.TransactionPromotionExceptio
n(SerializationInfo, StreamingContext)
System.Xml namespace
System.Xml.XmlException.XmlException(SerializationInfo, StreamingContext)
System.Xml.XmlException.GetObjectData(SerializationInfo, StreamingContext)
System.Xml.Schema namespace
System.Xml.Schema.XmlSchemaException.XmlSchemaException(SerializationInfo,
StreamingContext)
System.Xml.Schema.XmlSchemaException.GetObjectData(SerializationInfo,
StreamingContext)
System.Xml.Schema.XmlSchemaInferenceException.XmlSchemaInferenceException(
SerializationInfo, StreamingContext)
System.Xml.Schema.XmlSchemaInferenceException.GetObjectData(SerializationInfo
, StreamingContext)
System.Xml.Schema.XmlSchemaValidationException.XmlSchemaValidationExceptio
n(SerializationInfo, StreamingContext)
System.Xml.Schema.XmlSchemaValidationException.GetObjectData(SerializationInf
o, StreamingContext)
System.Xml.XPath namespace
System.Xml.XPath.XPathException.XPathException(SerializationInfo,
StreamingContext)
System.Xml.XPath.XPathException.GetObjectData(SerializationInfo,
StreamingContext)
System.Xml.Xsl namespace
System.Xml.Xsl.XsltCompileException.XsltCompileException(SerializationInfo,
StreamingContext)
System.Xml.Xsl.XsltCompileException.GetObjectData(SerializationInfo,
StreamingContext)
System.Xml.Xsl.XsltException.XsltException(SerializationInfo, StreamingContext)
System.Xml.Xsl.XsltException.GetObjectData(SerializationInfo, StreamingContext)
SYSLIB0053
AesGcm(Byte[])
AesGcm(ReadOnlySpan<Byte>)
See also
API obsoletions with non-default diagnostic IDs (.NET 7)
API obsoletions with non-default diagnostic IDs (.NET 6)
API obsoletions with non-default diagnostic IDs (.NET 5)
Obsolete features in .NET 5+
Backslash mapping in Unix file paths
Article • 02/01/2023
Backslash ( \ ) characters are valid in directory and file names on Unix. Starting in .NET 8,
the native CoreCLR runtime no longer converts \ characters to directory separators—
forward slashes ( / )—on Unix. This change enables .NET applications to be located on
paths with names that contain backslash characters. It also allows the native runtime,
dotnet host, and the ilasm and ildasm tools to access files on paths that contain
backslash characters.
Previous behavior
The native CoreCLR runtime automatically converted backslash ( \ ) characters in file
paths to forward slashes ( / ) on Unix.
New behavior
The native CoreCLR runtime doesn't convert any file path characters on Unix.
Version introduced
.NET 8 Preview 1
Type of breaking change
This change is a behavioral change.
Reason for change
Without this change, .NET apps located in directories that contain backslash characters
fail to start.
Recommended action
Use Path.DirectorySeparatorChar as a directory separator in your app instead of
hardcoding it to \ or / .
Use / as a directory separator on Unix in file paths that you pass to the dotnet
host, hosting APIs, and ilasm and ildasm tools.
Use / as a directory separator on Unix in file paths in various DOTNET_xxx
environment variables.
Affected APIs
Hosting APIs
System.Runtime.InteropServices.DllImportAttribute.Value
System.Runtime.InteropServices.NativeLibrary.Load
System.Runtime.InteropServices.NativeLibrary.TryLoad
System.Reflection.Assembly.LoadFrom
System.Reflection.Assembly.LoadFile
System.Reflection.Assembly.UnsafeLoadFrom(String)
System.Runtime.Loader.AssemblyLoadContext.LoadFromAssemblyPath(String)
System.Runtime.Loader.AssemblyLoadContext.LoadFromNativeImagePath(String,
String)
System.Runtime.Loader.AssemblyLoadContext.LoadUnmanagedDllFromPath(String
)
Base64.DecodeFromUtf8 methods
ignore whitespace
Article • 06/13/2023
The Convert.FromBase64String(String), Convert.FromBase64CharArray(Char[], Int32,
Int32), and corresponding Try methods on System.Convert ignore the ASCII whitespace
characters ' ', '\t', '\r', and '\n' and allow any amount of such whitespace to be in the
input. However, when the Base64.DecodeFromUtf8(ReadOnlySpan<Byte>, Span<Byte>,
Int32, Int32, Boolean) and Base64.DecodeFromUtf8InPlace(Span<Byte>, Int32) methods
were added, they didn't ignore these whitespace characters and instead failed to decode
any input that included whitespace. That made the behavior of the UTF16-based APIs
different from that of the UTF8-based APIs. It also meant that:
The Base64.DecodeFromUtf8 and Base64.DecodeFromUtf8InPlace methods couldn't
roundtrip the UTF-encoded base-64 encoded data produced by
Convert.FromBase64String(String) with the
Base64FormattingOptions.InsertLineBreaks option.
The new IsValid(ReadOnlySpan<Char>) and IsValid(ReadOnlySpan<Byte>)
methods would either need to have behavior inconsistent with each other or with
their corresponding methods for UTF-16 and UTF-8 data on Convert and Base64.
With this change, the DecodeFromUtf8(ReadOnlySpan<Byte>, Span<Byte>, Int32, Int32,
Boolean) and DecodeFromUtf8InPlace(Span<Byte>, Int32) methods now ignore
whitespace in the input.
Previous behavior
Base64.DecodeFromUtf8(ReadOnlySpan<Byte>, Span<Byte>, Int32, Int32, Boolean) and
Base64.DecodeFromUtf8InPlace(Span<Byte>, Int32) failed to process input that
contained whitespace and returned OperationStatus.InvalidData if any whitespace was
encountered.
New behavior
Base64.DecodeFromUtf8(ReadOnlySpan<Byte>, Span<Byte>, Int32, Int32, Boolean) and
Base64.DecodeFromUtf8InPlace(Span<Byte>, Int32) now ignore whitespace (specifically
' ', '\t', '\r', and '\n') in the input, which matches the behavior of
Convert.FromBase64String(String).
Version introduced
.NET 8 Preview 5
Type of breaking change
This change is a behavioral change.
Reason for change
The change was made so that:
The Base64 methods can decode a wider range of input data, including:
Data produced by Convert.ToBase64String with the
Base64FormattingOptions.InsertLineBreaks option.
Common formatting of data in configuration files and other real data sources.
The Base64 methods are consistent with the corresponding decoding APIs on
Convert.
The new Base64.IsValid(ReadOnlySpan<Char>) and
Base64.IsValid(ReadOnlySpan<Byte>) APIs could be added in a manner where their
behavior is consistent with each other and with the existing Convert and Base64
APIs.
Recommended action
If the new behavior is problematic for your code, you can call IndexOfAny(" \t\r\n"u8)
to search the input for the whitespace that previously would have triggered an
InvalidData result.
Affected APIs
System.Buffers.Text.Base64.DecodeFromUtf8(ReadOnlySpan<Byte>, Span<Byte>,
Int32, Int32, Boolean)
System.Buffers.Text.Base64.DecodeFromUtf8InPlace(Span<Byte>, Int32)
Boolean-backed enum type support
removed
Article • 08/03/2023
Support for formatting, parsing, and conversions of Boolean-backed enumeration types
has been removed.
Previous behavior
Previously, formatting, parsing, or converting a Boolean-backed enumeration type was
somewhat functional.
New behavior
Starting in .NET 8, an InvalidOperationException is thrown if you try to format, parse, or
convert a Boolean-backed enumeration type.
Version introduced
.NET 8 Preview 1
Type of breaking change
This change is a behavioral change.
Reason for change
This change was made to make the .NET runtime simpler, faster, and smaller. Formatting
and parsing Boolean-backed enumeration types is never used in practice and
complicates the implementation. Also, Boolean-backed enum types aren't expressible in
C#.
Recommended action
If you're using a Boolean-backed enumeration type, use a regular Boolean type or a
byte-backed enumeration type instead.
Affected APIs
System.Enum.Parse
System.Enum.TryParse
System.Enum.Format(Type, Object, String)
System.Enum.GetName
System.Enum.GetNames
System.Enum.GetValues
System.Enum.ToObject
FileStream writes when pipe is closed
Article • 01/31/2023
FileStream error handling on Windows has been updated to be consistent with
NamedPipeServerStream, NamedPipeClientStream, AnonymousPipeServerStream, and
AnonymousPipeClientStream.
Previous behavior
Previously, when writing to a FileStream that represented a closed or disconnected pipe,
the underlying operating system error was ignored and the write was reported as
successful. However, nothing was written to the pipe.
New behavior
Starting in .NET 8, when writing to a FileStream whose underlying pipe is closed or
disconnected, the write fails and an IOException is thrown.
Version introduced
.NET 8 Preview 1
Type of breaking change
This change is a behavioral change.
Reason for change
This change was made to unify the handling of edge cases and avoid silent errors that
are difficult to diagnose.
Recommended action
Close or disconnect the pipe after everything has been written.
Affected APIs
System.IO.FileStream.WriteByte(Byte)
System.IO.FileStream.Write
System.IO.FileStream.WriteAsync
GC.GetGeneration might return
Int32.MaxValue
Article • 05/05/2023
Starting in .NET 8, GC.GetGeneration might return Int32.MaxValue for objects allocated
on non-GC heaps (also referred as "frozen" heaps), where previously it returned 2. When
and how the runtime allocates objects on non-GC heaps is an internal implementation
detail. String literals, for example, are allocated on a non-GC heap, and the following
method call might return Int32.MaxValue.
C#
int gen = int GetGeneration("string");
Previous behavior
Previously, GC.GetGeneration returned integer values in the range of 0-2.
New behavior
Starting in .NET 8, GC.GetGeneration can return a value of 0, 1, 2, or Int32.MaxValue.
Version introduced
.NET 8 Preview 4
Type of breaking change
This change is a behavioral change.
Reason for change
.NET introduced a new, non-GC kind of heap that's slightly different from the existing
heaps, which are large object heap (LOH), small object heap (SOH), and pinned object
heap (POH).
Recommended action
Make sure you're not using the return value from GC.GetGeneration() as an array
indexer or for anything else where Int32.MaxValue is unexpected.
Affected APIs
System.GC.GetGeneration(Object)
System.GC.GetGeneration(WeakReference)
GetFolderPath behavior on Unix
Article • 01/26/2023
Starting in .NET 8, the behavior of Environment.GetFolderPath on Unix operating
systems has changed.
Change description
The following tables show how the returned path value changes for each Unix operating
system for various special folders.
Linux
SpecialFolder
value
Path (.NET 7 and
earlier)
Path (.NET 8 and later)
MyDocuments
$HOME
Uses XDG_DOCUMENTS_DIR if available; otherwise
$HOME/Documents
Personal
Uses XDG_DOCUMENTS_DIR if available; otherwise
$HOME
$HOME/Documents
macOS
SpecialFolder value
Path (.NET 7 and
earlier)
Path (.NET 8 and later)
MyDocuments
$HOME
NSDocumentDirectory
( $HOME/Documents )
Personal
$HOME
NSDocumentDirectory
( $HOME/Documents )
ApplicationData
$HOME/.config
NSApplicationSupportDirectory
(Library/Application Support)
LocalApplicationData
$HOME/.local/share
NSApplicationSupportDirectory
(Library/Application Support)
MyVideos
$HOME/Videos
NSMoviesDirectory
( $HOME/Movies )
Android
SpecialFolder value
Path (.NET 7 and earlier)
Path (.NET 8 and later)
SpecialFolder value
Path (.NET 7 and earlier)
Path (.NET 8 and later)
MyDocuments
$HOME
$HOME/Documents
Personal
$HOME
$HOME/Documents
Version introduced
.NET 8 Preview 1
Type of breaking change
This change is a behavioral change.
Reason for change
The previous behavior was incorrect and didn't meet user expectations for Linux,
macOS, and Android.
Recommended action
The most common break is if you're passing System.Environment.SpecialFolder.Personal
to Environment.GetFolderPath(Environment+SpecialFolder) on Unix to get the $HOME
directory ( Environment.GetFolderPath(Environment.SpecialFolder.Personal) ).
Environment.SpecialFolder.Personal and Environment.SpecialFolder.MyDocuments are
aliases for the same underlying enumeration value. If you're using
Environment.SpecialFolder.Personal in this way, change your code to pass
Environment.SpecialFolder.UserProfile instead
( Environment.GetFolderPath(Environment.SpecialFolder.UserProfile) ).
For other breaks, the recommended action is to do one of the following:
Migrate your application's files to the appropriate directory.
Add a fallback check for the previous location to your code.
Affected APIs
System.Environment.GetFolderPath(Environment+SpecialFolder)
System.Environment.GetFolderPath(Environment+SpecialFolder,
Environment+SpecialFolderOption)
GetSystemVersion no longer returns
ImageRuntimeVersion
Article • 09/09/2023
RuntimeEnvironment.GetSystemVersion() no longer returns
Assembly.ImageRuntimeVersion, which is a .NET Framework-oriented value. It's been
updated to return a more relevant value, however, the historical leading v has been
maintained.
Previous behavior
RuntimeEnvironment.GetSystemVersion() returned Assembly.ImageRuntimeVersion,
which is an indicator of .NET Framework in-place replacement, not a product release.
Example: v4.0.30319
New behavior
Starting in .NET 8, RuntimeEnvironment.GetSystemVersion() returns "v" concatenated
with Environment.Version, which is the version of the CLR.
Example: v8.0.0
Version introduced
.NET 8 RC 1
Type of breaking change
This change is a behavioral change.
Reason for change
The existing version wasn't useful or meaningful for .NET.
Recommended action
Update your code to expect the new version, or use
typeof(object).Assembly.ImageRuntimeVersion instead.
Affected APIs
System.Runtime.InteropServices.RuntimeEnvironment.GetSystemVersion()
See also
The following changes are related:
Improved .NET Core version APIs
FrameworkDescription's value is .NET instead of .NET Core
IndexOfAnyValues renamed to
SearchValues
Article • 06/13/2023
.NET 8 Preview 1 introduced a new System.Buffers.IndexOfAnyValues<T> type to speed
up IndexOfAny -like operations. In .NET 8 Preview 5, the type has been renamed to
SearchValues<T>.
Previous behavior
In previous preview versions of .NET 8, the affected types were named IndexOfAnyValues
and System.Buffers.IndexOfAnyValues<T> .
New behavior
IndexOfAnyValues and System.Buffers.IndexOfAnyValues<T> are now named
SearchValues and SearchValues<T>.
Version introduced
.NET 8 Preview 5
Type of breaking change
This change can affect source compatibility and binary compatibility.
Reason for change
The type was renamed to SearchValues to allow us to extend its functionality in the
future without making the name of the type misleading. IndexOfAnyValues was
introduced in a previous preview version of .NET 8 as a way to cache computation
associated with preparing any number of values for use in a search. We expected to only
use it with IndexOfAny and possibly Contains . However, there are other places that
could benefit from exposing overloads of other operations that would take an
IndexOfAnyValues , like Count , Replace , or Remove , and in those contexts, the
IndexOfAnyValues name doesn't make sense.
Recommended action
If you've written code using IndexOfAnyValues in a previous preview version of .NET 8,
replace all usages with SearchValues . This should be as simple as a text-based find-andreplace of "IndexOfAnyValues" with "SearchValues".
Affected APIs
System.Buffers.IndexOfAnyValues<T>
System.Buffers.IndexOfAnyValues.Create()
System.MemoryExtensions.IndexOfAny<T>(ReadOnlySpan<T>, IndexOfAnyValues<T>)
System.MemoryExtensions.IndexOfAny<T>(Span<T>, IndexOfAnyValues<T>)
System.MemoryExtensions.IndexOfAnyExcept<T>(ReadOnlySpan<T>,
IndexOfAnyValues<T>)
System.MemoryExtensions.IndexOfAnyExcept<T>(Span<T>, IndexOfAnyValues<T>)
ITypeDescriptorContext nullable
annotations
Article • 02/16/2023
System.ComponentModel.ITypeDescriptorContext has three properties that were
previously annotated as being non-nullable, but they were actually nullable in practice.
The nullable annotations for these properties have been updated to indicate that they're
nullable. This change can result in new build warnings related to use of nullable
members.
Previous behavior
Previously, the affected properties were annotated as not being nullable. You could
consume their values and assume they weren't null without any compile-time
warnings.
New behavior
Starting in .NET 8, the affected properties are annotated as being nullable. If you
consume their values without null checks, you'll get warnings at compile time.
Version introduced
.NET 8 Preview 1
Type of breaking change
This change can affect source compatibility.
Reason for change
The previous annotations of these properties were incorrect. This change applies the
appropriate annotations for the properties and ensures callers understand that the
values can be null .
Recommended action
Update calling code to guard against null for these properties.
Affected APIs
System.ComponentModel.ITypeDescriptorContext.Container
System.ComponentModel.ITypeDescriptorContext.Instance
System.ComponentModel.ITypeDescriptorContext.PropertyDescriptor
Legacy Console.ReadKey removed
Article • 01/31/2023
The ability to use the legacy Console.ReadKey implementation exposed via the
System.Console.UseNet6CompatReadKey JSON setting and the
DOTNET_SYSTEM_CONSOLE_USENET6COMPATREADKEY environment variable has been removed.
Previous behavior
Previously, you could request the .NET 6 console key parsing logic via a runtime
configuration switch.
New behavior
Starting in .NET 8, you can't request the .NET 6 compatibility mode for Console.ReadKey.
Version introduced
.NET 8 Preview 1
Type of breaking change
This change is a behavioral change.
Reason for change
The compatibility mode was introduced as a safety switch in case the Console.ReadKey
implementation rewrite introduced any bugs. Only one bug was reported, and it was
fixed in .NET 7, so there's no need to keep the previous implementation anymore.
Recommended action
If the new implementation doesn't work as expected, open a bug at
https://github.com/dotnet/runtime/issues
Affected APIs
so it can be fixed.
System.Console.ReadKey
Method builders generate parameters
with HasDefaultValue set to false
Article • 08/29/2023
System.Reflection.Emit.ConstructorBuilder and System.Reflection.Emit.MethodBuilder
now generate method parameters that, when reflected on, have
ParameterInfo.HasDefaultValue set to false .
Previous behavior
Previously, ConstructorBuilder and MethodBuilder generated IL for method parameters
where the HasDefaultValue of the parameters was set to true .
New behavior
Starting in .NET 8, ConstructorBuilder and MethodBuilder generate IL for method
parameters where the HasDefaultValue of the parameters is set to false , which is the
expected value.
Version introduced
.NET 8 Preview 5
Type of breaking change
This change is a behavioral change.
Reason for change
The previous behavior was incorrect, as no default parameter values were specified
when the method or constructor was defined.
Recommended action
If you use TypeBuilder.DefineConstructor or TypeBuilder.DefineMethod, make sure
consumers of the generated types' methods don't rely on the
ParameterInfo.HasDefaultValue property being true .
Affected APIs
System.Reflection.ParameterInfo.HasDefaultValue
Removed Boolean-based overloads of
ToFrozenDictionary/ToFrozenSet
Article • 08/10/2023
Overloads of ToFrozenDictionary and ToFrozenSet previously allowed for a Boolean
optimizeForReading argument to be supplied. These overloads have been removed in
.NET 8 Preview 7.
Previous behavior
In .NET 8 Preview 1 through Preview 6, you could call overloads of ToFrozenDictionary
and ToFrozenSet that had an optimizeForReading parameter. If you passed true , the
implementations constructed instances of FrozenDictionary<TKey,TValue> and
FrozenSet<T> that were optimized for performing lookups and reading data from the
collections, at the expense of potentially much more time spent in the
ToFrozenDictionary() and ToFrozenSet() calls.
New behavior
Starting in .NET 8 Preview 7, the affected overloads have been removed. Now all of the
ToFrozenDictionary and ToFrozenSet overloads spend extra time to construct a
collection that's optimized for reading.
Version introduced
.NET 8 Preview 7
Type of breaking change
This change can affect binary compatibility and source compatibility.
Reason for change
Construction performance has been optimized such that it now no longer makes sense
to differentiate modes. ToFrozenDictionary() and ToFrozenSet() now always produce
implementations optimized for reading, regardless of which overload is used.
Recommended action
If you called the overloads that had a Boolean parameter, remove the Boolean argument
from the call site.
Affected APIs
System.Collections.Frozen.FrozenDictionary.ToFrozenDictionary<TKey,TValue>
(System.Collections.Generic.IEnumerable<System.Collections.Generic.KeyValuePai
r<TKey,TValue>>,System.Boolean)
System.Collections.Frozen.FrozenDictionary.ToFrozenDictionary<TKey,TValue>
(System.Collections.Generic.IEnumerable<System.Collections.Generic.KeyValuePai
r<TKey,TValue>>,System.Collections.Generic.IEqualityComparer<TKey,TValue>,Syst
em.Boolean)
System.Collections.Frozen.FrozenSet.ToFrozenSet<TKey,TValue>
(System.Collections.Generic.IEnumerable<TKey,TValue>,System.Boolean)
System.Collections.Frozen.FrozenSet.ToFrozenSet<TKey,TValue>
(System.Collections.Generic.IEnumerable<TKey,TValue>,System.Collections.Generi
c.IEqualityComparer<TKey,TValue>,System.Boolean)
RuntimeIdentifier returns platform for
which the runtime was built
Article • 09/09/2023
RuntimeInformation.RuntimeIdentifier returns the platform for which the runtime was
built, rather than a value computed at run time.
Previous behavior
The value was a runtime identifier (RID) computed via OS files or APIs. This generally
meant it was a version-specific and distro-specific RID. For example, when running an
application on Windows 11, the value was win10-x64 or, on Ubuntu 20.04, it could be
ubuntu.20.04-x64 .
New behavior
Starting in .NET 8, the value is the RID for which the runtime was built. This means that
for portable builds of the runtime (all Microsoft-provided builds), the value is nonversion-specific and non-distro-specific. For example, the value on Windows 11 is winx64 , and on Ubuntu 20.04, it's linux-x64 . For non-portable builds (source-build), the
build sets a build RID that can have a version and distro, and that value is the RID that's
returned.
Version introduced
.NET 8 RC 1
Type of breaking change
This change is a behavioral change.
Reason for change
This change is in line with a .NET 8 change to RID-specific asset resolution and the move
away from a distro-aware runtime. RuntimeInformation.RuntimeIdentifier is an opaque
value that should represent the platform on which the host or runtime considers itself to
be running. In .NET 8, that corresponds to the platform for which the host or runtime is
built, rather than an RID computed at run time.
Recommended action
RuntimeInformation.RuntimeIdentifier is an opaque value and not intended to be
parsed into its component parts. For the OS version of the actual machine an application
is running on, use Environment.OSVersion. For a description, use
RuntimeInformation.OSDescription. For a specific ID (distro) and corresponding version
on Linux, you can read the os-release
file.
Affected APIs
System.Runtime.InteropServices.RuntimeInformation.RuntimeIdentifier
See also
.NET SDK uses a smaller RID graph
Host determines RID-specific assets
AesGcm authentication tag size on
macOS
Article • 01/25/2023
AesGcm on macOS only supports 16-byte (128-bit) authentication tags when using
Encrypt or Decrypt in .NET 8 and later versions.
Previous behavior
On macOS, Encrypt and Decrypt supported authentication tag sizes ranging from 12 to
16 bytes, provided OpenSSL was available.
In addition, the AesGcm.TagByteSizes property reported that it supported sizes ranging
from 12 to 16 bytes, inclusive.
New behavior
On macOS, Encrypt and Decrypt support 16-byte authentication tags only. If you use a
smaller tag size on macOS, an ArgumentException is thrown at run time.
The AesGcm.TagByteSizes property returns a value of 16 as the supported tag size.
Version introduced
.NET 8 Preview 1
Type of breaking change
This change is a behavioral change.
Reason for change
The AesGcm class on macOS previously relied on OpenSSL for underlying support.
OpenSSL is an external dependency that needed to be installed and configured
separately from .NET. AesGcm now uses Apple's CryptoKit to provide an implementation
of Advanced Encryption Standard with Galois/Counter Mode (AES-GCM) so that
OpenSSL is no longer a dependency for using AesGcm.
The CryptoKit implementation of AES-GCM does not support authentication tag sizes
other than 128-bits (16-bytes).
Recommended action
Use 128-bit authentication tags with AesGcm for macOS support.
Affected APIs
System.Security.Cryptography.AesGcm.TagByteSizes
System.Security.Cryptography.AesGcm.Encrypt
System.Security.Cryptography.AesGcm.Decrypt
RSA.EncryptValue and
RSA.DecryptValue are obsolete
Article • 01/25/2023
The following methods are obsolete in .NET 8 (and later versions):
RSA.EncryptValue(Byte[])
RSA.DecryptValue(Byte[])
RSACryptoServiceProvider.EncryptValue(Byte[])
RSACryptoServiceProvider.DecryptValue(Byte[])
All references to these methods will result in a SYSLIB0048 warning at compile time.
Previous behavior
Previously, code could call the affected methods without any compilation warnings.
However, they threw a NotSupportedException at run time.
New behavior
Starting in .NET 8, calling the affected methods produces a SYSLIB0048 compilation
warning.
Version introduced
.NET 8 Preview 1
Type of breaking change
This change can affect source compatibility.
Reason for change
The affected methods were never implemented and always threw a
NotSupportedException. Since the purpose of these methods is unclear and they
shouldn't be called, they were marked as obsolete.
Recommended action
To encrypt or decrypt with RSA, use RSA.Encrypt or RSA.Decrypt instead.
Affected APIs
System.Security.Cryptography.RSA.EncryptValue(Byte[])
System.Security.Cryptography.RSA.DecryptValue(Byte[])
System.Security.Cryptography.RSACryptoServiceProvider.EncryptValue(Byte[])
System.Security.Cryptography.RSACryptoServiceProvider.DecryptValue(Byte[])
Host determines RID-specific assets
Article • 08/22/2023
When running an application with RID-specific assets, the host determines which assets
are relevant for the platform on which it's running. This applies to both the application
itself and the resolution logic used by AssemblyDependencyResolver.
Previously, the host tried to compute the RID at run time and then read the RID graph to
determine which RID-specific assets matched or were compatible with the computed
RID. Now, the default behavior doesn't compute the RID or use the RID graph. Instead,
the host relies on a known list of RIDs based on how the runtime itself was built.
Previous behavior
Previously, the process for selecting RID-specific assets was:
1. Read the RID graph from the .deps.json file of the root framework
(Microsoft.NetCore.App).
2. Compute the current RID at run time and try to find an entry for it in the RID
graph. If it doesn't exist, check for a fallback RID (built into the host at compile
time).
3. Starting from the entry found in the RID graph, look for assets matching that RID.
4. Continue down the list of RIDs in the RID graph entry until an asset match is found
or the list ends.
If the RID graph didn't have the computed RID or the fallback RID, RID assets weren't
properly resolved.
New behavior
By default, the process no longer relies on the RID graph. Instead, it checks for a known
set of portable RIDs based on how the host was built. For example:
Linux
linux-x64
linux
unix-x64
unix
any
Windows
win-x64
win
any
macOS
osx-x64
osx
unix-x64
unix
For non-portable builds of the host or runtime, the build might also set a non-portable
RID that's checked first.
Version introduced
.NET 8 Preview 5
Type of breaking change
This change can affect binary compatibility and is also a behavioral change.
Reason for change
The RID graph was costly to maintain and understand, requiring .NET itself to be distroaware in a fragile manner. The .NET team and the community spend a non-trivial
amount of time updating the graph and backporting such updates to previous releases.
The long-term goal is to stop updating the RID graph, stop reading it, and eventually
remove it. This breaking change is a step towards that goal.
Recommended action
Use portable RIDs, for example, linux , linux-musl , osx , and win . For specialized use
cases, you can use APIs like NativeLibrary.SetDllImportResolver(Assembly,
DllImportResolver) or AssemblyLoadContext.ResolvingUnmanagedDll for custom
loading logic.
If you need to revert to the previous behavior, set the backwards compatibility switch
System.Runtime.Loader.UseRidGraph to true in your runtimeconfig.json file. Setting the
switch to true instructs the host to use the previous behavior of reading the RID graph.
Alternatively, you can add a RuntimeHostConfigurationOption MSBuild item in your
project file. For example:
XML
<ItemGroup>
<RuntimeHostConfigurationOption
Include="System.Runtime.Loader.UseRidGraph" Value="true" />
</ItemGroup>
Affected APIs
System.Runtime.Loader.AssemblyDependencyResolver
StripSymbols defaults to true
Article • 05/09/2023
When .NET 7 introduced Native AOT deployment, it also introduced the StripSymbols
property that optionally allows debugging symbols to be stripped from the produced
executable on Linux into a separate file. The default value of the property was false . In
.NET 8, the default value has changed to true .
Previous behavior
With PublishAOT , debugging symbols on Linux were placed into the produced
executable by default, with an opt-in option to place them into a separate .dbg file.
New behavior
With PublishAOT , debugging symbols on Linux are placed into a .dbg file by default, with
an opt-out option to place them into the executable.
Version introduced
.NET 8 Preview 4
Reason for change
Based on feedback, we determined that .NET users prefer the .NET-symbols convention
instead of the platform-native convention. However, the option to strip the symbols was
not discoverable enough.
Recommended action
If you rely on debugging symbols to be present in the main executable, add
<StripSymbols>false</StripSymbols> to your project file to restore the previous
behavior.
If you choose to use the new default, verify that the debugging symbols in .dbg
files are properly archived if you expect you'll need to debug the generated
executables.
Affected APIs
None.
Breaking changes in EF Core 8.0 (EF8)
Article • 08/24/2023
This page documents API and behavior changes that have the potential to break
existing applications updating to EF Core 8.0.
Summary
Breaking change
Impact
SQL Server date and time now scaffold to .NET DateOnly and TimeOnly
Medium
SQLite Math methods now translate to SQL
Low
Medium-impact changes
SQL Server date and time now scaffold to .NET DateOnly
and TimeOnly
Tracking Issue #24507
Old behavior
Previously, when scaffolding a SQL Server database with date or time columns, EF
would generate entity properties with types DateTime and TimeSpan.
New behavior
Starting with EF Core 8.0, date and time are scaffolded as DateOnly and TimeOnly.
Why
DateOnly and TimeOnly were introduced in .NET 6.0, and are a perfect match for
mapping the database date and time types. DateTime notably contains a time
component that goes unused and can cause confusion when mapping it to date , and
TimeSpan represents a time interval - possibly including days - rather than a time of day
at which an event occurs. Using the new types prevents bugs and confusion, and
provides clarity of intent.
Mitigations
This change only affects users which regularly re-scaffold their database into an EF code
model ("database-first" flow).
It is recommended to react to this change by modifying your code to use the newly
scaffolded DateOnly and TimeOnly types. However, if that isn't possible, you can edit
the scaffolding templates to revert to the previous mapping. To do this, set up the
templates as described on this page. Then, edit the EntityType.t4 file, find where the
entity properties get generated (search for property.ClrType ), and change the code to
the following:
c#
var clrType = property.GetColumnType() switch
{
"date" when property.ClrType == typeof(DateOnly) =>
typeof(DateTime),
"date" when property.ClrType == typeof(DateOnly?) =>
typeof(DateTime?),
"time" when property.ClrType == typeof(TimeOnly) =>
typeof(TimeSpan),
"time" when property.ClrType == typeof(TimeOnly?) =>
typeof(TimeSpan?),
_ => property.ClrType
};
usings.AddRange(code.GetRequiredUsings(clrType));
var needsNullable = Options.UseNullableReferenceTypes &&
property.IsNullable && !clrType.IsValueType;
var needsInitializer = Options.UseNullableReferenceTypes &&
!property.IsNullable && !clrType.IsValueType;
#>
public <#= code.Reference(clrType) #><#= needsNullable ? "?" : "" #> <#=
property.Name #> { get; set; }<#= needsInitializer ? " = null!;" : "" #>
<#
Low-impact changes
SQLite Math methods now translate to SQL
Tracking Issue #18843
Old Behavior
Previously only the Abs, Max, Min, and Round methods on Math were translated to SQL.
All other members would be evaluated on the client if they appeared in the final Select
expression of a query.
New behavior
In EF Core 8.0, all Math methods with corresponding SQLite math functions
are
translated to SQL.
These math functions have been enabled in the native SQLite library that we provide by
default (through our dependency on the SQLitePCLRaw.bundle_e_sqlite3 NuGet
package). They have also been enabled in the library provided by
SQLitePCLRaw.bundle_e_sqlcipher. If you're using one of these libraries, your application
should not be affected by this change.
There is a chance, however, that applications including the native SQLite library by other
means may not enable the math functions. In these cases, the Math methods will be
translated to SQL and encounter no such function errors when executed.
Why
SQLite added built-in math functions in version 3.35.0. Even though they're disabled by
default, they've become pervasive enough that we decided to provide default
translations for them in our EF Core SQLite provider.
We also collaborated with Eric Sink on the SQLitePCLRaw project to enable math
functions in all of the native SQLite libraries provided as part of that project.
Mitigations
The simplest way to fix breaks is, when possible, to enable the math function is the
native SQLite library by specifying the SQLITE_ENABLE_MATH_FUNCTIONS
compile-
time option.
If you don't control compilation of the native library, you can also fix breaks by create
the functions yourself at runtime using the Microsoft.Data.Sqlite APIs.
C#
sqliteConnection
.CreateFunction<double, double, double>(
"pow",
Math.Pow,
isDeterministic: true);
Alternatively, you can force client-evaluation by splitting the Select expression into two
parts separated by AsEnumerable .
C#
// Before
var query = dbContext.Cylinders
.Select(
c => new
{
Id = c.Id
// May throw "no such function: pow"
Volume = Math.PI * Math.Pow(c.Radius, 2) * c.Height
});
// After
var query = dbContext.Cylinders
// Select the properties you'll need from the database
.Select(
c => new
{
c.Id,
c.Radius,
c.Height
})
// Switch to client-eval
.AsEnumerable()
// Select the final results
.Select(
c => new
{
Id = c.Id,
Volume = Math.PI * Math.Pow(c.Radius, 2) * c.Height
});
ActivatorUtilities.CreateInstance
behaves consistently
Article • 03/08/2023
The behavior of ActivatorUtilities.CreateInstance is now more consistent with
CreateFactory(Type, Type[]). When IServiceProviderIsService isn't present in the
dependency injection (DI) container, CreateInstance falls back to the CreateFactory(Type,
Type[]) logic. In that logic, only one constructor is allowed to match with all the provided
input parameters.
In the more general case when IServiceProviderIsService is present, the CreateInstance
API prefers the longest constructor overload that has all its arguments available. The
arguments can be input to the API, registered in the container, or available from default
values in the constructor itself.
Consider the following class definition showing two constructors:
C#
public class A
{
A(B b, C c, string st = "default string") { }
A() { }
}
For this class definition, and when IServiceProviderIsService is present,
ActivatorUtilities.CreateInstance<A>(serviceProvider, new C()) instantiates A by
picking the first constructor that takes B , C , and string .
Version introduced
.NET 8 Preview 1
Previous behavior
ActivatorUtilities.CreateInstance behaved unexpectedly in some cases. It made sure all
required instances passed to it existed in the chosen constructor. However, the
constructor selection was buggy and unreliable.
New behavior
CreateInstance tries to find the longest constructor that matches all parameters based
on the behavior of IServiceProviderIsService.
If no constructors are found or if IServiceProviderIsService isn't present, it falls back
to CreateFactory(Type, Type[]) logic.
If it finds more than one constructor, it throws an InvalidOperationException.
7 Note
If IServiceProviderIsService is configured incorrectly or doesn't exist,
CreateInstance may function incorrectly or ambiguously.
Type of breaking change
This change is a behavioral change.
Reason for change
This change was introduced to fix a bug where the behavior changed depending on the
order of constructor overload definitions.
Recommended action
If your app starts behaving differently or throwing an exception after upgrading to .NET
8, carefully examine the constructor definitions for the affected instance type. Refer to
the New behavior section.
Affected APIs
Microsoft.Extensions.DependencyInjection.ActivatorUtilities.CreateInstance<T>
(IServiceProvider, Object[])
Microsoft.Extensions.DependencyInjection.ActivatorUtilities.CreateInstance(IService
Provider, Type, Object[])
See also
ActivatorUtilities.CreateInstance requires non-null provider
ActivatorUtilities.CreateInstance
requires non-null provider
Article • 03/08/2023
The two ActivatorUtilities.CreateInstance methods now throw an
ArgumentNullException exception if the provider parameter is null .
Version introduced
.NET 8 Preview 1
Previous behavior
A null value was allowed for the provider parameter. In some cases, the specified type
was still created correctly.
New behavior
When provider is null , an ArgumentNullException exception is thrown.
Type of breaking change
This change is a behavioral change.
Reason for change
We fixed various the parameter validation along with constructor-matching issues to
align with the intended purpose of CreateInstance. The CreateInstance() methods have
a non-nullable provider parameter, so it was generally expected that a null provider
wasn't allowed.
Recommended action
Pass a non-null IServiceProvider for the provider argument. If the provider also
implements IServiceProviderIsService, constructor arguments can be obtained through
that.
Alternatively, if your scenario doesn't require dependency injection, since
IServiceProvider is null , use Activator.CreateInstance instead.
Affected APIs
Microsoft.Extensions.DependencyInjection.ActivatorUtilities.CreateInstance<T>
(IServiceProvider, Object[])
Microsoft.Extensions.DependencyInjection.ActivatorUtilities.CreateInstance(IService
Provider, Type, Object[])
See also
ActivatorUtilities.CreateInstance behaves consistently
ConfigurationBinder throws for
mismatched value
Article • 03/08/2023
Previously, BinderOptions.ErrorOnUnknownConfiguration was used solely to raise an
exception if a value existed in the configuration but not in the model being bound to.
Now, if this property is set to true , an exception is also thrown if the value in the
configuration can't be converted to the type of value in the model.
Version introduced
.NET 8 Preview 1
Previous behavior
Previously, the following code silently swallowed the exceptions for the fields that
contained invalid enums:
C#
public enum TestSettingsEnum
{
Option1,
Option2,
}
public class MyModelContainingArray
{
public TestSettingsEnum[] Enums { get; set; }
}
public void SilentlySwallowsInvalidItems()
{
var dictionary = new Dictionary<string, string>
{
["Section:Enums:0"] = "Option1",
["Section:Enums:1"] = "Option3", // invalid - ignored
["Section:Enums:2"] = "Option4", // invalid - ignored
["Section:Enums:3"] = "Option2",
};
var configurationBuilder = new ConfigurationBuilder();
configurationBuilder.AddInMemoryCollection(dictionary);
var config = configurationBuilder.Build();
var configSection = config.GetSection("Section");
var model = configSection.Get<MyModelContainingArray>(o =>
o.ErrorOnUnknownConfiguration = true);
// Only Option1 and Option2 are in the bound collection at this point.
}
New behavior
Starting in .NET 8, if a configuration value can't be converted to the type of the value in
the model, an InvalidOperationException is thrown.
Type of breaking change
This change is a behavioral change.
Reason for change
The previous behavior was confusing for some developers. They set
BinderOptions.ErrorOnUnknownConfiguration to true and expected an exception to be
thrown if any issue was encountered when the configuration was bound.
Recommended action
If your app has configuration values that can't be converted to the properties in the
bound model, change or remove the values.
Alternatively, set BinderOptions.ErrorOnUnknownConfiguration to false .
Affected APIs
Microsoft.Extensions.Configuration.ConfigurationBinder.Bind(IConfiguration,
Object, Action<BinderOptions>)
Microsoft.Extensions.Configuration.ConfigurationBinder.Get<T>(IConfiguration,
Action<BinderOptions>)
Microsoft.Extensions.Configuration.ConfigurationBinder.Get(IConfiguration, Type,
Action<BinderOptions>)
ConfigurationManager package no
longer references
System.Security.Permissions
Article • 08/29/2023
The System.Configuration.ConfigurationManager package no longer references the
System.Security.Permissions package.
Version introduced
.NET 8 Preview 3
Previous behavior
The System.Configuration.ConfigurationManager package referenced the
System.Security.Permissions package.
New behavior
Starting in .NET 8, the System.Configuration.ConfigurationManager package does not
reference the System.Security.Permissions package.
Type of breaking change
This change can affect source compatibility.
Reason for change
This change avoids a dependency on System.Drawing.Common when
System.Configuration.ConfigurationManager is referenced, which is primarily an issue for
non-Windows operating systems.
The dependency on System.Drawing.Common was caused by the following package
dependencies:
txt
System.Configuration.ConfigurationManager
└──System.Security.Permissions
└──System.Windows.Extensions
└──System.Drawing.Common
Recommended action
If your app references the System.Configuration.ConfigurationManager package and you
also have a dependency on System.Security.Permissions or any of its dependencies,
which might include System.Windows.Extensions , System.Security.AccessControl , or
System.Drawing.Common , you'll need to reference those packages either directly or
indirectly.
Affected APIs
N/A
DirectoryServices package no longer
references System.Security.Permissions
Article • 08/29/2023
The System.DirectoryServices package no longer references the
System.Security.Permissions package.
Version introduced
.NET 8 Preview 3
Previous behavior
The System.DirectoryServices package referenced the System.Security.Permissions
package.
New behavior
Starting in .NET 8, the System.DirectoryServices package does not reference the
System.Security.Permissions package.
Type of breaking change
This change can affect source compatibility.
Reason for change
This change avoids a dependency on System.Drawing.Common when
System.DirectoryServices is referenced, which is primarily an issue for non-Windows
operating systems.
The dependency on System.Drawing.Common was caused by the following package
dependencies:
txt
System.DirectoryServices
└──System.Security.Permissions
└──System.Windows.Extensions
└──System.Drawing.Common
Recommended action
If your app references the System.DirectoryServices package and you also have a
dependency on System.Security.Permissions or any of its dependencies, which might
include System.Windows.Extensions or System.Drawing.Common , you'll need to reference
those packages either directly or indirectly.
Affected APIs
N/A
Empty keys added to dictionary by
configuration binder
Article • 07/31/2023
In previous versions, when configuration was bound to a dictionary type, any keys
without corresponding values in the configuration were skipped and weren't added to
the dictionary. The behavior has changed such that those keys are longer skipped but
instead automatically created with their default values. This change ensures that all keys
listed in the configuration will be present within the dictionary.
Version introduced
.NET 8 Preview 5
Previous behavior
Previously, empty keys in the configuration were skipped when bound to a dictionary
type. Consider the following configuration string and binding code.
C#
var json = @"{
""Queues"": {
""q1"": {
""V"": 1
},
""q2"": {
""V"": 2
},
""q3"": {
}
}
}";
C#
public class Q
{
public Dictionary<string, QueueValue> Queues { get; set; } = new();
}
public class QueueValue
{
public int V { get; set; }
}
var configuration = new ConfigurationBuilder()
.AddJsonStream(StringToStream(json))
.Build();
Q options = new Q();
configuration.Bind(options);
foreach (var kvp in options.Queues)
{
Console.WriteLine($"{kvp.Key}: {kvp.Value.V}");
}
Previously, you'd see the following output (notice that key q3 is missing):
Output
q1: 1
q2: 2
New behavior
Starting in .NET 8, empty configuration keys are added to the dictionary with their
default value during configuration binding.
Consider the code from the previous behavior section, which now outputs the following
text showing that q3 was added to the dictionary with its default value:
Output
q1: 1
q2: 2
q3: 0
Type of breaking change
This change is a behavioral change.
Reason for change
This user-requested change ensures that all keys listed in the configuration are present
within the dictionary. Having all keys present streamlines the process and avoids
potential issues with missing keys.
Recommended action
Verify and adapt your application logic to accommodate the presence of the newly
created dictionary entries with empty values. If the new behavior is undesirable, remove
empty-value entries from the configuration. By eliminating these entries, no dictionary
entries with empty values will be added during the binding process.
Affected APIs
Microsoft.Extensions.Configuration.ConfigurationBinder
IConfigurationRoot extension methods
Microsoft.Extensions.DependencyInjection.OptionsConfigurationServiceCollectionE
xtensions
HostApplicationBuilderSettings.Args
respected by HostApplicationBuilder
ctor
Article • 03/14/2023
The HostApplicationBuilder constructor that accepts a HostApplicationBuilderSettings
object now applies the HostApplicationBuilderSettings.Args property, regardless of
whether DisableDefaults is set to true or false .
Version introduced
.NET 8 Preview 2
Previous behavior
Previously, the HostApplicationBuilderSettings.Args property was ignored when
HostApplicationBuilderSettings.DisableDefaults was set to true .
New behavior
Starting in .NET 8, the HostApplicationBuilderSettings.Args value is added to
HostApplicationBuilder.Configuration regardless of whether DisableDefaults is set to
true or false .
Type of breaking change
This change is a behavioral change.
Reason for change
The behavior of ignoring HostApplicationBuilderSettings.Args was unexpected, even
when HostApplicationBuilderSettings.DisableDefaults was set to true . That's because if
the caller didn't want the command-line arguments applied to the
HostApplicationBuilder, they wouldn't have set them on the
HostApplicationBuilderSettings object. Since the caller did pass the command-line
arguments on the settings, those arguments should be respected.
Recommended action
If you don't want the command-line arguments to be added to the
HostApplicationBuilder configuration, leave the HostApplicationBuilderSettings.Args
property set to null .
Affected APIs
HostApplicationBuilder(HostApplicationBuilderSettings)
Date and time converters honor culture
argument
Article • 05/08/2023
The ConvertTo methods on the following classes now use the culture from the culture
parameter as the format provider for the date and time instead of
CultureInfo.CurrentCulture:
DateOnlyConverter
DateTimeConverter
DateTimeOffsetConverter
TimeOnlyConverter
Previous behavior
Previously, the affected APIs used CultureInfo.CurrentCulture as the format provider for
the date and time even though the caller specified a culture in the culture parameter.
Consider the following code snippet that sets the current culture to Spanish (Spain) but
passes a customized French culture to
DateTimeConverter.ConvertTo(ITypeDescriptorContext, CultureInfo, Object, Type).
C#
CultureInfo.CurrentCulture = new CultureInfo("es-ES");
Console.WriteLine($"Current culture: {CultureInfo.CurrentCulture}");
var dt1 = new DateTime(2022, 8, 1);
var frCulture = new CultureInfo("fr-FR");
frCulture.DateTimeFormat.ShortDatePattern = "dd MMMM yyyy";
Console.WriteLine(TypeDescriptor.GetConverter(dt1).ConvertTo(null,
frCulture, dt1, typeof(string)));
In .NET 7 and earlier versions, this code prints the date in the correct format but with the
name of the month in Spanish instead of French:
Output
Current culture: es-ES
01 agosto 2022
New behavior
Starting in .NET 8, the affected APIs use the culture specified by the culture parameter
as the format provider.
The code snippet shown in the Previous behavior correctly prints the name of the month
in French:
Output
Current culture: es-ES
01 août 2022
Version introduced
.NET 8 Preview 4
Type of breaking change
This change is a behavioral change.
Reason for change
This change fixes a bug where ConvertTo was not consistent with ConvertFrom . It used
the date and time format strings from the input culture but formatted the date and time
with CurrentCulture.
Recommended action
If you relied on the previous behavior, pass in CultureInfo.CurrentCulture, null , or a
custom culture for the culture parameter.
Affected APIs
System.ComponentModel.DateOnlyConverter.ConvertTo(ITypeDescriptorContext,
CultureInfo, Object, Type)
System.ComponentModel.DateTimeConverter.ConvertTo(ITypeDescriptorContext,
CultureInfo, Object, Type)
System.ComponentModel.DateTimeOffsetConverter.ConvertTo(ITypeDescriptorCon
text, CultureInfo, Object, Type)
System.ComponentModel.TimeOnlyConverter.ConvertTo(ITypeDescriptorContext,
CultureInfo, Object, Type)
TwoDigitYearMax default is 2049
Article • 01/27/2023
Calendar classes, such as GregorianCalendar, have a TwoDigitYearMax property that
defines the last year of a 100-year range that can be represented by a two-digit year.
This property is often used to translate a two-digit year to a four-digit year. Previously,
Calendar.TwoDigitYearMax defaulted to 2029 for GregorianCalendar and other
Gregorian-like calendars, such as JulianCalendar and EastAsianLunisolarCalendar. That
value meant that two-digit years from 00 to 29 translated to 2000-2029. Two-digit years
from 30 to 99 translated to 1930-1999. The default TwoDigitYearMax property value for
GregorianCalendar and other Gregorian-like calendars has now changed from 2029 to
2049. The new value means that two-digit years from 00 to 49 are translated to 20002049. Any year from 50 to 99 will be translated to 1950-1999.
In addition, on Windows, the default value of the TwoDigitYearMax property is now
obtained from the corresponding Windows setting (the default value for which is now
also 2049). This matches the behavior prior to .NET 5.
Date parsing is the functionality that's most affected by this change.
Previous behavior
In .NET 6 and .NET 7, if you didn't specify a value for TwoDigitYearMax, parsing a string
like "12/10/35" with the Gregorian calendar produced the date "December 10th, 1935".
New behavior
Starting in .NET 8, parsing a string like "12/10/35" with the Gregorian calendar produces
the date "December 10th, 2035".
Version introduced
.NET 8 Preview 1
Type of breaking change
This change is a behavioral change.
Reason for change
It's more logical to parse a two-digit year that's relatively close to the two digits of the
current year to produce a four-digit year in the current century instead of the previous
one. The Windows operating system also changed its default settings to the same
number (2049).
Recommended action
If you don't want your app to depend on the default value when parsing a string to a
date, you can control how a two-digit year is translated to a four-digit year by setting
the TwoDigitYearMax property. The following code shows how to set it for the invariant
culture.
C#
CultureInfo clonedInvariantCulture = (CultureInfo)
(CultureInfo.InvariantCulture.Clone());
clonedInvariantCulture.DateTimeFormat.Calendar.TwoDigitYearMax = 2039; //
Use any desired cutoff value.
DateTime dt = DateTime.Parse("12/25/45", clonedInvariantCulture);
Affected APIs
System.DateOnly.Parse
System.DateOnly.ParseExact
System.DateOnly.TryParse
System.DateOnly.TryParseExact
System.DateTime.Parse
System.DateTime.ParseExact
System.DateTime.TryParse
System.DateTime.TryParseExact
System.DateTimeOffset.Parse
System.DateTimeOffset.ParseExact
System.DateTimeOffset.TryParse
System.DateTimeOffset.TryParseExact
System.Globalization.GregorianCalendar.TwoDigitYearMax (and other Gregorianlike calendar types)
System.Globalization.GregorianCalendar.ToDateTime (and other Gregorian-like
calendar types)
System.Globalization.GregorianCalendar.ToFourDigitYear(Int32) (and other
Gregorian-like calendar types)
See also
Parse date and time strings in .NET
CreateObjectFlags.Unwrap only unwraps
on target instance
Article • 06/13/2023
Previously, if you called GetOrCreateObjectForComInstance(IntPtr, CreateObjectFlags)
on a ComWrappers instance with the CreateObjectFlags.Unwrap flag, a managed object
wrapper was unwrapped from any ComWrappers instance. Now when the flag is
specified, only wrappers from the ComWrappers instance that
GetOrCreateObjectFromComInstance was called on are unwrapped.
The Unwrap flag was the only API that reached "across" ComWrappers instances, so its
behavior was unintuitive. Additionally, the new ComWrappers.TryGetObject(IntPtr,
Object) API is available to unwrap a COM object from any ComWrappers instance.
Previous behavior
Calling GetOrCreateObjectForComInstance(IntPtr, CreateObjectFlags) on a
ComWrappers instance with the CreateObjectFlags.Unwrap flag unwrapped a managed
object wrapper from any ComWrappers instance.
New behavior
Calling GetOrCreateObjectForComInstance(IntPtr, CreateObjectFlags) on a
ComWrappers instance with the CreateObjectFlags.Unwrap flag only unwraps a
managed object wrapper from the ComWrappers instance that
GetOrCreateObjectForComInstance was called on. If given a wrapper from a different
ComWrappers instance, the ComWrappers instance creates a new wrapper.
Version introduced
.NET 8 Preview 5
Type of breaking change
This change is a behavioral change.
Reason for change
The previous behavior was unintuitive. It also broke the encapsulation experience where
developers can define how COM interop works for their code by using their own custom
ComWrappers instances.
Recommended action
If you want to keep the previous behavior, call ComWrappers.TryGetObject(IntPtr,
Object) before calling GetOrCreateObjectForComInstance(IntPtr, CreateObjectFlags).
Affected APIs
System.Runtime.InteropServices.ComWrappers.GetOrCreateObjectForComInstance
(IntPtr, CreateObjectFlags)
Custom marshallers require additional
members
Article • 09/08/2023
The custom marshaller analyzer has changed to require all element-focused marshal
modes to satisfy both the managed-to-unmanaged and unmanaged-to-managed
shapes.
Previous behavior
Custom marshallers with MarshalMode.ElementIn only needed a ConvertToUnmanaged
method. Custom marshallers with MarshalMode.ElementOut only needed a
ConvertToManaged method.
New behavior
Starting in .NET 8, SYSLIB1057 is reported for custom marshallers with
MarshalMode.ElementIn or MarshalMode.ElementOut that don't have both a
ConvertToUnmanaged and ConvertToManaged method.
Version introduced
.NET 8 RC 1
Type of breaking change
This change can affect source compatibility.
Reason for change
With the introduction of source-generated COM, the marshallers can be used in element
scenarios in both managed-to-unmanaged and unmanaged-to-managed scenarios. This
change updates the analyzer to ensure that user-defined marshallers have the required
members for all scenarios where the marshaller might be used.
Recommended action
Add both a ConvertToManaged and ConvertToUnmanaged method to the marshaller type.
Affected APIs
System.Runtime.InteropServices.Marshalling.CustomMarshallerAttribute
IDispatchImplAttribute API is removed
Article • 08/23/2023
The IDispatchImplAttribute implementation has officially been removed from .NET. This
type was only discoverable at run time and its removal has no impact on visible API
surface area. However, if an assembly targeting .NET Framework uses this type and is
loaded in .NET 8 or a later version, the runtime will throw a TypeLoadException.
Previous behavior
The IDispatchImplAttribute type could be found at run time, but none of the
documented semantics of the deprecated attribute applied.
New behavior
Starting in .NET 8, attempting to load an assembly that contains this attribute throws a
TypeLoadException.
Version introduced
.NET 8 Preview 6
Type of breaking change
This change can affect binary compatibility.
Reason for change
This attribute was removed as it was not longer respected and served no functional
purpose.
Recommended action
Remove use of this API in assemblies that are loaded in .NET 8 and later versions.
Affected APIs
System.Runtime.InteropServices.IDispatchImplAttribute
SafeHandle types must have public
constructor
Article • 06/07/2023
Historically, passing SafeHandle-derived types to P/Invokes and COM methods has
implicitly required a parameterless constructor of any visibility when a SafeHandle derived type is passed as a ref or out parameter or a return type. Source-generated
interop in .NET 7 and earlier .NET 8 preview versions allowed this behavior to enable
easier migration from DllImportAttribute-based P/Invokes. At the same time, we
updated the SafeHandle documentation to tell implementers to provide a public
parameterless constructor in their derived type. This breaking change makes that
recommendation a requirement for source-generated marshalling.
Previous behavior
A SafeHandle-derived type was required to have a parameterless constructor of any
visibility when it was used:
As a ref or out parameter or a return type in a LibraryImportAttribute-attributed
method.
In a method on a GeneratedComInterfaceAttribute-attributed interface.
New behavior
A SafeHandle-derived type is required to have a public parameterless constructor when
it's used:
As a ref or out parameter or a return type in a LibraryImportAttribute-attributed
method.
In a method on a GeneratedComInterfaceAttribute-attributed interface.
If the type doesn't have a public parameterless constructor, the interop source
generator emits a compile error.
Version introduced
.NET 8 Preview 5
Type of breaking change
This change can affect source compatibility.
Reason for change
The interop source generators are changing to push more code out of the source
generators themselves and into the core .NET libraries. As part of this change, the
interop team is starting to enforce the recommended guidelines for more maintainable
and understandable interop code.
Recommended action
Change the existing non- public parameterless constructor on the SafeHandle -derived
type to be public .
Affected APIs
System.Runtime.InteropServices.LibraryImportAttribute
System.Runtime.InteropServices.Marshalling.GeneratedComInterfaceAttribute
IntPtr no longer used for function
pointer types
Article • 03/22/2023
As a new reflection feature, a function pointer type is now a System.Type instance with
new capabilities such as Type.IsFunctionPointer. Previously, the System.Type instance
returned was the IntPtr type.
Using System.Type in this manner is similar to how other types are exposed, such as
pointers (Type.IsPointer) and arrays (Type.IsArray).
This new functionality is currently implemented in the CoreCLR runtime and in
MetadataLoadContext. Support for the Mono and NativeAOT runtimes is expected later.
A function pointer instance, which is a physical address to a function, continues to be
represented as an IntPtr; only the reflection type has changed.
Previous behavior
Previously, typeof(delegate*<void>()) returned the System.IntPtr type for a function
pointer type. Similarly, reflection also returned this type for a function pointer type, such
as with FieldInfo.FieldType. The IntPtr type didn't allow any access to the parameter
types, return type, or calling conventions.
New behavior
typeof and reflection now use System.Type for a function pointer type, which provides
access to the parameter types, return type, and calling conventions.
Version introduced
.NET 8 Preview 2
Type of breaking change
This change is a behavioral change.
Reason for change
This change adds the capability to obtain function pointer metadata including
parameter types, the return type, and the calling conventions. Function pointer support
was added with C# 9 and .NET 5, but reflection support wasn't added at that time.
Recommended action
If you want your code to support function pointers and to treat them specially, use the
new Type.IsFunctionPointer API.
Affected APIs
typeof keyword
System.Reflection.FieldInfo.FieldType
System.Reflection.PropertyInfo.PropertyType
System.Reflection.ParameterInfo.ParameterType
CLI console output uses UTF-8
Article • 04/06/2023
If the DOTNET_CLI_UI_LANGUAGE or VSLANG environment variable is set, the .NET CLI
console output and input encoding changes to UTF-8, so that the code page can
change to UTF-8 as well. This new behavior allows characters from languages set by
those environment variables to be rendered correctly.
This change only affects Windows operating systems (the encoding was okay on other
platforms). Moreover, it only applies to Windows 10 and later versions where the UI
culture set by the user is non-English.
Previous behavior
Characters in certain languages, including Chinese, German, Japanese, and Russian,
would sometimes display as garbled characters or as ? in the console. For example:
Console
C:\>dotnet build
MSBuild version 17.3.0-preview[...] for .NET
???????????????...
New behavior
Starting in .NET 7 (version 7.0.3xx) and .NET 8, characters render correctly. Both the
encoding and the code page change. For example:
Console
C:\>dotnet build
MSBuild version 17.3.0-preview[...] for .NET
正在确定要还原的项目…
Versions of Windows older than Windows 10 1909 don't fully support UTF-8 and may
experience issues after this change. (Starting in .NET 8 Preview 3 and .NET 7.0.300 SDK,
the .NET SDK no longer changes the encoding to UTF-8 on these versions, by default. To
opt back into using UTF-8 even on Windows 10 versions that don't support it, use the
DOTNET_CLI_FORCE_UTF8_ENCODING environment variable.)
In addition, there was an existing bug
where the SDK can affect the encoding of other
commands and programs called in the same command prompt after the SDK has
finished execution. Now that the SDK more frequently changes the encoding, the impact
of this bug may increase. However, the bug was fixed in .NET 8 Preview 3 and .NET
7.0.300 SDK. For more information, see SDK no longer changes console encoding after
completion.
Version introduced
7.0.3xx .NET 8 Preview 1
Type of breaking change
This change can affect source compatibility and binary compatibility. It's also a
behavioral change.
Reason for change
Using the .NET CLI in non-English languages provided a poor experience.
Developers that weren't already using the VSLANG and DOTNET_CLI_UI_LANGUAGE variables
aren't impacted. The impact should be minimal, as this language setting wouldn't have
worked well in the first place due to garbled characters. Also, only developers using
Windows 10 or later might be impacted, most of which are likely using version 1909 or
later.
The legacy scenarios are already less likely to support the broken languages, so it's
unlikely you'd want to use another language that might expose this break anyway.
Recommended action
If you're using an older version of windows 10, upgrade to version 1909 or later.
If you want to use a legacy console or are facing build issues or others due to the
encoding change, unset VSLANG and DOTNET_CLI_UI_LANGUAGE to disable this
change.
See also
SDK no longer changes console encoding when finished
Console encoding doesn't remain UTF-8
after completion
Article • 04/06/2023
The bug mentioned in the CLI console output uses UTF-8 breaking change, where the
.NET SDK changed the encoding of the entire console, has been fixed. The console
encoding no longer remains UTF-8 after the .NET SDK executes a command. It's possible
that users came to rely on that behavior, hence this is a breaking change.
In addition, the .NET SDK no longer changes the encoding to UTF-8 on older Windows
10 versions that don't fully support it.
Previous behavior
The SDK changed the encoding of a terminal after running a command such as
dotnet build .
The SDK used the UTF-8 encoding to correctly render non-English characters, even
on versions of windows 10 that did not officially support UTF-8. The behavior was
undefined on those versions.
New behavior
The SDK doesn't change the terminal encoding after exit for other programs.
By default, the SDK no longer uses UTF-8 for Windows versions that don't support
it.
Version introduced
7.0.3xx .NET 8 Preview 3
Type of breaking change
This change can affect binary compatibility. It's also a behavioral change.
Reason for change
There was an existing bug where the .NET SDK affected the encoding on the console for
other programs. That was a bug that was fixed, resulting in this breaking change.
Older versions of Windows 10 (that is, versions before the November 2019 update)
didn't support UTF-8, so the default behavior shouldn't be to use UTF-8 encoding.
Instead, an opt-in is now available.
Recommended action
If your app needs to change the code page on Windows, it can run a process to invoke
the chcp command. Your app shouldn't rely on the .NET SDK to change the encoding.
For older Windows 10 versions that don't officially support UTF-8 where you want the
.NET SDK to continue to change the encoding to UTF-8 for non-English languages, can
you set the environment variable DOTNET_CLI_FORCE_UTF8_ENCODING to true or 1.
See also
SDK no longer changes console encoding after completion
Containers default to use the 'latest' tag
Article • 08/01/2023
The default image tag used for .NET SDK-built containers changed from the value of the
Version of the project to the value latest .
Previous behavior
Previously, the image was built with a tag value of $(Version) , which enabled changing
the tag based on the same value that the rest of the .NET ecosystem uses.
New behavior
Starting in .NET 8, the generated image has the latest tag in all cases.
Version introduced
.NET 8 Preview 6
Type of change
This change is a behavioral change.
Reason for change
This change aligns the default containerization experience with the developer
experiences for other container tooling like the Docker CLI. It also makes the
development inner-loop of repeated container publishes easier to use with tools like
Docker Compose, because the version remains stable.
Recommended action
Explicitly set the version if you need it. The easiest way is to set the ContainerImageTag
property on the command line to an explicit version, for example,
/p:ContainerImageTag=1.2.3 . But you can also programmatically set the value as you
would any other MSBuild property. In a project file, you can continue to use the
$(Version) property by adding the ContainerImageTag property:
XML
<PropertyGroup>
<ContainerImageTag>$(Version)</ContainerImageTag>
</PropertyGroup>
Affected APIs
None.
'dotnet pack' uses Release configuration
Article • 02/08/2023
The dotnet pack command, which packs code into a NuGet package, now uses the
Release configuration instead of the Debug configuration by default.
Previous behavior
Previously, dotnet pack used the Debug configuration unless the configuration was
specified explicitly or PackRelease was set to true .
The PackRelease property was added in .NET 7 as a path forward to this breaking
change. Previously, you could set the DOTNET_CLI_ENABLE_PACK_RELEASE_FOR_SOLUTIONS
environment variable to use PackRelease in a project that was part of a Visual Studio
solution.
New behavior
If you're developing with the .NET 8 SDK or a later version, dotnet pack uses the
Release configuration by default for all projects. If you have a CI/CD script, tests, or
code where you've hardcoded Debug into an output path, this change may break your
workflow. Also, you won't be able to debug a packed app unless the Debug
configuration was explicitly specified (for example, using dotnet pack --configuration
Debug .
dotnet pack can pack for multiple target framework monikers (TFM) at the same time. If
your project targets multiple versions and you have different PackRelease values for
different targets, you can have a conflict where some TFMs pack the Release
configuration and others pack the Debug configuration.
For projects in a solution:
dotnet pack can pack all the projects in a Visual Studio solution if given a solution
file. For each project in the solution, the value of PackRelease is implicitly set to
true if it's undefined. In order for dotnet pack to determine the correct
configuration to use, all projects in the solution must agree on their value of
PackRelease .
This change might cause the performance of dotnet pack to regress, especially for
solutions that contain many projects. To address this, a new environment variable
DOTNET_CLI_LAZY_PUBLISH_AND_PACK_RELEASE_FOR_SOLUTIONS has been introduced.
The DOTNET_CLI_ENABLE_PACK_RELEASE_FOR_SOLUTIONS environment variable is no
longer recognized.
Version introduced
.NET 8 Preview 1
Type of breaking change
This change can affect source compatibility and is also a behavioral change.
Reason for change
In most cases when you create a package, you want your code to be optimized and can
keep the package smaller by excluding debugging information.
The DOTNET_CLI_ENABLE_PACK_RELEASE_FOR_SOLUTIONS environment variable was removed
since the behavior it enabled is now the default behavior and the granular control is no
longer necessary.
Recommended action
To disable the new behavior entirely, you can set the
DOTNET_CLI_DISABLE_PUBLISH_AND_PACK_RELEASE environment variable to true (or
any other value). This variable affects both dotnet publish and dotnet pack .
To explicitly specify the Debug configuration for packing, use the -c or -configuration option with dotnet pack .
If your CI/CD pipeline is broken due to hardcoded output paths, update the paths
to Release instead of Debug , disable the new behavior using the
DOTNET_CLI_DISABLE_PUBLISH_AND_PACK_RELEASE environment variable, or specify
that the Debug configuration should be used.
If you're packing a solution and it's broken because one or more projects explicitly
sets a value for PackRelease , you should explicitly set PackRelease to false in
each project:
XML
<PropertyGroup>
<PackRelease>false</PackRelease>
</PropertyGroup>
If you're packing a solution and the performance has regressed, you can set the
DOTNET_CLI_LAZY_PUBLISH_AND_PACK_RELEASE_FOR_SOLUTIONS environment variable to
true (or any other value) to remove the regression. If you use this variable and any
project defines PackRelease , all projects must define it, or you can use a
Directory.Build.Props file. This variable affects both dotnet publish and dotnet
pack .
See also
'dotnet publish' uses Release configuration
'dotnet publish' uses Release
configuration
Article • 02/08/2023
The dotnet publish command now uses the Release configuration instead of the Debug
configuration by default if the target framework is .NET 8 or a later version.
Previous behavior
Previously, dotnet publish used the Debug configuration unless the configuration was
specified explicitly or PublishRelease was set to true .
The PublishRelease property was added in .NET 7 as a path forward to this breaking
change. Previously, you could set the DOTNET_CLI_ENABLE_PUBLISH_RELEASE_FOR_SOLUTIONS
environment variable to use PublishRelease in a project that was part of a Visual Studio
solution.
New behavior
If you're developing with the .NET 8 SDK or a later version, dotnet publish uses the
Release configuration by default for projects whose TargetFramework is set to net8.0
or a later version. If you have a CI/CD script, tests, or code where you've hardcoded
Debug into an output path, this change may break your workflow.
If your project targets multiple versions, the new behavior only applies if you specify a
target framework of .NET 8 or later when you publish (for example, using dotnet
publish -f net8.0 ).
For projects in a solution:
dotnet publish can publish all the projects in a Visual Studio solution if given a
solution file. For the solution projects that target .NET 8 or later, the value of
PublishRelease is implicitly set to true if it's undefined. However, in order for
dotnet publish to determine the correct configuration to use for the solution, all
projects in the solution must agree on their value of PublishRelease . If an older
project in the solution has PublishRelease set to false , you should explicitly set
the property to false for any new .NET 8+ projects as well.
This change might cause the performance of dotnet publish to regress, especially
for solutions that contain many projects. To address this, a new environment
variable DOTNET_CLI_LAZY_PUBLISH_AND_PACK_RELEASE_FOR_SOLUTIONS has been
introduced.
The DOTNET_CLI_ENABLE_PUBLISH_RELEASE_FOR_SOLUTIONS environment variable is no
longer recognized.
Version introduced
.NET 8 Preview 1
Type of breaking change
This change can affect source compatibility and is also a behavioral change.
Reason for change
In most cases when you publish, you want your code to be optimized and can keep the
app smaller by excluding debugging information. Customers have asked for Release to
be the default configuration for publish for a long time. Also, Visual Studio has had this
behavior for many years.
The DOTNET_CLI_ENABLE_PUBLISH_RELEASE_FOR_SOLUTIONS environment variable was
removed since the behavior it enabled is now the default behavior and the granular
control is no longer necessary.
Recommended action
To disable the new behavior entirely, you can set the
DOTNET_CLI_DISABLE_PUBLISH_AND_PACK_RELEASE environment variable to true (or
any other value). This variable affects both dotnet publish and dotnet pack .
To explicitly specify the Debug configuration for publishing, use the -c or -configuration option with dotnet publish .
If your CI/CD pipeline is broken due to hardcoded output paths, update the paths
to Release instead of Debug , disable the new behavior using the
DOTNET_CLI_DISABLE_PUBLISH_AND_PACK_RELEASE environment variable, or specify
that the Debug configuration should be used.
If you're publishing a solution and it's broken, you can explicitly set
PublishRelease to true (or false to revert to the previous behavior).
XML
<PropertyGroup>
<PublishRelease>true</PublishRelease>
</PropertyGroup>
Alternatively, you can specify the property in a Directory.Build.Props file. However, if
you set it false in this file, you'll still need to explicitly set the property to false in
the .NET 8+ projects in the solution. Similarly, if some projects explicitly set a value
that's different from the value in the Directory.Build.Props file, publish will fail.
If you're publishing a solution and the performance has regressed, you can set the
DOTNET_CLI_LAZY_PUBLISH_AND_PACK_RELEASE_FOR_SOLUTIONS environment variable to
true (or any other value) to remove the regression. However, if you set this
variable and your solution contains a .NET 8+ project and a project that targets
.NET 7 or earlier, publishing will fail until all projects define PublishRelease . This
variable affects both dotnet publish and dotnet pack .
See also
'dotnet pack' uses Release configuration
'dotnet restore' produces security
vulnerability warnings
Article • 08/22/2023
The dotnet restore command, which restores the dependencies and tools of a project,
now produces security vulnerability warnings by default.
Previous behavior
Previously, dotnet restore did not emit any security vulnerability warnings by default.
New behavior
If you're developing with the .NET 8 SDK or a later version, dotnet restore produces
security vulnerability warnings by default for all restored projects. When you load a
solution or project, or run a CI/CD script, this change may break your workflow if you
have <TreatWarningsAsErrors> enabled.
Version introduced
.NET 8 Preview 4
Type of breaking change
This change is a behavioral change.
Reason for change
In most cases when you restore a package, you want to know whether the restored
package version contains any known security vulnerabilities. This functionality was
added as it is a highly requested feature and security concerns continue to increase each
year where known security issues can not be visible enough to taking immediate action.
Recommended action
To explicitly reduce the probability of this breaking your build due to warnings, you
can consider your usage of <TreatWarningsAsErrors> and use
<WarningsNotAsErrors>NU1901;NU1902;NU1903;NU1904</WarningsNotAsErrors> to
ensure known security vulnerabilities are still allowed in your environment.
If you want to set a different security audit level, add the <NuGetAuditLevel>
property to your project file with possible values of low , moderate , high , and
critical .
If you want to ignore these warnings, you can use <NoWarn> to suppress NU1091NU104 warnings.
To disable the new behavior entirely, you can set the <NuGetAudit> project
property to false .
See also
Auditing package dependencies for security vulnerabilities
MSBuild custom derived build events
deprecated
Article • 09/07/2023
Custom derived build events of any subclass of BuildEventArgs by any build extensibility
(mainly custom tasks) have been deprecated.
Previous behavior
Previously, you could derive from any subclass of BuildEventArgs and use those types
freely in custom tasks and other build extensibility points.
New behavior
Starting in .NET 8, a build error is issued if your code uses any type derived from
BuildEventArgs and you build using the .NET 8 version of MSBuild, that is, from the
command line:
Usage of unsecure BinaryFormatter during serialization of custom event type
'MyCustomBuildEventArgs'. This will be deprecated soon. Please use
Extended*EventArgs instead. More info: https://aka.ms/msbuild/eventargs
If you build from Visual Studio, there is no change in behavior unless you opt in by
setting the MSBUILDCUSTOMBUILDEVENTWARNING environment variable to 1 (available in
Visual Studio version 17.8 and later).
Version introduced
.NET 8 RC 1
Type of change
This change is a behavioral change.
Reason for change
BinaryFormatter serialization is obsolete in .NET 8 and later versions. Any use of
BinaryFormatter throws an exception at run time. Since MSBuild custom derived build
events use BinaryFormatter, your build would crash if you use these events in your build.
The new build error provides a more graceful failure.
Recommended action
Use one of the following newly introduced, built-in events for extensibility instead of
your custom derived build event:
Microsoft.Build.Framework.ExtendedCustomBuildEventArgs
Microsoft.Build.Framework.ExtendedBuildErrorEventArgs
Microsoft.Build.Framework.ExtendedBuildMessageEventArgs
Microsoft.Build.Framework.ExtendedBuildWarningEventArgs
Alternatively, you can temporarily disable the check by explicitly setting the environment
variable MSBUILDCUSTOMBUILDEVENTWARNING to something other than 1.
Affected APIs
Microsoft.Build.Framework.CustomBuildEventArgs
MSBuild respects
DOTNET_CLI_UI_LANGUAGE
Article • 06/13/2023
MSBuild now respects the DOTNET_CLI_UI_LANGUAGE environment variable and uses the
language specified by DOTNET_CLI_UI_LANGUAGE for its command-line output. This change
affects the output of the msbuild.exe , dotnet build , and dotnet msbuild commands.
Previous behavior
Previously, MSBuild command-line output was always in the operating system (OS)
language and used its own encoding, regardless of DOTNET_CLI_UI_LANGUAGE .
New behavior
MSBuild uses the language specified by DOTNET_CLI_UI_LANGUAGE instead of the OS
language for its command-line output.
On Windows, MSBuild output uses UTF-8 encoding now if DOTNET_CLI_UI_LANGUAGE is set
and UTF-8 is supported.
Version introduced
.NET 8 Preview 5
Type of breaking change
This change is a behavioral change.
Reason for change
Previously, output from commands like dotnet build was a mixture of the
DOTNET_CLI_UI_LANGUAGE language (for .NET SDK output) and the OS language (for
MSBuild output). For example, the "Build succeeded/failed" output used the OS
language. With this change, the language of .NET SDK and MSBuild output is consistent.
Recommended action
If you want to keep the old behavior, unset DOTNET_CLI_UI_LANGUAGE by using the
command set DOTNET_CLI_UI_LANGUAGE= (or a similar command for your shell to change
environment variables).
Runtime-specific apps no longer selfcontained
Article • 06/08/2023
Runtime-specific apps, or .NET apps with a RuntimeIdentifier , are no longer selfcontained by default. Instead, they are framework-dependent by default.
This is a breaking change in the following situations:
If you deployed, distributed, or published your app and didn't explicitly add the
SelfContained property, but also didn't require that the .NET runtime be installed
on the machine for it to work. In this case, you may have relied on the previous
behavior to produce a self-contained app by default.
If you rely on the IL Link tool. In this case, you'll have to take the steps described
under Recommended action to use IL Link again.
Previous behavior
Previously, if a runtime identifier (RID) was specified (via RuntimeIdentifier), the app was
published as self-contained, even if SelfContained wasn't explicitly specified.
In addition:
If PublishSelfContained wasn't explicitly set to false , the publish properties
PublishSingleFile and PublishAot implied a RuntimeIdentifier and therefore
SelfContained (if it wasn't specified) during operations including dotnet build ,
dotnet restore , and dotnet publish .
The PublishTrimmed property did not imply SelfContained .
The PublishReadyToRun property implied SelfContained if SelfContained wasn't
specified.
New behavior
Starting in .NET 8, for apps that target .NET 8 or a later version, RuntimeIdentifier no
longer implies SelfContained by default. Instead, apps that specify a runtime identifier
will be dependent on the .NET runtime by default (framework-dependent). Apps that
target .NET 7 or earlier versions aren't affected.
In addition:
If PublishSelfContained isn't explicitly set to false , the publish properties
PublishSingleFile and PublishAot now imply SelfContained (if it's not specified)
during dotnet publish only (that is, not for dotnet build or dotnet restore ).
The PublishTrimmed property also now implies SelfContained during dotnet
publish .
The PublishReadyToRun property no longer implies SelfContained if the project
targets .NET 8 or later.
7 Note
If you publish using msbuild /t:Publish , you must explicitly specify SelfContained
if you want your app to be self-contained, even if your project has one of the listed
publish properties.
Version introduced
.NET 8 Preview 5
Type of breaking change
This change can affect source compatibility and binary compatibility.
Reason for change
The new .NET SDK behavior aligns with Visual Studio behavior.
Framework-dependent apps are smaller by default, since there aren't copies of
.NET stored in each app.
When .NET is managed outside of the app (that is, for framework-dependent
deployments), .NET stays more secure and up-to-date. Apps that have their own
copy of the runtime don't get security updates. This change makes more apps
framework-dependent by default.
Ideally, command-line options are orthogonal. In this case, the tooling supports
both RID-specific self-contained deployment (SCD) and RID-specific frameworkdependent deployment (FDD). So it didn't make sense that no RID defaulted to
FDD and RID defaulted to SCD. This behavior was often confusing for users.
.NET 6 alerted users to this breaking change with the following warning:
warning NETSDK1179: One of '--self-contained' or '--no-self-contained' options are
required when '--runtime' is used.
Now that customers have had time to add SelfContained explicitly, it's okay to
introduce the break.
Recommended action
If you're using .NET 7 or an earlier version and relied on the previous behavior
where SelfContained was inferred, you'll see this warning:
For projects with TargetFrameworks >= 8.0, RuntimeIdentifier no longer
automatically gives a SelfContained app. To continue creating a .NET framework
independent app after upgrading to 8.0, consider setting SelfContained
explicitly.
Follow the guidance of the warning and set SelfContained to true in the project
file ( <SelfContained>true</SelfContained> ) or as a command-line argument, for
example, dotnet publish --self-contained .
If you're using .NET 8 and want to keep the previous behavior, set SelfContained
to true .
See also
.NET application publishing overview
.NET SDK uses a smaller RID graph
Article • 09/07/2023
Projects that target .NET 8 or later versions now use a smaller, "portable" runtime
identifier (RID) graph.
Previous behavior
The .NET SDK used a complex RID graph to determine assets when building or
publishing a project.
New behavior
Starting in .NET 8, the .NET SDK uses a smaller graph consisting of only portable RIDs,
for projects that target .NET 8 or a later version. This means that the SDK won't
recognize version-specific or distro-specific RIDs by default.
Version introduced
.NET 8 RC 1
Type of breaking change
This change is a behavioral change and can also affect source compatibility.
Reason for change
The RID graph was costly to maintain and understand, requiring .NET itself to be distroaware in a fragile manner. The .NET team and the community spend a non-trivial
amount of time updating the graph and backporting such updates to previous releases.
The long-term goal is to stop updating the RID graph, stop reading it, and eventually
remove it. This breaking change is a step towards that goal.
Recommended action
Use portable RIDs, for example, linux-<arch> , linux-musl-<arch> , osx-<arch> , and win<arch> .
If you need to revert to the previous behavior of using the old, full RID graph, you can
set the UseRidGraph MSBuild property to true in your project file. However, the old RID
graph won't be updated in the future to attempt to handle any other distros or
architectures.
See also
Host determines RID-specific assets
Trimming may not be used with .NET
standard or .NET framework
Article • 08/25/2023
Projects that set <PublishTrimmed>true</PublishTrimmed> ,
<IsTrimmable>true</IsTrimmable> or <EnableTrimAnalyzer>true</EnableTrimAnalyzer>
with a TargetFramework that is any version of .NET Standard or .NET Framework produce
a warning or error because trimming is unsupported for these target frameworks.
Previous behavior
Previously, when used in a .NET Standard or .NET Framework project, these settings
behaved as follows:
<PublishTrimmed>true</PublishTrimmed> would have no effect.
<IsTrimmable>true</IsTrimmable> would embed an assembly-level attribute
[assembly: AssemblyMetadata("IsTrimmable", "true")] into the output assembly.
That attribute opted the assembly into trimming when consumed in a trimmed app
(even an app that uses <TrimMode>partial</TrimMode> ).
<EnableTrimAnalyzer>true</EnableTrimAnalyzer> would enable trim analysis for the
library, using the .NET Standard or .NET Framework reference assemblies
corresponding to the library's TargetFramework even though these reference
assemblies aren't annotated for trimming.
New behavior
Starting in the .NET 8 SDK, in a project targeting .NET Standard or .NET Framework:
<PublishTrimmed> produces an error indicating that this setting is unsupported for
the target framework.
<IsTrimmable> or <EnableTrimAnalyzer> produce a warning indicating that
<IsTrimmable> is unsupported for the target framework. These settings otherwise
have no effect on the build output.
Version introduced
.NET 8 RC 1
Type of breaking change
This change is a behavioral change.
Reason for change
Allowing the use of <PublishTrimmed> in a .NET Standard or .NET Framework project
might have given the false impression that this setting was trimming the output, when
in fact it had no effect.
Allowing the use of <IsTrimmable> or <EnableTrimAnalyzer> in a .NET Standard or .NET
Framework project meant that it was easy for library authors to opt into trimming,
without being alerted about trim incompatibilities. Because the .NET Standard and .NET
Framework reference assemblies aren't annotated for trimming, there were no warnings
about uses of framework APIs that are incompatible with trimming.
Recommended action
Avoid setting <PublishTrimmed> in projects that target .NET Standard or .NET
Framework. Also avoid setting <PublishAot> which implies the former setting.
Avoid setting <IsTrimmable> or <EnableTrimAnalyzer> in libraries that target .NET
Standard or .NET Framework. Also avoid setting <IsAotCompatible> , which implies the
former settings. Instead, multi-target the library to include the latest TargetFramework ,
and enable <IsTrimmable> only for the supported target frameworks. Setting
<IsTrimmable> will run the latest version of the trim analyzer using trim-compatibility
annotations from the latest version of the framework.
For example, these settings multi-target to include net8.0 , and set <IsTrimmable> only
for this target framework. This logic uses IsTargetFrameworkCompatible so that it will
apply to any frameworks compatible with net6.0 , when trimming was first officially
supported. This way, the condition doesn't need to be updated when adding new target
frameworks.
XML
<PropertyGroup>
<TargetFrameworks>netstandard2.1;net8.0</TargetFrameworks>
<IsTrimmable
Condition="$([MSBuild]::IsTargetFrameworkCompatible('$(TargetFramework)',
'net6.0'))">true</IsTrimmable>
</PropertyGroup>
See also
Prepare .NET libraries for trimming
Version requirements for .NET 8 SDK
Article • 09/07/2023
Per the published support rules, we update the minimum Visual Studio and MSBuild
version for each new major release with a one quarter delay. For the .NET 8 release,
8.0.100 requires version 17.7 to be loaded but only supports targeting .NET 7 in that
version. To target net8.0 , you must use version 17.8 or later.
Version introduced
.NET SDK 8 RC 1
Previous behavior
.NET 8.0.1xx-preview1 required version 17.4 of Visual Studio and MSBuild. .NET 8.0.1xxpreview4 required version 17.6 of Visual Studio and MSBuild.
New behavior
Versions 8.0.1xx of the .NET SDK require Visual Studio version 17.7 and MSBuild version
17.7.
Reason for change
This is our standard support policy for the SDK as we can't support all prior versions of
Visual Studio and MSBuild.
Recommended action
Upgrade your Visual Studio version to the required version.
Affected APIs
N/A
See also
Targeting and support rules
BinaryFormatter disabled across most
project types
Article • 05/17/2023
The BinaryFormatter.Serialize(Stream, Object) and BinaryFormatter.Deserialize(Stream)
methods now throw a NotSupportedException at run time across nearly all project
types, including console applications.
Previous behavior
In .NET 7, the BinaryFormatter.Serialize(Stream, Object) and
BinaryFormatter.Deserialize(Stream) methods were marked obsolete and raised an error
at compile time. However, if your application suppressed the obsoletion, it could still call
the methods and they functioned properly in most project types (excluding ASP.NET,
WASM, and MAUI). For example, the APIs functioned correctly in a console app.
New behavior
Starting in .NET 8, the affected methods throw a NotSupportedException at run time
across all project types except Windows Forms and WPF. The APIs continue to remain
obsolete (as error) across all project types, including Windows Forms and WPF.
Version introduced
.NET 8 Preview 4
Type of breaking change
This change is a behavioral change.
Reason for change
This run-time change is the next stage of the BinaryFormatter obsoletion plan , in
which BinaryFormatter will eventually be removed from .NET.
Recommended action
The best course of action is to migrate away from BinaryFormatter due to its security
and reliability flaws.
However, should you need to continue using BinaryFormatter , you can set a
compatibility switch in your project file to re-enable BinaryFormatter functionality. For
more information, see the Recommended action section of the .NET 7 breaking change
notification. That compatibility switch continues to be honored in .NET 8.
Affected APIs
System.Runtime.Serialization.Formatters.Binary.BinaryFormatter.Serialize(Stream,
Object)
System.Runtime.Serialization.Formatters.Binary.BinaryFormatter.Deserialize(Stream)
See also
BinaryFormatter serialization methods are obsolete (.NET 5)
SerializationFormat.Binary is obsolete (.NET 7)
BinaryFormatter serialization APIs produce compiler errors (.NET 7)
Reflection-based deserializer resolves
metadata eagerly
Article • 09/15/2023
The System.Text.Json reflection-based serializer previously used a lazy loading approach
to resolve property metadata. That approach made it possible for POCOs that contained
unsupported property types to deserialize successfully, provided that the underlying
JSON didn't bind to any of the unsupported properties. (This was despite the fact that
instances of the same type would fail to serialize.)
Starting with .NET 8, the serializer has been changed so that all properties are resolved
eagerly in both serialization and deserialization. This change was made to add better
support for combining multiple resolvers, which necessitates early analysis of the
serialized type graph. A side-effect of this change is that if you depended on the
previous behavior, you could start seeing new runtime deserialization errors.
Previous behavior
The following deserialization code succeeded in .NET 7.
C#
var result = JsonSerializer.Deserialize<MyPoco>("""{ "Value": 1 }"""); //,
MyContext.Default.MyPoco);
Console.WriteLine(result.Value);
public class MyPoco
{
public int Value { get; set; }
public NestedValue Unsupported { get; set; }
}
public class NestedValue
{
public ReadOnlySpan<byte> Span => Array.Empty<byte>();
}
New behavior
Starting in .NET 8, the same code from the Previous behavior section throws an
InvalidOperationException at run time.
System.InvalidOperationException: The type 'System.ReadOnlySpan`1[System.Byte]'
of property 'Span' on type 'NestedValue' is invalid for serialization or deserialization
because it is a pointer type, is a ref struct, or contains generic parameters that have
not been replaced by specific types.
This error is consistent with the error that was thrown even in previous versions if you
attempted to serialize an instance of the same type. It's also consistent with the source
generator, which produces a compile-time error.
Version introduced
.NET 8 Preview 4
Type of breaking change
This change is a behavioral change.
Reason for change
This change was necessitated by new requirements related to fast-path serialization
support in combined source generated contexts (see dotnet/runtime#71933
Recommended action
If this change is problematic for you, you can:
Remove the unsupported property from your type.
Author a custom converter for the unsupported type.
Add the JsonIgnoreAttribute attribute:
C#
public class MyPoco
{
public int Value { get; set; }
[JsonIgnore]
public NestedValue Unsupported { get; set; }
}
).
Affected APIs
System.Text.Json.JsonSerializer.Deserialize
Anchor layout changes
Article • 01/25/2023
Control anchor computations have been changed to support high DPI devices. For more
information about the changes, see Anchor layout changes in .NET 8 .
Version introduced
.NET 8 Preview 1
Previous behavior
Certain applications using HighDpiMode.SystemAware or HighDpiMode.PerMonitorV2
mode and anchored controls encountered layout issues on high DPI devices.
New behavior
Applications using HighDpiMode.SystemAware or HighDpiMode.PerMonitorV2 mode
and anchored controls should have improved layout when rendered on high DPI
devices.
Change category
This change is a behavioral change.
Reason for change
This change is part of a broader effort to improve the Windows Forms user experience
on high DPI monitors. It enables developers to use an anchored layout for applications
on high DPI devices.
Recommended action
If the new behavior is problematic for you, you can opt out by setting
System.Windows.Forms.AnchorLayoutV2 to false in your runtimeconfig.json file.
runtimeconfig.template.json template file:
JSON
{
"configProperties": {
"System.Windows.Forms.AnchorLayoutV2": false
}
}
[appname].runtimeconfig.json output file:
JSON
{
"runtimeOptions": {
"configProperties": {
"System.Windows.Forms.AnchorLayoutV2": false
}
}
}
Affected APIs
N/A
See also
Anchor layout changes in .NET 8
DefaultValueAttribute removed from
some properties
Article • 03/17/2023
DefaultValueAttribute has been removed from control properties that are dependent on
the default font height.
Version introduced
.NET 8
Previous behavior
The affected properties were decorated with DefaultValueAttribute, and default values
were hardcoded according to an assumed application-wide font.
New behavior
Starting in .NET 8, the attribute is removed from certain properties. Design-time default
values are calculated at startup based on the current font metrics.
Change category
This change is a behavioral change.
Reason for change
The DefaultValueAttribute attribute is designed to define constant default values used
by the Windows Forms designer. In the past, this attribute was used to specify defaults
on certain properties that depend on the current font height. A new default font was
introduced in .NET Core 3.1, but the attribute values weren't updated accordingly.
Moreover, there's now an API to modify application font. Thus, it makes sense to use
dynamic default values instead of constant ones.
The designer provides methods to specify dynamic default values, however, for
properties decorated with DefaultValueAttribute, it always uses the constant default
value instead. The methods for specifying dynamic defaults preserve the design-time
functionality provided by the attribute.
Recommended action
DefaultValueAttribute is intended for internal use in design-time scenarios. You
shouldn't use it in other scenarios.
Affected APIs
The following table lists the affected properties.
Property
Change version
System.Windows.Forms.DataGridViewRow.Height
Preview 2
System.Windows.Forms.ListBox.ItemHeight
Preview 2
ExceptionCollection ctor throws
ArgumentException
Article • 02/15/2023
The ExceptionCollection constructor now throws an ArgumentException if the elements
in the input array are not of type Exception.
Version introduced
.NET 8 Preview 1
Previous behavior
Previously, the ExceptionCollection constructor did not check the type passed in, which
could delay failure until later in the process. No exceptions were thrown during object
creation.
New behavior
Starting in .NET 8, if the elements in the input array are not of type Exception, an
ArgumentException is thrown.
Change category
This change is a behavioral change.
Reason for change
This change helps to make exception types consistent across the code base.
Recommended action
For most scenarios, this change should not have a significant impact. However, consider
updating your code to handle ArgumentException at constructor call sites.
Affected APIs
ExceptionCollection(ArrayList) constructor
Forms scale according to
AutoScaleMode
Article • 02/15/2023
In PerMonitorV2-mode apps, Windows Forms has been using linear sizes (also known as
DPI-scaled sizes) provided by Windows for top-level windows, regardless of the
AutoScaleMode. This implementation was problematic when using the
AutoScaleMode.Font scaling mode, where Form scaling should be non-linear. The child
controls are scaled non-linearly and depend on the font that was assigned to the Form
or child controls.
This change enables WM_GETDPISCALEDSIZE message handling for top-level Form objects.
It utilizes WM_GETDPISCALEDSIZE to let Windows know that the Form may need nonlinear sizes depending on AutoScaleMode.
Version introduced
.NET 8 Preview 1
Previous behavior
Previously, in PerMonitorV2-mode apps, top-level windows were scaled by Windows
and disregarded AutoScaleMode as specified in the WinForms application. This
implementation led to inconsistent scaling between Form objects and their child
controls.
New behavior
In PerMonitorV2-mode apps, top-level windows (such as Forms) are scaled according to
AutoScaleMode. This implementation ensures that top-level windows scale consistently
with their child controls.
Change category
This change is a behavioral change.
Reason for change
This change was made to improve the high-DPI experience for Windows Forms apps in
PerMonitorV2 mode.
Recommended action
No action is required.
Affected APIs
N/A
See also
Top-level forms scale minimum and maximum size to DPI
ImageList.ColorDepth default is
Depth32Bit
Article • 02/15/2023
The default value for ImageList.ColorDepth has changed over time. Starting in .NET 8,
the default value has changed from Depth8Bit to Depth32Bit. This change affects both
new and existing applications if they're upgraded to target .NET 8.
Version introduced
.NET 8 Preview 1
Previous behavior
The default value for ImageList.ColorDepth was ColorDepth.Depth8Bit.
New behavior
If you haven't explicitly set ImageList.ColorDepth for an image list, the color depth will
automatically be reset to ColorDepth.Depth32Bit. This could increase your app's
memory usage.
Change category
This change is a behavioral change.
Reason for change
The default value was changed to improve image quality.
Recommended action
If you want to continue using the previous color depth, explicitly set
ImageList.ColorDepth to ColorDepth.Depth8Bit.
Affected APIs
System.Windows.Forms.ImageList.ColorDepth
TableLayoutStyleCollection throws
ArgumentException
Article • 02/15/2023
TableLayoutStyleCollection enforces the type passed to its collection operations. The
affected APIs now throw an ArgumentException instead of an InvalidCastException if the
input is not of type TableLayoutStyle.
Version introduced
.NET 8 Preview 1
Previous behavior
Previously, if the input couldn't be converted to type TableLayoutStyle, an
InvalidCastException was thrown.
New behavior
Starting in .NET 8, if the input can't be converted to type TableLayoutStyle, an
ArgumentException is thrown.
Change category
This change is a behavioral change.
Reason for change
This change helps to make exception types consistent across the code base.
Recommended action
For most scenarios, this change should not have a significant impact. However, if you
previously handled InvalidCastException, update your code to handle
ArgumentException instead.
Affected APIs
System.Windows.Forms.TableLayoutStyleCollection.System.Collections.IList.Add(Obj
ect)
System.Windows.Forms.TableLayoutStyleCollection.System.Collections.IList.Insert(In
t32, Object)
System.Windows.Forms.TableLayoutStyleCollection.System.Collections.IList.Remove
(Object)
Top-level forms scale minimum and
maximum size to DPI
Article • 02/15/2023
Top-level forms in Windows Forms now scale their MinimumSize and MaximumSize
values according to the dots per inch (DPI) of the monitor when running in
HighDpiMode.PerMonitorV2 mode.
Version introduced
.NET 8 Preview 1
Previous behavior
In .NET 8, the MinimumSize and MaximumSize values for top-level forms remained
constant regardless of the application DPI mode and the DPI of the monitor where the
form is rendered. This sometimes resulted in scaling limitations of the top-level form.
You can also opt into this behavior in .NET 7. To opt in, set the
System.Windows.Forms.ScaleTopLevelFormMinMaxSizeForDpi runtime configuration option
described in the Recommended action section.
New behavior
Starting in .NET 8, top-level forms scale their MinimumSize and MaximumSize values
according to the DPI of the monitor when running in HighDpiMode.PerMonitorV2
mode. The behavior of your app might change in the following ways:
Run-time dependencies might be impacted when the minimum and maximum size
of the form change.
New MinimumSizeChanged and MaximumSizeChanged events might be raised.
The scaled form size now has new constraint values for the minimum and
maximum sizes.
Change category
This change is a behavioral change.
Reason for change
This change is part of a broader effort to improve the Windows Forms user experience
on high DPI monitors. It enables developers to set minimum and maximum sizes for
top-Level forms without having to take the DPI of the monitor into account.
Recommended action
If the new behavior is problematic for you, you can opt out by setting
System.Windows.Forms.ScaleTopLevelFormMinMaxSizeForDpi to false in your
runtimeconfig.json file.
runtimeconfig.template.json template file:
JSON
{
"configProperties": {
"System.Windows.Forms.ScaleTopLevelFormMinMaxSizeForDpi": false
}
}
[appname].runtimeconfig.json output file:
JSON
{
"runtimeOptions": {
"configProperties": {
"System.Windows.Forms.ScaleTopLevelFormMinMaxSizeForDpi": false
}
}
}
Affected APIs
N/A
See also
Forms scale according to AutoScaleMode
WFDEV002 obsoletion is now an error
Article • 02/01/2023
The WFDEV002 obsoletion has been promoted from a warning to an error in .NET 8. Any
reference to DomainUpDown.DomainUpDownAccessibleObject will result in a
compilation error that can't be suppressed. In addition,
DomainUpDown.CreateAccessibilityInstance() now returns an object of the internal type
UpDownBase.UpDownBaseAccessibleObject .
Version introduced
.NET 8 Preview 1
Previous behavior
Previously, if you referenced the DomainUpDown.DomainUpDownAccessibleObject
type, you got compile-time warning WFDEV002.
Also, DomainUpDown.CreateAccessibilityInstance() returned an object of type
DomainUpDown.DomainUpDownAccessibleObject.
New behavior
If you reference the DomainUpDown.DomainUpDownAccessibleObject type, you'll get a
compile-time error with the same diagnostic ID (WFDEV002).
In addition, since the type has been removed,
DomainUpDown.CreateAccessibilityInstance() now returns an object of type
UpDownBase.UpDownBaseAccessibleObject (which is an internal type).
Change category
This change can affect source compatibility.
Reason for change
The DomainUpDown.DomainUpDownAccessibleObject class has always been
documented as "internal use only". All functionality of the class was moved to the base
class.
Recommended action
Update your code to use System.Windows.Forms.Control.ControlAccessibleObject or
AccessibleObject instead of DomainUpDown.DomainUpDownAccessibleObject.
Affected APIs
System.Windows.Forms.DomainUpDown.DomainUpDownAccessibleObject
System.Windows.Forms.DomainUpDown.CreateAccessibilityInstance()
Breaking changes in .NET 7
Article • 07/28/2023
If you're migrating an app to .NET 7, the breaking changes listed here might affect you.
Changes are grouped by technology area, such as ASP.NET Core or Windows Forms.
This article indicates whether each breaking change is binary compatible or source
compatible:
Binary compatible - Existing binaries will load and execute successfully without
recompilation, and the run-time behavior won't change.
Source compatible - Source code will compile successfully without changes when
targeting the new runtime or using the new SDK or component.
ASP.NET Core
Title
Binary
compatible
Source
compatible
API controller actions try to infer parameters from DI
✔️
❌
ASPNET-prefixed environment variable precedence
✔️
✔️
AuthenticateAsync for remote auth providers
✔️
❌
Authentication in WebAssembly apps
❌
✔️
Default authentication scheme
❌
✔️
Event IDs for some Microsoft.AspNetCore.Mvc.Core log
❌
✔️
Fallback file endpoints
❌
✔️
IHubClients and IHubCallerClients hide members
✔️
❌
Kestrel: Default HTTPS binding removed
❌
✔️
Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv and
libuv.dll removed
❌
❌
Microsoft.Data.SqlClient updated to 4.0.1
✔️
❌
Middleware no longer defers to endpoint with null request
delegate
❌
✔️
MVC's detection of an empty body in model binding changed
❌
✔️
messages changed
Title
Binary
compatible
Source
compatible
Output caching API changes
❌
❌
SignalR Hub methods try to resolve parameters from DI
✔️
❌
Core .NET libraries
Title
Binary
Source
compatible
compatible
API obsoletions with default diagnostic ID
✔️
❌
API obsoletions with non-default diagnostic IDs
✔️
❌
BinaryFormatter serialization APIs produce compiler errors
✔️
❌
BrotliStream no longer allows undefined
❌
✔️
C++/CLI projects in Visual Studio
✔️
❌
Changes to reflection invoke API exceptions
❌
✔️
Collectible Assembly in non-collectible
AssemblyLoadContext
❌
✔️
DateTime addition methods precision change
✔️
✔️
Equals method behavior change for NaN
❌
✔️
EventSource callback behavior
✔️
✔️
Generic type constraint on PatternContext<T>
❌
❌
Legacy FileStream strategy removed
❌
✔️
Library support for older frameworks
❌
❌
Maximum precision for numeric format strings
❌
✔️
SerializationFormat.Binary is obsolete
❌
❌
System.Drawing.Common config switch removed
✔️
✔️
System.Runtime.CompilerServices.Unsafe NuGet package
✔️
✔️
Time fields on symbolic links
❌
✔️
Tracking linked cache entries
❌
✔️
CompressionLevel values
Title
Binary
Source
compatible
compatible
❌
✔️
Validate CompressionLevel for BrotliStream
Configuration
Title
Binary compatible
Source compatible
❌
✔️
System.diagnostics entry in app.config
Cryptography
Title
Binary compatible
Source compatible
Decrypting EnvelopedCms doesn't double unwrap
❌
✔️
Dynamic X509ChainPolicy verification time
❌
✔️
X500DistinguishedName parsing of friendly names
❌
✔️
Deployment
Title
Binary compatible
Source compatible
All assemblies trimmed by default
✔️
❌
Multi-level lookup is disabled
❌
✔️
x86 host path on 64-bit Windows
✔️
✔️
TrimmerDefaultAction is deprecated
✔️
❌
Entity Framework Core
Breaking changes in EF Core 7
Extensions
Title
Binary
compatible
Source
compatible
Binding config to dictionary extends values
✔️
✔️
ContentRootPath for apps launched by Windows
❌
✔️
❌
✔️
Binary
Source
compatible
compatible
❌
✔️
Binary compatible
Source compatible
❌
✔️
Shell
Environment variable prefixes
Globalization
Title
Globalization APIs use ICU libraries on Windows
Server
Interop
Title
RuntimeInformation.OSArchitecture under emulation
.NET MAUI
Title
Binary
compatible
Source
compatible
Constructors accept base interface instead of concrete
type
❌
✔️
Flow direction helper methods removed
❌
❌
New UpdateBackground parameter
❌
✔️
ScrollToRequest property renamed
❌
❌
Some Windows APIs are removed
❌
❌
Networking
Title
Binary
Source
compatible
compatible
AllowRenegotiation default is false
❌
❌
Custom ping payloads on Linux
❌
✔️
Socket.End methods don't throw
❌
✔️
ObjectDisposedException
SDK and MSBuild
Title
Binary
compatible
Source
compatible
Automatic RuntimeIdentifier for certain projects
✔️
❌
Automatic RuntimeIdentifier for publish only
❌
❌
CLI console output uses UTF-8
❌
❌
Console encoding not UTF-8 after completion
❌
✔️
MSBuild serialization of custom types in .NET 7
❌
❌
Side-by-side SDK installations
❌
❌
Tool manifests in root folder
✔️
✔️
Version requirements for .NET 7 SDK
✔️
✔️
dotnet test: switch -a to alias --arch instead of --testadapter-path
❌
❌
dotnet test: switch -r to alias --runtime instead of --
❌
❌
--output option no longer is valid for solution-level
commands
❌
❌
SDK no longer calls ResolvePackageDependencies
✔️
❌
Binary
compatible
Source
compatible
❌
✔️
results-dir
Serialization
Title
DataContractSerializer retains sign when deserializing -0
Title
Binary
compatible
Source
compatible
Deserialize Version type with leading or trailing
whitespace
❌
✔️
JsonSerializerOptions copy constructor includes
JsonSerializerContext
❌
✔️
Polymorphic serialization for object types
❌
✔️
System.Text.Json source generator fallback
❌
✔️
Windows Forms
Title
Binary compatible
Source compatible
Obsoletions and warnings
✔️
❌
Some APIs throw ArgumentNullException
❌
✔️
XML and XSLT
Title
XmlSecureResolver is obsolete
See also
What's new in .NET 7
Binary compatible
Source compatible
❌
❌
API controller actions try to infer
parameters from DI
Article • 05/04/2023
The mechanism to infer binding sources of API controller action parameters now marks
parameters to be bound from the Dependency Injection (DI) container when the type is
registered in the container. In rare cases, this can break apps that have a type in DI that
is also accepted in API controller action methods.
Version introduced
ASP.NET Core 7.0
Previous behavior
If you wanted to bind a type registered in the DI container, it must be explicitly
decorated using an attribute that implements IFromServiceMetadata, such as
FromServicesAttribute:
C#
Services.AddScoped<SomeCustomType>();
[Route("[controller]")]
[ApiController]
public class MyController : ControllerBase
{
public ActionResult Get([FromServices]SomeCustomType service) => Ok();
}
If the attribute wasn't specified, the parameter was resolved from the request body sent
by the client:
C#
Services.AddScoped<SomeCustomType>();
[Route("[controller]")]
[ApiController]
public class MyController : ControllerBase
{
// Bind from the request body
[HttpPost]
public ActionResult Post(SomeCustomType service) => Ok();
}
New behavior
Types in DI are checked at app startup using IServiceProviderIsService to determine if an
argument in an API controller action comes from DI or from other sources.
In the following example, which assumes you're using the default DI container,
SomeCustomType comes from the DI container:
C#
Services.AddScoped<SomeCustomType>();
[Route("[controller]")]
[ApiController]
public class MyController : ControllerBase
{
// Bind from DI
[HttpPost]
public ActionResult Post(SomeCustomType service) => Ok();
}
The mechanism to infer binding sources of API controller action parameters follows the
following rules:
1. A previously specified BindingInfo.BindingSource is never overwritten.
2. A complex type parameter registered in the DI container is assigned
BindingSource.Services.
3. A complex type parameter not registered in the DI container is assigned
BindingSource.Body.
4. A parameter with a name that appears as a route value in any route template is
assigned BindingSource.Path.
5. All other parameters are assigned BindingSource.Query.
Type of breaking change
This change affects source compatibility.
Reason for change
This same behavior is already implemented in minimal APIs.
The likelihood of breaking apps is low as it isn't common to have a type in DI and as an
argument in your API controller action at the same time.
Recommended action
If you're broken by this change, you can disable the feature by setting
DisableImplicitFromServicesParameters to true:
C#
Services.Configure<ApiBehaviorOptions>(options =>
{
options.DisableImplicitFromServicesParameters = true;
});
If you're broken by the change, but you want to bind from DI for specific API controller
action parameters, you can disable the feature as shown above and use an attribute that
implements IFromServiceMetadata, such as FromServicesAttribute:
C#
Services.AddScoped<SomeCustomType>();
[Route("[controller]")]
[ApiController]
public class MyController : ControllerBase
{
// Bind from DI
[HttpPost]
public ActionResult Post([FromServices]SomeCustomType service) => Ok();
}
Affected APIs
API controller actions
ASPNET-prefixed environment variable
precedence
Article • 12/07/2022
Starting in .NET 7, and only when using the WebApplicationBuilder host, command-line
arguments and DOTNET_ -prefixed environment variables override ASPNET_ -prefixed
environment variables when reading from default host configuration sources. These
sources are used to read host variables, such as the content root path and environment
name, when the WebApplicationBuilder is constructed and serve as a base for app
configuration.
ASPNET_ -prefixed environment variables now have the lowest precedence of all of the
default host configuration sources for WebApplicationBuilder . For other hosts, such as
ConfigureWebHostDefaults and WebHost.CreateDefaultBuilder , ASPNET_ -prefixed
environment variables still have the highest precedence.
Version introduced
ASP.NET Core 7.0
Previous behavior
ASPNET_ -prefixed environment variables overrode command-line arguments and
DOTNET_ -prefixed environment variables when reading WebApplicationBuilder 's default
host configuration.
New behavior
Command-line arguments and DOTNET_ -prefixed environment variables override
ASPNET_ -prefixed environment variables when reading WebApplicationBuilder 's default
host configuration.
Type of breaking change
This is a behavioral change.
Reason for change
This change was made to prevent environment variables from overriding explicit
command-line arguments when reading host variables. The new behavior is more
consistent with application configuration, which has always given command-line
arguments the highest precedence.
Recommended action
If you were using ASPNETCORE_ -prefixed environment variables to override commandline arguments or DOTNET_ -prefixed environment variables, use something with a higher
priority. This could mean using custom WebApplicationOptions, which overrides all
default hosting configuration sources.
Affected APIs
Microsoft.AspNetCore.Builder.WebApplicationBuilder
See also
Default host configuration sources
AuthenticateAsync for remote auth
providers
Article • 11/08/2022
Remote authentication providers like OpenIdConnect, WsFederation, and OAuth have
been updated to avoid unnecessary errors when there's no user information available on
the request.
Version introduced
.NET 7
Previous behavior
Previously, when AuthenticateAsync was called on a remote authentication provider and
there was no current user, the call failed with a message similar to OpenIdConnect was
not authenticated. Failure message: Not authenticated .
New behavior
Starting in .NET 7, AuthenticateAsync returns AuthenticateResult.NoResult(), an
anonymous identity.
Type of breaking change
This change can affect binary compatibility.
Reason for change
The previous behavior:
Was inconsistent with Cookie and Negotiate authentication, which return
AuthenticateResult.NoResult().
Caused excess failure logs, especially if the remote authentication handler was set
as the default handler and invoked per request.
Recommended action
If you have code that directly invokes AuthenticateAsync, check it to ensure it properly
handles AuthenticateResult.NoResult() and anonymous or empty ClaimsIdentity
instances.
Affected APIs
Microsoft.AspNetCore.Authentication.AuthenticationHttpContextExtensions.Authe
nticateAsync
Authentication in WebAssembly apps
Article • 12/02/2022
We updated the support for authentication in Blazor WebAssembly apps to rely on the
history state instead of query strings in the URL. As a result, existing applications that
pass the return URL through the query string will fail to redirect back to the original
page after a successful login.
Existing applications should use the new NavigateToLogin extension method as it's able
to flow the data to the login page correctly.
Version introduced
.NET 7
Previous behavior
The return URL was specified in the query string as ?returnUrl=<<return-url>> .
New behavior
Starting in .NET 7, the return URL and other parameters passed to the
authentication/login page are passed via the history.state entry of the page.
Type of breaking change
This change can affect binary compatibility.
Reason for change
We decided to switch to using history.state instead of the query string as it simplifies
the implementation and removes the surface attack area associated with passing data
through the query string.
Recommended action
Most apps have a RedirectToLogin.razor file that you can update as follows:
razor
@inject NavigationManager Navigation
@using Microsoft.AspNetCore.Components.WebAssembly.Authentication
@using Microsoft.Extensions.Options
@inject
IOptionsSnapshot<RemoteAuthenticationOptions<ApiAuthorizationProviderOptions
>> Options
@code {
protected override void OnInitialized()
{
Navigation.NavigateToLogin(Options.Get(Microsoft.Extensions.Options.Options.
DefaultName).AuthenticationPaths.LogInPath);
}
}
Affected APIs
Microsoft.AspNetCore.Components.WebAssembly.Authentication.SignOutSessionS
tateManager (obsoleted in favor of new
NavigationManagerExtensions.NavigateToLogout method)
See also
RedirectToLogin component - Blazor WebAssembly
Default authentication scheme
Article • 05/04/2023
Starting in .NET 7, we introduced new behavior in the authentication area in ASP.NET
Core.
Previously, users were required to set the default authentication scheme, which is used
by authentication and authorization handlers, in the AddAuthentication call:
C#
builder.Services.AddAuthentication("MyDefaultScheme");
Moving forward, when a single authentication scheme is registered, that scheme is
treated as the default scheme. For example, "MyDefaultScheme" is treated as the default
scheme in the following code.
C#
builder.Services.AddAuthentication().AddOAuth("MyDefaultScheme");
This change might expose unintended behavior changes in applications, such as
authentication options being validated earlier than expected.
Version introduced
ASP.NET Core 7.0
Previous behavior
Previously, when users did not provide a default scheme in the AddAuthentication call,
no default scheme was set.
C#
builder.Services.AddAuthentication().AddCookie();
This impacted the behavior of authentication handlers in the application layer.
New behavior
Starting in ASP.NET Core 7.0, if (and only if) a single scheme is registered in an
application, that scheme is treated as the default. In the following code, the
CookieDefaults.AuthenticationScheme is treated as the default scheme.
C#
builder.Services.AddAuthentication().AddCookie();
However, in the next code snippet, no default is set because multiple schemes are
registered.
C#
builder.Services.AddAuthentication().AddCookie().AddJwtBearer();
Type of breaking change
This change affects binary compatibility.
Reason for change
This change was made to reduce boilerplate when configuring authentication and to set
up sensible defaults.
Recommended action
The change only impacts applications that have a single scheme registered. For those
scenarios, it's recommended to ensure that your application is prepared to handle the
assumption that a single scheme is the default. For example, ensure that the options
associated with that scheme are configured correctly.
Alternatively, you can disable the new behavior by setting the
Microsoft.AspNetCore.Authentication.SuppressAutoDefaultScheme app context flag.
Affected APIs
Authentication APIs.
Event IDs for some
Microsoft.AspNetCore.Mvc.Core log
messages changed
Article • 05/04/2023
As part of updating the Microsoft.AspNetcore.Mvc.Core assembly to use
LoggerMessageAttribute, the ASP.NET Core team discovered logger event IDs that were
reused within a single log category. Event IDs and names should be unique so that
different message types can be identified. These IDs have been updated to ensure that
they're unique within a single log category.
Version introduced
ASP.NET Core 7.0
Previous behavior
Some logger event IDs within the Microsoft.AspNetCore.Mvc.Core assembly were reused
within a single log category.
New behavior
Duplicate logger event IDs within a single log category within the
Microsoft.AspNetCore.Mvc.Core assembly have been updated.
Type of breaking change
This change affects binary compatibility.
Reason for change
Logger event IDs and names should be unique so that different message types can be
identified.
Recommended action
If you have code or configuration that references the old logger event IDs, update those
references to use the new IDs.
Affected APIs
Not detectable via API analysis.
Fallback file endpoints
Article • 10/13/2022
The ConsumesAttribute attribute allows controller actions to specify their supported
content types. Starting in .NET 6, if a fallback file endpoint was configured, it could
match routes that were discarded because the request had a different content type than
what was specified in an action's ConsumesAttribute. The .NET 6 behavior was an
undesirable change from the .NET 5 behavior. This breaking change partially addresses
the issue by making fallback file endpoints only match GET and HEAD requests.
Version introduced
ASP.NET Core 7.0 RC 2
Previous behavior
Endpoints configured with StaticFilesEndpointRouteBuilderExtensions.MapFallbackToFile
matched requests made with any request method.
New behavior
Endpoints configured with StaticFilesEndpointRouteBuilderExtensions.MapFallbackToFile
only match HEAD and GET requests.
Type of breaking change
This change can affect binary compatibility.
Reason for change
This change partially reverts a larger breaking change accidentally introduced in .NET 6.
Since it's highly unusual to expect a fallback file response when making a request with a
method other than HEAD or GET , the impact of this breaking change should be minimal.
Recommended action
If you want fallback file endpoints to match requests with methods other than HEAD or
GET, you can specify additional HTTP request methods using WithMetadata() . For
example:
C#
endpoints.MapFallbackToFile("index.html")
.WithMetadata(new HttpMethodMetadata(new[] { /* List supported methods
here */ }));
Affected APIs
Microsoft.AspNetCore.Builder.StaticFilesEndpointRouteBuilderExtensions.MapFallba
ckToFile
IHubClients and IHubCallerClients hide
members
Article • 06/24/2022
To add support for client results, IHubClients and IHubCallerClients now hide interface
members IClientProxy Client(string connectionId); and IClientProxy Caller { get;
} with ISingleClientProxy Client(string connectionId); and ISingleClientProxy
Caller { get; } .
This is not a breaking change to production code unless you use reflection to call the
affected Client or Caller methods. You may need to update unit testing SignalR Hubs.
Version introduced
ASP.NET Core 7.0
Previous behavior
When using a testing library like Moq to unit test a SignalR Hub, you could write code
similar to the following:
C#
var hub = new MyHub();
var mockCaller = new Mock<IHubCallerClients>();
var mockClientProxy = new Mock<IClientProxy>();
mockCaller.Setup(x => x.Caller).Returns(mockClientProxy.Object);
hub.Clients = mockCaller.Object;
class MyHub : Hub { }
New behavior
C#
var hub = new MyHub();
var mockCaller = new Mock<IHubCallerClients>();
var mockClientProxy = new Mock<ISingleClientProxy>(); // <-- updated code
mockCaller.Setup(x => x.Caller).Returns(mockClientProxy.Object);
hub.Clients = mockCaller.Object;
class MyHub : Hub { }
Type of breaking change
This change affects source compatibility.
Reason for change
The change was made to add new functionality to SignalR. It is non-breaking in normal
use cases, however, it may break test code, which is easily updated.
Recommended action
Update test code to use the ISingleClientProxy interface when using reflection or
reflection-based code.
Affected APIs
Microsoft.AspNetCore.SignalR.IHubClients
Microsoft.AspNetCore.SignalR.IHubCallerClients
Kestrel: Default HTTPS binding removed
Article • 05/04/2023
The default HTTPS address and port have been removed from Kestrel in .NET 7. This
change is part of dotnet/aspnetcore#42016
, which will improve the overall developer
experience when dealing with HTTPS.
Version introduced
ASP.NET Core 7.0
Previous behavior
Previously, if no values for the address and port were specified explicitly but a local
development certificate was available, Kestrel defaulted to binding to both
http://localhost:5000 and https://localhost:5001 .
New behavior
Users must now manually bind to HTTPS and specify the address and port explicitly,
through one of the following means:
The launchSettings.json file
The ASPNETCORE_URLS environment variable
The --urls command-line argument
The urls host configuration key
The UseUrls(IWebHostBuilder, String[]) extension method
HTTP binding is unchanged.
Type of breaking change
This change affects binary compatibility.
Reason for change
The previous eager-binding behavior occurs without regard to the configured
environment and can lead to a poor developer experience when the certificate has not
yet been trusted (that is, trusted as root certificate authority because it's self-signed).
Clients often produce a poor user experience when hitting an HTTPS endpoint with an
untrusted certificate. For example, they might fail silently or show an error or warning
screen that alarms the user.
Recommended action
If you weren't using the default https://localhost:5001 binding, no changes are
required. However, if you were using this binding, see Configure endpoints for the
ASP.NET Core Kestrel web server to learn how you can update your server to enable
HTTPS.
Affected APIs
N/A
Microsoft.AspNetCore.Server.Kestrel.Tra
nsport.Libuv and libuv.dll removed
Article • 05/04/2023
Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv and libuv.dll have been removed.
Version introduced
ASP.NET Core 7.0
Previous behavior
Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv was obsoleted in ASP.NET Core 5.0.
Its functionality was replaced by the Sockets transport.
New behavior
Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv and its libuv.dll dependency have
been removed.
Type of breaking change
This change affects binary compatibility and source compatibility.
Reason for change
To eliminate ongoing maintenance costs associated with this obsolete component.
Recommended action
Remove project references to Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.
Remove code from Program.cs that calls UseLibuv . The Sockets transport will be used by
default.
Affected APIs
Microsoft.AspNetCore.Hosting.WebHostBuilderLibuvExtensions.UseLibuv
Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.LibuvTransportOptions
Microsoft.Data.SqlClient updated to
4.0.1
Article • 05/04/2023
The Microsoft.Data.SqlClient package has been updated to 4.0.1.
Version introduced
ASP.NET Core 7.0
Previous behavior
The previous version of the package was 1.0.19249.1 .
New behavior
The new version of the Microsoft.Data.SqlClient package is 4.0.1 . You can see
breaking changes in the 4.0 band in the Microsoft.Data.SqlClient 4.0.0 Release Notes .
By default, Encrypt now equals true . For more information, see Encrypt default value
set to true .
Type of breaking change
This change affects source compatibility.
Reason for change
For improvements in the underlying libraries.
Recommended action
The ASP.NET Core team didn't have to react to any public API changes from this change.
However, it's possible that there are breaking changes in the packages themselves that
you'll need to react to.
Affected APIs
Microsoft.Extensions.Caching.SqlServer
Middleware no longer defers to
endpoint with null request delegate
Article • 05/04/2023
As detailed in https://github.com/dotnet/aspnetcore/issues/42413 , the file-serving
middleware ( DefaultFilesMiddleware , DirectoryBrowserMiddleware , and
StaticFileMiddleware ) have been updated to no longer no-op (that is, defer to the next
middleware in the pipeline) when there's an active endpoint with a null request
delegate.
Version introduced
ASP.NET Core 7.0
Previous behavior
Previously, if the current request had an active endpoint, that is,
HttpContext.GetEndpoint() != null , the file-serving middleware would perform no
action and simply delegate to the next middleware in the request pipeline.
New behavior
The file-serving middleware will now only perform no action if there's an active endpoint
and its RequestDelegate property value is not null , that is,
HttpContext.GetEndpoint()?.RequestDelegate is not null .
Type of breaking change
This change affects binary compatibility.
Reason for change
This change enables endpoints to share metadata with endpoint-aware middleware
while allowing other middleware that would otherwise defer to also function. Endpoints
can be active in the request for the purposes of setting and sharing metadata with
middleware that are endpoint-aware so that they can perform their intended function.
Other middleware that would previously defer their behavior when an endpoint was
active, for example, the file-serving middleware, can also function.
For example, an endpoint with a null request delegate containing authorization
metadata can be set as the active endpoint for a request. This causes the
AuthorizationMiddleware to enforce authorization requirements, which, if satisfied,
would allow the StaticFileMiddleware to serve the requested files.
Recommended action
If you rely on setting an active endpoint on the request to suppress the behavior of the
file-serving middleware, ensure that the endpoint has a non-null value set for its
RequestDelegate property.
Affected APIs
IApplicationBuilder.UseStaticFiles()
IApplicationBuilder.UseDefaultFiles()
IApplicationBuilder.UseDirectoryBrowser()
MVC's detection of an empty body in
model binding changed
Article • 05/04/2023
The mechanism to detect an empty request body during MVC model binding now uses
IHttpRequestBodyDetectionFeature.CanHaveBody. This property is set to:
true :
For HTTP/1.x requests with a non-zero Content-Length request header or a
Transfer-Encoding: chunked request header.
For HTTP/2 requests that don't set the END_STREAM flag on the initial header's
frame.
false :
For HTTP/1.x requests with no Content-Length or Transfer-Encoding: chunked
request headers, or if the Content-Length request header is 0 .
For HTTP/1.x requests with a Connection: Upgrade request header, such as for
WebSockets requests. There's no HTTP request body for such requests, so no
data should be received until after the upgrade.
For HTTP/2 requests that set the END_STREAM flag on the initial header's frame.
The previous behavior performed a minimal validation of Content-Length = 0 . In some
scenarios, when requests don't include all required HTTP headers and flags, request
bodies can now be detected as having an empty body and report a failure to the client.
Version introduced
ASP.NET Core 7.0
Previous behavior
If a controller action binds a parameter from the request body and the client request
doesn't include a Content-Length request header, the framework throws an internal
exception during deserialization of the request body. For example, the
System.Text.Json -based input formatter throws an exception similar to the following:
System.Text.Json.JsonException: 'The input does not contain any JSON tokens.
Expected the input to start with a valid JSON token, when isFinalBlock is
true.
Path: $ | LineNumber: 0 | BytePositionInLine: 0.'
The following example JSON shows the previous exception formatted as a
ProblemDetails response:
JSON
{
"type": "https://tools.ietf.org/html/rfc7231#section-6.5.1",
"title": "One or more validation errors occurred.",
"status": 400,
"traceId": "00-34e98b5841b88bfb5476965efd9d9c8c-5bb16bc50dfbabb7-00",
"errors": {
"$": [
"The input does not contain any JSON tokens. Expected the input to
start with a valid JSON token, when isFinalBlock is true. Path: $ |
LineNumber: 0 | BytePositionInLine: 0."
],
"value": [
"The value field is required."
]
}
}
New behavior
Deserialization is no longer attempted if
IHttpRequestBodyDetectionFeature.CanHaveBody is false . The following example
ProblemDetails response shows how the error message returned to clients indicates that
the request body is empty:
JSON
{
"type": "https://tools.ietf.org/html/rfc7231#section-6.5.1",
"title": "One or more validation errors occurred.",
"status": 400,
"traceId": "00-0f87920dc675fdfdf8d7638d3be66577-bd6bdbf32d21b714-00",
"errors": {
"": [
"A non-empty request body is required."
],
"value": [
"The value field is required."
]
}
}
Type of breaking change
This change affects binary compatibility.
Reason for change
To align with other parts of the framework that use
IHttpRequestBodyDetectionFeature.CanHaveBody and to fix Optional [FromBody] Model
Binding is not working (dotnet/aspnetcore #29570)
.
Recommended action
No changes are required. However, if you're seeing unexpected behavior, ensure that
client requests send the appropriate HTTP headers.
Affected APIs
MVC controller actions
Output caching API changes
Article • 10/27/2022
Some APIs in the Microsoft.AspNetCore.OutputCaching namespace have changed to
better represent their intent.
The following APIs were removed:
OutputCachePolicyBuilder.#ctor
OutputCachePolicyBuilder.Clear
The following APIs were renamed:
Previous name
New name
AllowLocking(System.Boolean)
SetLocking(Boolean)
VaryByRouteValue(System.String[])
SetVaryByRouteValue(String[])
VaryByQuery(System.String[])
SetVaryByQuery(String[])
VaryByHeader(System.String[])
SetVaryByHeader(String[])
The following APIs were added:
CacheVaryByRules.VaryByHost
OutputCacheOptions.AddPolicy(String, Action<OutputCachePolicyBuilder>,
Boolean)
OutputCacheOptions.AddBasePolicy(Action<OutputCachePolicyBuilder>, Boolean)
Microsoft.Extensions.DependencyInjection.OutputCacheConventionBuilderExtensio
ns.CacheOutput<TBuilder>(TBuilder, Action<OutputCachePolicyBuilder>, Boolean)
Version introduced
ASP.NET Core 7.0 RC 2
Previous behavior
OutputCachePolicyBuilder.VaryByQuery(System.String[]) was additive: every call added
more query string keys.
New behavior
The OutputCachePolicyBuilder.VaryByQuery(System.String[]) method is now named
OutputCachePolicyBuilder.SetVaryByQuery(String[]), and each call replaces existing
query string keys.
For other changes, see the first section of this article.
Type of breaking change
This change affects source compatibility and binary compatibility.
Reason for change
This change was made to improve the consistency of method names and to remove
ambiguity in their behavior.
Recommended action
Recompile any projects built with an earlier SDK. If you referenced any of these method
names directly, update the source to reflect the new names.
Affected APIs
Microsoft.AspNetCore.OutputCaching.OutputCachePolicyBuilder.#ctor
Microsoft.AspNetCore.OutputCaching.OutputCachePolicyBuilder.Clear
Microsoft.AspNetCore.OutputCaching.OutputCachePolicyBuilder.AllowLocking(Syste
m.Boolean)
Microsoft.AspNetCore.OutputCaching.OutputCachePolicyBuilder.VaryByRouteValue(S
ystem.String[])
Microsoft.AspNetCore.OutputCaching.OutputCachePolicyBuilder.VaryByQuery(System
.String[])
Microsoft.AspNetCore.OutputCaching.OutputCachePolicyBuilder.VaryByHeader(Syste
m.String[])
SignalR Hub methods try to resolve
parameters from DI
Article • 05/04/2023
SignalR Hub methods now support injecting services from your Dependency Injection
(DI) container. In rare cases, this can break apps that have a type in DI that is also
accepted in Hub methods from SignalR client messages.
Version introduced
ASP.NET Core 7.0
Previous behavior
If you accepted a type in your Hub method that was also in your Dependency Injection
container, the type would always be resolved from a message sent by the client.
C#
Services.AddScoped<SomeCustomType>();
class MyHub : Hub
{
// type always comes from the client, never comes from DI
public Task Method(string text, SomeCustomType type) =>
Task.CompletedTask;
}
New behavior
Types in DI are checked at app startup using IServiceProviderIsService to determine if an
argument in a Hub method comes from DI or from the client.
In the following example, which assumes you're using the default DI container,
SomeCustomType comes from the DI container instead of from the client. If the client tries
to send SomeCustomType , it gets an error:
C#
Services.AddScoped<SomeCustomType>();
class MyHub : Hub
{
// comes from DI by default
public Task Method(string text, SomeCustomType type) =>
Task.CompletedTask;
}
Type of breaking change
This change affects source compatibility.
Reason for change
This change improves the user experience when using different services in different Hub
methods. It also improves performance by not requiring the user to inject all
dependencies in the Hub's constructor.
The likelihood of breaking apps is low as it isn't common to have a type in DI and as an
argument in your Hub method at the same time.
Recommended action
If you're broken by this change, you can disable the feature by setting
DisableImplicitFromServicesParameters to true:
C#
Services.AddSignalR(options =>
{
options.DisableImplicitFromServicesParameters = true;
});
If you're broken by the change, but you want to use the feature without breaking clients,
you can disable the feature as shown above and use an attribute that implements
IFromServiceMetadata on new arguments/Hub methods:
C#
Services.AddScoped<SomeCustomType>();
Services.AddScoped<SomeCustomType2>();
class MyHub : Hub
{
// old method with new feature (non-breaking), only SomeCustomType2 is
resolved from DI
public Task MethodA(string arguments, SomeCustomType type,
[FromServices] SomeCustomType2 type2);
// new method
public Task MethodB(string arguments, [FromServices] SomeCustomType
type);
}
Affected APIs
Hub methods
API obsoletions with default diagnostic
ID (.NET 7)
Article • 11/08/2022
Several APIs have been marked as obsolete in .NET 7. Referencing these APIs in your
code will result in build warnings. In C#, the compiler diagnostic for these obsoletions is
CS0618.
Previous behavior
Previously, the affected APIs could be referenced without any build warnings.
New behavior
Starting in .NET 7, referencing the affected APIs will result in build warnings.
Version introduced
.NET 7 Preview 3
Type of breaking change
These obsoletions can affect source compatibility.
Reason for change
These APIs were previously marked obsolete in the implementation assemblies but not
in the reference assemblies. The reference assemblies have now been updated to match
the implementation assemblies.
Recommended action
Follow the recommended action that's emitted when you use the obsolete API.
Affected APIs
System.ComponentModel.IComNativeDescriptorHandler
System.ComponentModel.MemberDescriptor.GetInvokee(Type, Object)
System.ComponentModel.RecommendedAsConfigurableAttribute
System.Data.OleDb.OleDbParameterCollection.Add(String, Object)
System.Net.FileWebRequest.GetObjectData(SerializationInfo, StreamingContext)
System.Net.FileWebRequest.System.Runtime.Serialization.ISerializable.GetObjectDa
ta(SerializationInfo, StreamingContext)
System.Net.FileWebResponse.GetObjectData(SerializationInfo, StreamingContext)
System.Net.FileWebResponse.System.Runtime.Serialization.ISerializable.GetObjectD
ata(SerializationInfo, StreamingContext)
System.Net.Http.HttpRequestMessage.Properties
System.Net.WebRequest.GetObjectData(SerializationInfo, StreamingContext)
System.Net.WebRequest.System.Runtime.Serialization.ISerializable.GetObjectData(S
erializationInfo, StreamingContext)
WebResponse(SerializationInfo, StreamingContext) constructor
System.Net.WebResponse.GetObjectData(SerializationInfo, StreamingContext)
System.Net.WebResponse.System.Runtime.Serialization.ISerializable.GetObjectData
(SerializationInfo, StreamingContext)
System.Security.Cryptography.PasswordDeriveBytes.GetBytes(Int32)
System.Web.HttpUtility.UrlEncodeUnicode(String)
System.Web.HttpUtility.UrlEncodeUnicodeToBytes(String)
API obsoletions with non-default
diagnostic IDs (.NET 7)
Article • 11/08/2022
Some APIs have been marked as obsolete, starting in .NET 7. This breaking change is
specific to APIs that have been marked as obsolete with a custom diagnostic ID.
Suppressing the default obsoletion diagnostic ID, which is CS0618 for the C# compiler,
does not suppress the warnings that the compiler generates when these APIs are used.
Change description
In previous .NET versions, these APIs can be used without any build warning. In .NET 7
and later versions, use of these APIs produces a compile-time warning or error with a
custom diagnostic ID. The use of custom diagnostic IDs allows you to suppress the
obsoletion warnings individually instead of blanket-suppressing all obsoletion warnings.
The following table lists the custom diagnostic IDs and their corresponding warning
messages for obsoleted APIs.
Diagnostic
ID
Description
Severity
SYSLIB0036
Regex.CompileToAssembly is obsolete and not supported. Use
RegexGeneratorAttribute with the regular expression source generator
Warning
instead.
SYSLIB0037
AssemblyName members HashAlgorithm, ProcessorArchitecture, and
VersionCompatibility are obsolete and not supported.
Warning
SYSLIB0038
SerializationFormat.Binary is obsolete and should not be used.
Warning
SYSLIB0039
TLS versions 1.0 and 1.1 have known vulnerabilities and are not
Warning
recommended. Use a newer TLS version instead, or use
SslProtocols.None to defer to OS defaults.
SYSLIB0040
EncryptionPolicy.NoEncryption and EncryptionPolicy.AllowNoEncryption
significantly reduce security and should not be used in production code.
Warning
SYSLIB0041
The default hash algorithm and iteration counts in Rfc2898DeriveBytes
constructors are outdated and insecure. Use a constructor that accepts
the hash algorithm and the number of iterations.
Warning
Diagnostic
ID
Description
Severity
SYSLIB0042
ToXmlString and FromXmlString have no implementation for elliptic
Warning
curve cryptography (ECC) types, and are obsolete. Use a standard import
and export format such as ExportSubjectPublicKeyInfo or
ImportSubjectPublicKeyInfo for public keys, and ExportPkcs8PrivateKey
or ImportPkcs8PrivateKey for private keys.
SYSLIB0043
ECDiffieHellmanPublicKey.ToByteArray() and the associated constructor
do not have a consistent and interoperable implementation on all
Warning
platforms. Use ECDiffieHellmanPublicKey.ExportSubjectPublicKeyInfo()
instead.
SYSLIB0044
AssemblyName.CodeBase and AssemblyName.EscapedCodeBase are
obsolete.
Warning
SYSLIB0045
Cryptographic factory methods accepting an algorithm name are
Warning
obsolete. Use the parameterless Create factory method on the
algorithm type instead.
SYSLIB0047
XmlSecureResolver is obsolete. Use XmlResolver.ThrowingResolver
Warning
instead to forbid resolution of external XML resources.
Version introduced
.NET 7
Type of breaking change
These obsoletions can affect source compatibility.
Recommended action
Follow the specific guidance provided for the each diagnostic ID using the URL link
provided on the warning.
Warnings or errors for these obsoletions can't be suppressed using the standard
diagnostic ID for obsolete types or members; use the custom SYSLIBxxxx
diagnostic ID value instead.
Affected APIs
SYSLIB0036
Regex.CompileToAssembly
SYSLIB0037
AssemblyName.HashAlgorithm
AssemblyName.ProcessorArchitecture
AssemblyName.VersionCompatibility
SYSLIB0038
System.Data.SerializationFormat.Binary
SYSLIB0039
System.Security.Authentication.SslProtocols.Tls
System.Security.Authentication.SslProtocols.Tls11
SYSLIB0040
System.Net.Security.EncryptionPolicy.AllowNoEncryption
System.Net.Security.EncryptionPolicy.NoEncryption
SYSLIB0041
Rfc2898DeriveBytes(String, Byte[])
Rfc2898DeriveBytes(String, Int32)
Rfc2898DeriveBytes(Byte[], Byte[], Int32)
Rfc2898DeriveBytes(String, Byte[], Int32)
Rfc2898DeriveBytes(String, Int32, Int32)
SYSLIB0042
System.Security.Cryptography.ECDiffieHellmanCng.FromXmlString(String,
ECKeyXmlFormat)
System.Security.Cryptography.ECDiffieHellmanCng.ToXmlString(ECKeyXmlFormat)
System.Security.Cryptography.ECDiffieHellmanCngPublicKey.FromXmlString(String)
System.Security.Cryptography.ECDiffieHellmanCngPublicKey.ToXmlString()
System.Security.Cryptography.ECDiffieHellmanPublicKey.ToXmlString()
System.Security.Cryptography.ECDsaCng.FromXmlString(String, ECKeyXmlFormat)
System.Security.Cryptography.ECDsaCng.ToXmlString(ECKeyXmlFormat)
SYSLIB0043
System.Security.Cryptography.ECDiffieHellmanPublicKey.ToByteArray()
ECDiffieHellmanPublicKey(Byte[])
SYSLIB0045
System.Security.Cryptography.Aes.Create(String)
System.Security.Cryptography.AsymmetricAlgorithm.Create(String)
System.Security.Cryptography.DES.Create(String)
System.Security.Cryptography.ECDiffieHellman.Create(String)
System.Security.Cryptography.ECDsa.Create(String)
System.Security.Cryptography.HashAlgorithm.Create(String)
System.Security.Cryptography.KeyedHashAlgorithm.Create(String)
System.Security.Cryptography.RandomNumberGenerator.Create(String)
System.Security.Cryptography.RC2.Create(String)
System.Security.Cryptography.Rijndael.Create(String)
System.Security.Cryptography.RSA.Create(String)
System.Security.Cryptography.SHA1.Create(String)
System.Security.Cryptography.SHA256.Create(String)
System.Security.Cryptography.SHA384.Create(String)
System.Security.Cryptography.SHA512.Create(String)
System.Security.Cryptography.SymmetricAlgorithm.Create(String)
System.Security.Cryptography.TripleDES.Create(String)
SYSLIB0047
System.Xml.XmlSecureResolver
See also
API obsoletions with non-default diagnostic IDs (.NET 6)
API obsoletions with non-default diagnostic IDs (.NET 5)
Obsolete features in .NET 5+
BrotliStream no longer allows undefined
CompressionLevel values
Article • 11/30/2022
The BrotliStream constructors that take a CompressionLevel argument no longer allow
values that aren't defined in the CompressionLevel enumeration. If you pass an invalid
value, an ArgumentException is thrown.
Previous behavior
BrotliStream allowed you to pass an arbitrary compression level to the constructor by
casting the desired level directly to CompressionLevel. For example:
C#
BrotliStream brotli = new BrotliStream(baseStream,
(CompressionLevel)5); // Use level 5
However, if an arbitrary level was provided, that was passed through as-is to the
underlying library, resulting in inconsistent and potentially unexpected behavior.
New behavior
BrotliStream only allows the values defined in CompressionLevel. If you pass an
undefined value to the constructor, an ArgumentException is thrown.
Version introduced
.NET 7
Type of breaking change
This change can affect binary compatibility.
Reason for change
The purpose of the CompressionLevel enumeration is to let developers use compression
algorithms without needing to understand the meaning of their tuning parameters.
If an arbitrary level was provided, that was passed through as-is to the underlying
library, resulting in inconsistent and potentially unexpected behavior. With this change,
the behavior is aligned with other compression streams, for example, DeflateStream.
With the new tuning of the CompressionLevel values and the addition of
CompressionLevel.SmallestSize, it's now possible to have a variety of trade-offs in the
compression algorithms. Users can continue to rely on CompressionLevel values as
being abstractions of such trade-offs.
Recommended action
If you were relying on passing undefined values as the CompressionLevel, revisit your
use case and decide which documented value is the most optimal for it.
Affected APIs
BrotliStream(Stream, CompressionLevel, Boolean)
BrotliStream(Stream, CompressionLevel)
C++/CLI projects in Visual Studio
Article • 04/19/2023
.NET 7 includes generic math APIs that use static abstract interface members on
primitive types, such as Int32. Earlier versions of the C++/CLI compiler were
incompatible with such members. Because those members are used on many primitive
types, compilation errors will occur when targeting net7.0 , even if you don't use the
generic math features directly.
Beyond C++/CLI, it's also possible that the introduction of static abstract interface
members on primitive System types will surface issues in other tools that aren't forwardcompatible with the usage of this new language construct. If you're a tool author, you'll
need to update any tools impacted by this change to accommodate the usage of static
abstract interface members. If you need help, file an issue in the dotnet/runtime repo
to request guidance.
Previous behavior
Previously, compiling .NET projects using C++/CLI did not result in errors related to
members on primitive System types.
New behavior
Compiling a net7.0 project using C++/CLI in a release of Visual Studio prior to version
17.2 will result in many errors similar to this example:
txt
error C2253: 'System.Int32.Parse': pure specifier or abstract override
specifier only allowed on virtual function
Other than upgrading, there's no way to work around this compiler error. It's generated
because of static abstract interface members on primitive System types. When you
upgrade to Visual Studio 2022 version 17.2, the compilation errors will no longer occur.
Implicitly implemented static abstract interface members can be invoked, but even
with Visual Studio 2022 version 17.2, C++/CLI doesn't support invoking explicitly
implemented static abstract interface members.
Version introduced
.NET 7
Type of breaking change
This change can affect source compatibility.
Reason for change
.NET 7 includes the new API definitions for the generic math feature set. These APIs were
introduced in .NET 6 as a preview feature and required you to install the
System.Runtime.Experimental package to gain access. Starting in .NET 7, these generic
math APIs are included "in box".
Recommended action
To continue using C++/CLI with .NET 7, upgrade to Visual Studio 2022 version 17.2 or a
later version.
Affected APIs
N/A
See also
Visual Studio 2022 version 17.2 - release notes
Collectible Assembly in non-collectible
AssemblyLoadContext
Article • 11/08/2022
.NET incorrectly allowed garbage-collectible assemblies to resolve into a non-collectible
AssemblyLoadContext. In some cases, this lead to runtime crashes or unexpected
NullReferenceException exceptions. This change prevents the incorrect behavior by
throwing an exception when the AssemblyLoadContext.Load(AssemblyName) or
AssemblyLoadContext.Resolving event returns a collectible Assembly and the
AssemblyLoadContext is non-collectible.
Previous behavior
Returning a collectible Assembly in the AssemblyLoadContext.Load(AssemblyName)
override or the AssemblyLoadContext.Resolving event of a non-collectible
AssemblyLoadContext doesn't cause any exceptions to be thrown.
New behavior
Returning a collectible Assembly in the AssemblyLoadContext.Load(AssemblyName)
override or the AssemblyLoadContext.Resolving event of a non-collectible
AssemblyLoadContext throws a System.IO.FileLoadException with a
NotSupportedException as the inner exception.
Version introduced
.NET 7
Type of breaking change
This change can affect binary compatibility.
Reason for change
This change fixes a bug. The collectible Assembly would be garbage-collected while the
AssemblyLoadContext that has a reference to it is alive for the rest of the process
lifetime. If the code running in that context references anything from that Assembly after
it's collected, it would crash the runtime or result in a NullReferenceException,
AccessViolationException, or other kinds of bad behavior.
Recommended action
Don't return collectible assemblies in AssemblyLoadContext.Load(AssemblyName) or the
AssemblyLoadContext.Resolving event of a non-collectible AssemblyLoadContext. A
possible workaround is to change the AssemblyLoadContext to be collectible by passing
true for the isCollectible parameter in its constructor, and then keep a reference to
that AssemblyLoadContext forever to make sure it's never collected.
Affected APIs
System.Runtime.Loader.AssemblyLoadContext.Load(AssemblyName)
System.Runtime.Loader.AssemblyLoadContext.Resolving event
See also
Use collectible AssemblyLoadContext
DateTime addition methods precision
change
Article • 02/03/2023
In .NET 6 and earlier versions, the value parameter of the DateTime addition methods
was rounded to the nearest millisecond. In .NET 7 and later versions, the full Double
precision of the value parameter is used. However, due to the inherent imprecision of
floating-point math, the resulting precision will vary.
Previous behavior
Previously, the double value parameter of the DateTime Add* methods, for example,
DateTime.AddDays(Double), was rounded to the nearest millisecond.
New behavior
Starting in .NET 7, the full precision of the double value parameter is used, improving
the precision of the affected methods.
Version introduced
.NET 7
Type of breaking change
This change is a behavioral change.
Reason for change
This change was made in response to a community request to improve the precision in
DateTime.
Recommended action
No specific action unless you have code that depends on the precision of the Add*
methods. In that case, review your code and retest it to avoid any surprises with the
precision change.
Affected APIs
System.DateTime.AddDays(Double)
System.DateTime.AddHours(Double)
System.DateTime.AddMicroseconds(Double)
System.DateTime.AddMilliseconds(Double)
System.DateTime.AddMinutes(Double)
System.DateTime.AddSeconds(Double)
Equals method behavior change for
NaN
Article • 11/08/2022
The Equals(T other) instance method for the following types was updated to meet the
IEquatable<T> implementation requirements. As a result, the method now correctly
handles NaN. This change ensures the types can correctly be used alongside
GetHashCode , Dictionary<TKey,TValue>, and other hash sets.
System.Numerics.Matrix3x2
System.Numerics.Matrix4x4
System.Numerics.Plane
System.Numerics.Quaternion
System.Numerics.Vector2
System.Numerics.Vector3
System.Numerics.Vector4
System.Numerics.Vector<T>
System.Runtime.Intrinsics.Vector64<T>
System.Runtime.Intrinsics.Vector128<T>
System.Runtime.Intrinsics.Vector256<T>
Previous behavior
Previously, the Equals(T other) instance method followed the IEEE 754 requirements
and deferred to the == implementation. This meant that NaN != NaN , even when the two
NaN are bitwise identical.
For example:
C#
float f = float.NaN;
Console.WriteLine(f == f);
Console.WriteLine(f.Equals(f));
// False
// True
While for several of the listed types:
C#
Vector2 v = new Vector2(float.NaN);
Console.WriteLine(v == v);
// False
Console.WriteLine(v.Equals(v));
// False
This is problematic because using one of these types as a key in a dictionary meant that
the key could never be resolved:
C#
Vector2 v = new Vector2(float.NaN);
var s = new HashSet<Vector2>();
s.Add(v);
Console.WriteLine(s.Contains(v)); // False
New behavior
The behavior is now the same as for the primitive floating-point types, which is that the
== and != methods continue to follow the IEEE 754 requirements where NaN != NaN .
But the Equals(T other) instance methods follow the IEquatable<T> requirements so
that NaN.Equals(NaN) .
For example (no change):
C#
float f = float.NaN;
Console.WriteLine(f == f);
Console.WriteLine(f.Equals(f));
// False
// True
While for several of the listed types (the second line now prints True ):
C#
Vector2 v = new Vector2(float.NaN);
Console.WriteLine(v == v);
// False
Console.WriteLine(v.Equals(v));
// True
And when used in some hash set (the output now prints True ):
C#
Vector2 v = new Vector2(float.NaN);
var s = new HashSet<Vector2>();
s.Add(v);
Console.WriteLine(s.Contains(v)); // True
Version introduced
.NET 7
Type of breaking change
This change can affect binary compatibility.
Reason for change
The previous implementation did not meet the implementation requirements of
IEquatable<T> or object.Equals(object obj) . This resulted in the affected types not
being usable in hash sets or with GetHashCode .
Recommended action
If you prefer the previous behavior, switch to using == or != instead of Equals(T
other) .
Affected APIs
System.Numerics.Matrix3x2.Equals
System.Numerics.Matrix4x4.Equals
System.Numerics.Plane.Equals
System.Numerics.Quaternion.Equals
System.Numerics.Vector2.Equals
System.Numerics.Vector3.Equals
System.Numerics.Vector4.Equals
System.Numerics.Vector<T>.Equals
System.Runtime.Intrinsics.Vector64<T>.Equals
System.Runtime.Intrinsics.Vector128<T>.Equals
System.Runtime.Intrinsics.Vector256<T>.Equals
EventSource callback behavior
Article • 06/16/2023
For an EventCommand.Disable, the EventSource is now marked as disabled before the
callback is issued.
Previous behavior
Previously, the EventSource.OnEventCommand(EventCommandEventArgs) callback was
issued for an EventCommand.Disable prior to setting m_eventSourceEnabled=false .
This meant that EventSource.IsEnabled() returned true in the
OnEventCommand(EventCommandEventArgs) callback for a user EventSource, even if
the command led to the EventSource being disabled. The callback happened after the
ability to dispatch events was turned off though, so even if an EventSource tried to fire
an event, it wasn't written.
New behavior
Now, the EventSource is marked as disabled before the callback is issued for an
EventCommand.Disable.
Version introduced
.NET 6 servicing
.NET 7 servicing
Type of breaking change
This change is a behavioral change.
Reason for change
This change was necessary to support multiple EventCounter instances. The ability to
have multiple instances has been requested by multiple customers.
In addition, EventCommand.Enable has always issued a consistent view:
EventSource.IsEnabled() accurately reports the enabled status, and EventSource can
write events from the OnEventCommand callback. This change makes the
EventCommand.Disable behavior consistent with EventCommand.Enable .
Recommended action
It's unlikely that there's a scenario where the previous behavior is desired, and there's no
way to revert the behavior.
Affected APIs
System.Diagnostics.Tracing.EventCommand.Disable
Generic type constraint on
PatternContext<T>
Article • 11/08/2022
As part of annotating the .NET library for nullable reference types, a new generic
constraint was added to PatternContext<TFrame>. If you're consuming this class
directly, your code may break if the TFrame type is not a struct.
Previous behavior
Previously, PatternContext<TFrame> allowed any type to fill the TFrame type parameter.
New behavior
Starting in .NET 7, the generic type parameter on PatternContext<TFrame>, TFrame , is
constrained to be a struct.
Version introduced
.NET 7
Type of breaking change
This change can affect source compatibility and binary compatibility.
Reason for change
This change was necessary to annotate the type correctly for nullable contexts.
Recommended action
If you're currently using this type in your code, we recommend that you remove it. This
type supports infrastructure and is not intended to be used directly from your code.
Affected APIs
Microsoft.Extensions.FileSystemGlobbing.Internal.PatternContexts.PatternContext<
TFrame>
Legacy FileStream strategy removed
Article • 05/25/2023
The AppContext switch System.IO.UseNet5CompatFileStream and the ability to use the
legacy FileStream implementation were removed.
Previous behavior
The legacy FileStream implementation was available and you could opt into it by using
the UseNet5CompatFileStream switch or the DOTNET_SYSTEM_IO_USENET5COMPATFILESTREAM
environment variable.
New behavior
Starting in .NET 7, you can no longer opt in to use the legacy FileStream
implementation.
Version introduced
.NET 7 Preview 1
Type of breaking change
This change can affect binary compatibility.
Reason for change
The UseNet5CompatFileStream switch and DOTNET_SYSTEM_IO_USENET5COMPATFILESTREAM
environment variable were included in .NET 6 in case the new implementation caused
breaking changes. Any breaking changes have now been fixed. Since there are no more
bugs introduced by the FileStream changes, the compatibility mode was removed and
with it all the legacy code, which makes the codebase easier to maintain.
Recommended action
If you're currently using the switch (or the DOTNET_SYSTEM_IO_USENET5COMPATFILESTREAM
environment variable) to opt into legacy code and are upgrading to .NET 7, the switch
will no longer have any effect and you should remove it.
Affected APIs
System.IO.FileStream
System.IO.File.Create(String)
System.IO.File.Create(String, Int32)
System.IO.File.Create(String, Int32, FileOptions)
System.IO.File.Create(String, Int32, FileOptions, FileSecurity)
System.IO.File.Open(String, FileMode)
System.IO.File.Open(String, FileStreamOptions)
System.IO.File.Open(String, FileMode, FileAccess)
System.IO.File.Open(String, FileMode, FileAccess, FileShare)
System.IO.File.OpenRead(String)
System.IO.File.OpenWrite(String)
System.IO.FileSystemAclExtensions.Create(FileInfo, FileMode, FileSystemRights,
FileShare, Int32, FileOptions, FileSecurity)
System.IO.FileInfo.Create()
System.IO.FileInfo.Open
System.IO.FileInfo.OpenRead()
System.IO.FileInfo.OpenWrite()
See also
FileStream no longer synchronizes file offset with OS (.NET 6)
FileStream.Position updates after ReadAsync or WriteAsync completes (.NET 6)
Library support for older frameworks
Article • 11/08/2022
Installing the core libraries packages into projects whose target framework is older than
the following versions is no longer supported:
.NET Framework 4.6.2
.NET 6
.NET Standard 2.0
Previous behavior
The latest non-prerelease core libraries packages that were part of the ".NET 6" wave
were supported when used from projects targeting .NET Framework 4.6.1 or later, .NET
Core 3.1 and later, or .NET Standard 2.0 or later.
New behavior
.NET 7 core libraries packages are supported for use in projects targeting .NET
Framework 4.6.2 and later, .NET 6 and later, or .NET Standard 2.0 or later.
Version introduced
.NET 7
Type of breaking change
This change can affect source compatibility and binary compatibility.
Reason for change
Building packages for all existing frameworks increases the complexity and size of a
package. The following frameworks are now out-of-support and we no longer build
packages for these frameworks:
.NET Framework 4.6.1
.NET Core 3.1
.NET 5
Recommended action
If your project is no longer being evolved and only maintained, simply don't
upgrade the impacted packages.
If your project is actively developed, upgrade it to one of the following
frameworks:
.NET Framework 4.6.2
.NET Core 6
.NET Standard 2.0
Affected APIs
The following packages no longer ship old frameworks:
Microsoft.Bcl.AsyncInterfaces
Microsoft.Extensions.Caching.Abstractions
Microsoft.Extensions.Caching.Memory
Microsoft.Extensions.Configuration
Microsoft.Extensions.Configuration.Abstractions
Microsoft.Extensions.Configuration.Binder
Microsoft.Extensions.Configuration.CommandLine
Microsoft.Extensions.Configuration.EnvironmentVariables
Microsoft.Extensions.Configuration.FileExtensions
Microsoft.Extensions.Configuration.Ini
Microsoft.Extensions.Configuration.Json
Microsoft.Extensions.Configuration.UserSecrets
Microsoft.Extensions.Configuration.Xml
Microsoft.Extensions.DependencyInjection
Microsoft.Extensions.DependencyInjection.Abstractions
Microsoft.Extensions.DependencyInjection.Specification.Tests
Microsoft.Extensions.DependencyModel
Microsoft.Extensions.FileProviders.Abstractions
Microsoft.Extensions.FileProviders.Composite
Microsoft.Extensions.FileProviders.Physical
Microsoft.Extensions.FileSystemGlobbing
Microsoft.Extensions.Hosting
Microsoft.Extensions.Hosting.Abstractions
Microsoft.Extensions.Hosting.Systemd
Microsoft.Extensions.Hosting.WindowsServices
Microsoft.Extensions.Http
Microsoft.Extensions.Logging
Microsoft.Extensions.Logging.Abstractions
Microsoft.Extensions.Logging.Configuration
Microsoft.Extensions.Logging.Console
Microsoft.Extensions.Logging.Debug
Microsoft.Extensions.Logging.EventLog
Microsoft.Extensions.Logging.EventSource
Microsoft.Extensions.Logging.TraceSource
Microsoft.Extensions.Options
Microsoft.Extensions.Options.ConfigurationExtensions
Microsoft.Extensions.Options.DataAnnotations
Microsoft.Extensions.Primitives
Microsoft.NET.WebAssembly.Threading
Microsoft.NETCore.Platforms
Microsoft.Win32.Registry.AccessControl
Microsoft.Win32.SystemEvents
Microsoft.Windows.Compatibility
Microsoft.XmlSerializer.Generator
System.CodeDom
System.Collections.Immutable
System.ComponentModel.Composition
System.ComponentModel.Composition.Registration
System.Composition
System.Composition.AttributedModel
System.Composition.Convention
System.Composition.Hosting
System.Composition.Runtime
System.Composition.TypedParts
System.Configuration.ConfigurationManager
System.Data.Odbc
System.Data.OleDb
System.Diagnostics.DiagnosticSource
System.Diagnostics.EventLog
System.Diagnostics.PerformanceCounter
System.DirectoryServices
System.DirectoryServices.AccountManagement
System.DirectoryServices.Protocols
System.Drawing.Common
System.Formats.Asn1
System.Formats.Cbor
System.IO.Hashing
System.IO.Packaging
System.IO.Pipelines
System.IO.Ports
System.Management
System.Memory.Data
System.Net.Http.Json
System.Net.Http.WinHttpHandler
System.Numerics.Tensors
System.Reflection.Context
System.Reflection.Metadata
System.Reflection.MetadataLoadContext
System.Resources.Extensions
System.Runtime.Caching
System.Runtime.Serialization.Schema
System.Security.Cryptography.Cose
System.Security.Cryptography.Pkcs
System.Security.Cryptography.ProtectedData
System.Security.Cryptography.Xml
System.Security.Permissions
System.ServiceModel.Syndication
System.ServiceProcess.ServiceController
System.Speech
System.Text.Encoding.CodePages
System.Text.Encodings.Web
System.Text.Json
System.Threading.AccessControl
System.Threading.Channels
System.Threading.RateLimiting
System.Threading.Tasks.Dataflow
System.Windows.Extensions
Maximum precision for numeric format
strings
Article • 11/08/2022
The maximum precision when formatting numbers as strings using ToString and
TryFormat has been changed from Int32.MaxValue to 999,999,999. (The maximum
precision was previously changed to Int32.MaxValue in .NET 6.)
In addition, the maximum exponent allowed when parsing a BigInteger from a string has
been limited to 999,999,999.
Previous behavior
In .NET 6, the standard numeric format parsing logic was limited to a precision of
Int32.MaxValue or less. The intended behavior was to throw a FormatException for any
precision larger than Int32.MaxValue. However, due to a bug, .NET 6 didn't throw that
exception for some such inputs. The intended behavior was:
C#
double d = 123.0;
d.ToString("E" + int.MaxValue.ToString()); // Doesn't throw.
long intMaxPlus1 = (long)int.MaxValue + 1;
string intMaxPlus1String = intMaxPlus1.ToString();
Assert.Throws<FormatException>(() => d.ToString("E" + intMaxPlus1String));
// Throws.
In addition, there was no limit on the exponent size when parsing a BigInteger from a
string.
New behavior
Starting in .NET 7, .NET supports precision up to 999,999,999. A FormatException is
thrown if the precision is greater than 999,999,999. This change was implemented in the
parsing logic that affects all numeric types.
C#
double d = 123.0;
Assert.Throws<FormatException>(() => d.ToString("E" +
int.MaxValue.ToString())); // Throws.
long intMaxPlus1 = (long)int.MaxValue + 1;
string intMaxPlus1String = intMaxPlus1.ToString();
Assert.Throws<FormatException>(() => d.ToString("E" + intMaxPlus1String));
// Throws.
d.ToString("E999999999"); // Doesn't throw.
d.ToString("E00000999999999"); // Doesn't throw.
In addition, if you attempt to parse a BigInteger with an exponent greater than
999,999,999 from a string, a FormatException is thrown.
Version introduced
.NET 7
Type of breaking change
This change can affect binary compatibility.
Reason for change
The behavior that was introduced in .NET 6 was intended to throw a FormatException
for any precision larger than Int32.MaxValue. However, due to a bug, it did not throw
that exception for some inputs larger than the maximum. This change fixes the bug by
limiting the precision to 999,999,999.
Recommended action
In most cases, no action is necessary, because it's unlikely that you're already using a
precision higher than 999,999,999 in your format strings.
Affected APIs
This change was implemented in the parsing logic that affects all numeric types.
System.Numerics.BigInteger.ToString(String)
System.Numerics.BigInteger.ToString(String, IFormatProvider)
System.Numerics.BigInteger.TryFormat(Span<Char>, Int32, ReadOnlySpan<Char>,
IFormatProvider)
System.Int32.ToString(String)
System.Int32.ToString(String, IFormatProvider)
System.Int32.TryFormat(Span<Char>, Int32, ReadOnlySpan<Char>,
IFormatProvider)
System.UInt32.ToString(String)
System.UInt32.ToString(String, IFormatProvider)
System.UInt32.TryFormat(Span<Char>, Int32, ReadOnlySpan<Char>,
IFormatProvider)
System.Byte.ToString(String)
System.Byte.ToString(String, IFormatProvider)
System.Byte.TryFormat(Span<Char>, Int32, ReadOnlySpan<Char>,
IFormatProvider)
System.SByte.ToString(String)
System.SByte.ToString(String, IFormatProvider)
System.SByte.TryFormat(Span<Char>, Int32, ReadOnlySpan<Char>,
IFormatProvider)
System.Int16.ToString(String)
System.Int16.ToString(String, IFormatProvider)
System.Int16.TryFormat(Span<Char>, Int32, ReadOnlySpan<Char>,
IFormatProvider)
System.UInt16.ToString(String)
System.UInt16.ToString(String, IFormatProvider)
System.UInt16.TryFormat(Span<Char>, Int32, ReadOnlySpan<Char>,
IFormatProvider)
System.Numerics.BigInteger.Parse
System.Numerics.BigInteger.TryParse
System.Int64.ToString(String)
System.Int64.ToString(String, IFormatProvider)
System.Int64.TryFormat(Span<Char>, Int32, ReadOnlySpan<Char>,
IFormatProvider)
System.UInt64.ToString(String)
System.UInt64.ToString(String, IFormatProvider)
System.UInt64.TryFormat(Span<Char>, Int32, ReadOnlySpan<Char>,
IFormatProvider)
System.Half.ToString(String)
System.Half.ToString(String, IFormatProvider)
System.Half.TryFormat(Span<Char>, Int32, ReadOnlySpan<Char>, IFormatProvider)
System.Single.ToString(String)
System.Single.ToString(String, IFormatProvider)
System.Single.TryFormat(Span<Char>, Int32, ReadOnlySpan<Char>,
IFormatProvider)
System.Double.ToString(String)
System.Double.ToString(String, IFormatProvider)
System.Double.TryFormat(Span<Char>, Int32, ReadOnlySpan<Char>,
IFormatProvider)
System.Decimal.ToString(String)
System.Decimal.ToString(String, IFormatProvider)
System.Decimal.TryFormat(Span<Char>, Int32, ReadOnlySpan<Char>,
IFormatProvider)
See also
Standard numeric format parsing precision breaking change (.NET 6)
Standard numeric format strings
Character literals in custom format strings
Changes to reflection invoke API
exceptions
Article • 11/08/2022
The exceptions thrown when calling reflection invoke APIs have changed.
Previous behavior
Previously, when an invoked method that returns a value by reference returned
null , a NullReferenceException was thrown.
For constructors, the following exceptions were thrown:
Transient exceptions, including OutOfMemoryException.
An OverflowException when a negative value was passed for the length
parameter of an array.
When null was passed for a byref-like parameter without the ref modifier (that
is, passed by value), no exception was thrown, and the runtime substituted a
default value for the null value.
New behavior
Starting in .NET 7:
Instead of throwing the originating exception (including NullReferenceException
and OutOfMemoryException mentioned in Previous behavior),
TargetInvocationException is thrown in all cases after the initial Invoke()
parameters are validated. The inner exception contains the originating exception.
NotSupportedException is thrown when null is passed for a byref-like parameter
when the parameter is declared as being "by value" (that is, it has no ref
modifier). For the related case when the parameter is passed by reference (that is,
it has the ref modifier), the previous and new behavior are the same: a
NotSupportedException is thrown.
Version introduced
.NET 7
Type of breaking change
This change can affect binary compatibility.
Reason for change
Throwing TargetInvocationException instead of the originating exception makes the
experience more consistent. It properly layers exceptions caused by the validation of the
incoming parameters (which aren't wrapped with TargetInvocationException) versus
exceptions thrown due to the implementation of the target method (which are
wrapped). Consistent rules provide more consistent experiences across different
implementations of the CLR and of Invoke APIs.
The change to throw NotSupportedException when a byref-like type is passed to an
Invoke() API fixes an oversight of the original implementation, which did not throw. The
original implementation gave the appearance that ref struct types are supported by
the Invoke() APIs, when they aren't. Since the current Invoke() APIs use System.Object
for parameter types, and a ref struct type can't be boxed to System.Object, it's an
unsupported scenario.
Recommended action
If you don't use BindingFlags.DoNotWrapExceptions when calling Invoke() , and you
have catch statements around the Invoke() APIs for exceptions other than
TargetInvocationException, consider changing or removing those catch statements. The
other exceptions will no longer be thrown as a result of the invocation. If, however,
you're catching exceptions from argument validation that happens before attempting to
invoke the target method, you should keep those catch statements. Invalid arguments
that are validated before attempting to invoke are thrown without being wrapped with
TargetInvocationException and did not change semantics.
Consider using BindingFlags.DoNotWrapExceptions so that TargetInvocationException is
never thrown. In this case, the originating exception won't be wrapped by a
TargetInvocationException. In most cases, not wrapping the exception improves the
chances of the diagnosing the actual issue since not all exception reporting tools display
the inner exception. In addition, by using BindingFlags.DoNotWrapExceptions, the same
exceptions will be thrown as when calling the method directly (without reflection). This is
desirable in most cases, since the choice of whether reflection is used or not can be
arbitrary or an implementation detail that does not need to surface to the caller.
In the rare case that you need to pass a default value to a method through reflection
that contains a byref-like parameter that's passed "by value", you can add a wrapper
method that omits the parameter and calls the target method with a default value for
that parameter.
Affected APIs
System.Reflection.MethodBase.Invoke(Object, Object[])
System.Reflection.MethodBase.Invoke(Object, BindingFlags, Binder, Object[],
CultureInfo)
System.Reflection.ConstructorInfo.Invoke(Object[])
System.Reflection.ConstructorInfo.Invoke(BindingFlags, Binder, Object[],
CultureInfo)
System.Reflection.PropertyInfo.GetValue(Object)
System.Reflection.PropertyInfo.GetValue(Object, Object[])
System.Reflection.PropertyInfo.GetValue(Object, BindingFlags, Binder, Object[],
CultureInfo)
System.Reflection.PropertyInfo.SetValue(Object, Object)
System.Reflection.PropertyInfo.SetValue(Object, Object, Object[])
System.Reflection.PropertyInfo.SetValue(Object, Object, BindingFlags, Binder,
Object[], CultureInfo)
System.Reflection.Emit.DynamicMethod.Invoke(Object, BindingFlags, Binder,
Object[], CultureInfo)
System.Activator.CreateInstance(Type, Object[])
System.Activator.CreateInstance(Type, Object[], Object[])
System.Activator.CreateInstance(Type, BindingFlags, Binder, Object[], CultureInfo)
System.Activator.CreateInstance(Type, BindingFlags, Binder, Object[], CultureInfo,
Object[])
System.Activator.CreateInstance(String, String, Boolean, BindingFlags, Binder,
Object[], CultureInfo, Object[])
System.Drawing.Common config switch
removed
Article • 05/22/2023
The System.Drawing.Common
NuGet package has been attributed as a Windows-
specific library since .NET 6 and using it resulted in compile-time warnings and run-time
exceptions. The runtime configuration switch to re-enable usage of the package on nonWindows operating systems has been removed in .NET 7.
Old behavior
Prior to .NET 6, using the System.Drawing.Common package did not produce any
compile-time warnings, and no run-time exceptions were thrown. In .NET 6, you could
set the System.Drawing.EnableUnixSupport runtime configuration setting to re-enable
non-Windows support.
New behavior
Starting in .NET 7, the System.Drawing.EnableUnixSupport switch has been removed and
you can no longer use the System.Drawing.Common
package on non-Windows
operating systems.
Version introduced
.NET 7
Type of breaking change
This change is a behavioral change.
Reason for change
The switch to re-enable functionality on non-Windows operating systems was added in
.NET 6 to give customers time to migrate to an alternative, modern library. Now that
.NET 7 has been released, the switch has been removed. For more information, see
Reason for change (.NET 6 breaking change).
Recommended action
To use these APIs for cross-platform apps, migrate to an alternative library, such as one
of the following:
ImageSharp
SkiaSharp
Microsoft.Maui.Graphics
Affected APIs
See Affected APIs (.NET 6 breaking change).
See also
System.Drawing.Common only supported on Windows (.NET 6)
System.Drawing.Common only supported on Windows - dotnet/designs
System.Runtime.CompilerServices.Unsaf
e NuGet package
Article • 11/08/2022
New versions of the System.Runtime.CompilerServices.Unsafe NuGet package
will no
longer be produced.
Previous behavior
New versions of the System.Runtime.CompilerServices.Unsafe NuGet package
were
produced with each new .NET [Core] version.
New behavior
Starting in .NET 7, new functionality will ship as part of the shared framework for .NET.
Version introduced
.NET 7 Preview 3
Reason for change
There were previously two different implementations of the Unsafe type: one that was
referenced and used inside the core libraries, and one that shipped as a separate NuGet
package. This duplicated code and was also a maintenance burden for JIT and AOT, so
we unified the implementations. The NuGet package no longer needs to be produced,
as the unified implementation of Unsafe ships as part of the shared framework for .NET.
Recommended action
You can continue to use older versions of the packages if you're targeting .NET 6 or
earlier. But starting from .NET 7, you should remove the package dependency and
instead use the API that comes as part of the shared framework.
Affected APIs
All of the APIs under System.Runtime.CompilerServices.Unsafe.
Time fields on symbolic links
Article • 11/04/2022
When changes are made to the following time-related fields on a symbolic link
("symlink"), the updates now affect the symlink itself and not the target:
CreationTime
CreationTimeUtc
LastAccessTime
LastAccessTimeUtc
LastWriteTime
LastWriteTimeUtc
Previous behavior
Previously, updating any of the time-related fields on a symlink affected the fields of the
symlink's target.
Consider the following program that prints the various time field values on a file and its
symbolic link, updates the symlink's time field values to 1 day later, and then reprints
the time field values on both the file and symlink.
C#
string filename = "file";
string linkname = "link";
// Create a file and symlink.
File.Create(filename).Dispose();
File.CreateSymbolicLink(linkname, filename);
Console.WriteLine("Before update:");
PrintMetadata(filename);
PrintMetadata(linkname);
UpdateMetadata(linkname);
Console.WriteLine("\nAfter update:");
PrintMetadata(filename);
PrintMetadata(linkname);
static void UpdateMetadata(string filename)
{
DateTime tomorrow = DateTime.Now.AddDays(1);
File.SetCreationTime(filename, tomorrow);
File.SetLastAccessTime(filename, tomorrow);
File.SetLastWriteTime(filename, tomorrow);
File.SetAttributes(filename, File.GetAttributes(filename) |
FileAttributes.Offline);
}
static void PrintMetadata(string filename)
{
Console.WriteLine($"---{filename}---");
Console.WriteLine("Creation:\t" + File.GetCreationTime(filename));
Console.WriteLine("Last access:\t" + File.GetLastAccessTime(filename));
Console.WriteLine("Last write:\t" + File.GetLastWriteTime(filename));
Console.WriteLine("Attributes:\t" + File.GetAttributes(filename));
}
Previously, after updating the values on the symlink, only the target file's time fields
were updated. The output of the preceding program was as follows:
Output
Before update:
---file--Creation:
Last access:
Last write:
Attributes:
---link--Creation:
Last access:
Last write:
Attributes:
After update:
---file--Creation:
Last access:
Last write:
Attributes:
---link--Creation:
Last access:
Last write:
Attributes:
9/29/2022 10:35:40 AM
9/29/2022 10:35:40 AM
9/29/2022 10:35:40 AM
Archive
9/29/2022 10:35:40 AM
9/29/2022 10:35:40 AM
9/29/2022 10:35:40 AM
Archive, ReparsePoint
9/30/2022 10:35:40 AM
9/30/2022 10:35:40 AM
9/30/2022 10:35:40 AM
Archive
9/29/2022 10:35:40 AM
9/29/2022 10:35:40 AM
9/29/2022 10:35:40 AM
Archive, ReparsePoint, Offline
New behavior
Starting in .NET 7, updating any of the time-related fields on a symlink affects the fields
of the symlink itself and not the target file.
The output of the program shown in the Previous behavior section is as follows:
Output
Before update:
---file--Creation:
Last access:
Last write:
Attributes:
---link--Creation:
Last access:
Last write:
Attributes:
After update:
---file--Creation:
Last access:
Last write:
Attributes:
---link--Creation:
Last access:
Last write:
Attributes:
9/29/2022 10:33:39 AM
9/29/2022 10:33:39 AM
9/29/2022 10:33:39 AM
Archive
9/29/2022 10:33:39 AM
9/29/2022 10:33:39 AM
9/29/2022 10:33:39 AM
Archive, ReparsePoint
9/29/2022 10:33:39 AM
9/29/2022 10:33:39 AM
9/29/2022 10:33:39 AM
Archive
9/30/2022 10:33:39 AM
9/30/2022 10:33:39 AM
9/30/2022 10:33:39 AM
Archive, ReparsePoint, Offline
Version introduced
.NET 7 Preview 1
Type of breaking change
This change can affect binary compatibility.
Reason for change
The previous behavior was unexpected and undesirable in some cases:
It was inconsistent with the behavior of the properties and methods that get the
same fields.
It was also impossible to actually update the fields in the symlink itself using .NET
APIs.
Recommended action
If you were relying on this behavior to set values on the symlink's target, setting one of
the *Time fields in a symlink will no longer affect the target. You can use the new
symbolic link APIs to obtain the target of a symlink and then update that file system
object instead.
C#
FileSystemInfo? targetInfo = linkInfo.ResolveLinkTarget(returnFinalTarget:
true);
if (targetInfo != null)
{
// Update the properties accordingly.
targetInfo.LastWriteTime = DateTime.Now;
}
Affected APIs
System.IO.Directory.SetCreationTime(String, DateTime)
System.IO.Directory.SetCreationTimeUtc(String, DateTime)
System.IO.Directory.SetLastAccessTime(String, DateTime)
System.IO.Directory.SetLastAccessTimeUtc(String, DateTime)
System.IO.Directory.SetLastWriteTime(String, DateTime)
System.IO.Directory.SetLastWriteTimeUtc(String, DateTime)
System.IO.File.SetCreationTime(String, DateTime)
System.IO.File.SetCreationTimeUtc(String, DateTime)
System.IO.File.SetLastAccessTime(String, DateTime)
System.IO.File.SetLastAccessTimeUtc(String, DateTime)
System.IO.File.SetLastWriteTime(String, DateTime)
System.IO.File.SetLastWriteTimeUtc(String, DateTime)
System.IO.FileSystemInfo.CreationTime
System.IO.FileSystemInfo.CreationTimeUtc
System.IO.FileSystemInfo.LastAccessTime
System.IO.FileSystemInfo.LastAccessTimeUtc
System.IO.FileSystemInfo.LastWriteTime
System.IO.FileSystemInfo.LastWriteTimeUtc
Tracking linked cache entries
Article • 11/08/2022
MemoryCache tracks linked cache entries so that options are propagated. In the
following example, the expirationToken added for child is also applied to parent :
C#
using (var parent = cache.CreateEntry(key))
{
parent.SetValue(obj);
using (var child = cache.CreateEntry(key1))
{
child.SetValue(obj);
child.AddExpirationToken(expirationToken);
}
}
For performance reasons, .NET 7 no longer tracks linked cache entries by default.
However, you can enable tracking using a new option.
Version introduced
.NET 7
Previous behavior
Prior to .NET 7, MemoryCache tracked linked cache entries to allow for options to be
propagated. The tracking could not be disabled.
New behavior
Starting in .NET 7, MemoryCache does not track linked cache entries, by default. The
MemoryCacheOptions.TrackLinkedCacheEntries option was added so you can control
whether linked cache entries are tracked or not.
Type of breaking change
This change can affect binary compatibility.
Reason for change
This change was introduced to improve performance. Tracking internally uses
AsyncLocal<T>, which is expensive and adds non-trivial overhead.
Recommended action
If you want MemoryCache to continue tracking linked cache entries so options can be
propagated, set MemoryCacheOptions.TrackLinkedCacheEntries to true .
C#
var options = new MemoryCacheOptions
{
TrackLinkedCacheEntries = true
};
var cache = new MemoryCache(options);
Affected APIs
System.Runtime.Caching.MemoryCache
Microsoft.Extensions.Caching.Memory.MemoryCacheOptions
Validate CompressionLevel for
BrotliStream
Article • 11/08/2022
The CompressionLevel argument that's passed to BrotliStream constructors is now
validated to be one of the defined values of the enumeration.
Previous behavior
Passing any value between 0 and 11 for the CompressionLevel parameter was
considered valid. The value would either map to one of the enumeration's defined
values or be passed as-is to the underlying Brotli implementation.
New behavior
The only valid values for the CompressionLevel parameter of BrotliStream constructors
are:
CompressionLevel.Optimal
CompressionLevel.Fastest
CompressionLevel.NoCompression
CompressionLevel.SmallestSize
If you pass any other value, an ArgumentException is thrown at run time.
Version introduced
.NET 7
Type of breaking change
This change can affect binary compatibility.
Reason for change
Being able to pass arbitrary values that aren't defined by the CompressionLevel
enumeration is unexpected and undocumented, and is likely to lead to mistakes.
Recommended action
If necessary, change your code to pass in one of the valid CompressionLevel values.
Affected APIs
BrotliStream(Stream, CompressionLevel, Boolean)
BrotliStream(Stream, CompressionLevel)
System.diagnostics entry in app.config
Article • 11/08/2022
For applications that have an app.config file, the <configuration><configSections> entry
is no longer allowed to contain a <section name="system.diagnostics"> entry. If present,
you must remove the entry.
Having a <section name="system.diagnostics"> entry throws the following run-time
exception when the configuration system is first used:
ConfigurationErrorsException: Section or group name 'system.diagnostics' is
already defined. Updates to this may only occur at the configuration level where
it is defined.
For example, the following app.config file contains the unnecessary entry:
XML
<configuration>
<configSections>
<section name="system.diagnostics"
type="System.Diagnostics.SystemDiagnosticsSection,
System, Version=4.0.0.0, Culture=neutral,
PublicKeyToken=b77a5c561934e089"/>
</configSections>
This break will likely only occur for apps that:
Were migrated from .NET Framework to .NET.
Had explicitly added <section name="system.diagnostics"> to the app.config file to
support manual reading of the <system.diagnostics> section.
Were upgraded to .NET 7, which has an implicit <section
name="system.diagnostics"> entry.
Previous behavior
Specifying <section name="system.diagnostics"> was allowed and necessary if there was
a later <system.diagnostics> configuration section like the following:
XML
<configuration>
<system.diagnostics>
However, the section wasn't automatically read. That's because System.Diagnostics did
not yet support the feature to add listeners and configure other diagnostic features by
processing that section.
.NET Framework, however, does support processing of a <system.diagnostics> section
and has a <section name="system.diagnostics"> entry in the machine.config file.
New behavior
System.Diagnostics now supports reading the <system.diagnostics> section from the
config file and adds an implicit <section name="system.diagnostics"> entry. Having an
explicit <section name="system.diagnostics"> entry in the app.config file causes a
duplicate entry, which throws ConfigurationErrorsException.
Version introduced
.NET 7 RC 1
Type of breaking change
This change can affect binary compatibility.
Reason for change
To support a new feature where System.Diagnostics reads from the app.config file, we
had to add the implicit <section name="system.diagnostics"> element.
Recommended action
Remove the unnecessary <section name="system.diagnostics" ... > section.
Affected APIs
N/A
Dynamic X509ChainPolicy verification
time
Article • 11/08/2022
In previous versions of .NET, the X509ChainPolicy.VerificationTime value was assigned to
DateTime.Now when the X509ChainPolicy object was constructed. Using the same
X509ChainPolicy object for multiple calls to X509Chain.Build(X509Certificate2) resulted
in all chain builds using that same value as the verification time, no matter how much
time had passed since the object was created.
The new default behavior is to use the value of DateTime.Now when X509Chain.Build() is
invoked as the verification time. This change doesn't affect chain builds that explicitly
assign X509ChainPolicy.VerificationTime .
Previous behavior
The X509ChainPolicy.VerificationTime value was assigned to DateTime.Now when the
X509ChainPolicy object was constructed. This value was used in all subsequent
X509Chain.Build(X509Certificate2) calls (unless or until the value was reassigned at a
later time).
New behavior
The X509ChainPolicy.VerificationTime value is assigned to DateTime.Now when the
X509ChainPolicy object is constructed, but the new
X509ChainPolicy.VerificationTimeIgnored property defaults to true . When this
property has a value of true , the X509Chain.Build(X509Certificate2) method uses
DateTime.Now as the verification time instead of X509ChainPolicy.VerificationTime
when building the chain.
Assigning a value to the X509ChainPolicy.VerificationTime property automatically sets
VerificationTimeIgnored to false .
Version introduced
.NET 7
Type of breaking change
This change can affect binary compatibility.
Reason for change
Callers who cache configured X509ChainPolicy objects were often surprised that their
validation was slowly moving further back in time. This change makes long-lived
X509ChainPolicy objects easier to work with and doesn't significantly impact short-lived
objects.
Recommended action
The following callers aren't impacted by the change:
Callers that don't have long-lived X509ChainPolicy objects.
Callers that explicitly assign the X509ChainPolicy.VerificationTime property.
Callers that have a long-lived X509ChainPolicy object that wish to use the previous
behavior can either assign the new X509ChainPolicy.VerificationTimeIgnored property
to false or assign the X509ChainPolicy.VerificationTime property to DateTime.Now .
C#
var policy = new X509ChainPolicy
{
// ...
VerificationTime = DateTime.Now,
};
or
C#
var policy = new X509ChainPolicy
{
// ...
VerificationTimeIgnored = false,
};
Affected APIs
System.Security.Cryptography.X509Certificates.X509ChainPolicy
System.Security.Cryptography.X509Certificates.X509ChainPolicy.VerificationTime
System.Security.Cryptography.X509Certificates.X509ChainPolicy.VerificationTime
Ignored
Decrypting EnvelopedCms doesn't
double unwrap
Article • 11/08/2022
In .NET Core 2.0 on macOS and Linux, the EnvelopedCms implementation incorrectly
wrapped content in an extra ASN.1 OCTET STRING value. To maintain compatibility
when processing content created with this error, the EnvelopedCms class still looked at
the decrypted content and tried to remove the extra data. EnvelopedCms removed the
extra data on Windows when using an external private key, and on all other operating
systems for any decryption.
Unfortunately, this opportunistic compatibility code cannot distinguish between
documents that were created incorrectly and documents that were created correctly but
have the same data shape.
Previous behavior
Previously, if the decrypted content started with the byte value 0x04 and a legally
encoded ASN.1 BER length value that was less than or equal to the number of bytes
remaining in the content, the data provided in the envelopedCms.ContentInfo.Content
property only received the data associated with the content octets portion of the value
when treated as an ASN.1 OCTET STRING.
For example, if the initially decrypted content was the byte series { 0x04, 0x03, 0x01,
0x02, 0x03 } or { 0x04, 0x03, 0x01, 0x02, 0x03, [continued content] } , the value of
envelopedCms.ContentInfo.Content was the byte series { 0x01, 0x02, 0x03 } .
Values that did not start with 0x04 , or started with 0x04 but were not followed by an
acceptably encoded length value, were fully reported.
For some overloads of EnvelopedCms.Decrypt, this behavior only occurred on nonWindows operating systems. For more information, see Affected APIs.
New behavior
The EnvelopedCms class no longer attempts to work around the previous issue, and
always reports the decrypted content faithfully.
If you're processing documents that were created by the .NET Core 2.0 version of the
EnvelopedCms class on macOS or Linux, you'll see extra data at the beginning of the
content.
Version introduced
.NET 7
Type of breaking change
This change can affect binary compatibility.
Reason for change
The compatibility code could not differentiate between incorrectly created documents
and documents that were legitimately transporting data that looked like a BER-encoded
ASN.1 OCTET STRING.
Due to the nature of the BER encoding, callers that were negatively impacted by this
compatibility code could not easily recover their missing data.
Recommended action
Callers that read documents created with EnvelopedCms on .NET Core 2.0 for macOS or
Linux can add code to remove the extra data. To remove it, use the AsnDecoder class
from the System.Formats.Asn1 NuGet package , which is already a dependency of the
EnvelopedCms class.
C#
envelopedCms.Decrypt(...);
byte[] content = envelopedCms.ContentInfo.Content;
if (envelopedCms.ContentInfo.Oid.Value == "1.2.840.113549.1.7.1")
{
if (content?.Length > 0 && content[0] == 0x04)
{
try
{
content = AsnDecoder.ReadOctetString(content,
AsnEncodingRules.BER, out _);
}
catch (AsnContentException)
{
}
}
}
Affected APIs
System.Security.Cryptography.Pkcs.EnvelopedCms.Decrypt(RecipientInfo,
AsymmetricAlgorithm)
System.Security.Cryptography.Pkcs.EnvelopedCms.Decrypt(RecipientInfo,
X509Certificate2Collection) (non-Windows only)
System.Security.Cryptography.Pkcs.EnvelopedCms.Decrypt(RecipientInfo) (nonWindows only)
System.Security.Cryptography.Pkcs.EnvelopedCms.Decrypt(X509Certificate2Collecti
on) (non-Windows only)
X500DistinguishedName parsing of
friendly names
Article • 11/08/2022
On Linux and macOS, a distinguished name with a relative distinguished name
component that is prefixed with "OID." followed by a friendly name will no longer parse.
For example, OID.STREET=MainStreet no longer parses.
Previous behavior
On Linux and macOS only, a distinguished name would successfully parse even if the
object identifier (OID) was a friendly name.
New behavior
Attempting to parse a distinguished name with a component prefixed with "OID." but
not followed by a well-formed, dotted-decimal OID throws a CryptographicException.
Version introduced
.NET 7
Type of breaking change
This change can affect binary compatibility.
Reason for change
Windows does not permit distinguished names with friendly name OIDs, and that it
worked in Linux and macOS was coincidence and not intentional. To bring consistency
throughout platforms, the parsing logic was improved to not accept this form.
Recommended action
Change "OID."-prefixed relative distinguished name components to use an OID, such as
OID.1.2.3.4=MyValue .
Affected APIs
X500DistinguishedName(String)
X500DistinguishedName(String, X500DistinguishedNameFlags)
All assemblies trimmed by default
Article • 11/08/2022
Trimming now trims all assemblies in console apps, by default. This change only affects
apps that are published with PublishTrimmed=true , and it only breaks apps that had
existing trim warnings. It also only affects plain .NET apps that don't use the Windows
Desktop, Android, iOS, WASM, or ASP.NET SDK.
Previous behavior
Previously, only assemblies that were opted-in with <IsTrimmable>true</IsTrimmable> in
the library project file were trimmed.
New behavior
Starting in .NET 7, trimming trims all the assemblies in the app by default. Apps that may
have previously worked with PublishTrimmed may not work in .NET 7. However, only
apps with trim warnings will be affected. If your app has no trim warnings, the change in
behavior should not cause any adverse effects, and will likely decrease the app size.
If your app did have trim warnings, you may see changes in behavior or exceptions. For
example, an app that uses Newtonsoft.Json or System.Text.Json without source
generation to serialize and deserialize a type in the user project may have functioned
before the change, because types in the user project were fully preserved. However, one
or more trim warnings (warning codes ILxxxx ) would have been present. Now, types in
the user project are trimmed, and serialization may fail or produce unexpected results.
Version introduced
.NET 7
Type of breaking change
This change can affect source compatibility.
Reason for change
This change helps to decrease app size without users having to explicitly opt in and
aligns with user expectations that the entire app is trimmed unless noted otherwise.
Recommended action
The best resolution is to resolve all the trim warnings in your application. For
information about resolving the warnings in your own libraries, see Introduction to trim
warnings. For other libraries, contact the author to request that they resolve the
warnings, or choose a different library that already supports trimming. For example, you
can switch to System.Text.Json with source generation, which supports trimming, instead
of Newtonsoft.Json .
To revert to the previous behavior, set the TrimMode property to partial , which is the
pre-.NET 7 behavior.
XML
<TrimMode>partial</TrimMode>
The default .NET 7+ value is full :
XML
<TrimMode>full</TrimMode>
Affected APIs
None.
See also
Trimming options
Multi-level lookup is disabled
Article • 11/08/2022
On Windows, framework-dependent .NET applications no longer search for frameworks
in multiple install locations.
Previous behavior
In previous versions, a framework-dependent .NET application searched for frameworks
in multiple install locations on Windows. The locations were:
When running the application through dotnet, subdirectories relative to the
dotnet executable.
When running the application through its executable ( apphost ), the location
specified by the value of the DOTNET_ROOT environment variable (if set).
The globally registered install location in
HKLM\SOFTWARE\dotnet\Setup\InstalledVersions<arch>\InstallLocation (if set).
The default install location of %ProgramFiles%\dotnet (or
%ProgramFiles(x86)%\dotnet for 32-bit processes on 64-bit Windows).
This multi-level lookup behavior was enabled by default but could be disabled by
setting the environment variable DOTNET_MULTILEVEL_LOOKUP=0 .
New behavior
Applications that target .NET 7 or a later version only look for frameworks in one
location, which is the first location where a .NET installation is found. When running an
application through dotnet, frameworks are only searched for in subdirectories relative
to the dotnet executable. When running an application through its executable
( apphost ), frameworks are only searched for in the first of the following locations where
.NET is found:
The location specified by the value of the DOTNET_ROOT environment variable (if
set).
The globally registered install location in
HKLM\SOFTWARE\dotnet\Setup\InstalledVersions<arch>\InstallLocation (if set).
The default install location of %ProgramFiles%\dotnet (or
%ProgramFiles(x86)%\dotnet for 32-bit processes on 64-bit Windows).
Version introduced
.NET 7
Type of breaking change
This change can affect binary compatibility.
Reason for change
There've been numerous issues caused by multi-level lookup:
Confusion for users: application can pick a global or default install location despite
running .NET from a private install.
Inconsistency between platforms (Windows versus non-Windows).
Behavior breaks, often in automated systems: a new global .NET install can affect
otherwise isolated builds and tests.
Performance issues.
Recommended action
Make sure the required version of .NET is installed at the single .NET install location. The
error messages that are emitted on failure to launch include the expected location.
Affected APIs
None.
x86 host path on 64-bit Windows
Article • 06/13/2023
The x86 versions of .NET installers for Windows have been modified to no longer add
the x86 host location (Program Files (x86)\dotnet) to the PATH environment variable on
64-bit Windows systems.
With this change, if the x86 host location was added to PATH by a prior version of .NET,
the x86 versions of .NET installers and .NET updates will remove it on upgrade.
This change affects .NET Core 3.1, .NET 6, .NET 7, and future versions.
This change only affects the dotnet host. It doesn't affect 32-bit/x86 application hosts,
like myapp.exe. Those hosts will continue to find the x86 runtime correctly (assuming it's
installed).
Previous behavior
The x86 host location was added to PATH , even on x64/Arm64 systems. Depending on
which .NET architecture installer was run first, a user's machine could have either the
native (x64/Arm64) or x86 host listed first in PATH .
New behavior
Going forward, the x86 host location is only added to the PATH environment variable on
x86 systems and will be removed on upgrade of .NET or Visual Studio on any x64 and
arm64 systems.
Version introduced
.NET 7
Reason for change
Currently, the x86 host location is added to PATH , even on x64/Arm64 systems.
Depending on which .NET architecture installer is run first, a user's machine could have
either the native (x64/Arm64) or the x86 host as the first location in the PATH list. This
ambiguity causes problems with the initial .NET installation and during .NET servicing
events. Any of these installation scenarios can modify the order of .NET hosts in PATH ,
making it non-deterministic. There's a high chance of behavior regression of the .NET
runtime.
This change streamlines the dotnet host experience on Windows 64-bit systems. Only
64-bit hosts will be available in the system's PATH environment variable: the x64 host on
x64 systems and the Arm64 host on Arm64 systems. We removed the ambiguity in the
order of dotnet hosts in PATH , and only one host will be present.
Recommended action
If you need the x86 host in the PATH environment variable on x64/Arm64 systems, add
the host location to PATH manually.
Affected APIs
None.
MSBuild property TrimmerDefaultAction
is deprecated
Article • 01/20/2023
The value of the TrimmerDefaultAction property is now ignored by the publish process.
Previous behavior
Previously, only assemblies that were opted-in with <IsTrimmable>true</IsTrimmable> in
the library project file were trimmed with the action specified by the
TrimmerDefaultAction . In .NET 6, the default value for that property was copy . While
apps with trim warnings were more likely to work with this default, run-time behavior
could still be affected. In addition, the copy action caused apps to be larger than if the
entire app was trimmed.
New behavior
Starting in .NET 7, the property TrimmerDefaultAction is ignored and publishing behaves
as if it was set to link all the time. This means all assemblies are fully trimmed, whether
they opt in or not. As a result, applications with trim warnings may see changes in
behavior or run-time exceptions. For more information and instructions for restoring the
previous behavior, see All assemblies trimmed by default.
Version introduced
.NET 7
Type of breaking change
This change can affect source compatibility.
Reason for change
This change streamlines trimming options.
Recommended action
The best resolution is to resolve all the trim warnings in your application. For
information about resolving the warnings in your own libraries, see Introduction to trim
warnings. For other libraries, contact the author to request that they resolve the
warnings, or choose a different library that already supports trimming. For example, you
can switch to System.Text.Json with source generation, which supports trimming, instead
of Newtonsoft.Json . With that library, you should no longer need to use
TrimmerDefaultAction .
To revert to the previous behavior, use global.json to pin your project to .NET 6 SDK.
See also
Trimming options
All assemblies trimmed by default
Breaking changes in EF Core 7.0 (EF7)
Article • 03/27/2023
The following API and behavior changes have the potential to break existing
applications updating to EF Core 7.0.
Target Framework
EF Core 7.0 targets .NET 6. This means that existing applications that target .NET 6 can
continue to do so. Applications targeting older .NET, .NET Core, and .NET Framework
versions will need to target .NET 6 or .NET 7 to use EF Core 7.0.
Summary
Breaking change
Impact
Encrypt defaults to true for SQL Server connections
High
Some warnings will again throw exceptions by default
High
SQL Server tables with triggers or certain computed columns now require special EF
Core configuration
High
SQLite tables with AFTER triggers and virtual tables now require special EF Core
configuration
High
Orphaned dependents of optional relationships are not automatically deleted
Medium
Cascade delete is configured between tables when using TPT mapping with SQL Server
Medium
Key properties may need to be configured with a provider value comparer
Low
Check constraints and other table facets are now configured on the table
Low
Navigations from new entities to deleted entities are not fixed up
Low
Using FromSqlRaw and related methods from the wrong provider throws
Low
Scaffolded OnConfiguring no longer calls IsConfigured
Low
High-impact changes
Encrypt defaults to true for SQL Server connections
Tracking Issue: SqlClient #1210
) Important
This is a severe breaking change in the Microsoft.Data.SqlClient
package. There
is nothing that can be done in EF Core to revert or mitigate this change. Please
direct feedback to the Microsoft.Data.SqlClient GitHub Repo
Microsoft Support Professional
or contact a
for additional questions or help.
Old behavior
SqlClient connection strings use Encrypt=False by default. This allows connections on
development machines where the local server does not have a valid certificate.
New behavior
SqlClient connection strings use Encrypt=True by default. This means that:
The server must be configured with a valid certificate
The client must trust this certificate
If these conditions are not met, then a SqlException will be thrown. For example:
A connection was successfully established with the server, but then an error
occurred during the login process. (provider: SSL Provider, error: 0 - The certificate
chain was issued by an authority that is not trusted.)
Why
This change was made to ensure that, by default, either the connection is secure or the
application will fail to connect.
Mitigations
There are three ways to proceed:
1. Install a valid certificate on the server. Note that this is an involved process and
requires obtaining a certificate and ensuring it is signed by an authority trusted by
the client.
2. If the server has a certificate, but it is not trusted by the client, then
TrustServerCertificate=True to allow bypassing the normal trust mechanims.
3. Explicitly add Encrypt=False to the connection string.
2 Warning
Options 2 and 3 both leave the server in a potentially insecure state.
Some warnings throw exceptions by default again
Tracking Issue #29069
Old behavior
In EF Core 6.0, a bug in the SQL Server provider meant that some warnings that are
configured to throw exceptions by default were instead being logged but not throwing
exceptions. These warnings are:
EventId
Description
RelationalEventId.AmbientTransactionWarning
An application may have
expected an ambient
transaction to be used
when it was actually
ignored.
RelationalEventId.IndexPropertiesBothMappedAndNotMappedToTable
An index specifies
properties some of which
are mapped and some of
which are not mapped to
a column in a table.
RelationalEventId.IndexPropertiesMappedToNonOverlappingTables
An index specifies
properties which map to
columns on nonoverlapping tables.
RelationalEventId.ForeignKeyPropertiesMappedToUnrelatedTables
A foreign key specifies
properties which don't
map to the related
tables.
New behavior
Starting with EF Core 7.0, these warnings again, by default, result in an exception being
thrown.
Why
These are issues that very likely indicate an error in the application code that should be
fixed.
Mitigations
Fix the underlying issue that is the reason for the warning.
Alternately, the warning level can be changed so that it is logged only or suppressed
entirely. For example:
C#
protected override void OnConfiguring(DbContextOptionsBuilder
optionsBuilder)
=> optionsBuilder
.ConfigureWarnings(b =>
b.Ignore(RelationalEventId.AmbientTransactionWarning));
SQL Server tables with triggers or certain computed
columns now require special EF Core configuration
Tracking Issue #27372
Old behavior
Previous versions of the SQL Server provider saved changes via a less efficient technique
which always worked.
New behavior
By default, EF Core now saves changes via a significantly more efficient technique;
unfortunately, this technique is not supported on SQL Server if the target table has
database triggers, or certain types of computed columns. See the SQL Server
documentation for more details.
Why
The performance improvements linked to the new method are significant enough that
it's important to bring them to users by default. At the same time, we estimate usage of
database triggers or the affected computed columns in EF Core applications to be low
enough that the negative breaking change consequences are outweighed by the
performance gain.
Mitigations
You can let EF Core know that the target table has a trigger; doing so will revert to the
previous, less efficient technique. This can be done by configuring the corresponding
entity type as follows:
C#
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Blog>()
.ToTable(tb => tb.HasTrigger("SomeTrigger"));
}
Note that doing this doesn't actually make EF Core create or manage the trigger in any
way - it currently only informs EF Core that triggers are present on the table. As a result,
any trigger name can be used, and this can also be used if an unsupported computed
column is in use (regardless of triggers).
If most or all of your tables have triggers, you can opt out of using the newer, efficient
technique for all your model's tables by using the following model building convention:
C#
public class BlankTriggerAddingConvention : IModelFinalizingConvention
{
public virtual void ProcessModelFinalizing(
IConventionModelBuilder modelBuilder,
IConventionContext<IConventionModelBuilder> context)
{
foreach (var entityType in modelBuilder.Metadata.GetEntityTypes())
{
var table = StoreObjectIdentifier.Create(entityType,
StoreObjectType.Table);
if (table != null
&& entityType.GetDeclaredTriggers().All(t =>
t.GetDatabaseName(table.Value) == null))
{
entityType.Builder.HasTrigger(table.Value.Name +
"_Trigger");
}
foreach (var fragment in
entityType.GetMappingFragments(StoreObjectType.Table))
{
if (entityType.GetDeclaredTriggers().All(t =>
t.GetDatabaseName(fragment.StoreObject) == null))
{
entityType.Builder.HasTrigger(fragment.StoreObject.Name
+ "_Trigger");
}
}
}
}
}
Use the convention on your DbContext by overriding ConfigureConventions :
C#
protected override void ConfigureConventions(ModelConfigurationBuilder
configurationBuilder)
{
configurationBuilder.Conventions.Add(_ => new
BlankTriggerAddingConvention());
}
This effectively calls HasTrigger on all your model's tables, instead of you having to do it
manually for each and every table.
SQLite tables with AFTER triggers and virtual tables now
require special EF Core configuration
Tracking Issue #29916
Old behavior
Previous versions of the SQLite provider saved changes via a less efficient technique
which always worked.
New behavior
By default, EF Core now saves changes via a more efficient technique, using the
RETURNING clause. Unfortunately, this technique is not supported on SQLite if target
table is has database AFTER triggers, is virtual, or if older versions of SQLite are being
used. See the SQLite documentation
Why
for more details.
The simplifications and performance improvements linked to the new method are
significant enough that it's important to bring them to users by default. At the same
time, we estimate usage of database triggers and virtual tables in EF Core applications
to be low enough that the negative breaking change consequences are outweighed by
the performance gain.
Mitigations
In EF Core 8.0, a mechanism will be introduced that will allow specifying whether to use
the new mechanism on a table-by-table basis. With EF Core 7.0, it's possible to revert to
the old mechanism for the entire application by inserting the following code in your
context configuration:
c#
protected override void OnConfiguring(DbContextOptionsBuilder
optionsBuilder)
=> optionsBuilder
.UseSqlite(...)
.ReplaceService<IUpdateSqlGenerator, SqliteLegacyUpdateSqlGenerator>
();
Medium-impact changes
Orphaned dependents of optional relationships are not
automatically deleted
Tracking Issue #27217
Old behavior
A relationship is optional if its foreign key is nullable. Setting the foreign key to null
allows the dependent entity exist without any related principal entity. Optional
relationships can be configured to use cascade deletes, although this is not the default.
An optional dependent can be severed from its principal by either setting its foreign key
to null, or clearing the navigation to or from it. In EF Core 6.0, this would cause the
dependent to be deleted when the relationship was configured for cascade delete.
New behavior
Starting with EF Core 7.0, the dependent is no longer deleted. Note that if the principal
is deleted, then the dependent will still be deleted since cascade deletes are configured
for the relationship.
Why
The dependent can exist without any relationship to a principal, so severing the
relationship should not cause the entity to be deleted.
Mitigations
The dependent can be explicitly deleted:
C#
context.Remove(blog);
Or SaveChanges can be overridden or intercepted to delete dependents with no principal
reference. For example:
C#
context.SavingChanges += (c, _) =>
{
foreach (var entry in ((DbContext)c!).ChangeTracker
.Entries<Blog>()
.Where(e => e.State == EntityState.Modified))
{
if (entry.Reference(e => e.Author).CurrentValue == null)
{
entry.State = EntityState.Deleted;
}
}
};
Cascade delete is configured between tables when using
TPT mapping with SQL Server
Tracking Issue #28532
Old behavior
When mapping an inheritance hierarchy using the TPT strategy, the base table must
contain a row for every entity saved, regardless of the actual type of that entity. Deleting
the row in the base table should delete rows in all the other tables. EF Core configures a
cascade deletes for this.
In EF Core 6.0, a bug in the SQL Server database provider meant that these cascade
deletes were not being created.
New behavior
Starting with EF Core 7.0, the cascade deletes are now being created for SQL Server just
as they always were for other databases.
Why
Cascade deletes from the base table to the sub-tables in TPT allow an entity to be
deleted by deleting its row in the base table.
Mitigations
In most cases, this change should not cause any issues. However, SQL Server is very
restrictive when there are multiple cascade behaviors configured between tables. This
means that if there is an existing cascading relationship between tables in the TPT
mapping, then SQL Server may generate the following error:
Microsoft.Data.SqlClient.SqlException: The DELETE statement conflicted with the
REFERENCE constraint "FK_Blogs_People_OwnerId". The conflict occurred in
database "Scratch", table "dbo.Blogs", column 'OwnerId'. The statement has been
terminated.
For example, this model creates a cycle of cascading relationships:
C#
[Table("FeaturedPosts")]
public class FeaturedPost : Post
{
public int ReferencePostId { get; set; }
public Post ReferencePost { get; set; } = null!;
}
[Table("Posts")]
public class Post
{
public int Id { get; set; }
public string? Title { get; set; }
public string? Content { get; set; }
}
One of these will need to be configured to not use cascade deletes on the server. For
example, to change the explicit relationship:
C#
modelBuilder
.Entity<FeaturedPost>()
.HasOne(e => e.ReferencePost)
.WithMany()
.OnDelete(DeleteBehavior.ClientCascade);
Or to change the implicit relationship created for the TPT mapping:
C#
modelBuilder
.Entity<FeaturedPost>()
.HasOne<Post>()
.WithOne()
.HasForeignKey<FeaturedPost>(e => e.Id)
.OnDelete(DeleteBehavior.ClientCascade);
Low-impact changes
Key properties may need to be configured with a
provider value comparer
Tracking Issue #27738
Old behavior
In EF Core 6.0, key values taken directly from the properties of entity types were used for
comparison of key values when saving changes. This would make use of any custom
value comparer configured on these properties.
New behavior
Starting with EF Core 7.0, database values are used for these comparisons. This "just
works" for the vast majority of cases. However, if the properties were using a custom
comparer, and that comparer cannot be applied to the database values, then a "provider
value comparer" may be needed, as shown below.
Why
Various entity-splitting and table-splitting can result in multiple properties mapped to
the same database column, and vice-versa. This requires values to be compared after
conversion to value that will be used in the database.
Mitigations
Configure a provider value comparer. For example, consider the case where a value
object is being used as a key, and the comparer for that key uses case-insensitive string
comparisons:
C#
var blogKeyComparer = new ValueComparer<BlogKey>(
(l, r) => string.Equals(l.Id, r.Id, StringComparison.OrdinalIgnoreCase),
v => v.Id.ToUpper().GetHashCode(),
v => v);
var blogKeyConverter = new ValueConverter<BlogKey, string>(
v => v.Id,
v => new BlogKey(v));
modelBuilder.Entity<Blog>()
.Property(e => e.Id).HasConversion(
blogKeyConverter, blogKeyComparer);
The database values (strings) cannot directly use the comparer defined for BlogKey
types. Therefore, a provider comparer for case-insensitive string comparisons must be
configured:
C#
var caseInsensitiveComparer = new ValueComparer<string>(
(l, r) => string.Equals(l, r, StringComparison.OrdinalIgnoreCase),
v => v.ToUpper().GetHashCode(),
v => v);
var blogKeyComparer = new ValueComparer<BlogKey>(
(l, r) => string.Equals(l.Id, r.Id, StringComparison.OrdinalIgnoreCase),
v => v.Id.ToUpper().GetHashCode(),
v => v);
var blogKeyConverter = new ValueConverter<BlogKey, string>(
v => v.Id,
v => new BlogKey(v));
modelBuilder.Entity<Blog>()
.Property(e => e.Id).HasConversion(
blogKeyConverter, blogKeyComparer, caseInsensitiveComparer);
Check constraints and other table facets are now
configured on the table
Tracking Issue #28205
Old behavior
In EF Core 6.0, HasCheckConstraint , HasComment , and IsMemoryOptimized were called
directly on the entity type builder. For example:
C#
modelBuilder
.Entity<Blog>()
.HasCheckConstraint("CK_Blog_TooFewBits", "Id > 1023");
modelBuilder
.Entity<Blog>()
.HasComment("It's my table, and I'll delete it if I want to.");
modelBuilder
.Entity<Blog>()
.IsMemoryOptimized();
New behavior
Starting with EF Core 7.0, these methods are instead called on the table builder:
C#
modelBuilder
.Entity<Blog>()
.ToTable(b => b.HasCheckConstraint("CK_Blog_TooFewBits", "Id > 1023"));
modelBuilder
.Entity<Blog>()
.ToTable(b => b.HasComment("It's my table, and I'll delete it if I want
to."));
modelBuilder
.Entity<Blog>()
.ToTable(b => b.IsMemoryOptimized());
The existing methods have been marked as Obsolete . They currently have the same
behavior as the new methods, but will be removed in a future release.
Why
These facets apply to tables only. They will not be applied to any mapped views,
functions, or stored procedures.
Mitigations
Use the table builder methods, as shown above.
Navigations from new entities to deleted entities are not
fixed up
Tracking Issue #28249
Old behavior
In EF Core 6.0, when a new entity is tracked either from a tracking query or by attaching
it to the DbContext , then navigations to and from related entities in the Deleted state
are fixed up.
New behavior
Starting with EF Core 7.0, navigations to and from Deleted entities are not fixed up.
Why
Once an entity is marked as Deleted it rarely makes sense to associate it with nondeleted entities.
Mitigations
Query or attach entities before marking entities as Deleted , or manually set navigation
properties to and from the deleted entity.
Using FromSqlRaw and related methods from the wrong
provider throws use-the-correct-method
Tracking Issue #26502
Old behavior
In EF Core 6.0, using the Azure Cosmos DB FromSqlRaw extension method when using a
relational provider, or the relational FromSqlRaw extension method when using the
Azure Cosmos DB provider could silently fail.
New behavior
Starting with EF Core 7.0, using the wrong extension method will throw an exception.
Why
The correct extension method must be used for it to function correctly in all situations.
Mitigations
Use the correct extension method for the provider being used. If multiple providers are
referenced, then call the extension method as a static method. For example:
C#
var result = CosmosQueryableExtensions.FromSqlRaw(context.Blogs, "SELECT
...").ToList();
Or:
C#
var result = RelationalQueryableExtensions.FromSqlRaw(context.Blogs, "SELECT
...").ToList();
Scaffolded OnConfiguring no longer calls IsConfigured
Tracking Issue #4274
Old behavior
In EF Core 6.0, the DbContext type scaffolded from an existing database contained a call
to IsConfigured . For example:
C#
protected override void OnConfiguring(DbContextOptionsBuilder
optionsBuilder)
{
if (!optionsBuilder.IsConfigured)
{
#warning To protect potentially sensitive information in your connection
string, you should move it out of source code. You can avoid scaffolding the
connection string by using the Name= syntax to read it from configuration see https://go.microsoft.com/fwlink/?linkid=2131148. For more guidance on
storing connection strings, see http://go.microsoft.com/fwlink/?
LinkId=723263.
optionsBuilder.UseNpgsql("MySecretConnectionString");
}
}
New behavior
Starting with EF Core 7.0, the call to IsConfigured is no longer included.
Why
There are very limited scenarios where the database provider is configured inside your
DbContext in some cases, but only if the context is not configured already. Instead,
leaving OnConfiguring here makes it more likely that a connection string containing
sensitive information is left in the code, despite the compile-time warning. Thus the
extra safely and cleaner code from removing this was deemed worthwhile, especially
given that the --no-onconfiguring (.NET CLI) or -NoOnConfiguring (Visual Studio
Package Manager Console) flag can be used to prevent scaffolding of the OnConfiguring
method, and that customizable templates exist to add back IsConfigured if it is really
needed.
Mitigations
Either:
Use the --no-onconfiguring (.NET CLI) or -NoOnConfiguring (Visual Studio Package
Manager Console) argument when scaffolding from an existing database.
Customize the T4 templates to add back the call to IsConfigured .
Globalization APIs use ICU libraries on
Windows Server 2019
Article • 08/29/2023
.NET 7 and later versions use International Components for Unicode (ICU)
libraries for
globalization functionality when running on Windows Server 2019. Non-server editions
of Windows have been using ICU since .NET 5. However, .NET 7 introduced support for
loading ICU in earlier Windows client versions, specifically Windows 10 versions 1703,
1709, 1803, and 1809.
Previous behavior
In .NET 5 and .NET 6, the .NET libraries used National Language Support (NLS) APIs for
globalization functionality on Windows Server 2019. For example, NLS functions were
used to compare strings, get culture information, and perform string casing in the
appropriate culture. This behavior also applied to Windows 10 client versions, such as
1703, 1709, 1803, and 1809.
New behavior
Starting in .NET 7, if an app is running on Windows Server 2019 or Windows 10 client
versions 1703, 1709, 1803, and 1809, .NET libraries use ICU
globalization APIs, by
default. (Non-server Windows versions have already been using ICU since .NET 5, so
there is no change for these versions.)
Behavioral differences
You might see changes in your app even if you don't realize you're using globalization
facilities. The following example shows one of the behavioral changes you might see,
but there are others too.
Currency symbol
Consider the following code that formats a string using the currency format specifier C .
The current thread's culture is set to a culture that includes only the language and not
the country or region.
C#
System.Threading.Thread.CurrentThread.CurrentCulture = new
System.Globalization.CultureInfo("de");
string text = string.Format("{0:C}", 100);
In .NET 5 and .NET 6 on Windows Server 2019 or Windows 10 client versions 1703,
1709, 1803, and 1809, the value of text is "100,00 €" .
In .NET 7 on Windows Server 2019 or Windows 10 client versions 1703, 1709, 1803,
and 1809, the value of text is "100,00 ¤" , which uses the international currency
symbol instead of the euro. In ICU, the design is that a currency is a property of a
country or region, not a language.
Reason for change
.NET introduced some APIs that depend on ICU libraries, for example,
TimeZoneInfo.TryConvertIanaIdToWindowsId(String, String). Users who wanted to
use such APIs on Windows Server 2019 were required to manually deploy ICU
libraries with their binaries, using the ICU App Local feature. This wasn't a great
solution, because the code can be in a library that can't control forcing ICU
libraries to be installed with whatever app or service is using the library.
If Windows Server 2019 is automatically provided by a cloud platform (like Azure),
the deployed service doesn't necessarily know it's going to run on such a server.
Also, the service owner has to manage if/when to deploy ICU binaries. In addition,
every service deployed to the cloud using Windows Server 2019 that wants to use
the new .NET ICU-dependent APIs needs to deploy the ICU binaries with the
service. This can bloat the size on the disk on the server.
Some users prefer using ICU by default because it conforms more to the Unicode
Standard.
Version introduced
.NET 7
Recommended action
If you're using .NET 7 on Windows Server 2019 or Windows 10 client versions 1703,
1709, 1803, or 1809, we recommend testing your app or service before shipping it to
ensure the behavior is as expected and doesn't break any users.
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.
Affected APIs
System.Span<T>
System.String
Most types in the System.Globalization namespace
System.Array.Sort (when sorting an array of strings)
System.Collections.Generic.List<T>.Sort() (when the list elements are strings)
System.Collections.Generic.SortedDictionary<TKey,TValue> (when the keys are
strings)
System.Collections.Generic.SortedList<TKey,TValue> (when the keys are strings)
System.Collections.Generic.SortedSet<T> (when the set contains strings)
See also
Globalization APIs use ICU libraries on Windows 10
Binding config to dictionary extends
values
Article • 08/03/2023
When binding a configuration using a Dictionary<TKey,TValue> object where the value
is a mutable collection type, binding to the same key more than once now extends the
values collection instead of replacing the whole collection with the new value.
Version introduced
.NET 7
Previous behavior
Consider the following code that binds a configuration that has a single key named Key
to a dictionary multiple times.
C#
using Microsoft.Extensions.Configuration;
IConfiguration config = new ConfigurationBuilder()
.AddInMemoryCollection()
.Build();
config["Key:0"] = "NewValue";
var dict = new Dictionary<string, string[]>() { { "Key", new[] {
"InitialValue" } } };
Console.WriteLine($"Initially: {String.Join(", ", dict["Key"])}");
config.Bind(dict);
Console.WriteLine($"Bind: {String.Join(", ", dict["Key"])}");
config.Bind(dict);
Console.WriteLine($"Bind again: {String.Join(", ", dict["Key"])}");
Prior to .NET 7, the value for Key was overwritten one each bind. The code produced the
following output:
Output
Initially: InitialValue
Bind: NewValue
Bind again: NewValue
New behavior
Starting in .NET 7, the dictionary value is extended each time the same key is bound,
adding the new value but also keeping any existing values in the array. The same code
from the Previous behavior section produces the following output:
Output
Initially: InitialValue
Bind: InitialValue, NewValue
Bind again: InitialValue, NewValue, NewValue
Type of breaking change
This change is a behavioral change.
Reason for change
This change improves binding behavior by not overriding previously added values in
dictionary value arrays.
Recommended action
If the new behavior is not satisfactory, you can manually manipulate the values inside
the array after binding.
Affected APIs
ConfigurationBinder methods
ContentRootPath for apps launched by
Windows Shell
Article • 11/08/2022
The IHostEnvironment.ContentRootPath property represents the default directory where
appsettings.json and other content files are loaded in a hosted application, including
ASP.NET apps. This property's value defaults to Environment.CurrentDirectory, which is
the current working directory of the application. This behavior allows the same app to
be executed under different working directories and use the content from each
directory.
When a Windows process (either application or service) is launched without specifying a
working directory, the working directory of the process that created it is used. The
working directory of Windows Shell or services.exe is %windir%\system32 (or the System
special folder). When either of those processes launches a hosted app, the
ContentRootPath property is set to %windir%\system32.
This behavior is confusing and causes hosted applications to fail, because the
application tries to load files from the %windir%\system32 directory, but it doesn't exist.
For example, the appsettings.json file is not found at run time and the settings aren't
applied.
Starting with .NET 7, when a hosted application is launched with the current directory
set to the System special folder, it defaults the ContentRootPath property to
AppContext.BaseDirectory.
Version introduced
.NET 7
Previous behavior
Host.CreateDefaultBuilder defaulted the ContentRootPath property to
Environment.CurrentDirectory regardless of the value of the current directory.
New behavior
Host.CreateDefaultBuilder no longer defaults the ContentRootPath property to the
current directory if it's the System special folder on Windows. Instead, the base directory
of the application is used.
Type of breaking change
This change can affect binary compatibility.
Reason for change
App developers weren't expecting ContentRootPath to be C:\Windows\system32 when
their application was launched by Windows in certain cases (for example, when the app
was packaged as an MSIX or started from the Start Menu). In these cases, it's better to
default the ContentRootPath property to the base directory of the application.
Recommended action
If you want to use the previous behavior, you can set the ContentRootPath property
explicitly when creating the IHostBuilder:
C#
Host
.CreateDefaultBuilder()
.UseContentRoot(Environment.CurrentDirectory)
....
Affected APIs
Microsoft.Extensions.Hosting.Host.CreateDefaultBuilder
Environment variable prefixes
Article • 11/08/2022
Hierarchical data is represented using : as the level delimiter. However, for
environmental variables, the : character is normalized to __ , because the latter is
supported on all platforms. This change affects how normalized and non-normalized
prefixes and keys are compared. Specifically, you can now add environment variables by
specifying a prefix containing either : or __ as the delimiter. Either syntax will match
any environment variables with a matching prefix followed by either a : or __ . Some
environment variables that theoretically didn't match the filter previously may now
match the filter.
Version introduced
.NET 7 Preview 4
Previous behavior
Previously, the following code printed False :
C#
using Microsoft.Extensions.Configuration;
const string myValue = "value1";
Environment.SetEnvironmentVariable("MY_PREFIX__ConfigKey", myValue);
IConfiguration config = new ConfigurationBuilder()
.AddEnvironmentVariables(prefix: "MY_PREFIX__")
.Build();
var loadedValue = config.GetValue<string?>("ConfigKey", null);
Console.WriteLine(String.Equals(myValue, loadedValue));
// False
In order for the MY_PREFIX__ConfigKey environment variable to be added to the
configuration, you had to add environment variables using a delimiter of : instead of
__ :
C#
using Microsoft.Extensions.Configuration;
const string myValue = "value1";
Environment.SetEnvironmentVariable("MY_PREFIX__ConfigKey", myValue);
IConfiguration config = new ConfigurationBuilder()
.AddEnvironmentVariables(prefix: "MY_PREFIX:")
.Build();
var loadedValue = config.GetValue<string?>("ConfigKey", null);
Console.WriteLine(String.Equals(myValue, loadedValue));
// True
New behavior
Starting in .NET 7, the following code prints True :
C#
using Microsoft.Extensions.Configuration;
const string myValue = "value1";
Environment.SetEnvironmentVariable("MY_PREFIX__ConfigKey", myValue);
IConfiguration config = new ConfigurationBuilder()
.AddEnvironmentVariables(prefix: "MY_PREFIX__")
.Build();
var loadedValue = config.GetValue<string?>("ConfigKey", null);
Console.WriteLine(String.Equals(myValue, loadedValue));
// True
Type of breaking change
This change can affect binary compatibility.
Reason for change
This change was made to fix an unintentional behavior change for normalizing
environment variable prefix filters in .NET 6. The new behavior matches the .NET 5
behavior.
Recommended action
Most developers won't be affected by this change, since it corrects previously erroneous
behavior. In the unlikely case that you relied on the fact that a prefix containing __
didn't match an environment variable containing __ , consider changing the prefixes of
those variables.
Affected APIs
Microsoft.Extensions.Configuration.EnvironmentVariablesExtensions.AddEnvironme
ntVariables(IConfigurationBuilder, String)
See also
Configuration in .NET
RuntimeInformation.OSArchitecture
under emulation
Article • 09/14/2022
System.Runtime.InteropServices.RuntimeInformation.OSArchitecture now returns the
correct value under emulation.
Previous behavior
Previously, RuntimeInformation.OSArchitecture returned Architecture.X64 in emulated
processes on Windows Arm 64-bit and macOS Apple Silicon systems.
New behavior
Starting in .NET 7, RuntimeInformation.OSArchitecture returns Architecture.Arm64 in
emulated processes on Windows Arm 64-bit and macOS Apple Silicon systems.
Version introduced
7 Preview 6
Type of breaking change
This change can affect binary compatibility.
Reason for change
The previous behavior was incorrect.
Recommended action
Code that expects the process architecture should be changed to call
RuntimeInformation.ProcessArchitecture instead.
Affected APIs
System.Runtime.InteropServices.RuntimeInformation.OSArchitecture
Constructors accept base interface
instead of concrete type
Article • 11/16/2022
The constructors of the following types now accept the base interface ( IWebViewHandler )
instead of a specific, concrete type that implements the interface ( WebViewHandler ):
Microsoft.Maui.Platform.MauiWebChromeClient
Microsoft.Maui.Platform.MauiWebViewNavigationDelegate
Version introduced
.NET 7
Previous behavior
The constructors of MauiWebChromeClient and MauiWebViewNavigationDelegate required
the caller to pass in a concrete Microsoft.Maui.Handlers.WebViewHandler instance.
New behavior
The constructors of MauiWebChromeClient and MauiWebViewNavigationDelegate now
accept any implementation of the interface Microsoft.Maui.Handlers.IWebViewHandler .
Type of breaking change
This change can affect binary compatibility.
Reason for change
The original constructors required the caller to pass in a concrete type that's not usercontrollable and could throw at any point depending on the library author or order of
imports. The new implementation just requires the base interface, which is something a
developer can actually use.
Recommended action
If you've overridden the WKUIDelegate mapper for the iOS or Mac Catalyst
WebViewHandler , multi-target net6.0-ios and net7.0-ios or net6.0-maccatalyst
and net7.0-maccatalyst .
If you've overridden the WebChromeClient mapper for the Android WebViewHandler ,
multi-target net6.0-android and net7.0-android .
Otherwise, no action is necessary.
Affected APIs
Microsoft.Maui.Platform.MauiWebChromeClient constructor
Microsoft.Maui.Platform.MauiWebViewNavigationDelegate constructor
Flow direction helper methods removed
Article • 11/16/2022
The entire system for flow direction was rewritten and the following APIs have been
removed:
Microsoft.Maui.IViewExtensions
Microsoft.Maui.IViewExtensions.GetEffectiveFlowDirection(Microsoft.Maui.IView
)
Microsoft.Maui.Layouts.LayoutExtensions.ShouldArrangeLeftToRight(Microsoft.Mau
i.IView)
Microsoft.Maui.Platform.TextAlignmentExtensions.AdjustForFlowDirection(UIKit.U
ITextAlignment,Microsoft.Maui.IView)
Version introduced
.NET 7
Previous behavior
These helper methods existed to calculate the flow direction of test and UI components.
New behavior
The methods have been removed.
Type of breaking change
This change can affect binary compatibility and source compatibility.
Reason for change
The previous implementation was incorrect and a performance bottleneck. The entire
system for flow direction was rewritten for .NET 7 and the affected APIS no longer had
any value or performed any function, so they were removed.
Recommended action
If you were calling these APIs, remove the calls.
Affected APIs
Microsoft.Maui.IViewExtensions
Microsoft.Maui.IViewExtensions.GetEffectiveFlowDirection(Microsoft.Maui.IView
)
Microsoft.Maui.Layouts.LayoutExtensions.ShouldArrangeLeftToRight(Microsoft.Mau
i.IView)
Microsoft.Maui.Platform.TextAlignmentExtensions.AdjustForFlowDirection(UIKit.U
ITextAlignment,Microsoft.Maui.IView)
New UpdateBackground parameter
Article • 11/16/2022
When updating the button background on iOS, the border was also required to be
drawn. This change adds a new parameter to ViewExtensions.UpdateBackground() to
pass in the stroke information.
Version introduced
.NET 7
Previous behavior
Previously there was no way to draw the border on buttons if there was a gradient or
complex background.
New behavior
The new, optional parameter now allows for additional border information.
Type of breaking change
This change can affect binary compatibility.
Reason for change
We need more information when drawing the button background or border, so we
added a new parameter to pass this information.
Recommended action
No action is required if you're calling from source. Otherwise, multi-target to support
net6.0-ios or net6.0-maccatalyst .
Affected APIs
Microsoft.Maui.Platform.ViewExtensions.UpdateBackground(UIKit.UIView,Microsoft
.Maui.Graphics.Paint)
ScrollToRequest property renamed
Article • 11/16/2022
The ScrollToRequest.HoriztonalOffset property has been renamed to
HorizontalOffset .
Version introduced
.NET 7
Previous behavior
The property name was misspelled.
New behavior
The property name and a corresponding constructor parameter name have been
changed to correct a typo.
Type of breaking change
This change can affect binary compatibility and source compatibility.
Reason for change
There was a typo in the property name. Renaming a C# record requires a fair amount of
work and this API is both new and rarely used by developers.
Recommended action
If you don't have a custom command mapper for RequestScrollTo in the
Microsoft.Maui.Handlers.ScrollViewHandler handler, then no action is needed.
Otherwise, update your code to use the new spelling.
Affected APIs
Microsoft.Maui.ScrollToRequest.HorizontalOffset property
ScrollToRequest(double,double,bool) constructor
Some Windows APIs are removed
Article • 11/16/2022
The following APIs have been removed:
Microsoft.Maui.ApplicationModel.IWindowStateManager.ActiveWindowDisplayChange
d event
Microsoft.Maui.ApplicationModel.IWindowStateManager.OnWindowMessage(System.Int
Ptr,uint,System.IntPtr,System.IntPtr) method
Microsoft.Maui.ApplicationModel.Platform.OnWindowMessage(System.IntPtr,uint,Sy
stem.IntPtr,System.IntPtr) method
Version introduced
.NET 7
Previous behavior
Previously, the IWindowStateManager.OnWindowMessage() method was an implementation
detail that ran a check on the parameters to determine if the
ActiveWindowDisplayChanged event should be raised. The ActiveWindowDisplayChanged
event could be invoked if the concrete type was able to observe changes of the display
that contained the currently active window.
You could call the Platform.OnWindowMessage() method to pass messages. This method
called into IWindowStateManager.OnWindowMessage() and then that would optionally
cause the ActiveWindowDisplayChanged event to fire.
New behavior
The APIs are now removed.
Type of breaking change
This change can affect binary compatibility and source compatibility.
Reason for change
The ActiveWindowDisplayChanged event should never have been public because it was an
implementation detail and provided no additional information that "something had
changed". We removed it to avoid confusion with the MainDisplayInfoChanged event and
to reduce potential implementation complexity for both users and the SDK itself.
The IWindowStateManager.OnWindowMessage() method was also an implementation detail
and should never have been public. This API no longer does anything and thus was
removed.
The Platform.OnWindowMessage() method was implemented to work around a limitation
of Xamarin.Forms and Xamarin.Essentials. It was brought forward into .NET MAUI to aid
migration and used in .NET 6. However, in .NET 7, this API no longer does anything and
was removed.
Recommended action
If you're not building your app for Windows, no action is necessary.
If you were using ActiveWindowDisplayChanged , use
Microsoft.Maui.Devices.DeviceDisplay.Current.MainDisplayInfoChanged instead.
If you were calling either of the OnWindowMessage() methods, remove the call.
Affected APIs
Microsoft.Maui.ApplicationModel.IWindowStateManager.ActiveWindowDisplayChange
d
Microsoft.Maui.ApplicationModel.IWindowStateManager.OnWindowMessage(System.Int
Ptr,uint,System.IntPtr,System.IntPtr)
Microsoft.Maui.ApplicationModel.Platform.OnWindowMessage(System.IntPtr,uint,Sy
stem.IntPtr,System.IntPtr)
AllowRenegotiation default is false
Article • 11/08/2022
The default value of SslServerAuthenticationOptions.AllowRenegotiation has been
changed to false .
Previous behavior
In previous versions, client-side renegotiation was allowed by the server by default.
New behavior
Starting in .NET 7, client-side renegotiation must be explicitly enabled on the server side.
Version introduced
.NET 7
Type of breaking change
This change can affect binary compatibility and source compatibility.
Reason for change
Client-side renegotiation is viewed as insecure by the industry. For example, it has been
removed from TLS 1.3 entirely. Therefore, we should disable it by default.
Recommended action
If client-side renegotiation is required, set
SslServerAuthenticationOptions.AllowRenegotiation to true when initializing the server
side of the SslStream.
Affected APIs
System.Net.Security.SslServerAuthenticationOptions
System.Net.Security.SslStream.AuthenticateAsServer(X509Certificate, Boolean,
Boolean)
System.Net.Security.SslStream.AuthenticateAsServer(X509Certificate, Boolean,
SslProtocols, Boolean)
System.Net.Security.SslStream.AuthenticateAsServer(X509Certificate)
Custom ping payloads on Linux
Article • 11/08/2022
On Linux, non-privileged processes can't send raw IP packets. Ping functionality is
implemented by interfacing with the ping utility. However, this utility doesn't support
specifying a custom payload for the Internet Control Message Protocol (ICMP) ping
packets. .NET 7 adds a check for such cases and throws an exception if a custom
payload is specified.
Previous behavior
In previous versions, the ping packet payload was silently ignored (that is, it wasn't sent)
on non-privileged Linux processes.
New behavior
Starting in .NET 7, a PlatformNotSupportedException is thrown if you attempt to send a
custom ping packet payload when running in non-privileged Linux process.
Version introduced
.NET 7
Type of breaking change
This change can affect binary compatibility.
Reason for change
It's better to signal to the user that the operation cannot be performed instead of
silently dropping the payload.
Recommended action
If a ping payload is necessary, run the application as root , or grant the cap_net_raw
capability using the setcap utility.
Otherwise, use an overload of Ping.SendPingAsync that does not accept a custom
payload, or pass in an empty array.
Affected APIs
System.Net.NetworkInformation.Ping.Send(IPAddress, Int32, Byte[], PingOptions)
System.Net.NetworkInformation.Ping.Send(IPAddress, Int32, Byte[])
System.Net.NetworkInformation.Ping.Send(String, Int32, Byte[], PingOptions)
System.Net.NetworkInformation.Ping.Send(String, Int32, Byte[])
System.Net.NetworkInformation.Ping.SendAsync(IPAddress, Int32, Byte[],
PingOptions, Object)
System.Net.NetworkInformation.Ping.SendAsync(IPAddress, Int32, Byte[], Object)
System.Net.NetworkInformation.Ping.SendAsync(String, Int32, Byte[], PingOptions,
Object)
System.Net.NetworkInformation.Ping.SendAsync(String, Int32, Byte[], Object)
System.Net.NetworkInformation.Ping.SendPingAsync(IPAddress, Int32, Byte[],
PingOptions)
System.Net.NetworkInformation.Ping.SendPingAsync(IPAddress, Int32, Byte[])
System.Net.NetworkInformation.Ping.SendPingAsync(String, Int32, Byte[],
PingOptions)
System.Net.NetworkInformation.Ping.SendPingAsync(String, Int32, Byte[])
Socket.End methods don't throw
ObjectDisposedException
Article • 11/08/2022
System.Net.Sockets.Socket.End* methods (for example, EndSend) throw a
SocketException instead of an ObjectDisposedException if the socket is closed.
Previous behavior
Previously, the affected methods threw an ObjectDisposedException for closed sockets.
New behavior
Starting in .NET 7, the affected methods throw a SocketException with SocketErrorCode
set to SocketError.OperationAborted for closed sockets.
Version introduced
.NET 7
Type of breaking change
This change can affect binary compatibility.
Reason for change
The asynchronous programming model (APM) APIs are those named Begin* and End* .
Starting with .NET 6, these legacy APIs are backed with a Task -based implementation as
part of an effort to consolidate and simplify the Socket codebase. Unfortunately, with
the 6.0 implementation, unexpected events were sometimes raised on
TaskScheduler.UnobservedTaskException. This happened even when the APIs were used
correctly, meaning that the calling code always invoked the End* methods, including
when the socket was closed.
The change to throw a SocketException was made to ensure that no unobserved
exceptions are leaked in such cases.
Recommended action
If your code catches an ObjectDisposedException from any of the Socket.End* methods,
change it to catch SocketException and refer to SocketException.SocketErrorCode to
query the underlying reason.
7 Note
APM code should always make sure that End* methods are invoked after the
corresponding Begin* methods, even if the socket is closed.
Affected APIs
System.Net.Sockets.Socket.EndConnect(IAsyncResult)
System.Net.Sockets.Socket.EndDisconnect(IAsyncResult)
System.Net.Sockets.Socket.EndSend
System.Net.Sockets.Socket.EndSendFile(IAsyncResult)
System.Net.Sockets.Socket.EndSendTo(IAsyncResult)
System.Net.Sockets.Socket.EndReceive
System.Net.Sockets.Socket.EndAccept
Automatic RuntimeIdentifier for certain
projects
Article • 01/14/2023
Projects that specify any of the following properties now get a runtime identifier (RID)
automatically. An RID enables publishing a self-contained deployment.
SelfContained
PublishAot
PublishReadyToRun
PublishSingleFile
PublishSelfContained (.NET SDK 7.0.200 and later versions only)
The following projects might be affected by this change:
Old projects that circumvented the missing runtime identifier error.
Projects that have RuntimeIdentifiers but not RuntimeIdentifier .
Projects that use hard-coded paths without RIDs.
Projects that had these properties but used a build instead of a publish and
accepted publish being in a broken state.
There are other potential nuances that could break individual situations that we're not
yet aware of.
7 Note
This change was restricted to the dotnet publish command in .NET 7.0.200 SDK.
For more information, see Automatic RuntimeIdentifier for publish only.
Version introduced
.NET 7
Previous behavior
Previously, these projects failed to publish with errors such as:
It is not supported to publish an application to a single-file without specifying a
RuntimeIdentifier. Please either specify a RuntimeIdentifier or set PublishSingleFile
to false.
OR
error NETSDK1031: It is not supported to build or publish a self-contained
application without specifying a RuntimeIdentifier. You must either specify a
RuntimeIdentifier or set SelfContained to false.
In some cases, such as PublishSingleFile or with special RuntimeIdentifiers logic,
projects might have built successfully without a RuntimeIdentifier .
New behavior
Projects that specify any of the properties listed at the beginning of this article get a
RuntimeIdentifier automatically. This new behavior can cause build failures on projects
that rely on RuntimeIdentifiers but not RuntimeIdentifier , because RuntimeIdentifier
can affect the output path distinctly from RuntimeIdentifiers . It can also cause failures
on AnyCPU projects that rely on PublishSingleFile but don't always give a
RuntimeIdentifier when taking other actions. These failures can appear as follows:
The target process exited without raising a CoreCLR started event. Ensure that the
target process is configured to use .NET Core.
Type of breaking change
This change can affect source compatibility.
Reason for change
A majority of .NET projects fail to publish using the mentioned properties without
RuntimeIdentifier set. This change reduces the need to add the RID manually every
time you use the mentioned properties.
Recommended action
If your project is impacted, you can disable the automatic RuntimeIdentifier by adding
<UseCurrentRuntimeIdentifier>false</UseCurrentRuntimeIdentifier> to your project file.
If you encounter a break due to the output path changing, add
<AppendRuntimeIdentifierToOutputPath>false</AppendRuntimeIdentifierToOutputPath> to
your project file.
See also
Automatic RuntimeIdentifier for publish only
Automatic RuntimeIdentifier for publish
only
Article • 01/14/2023
In the 7.0.100 SDK, there was a change to automatically add <RuntimeIdentifier> to
projects with properties that require a runtime identifier (RID). Those properties are as
follows:
SelfContained
PublishAot
PublishReadyToRun
PublishSingleFile
PublishSelfContained
However, all of these properties except for SelfContained are only used for publishing.
Yet the implicit <RuntimeIdentifier> was added for any dotnet operation if these
properties were in the project file or specified as part of the dotnet command.
Now, the automatic RID for these properties, excluding SelfContained , is only added
during publish. In addition, the automatic RID is only added when using the dotnet
publish CLI command. It's not added when you publish from Visual Studio or msbuild ,
as those are separate mechanisms, and Visual Studio should provide its own RID.
Version introduced
.NET 7.0.200 SDK
Previous behavior
Previously, if you specified any of the mentioned properties, the RID was automatically
added to the project.
New behavior
The RID is only automatically added for the dotnet publish command.
If you performed a restore without an RID in .NET 7 and use it to restore for a publish -no-restore command on a project with one of the mentioned properties, you'll need to
specify an RID using dotnet restore -r <RID> .
Type of breaking change
This change can affect binary compatibility and source compatibility.
Reason for change
The RID change was a breaking change, and there was no need for a publish property
change to break dotnet build and other non-publish-related commands.
Recommended action
For an action like restore followed by publish --no-restore , you must add the RID by
using dotnet restore -r RID . In this case, it's also better to be explicit when you publish
so the publish has the same RID (using dotnet publish -r RID ). Alternatively, you can
remove --no-restore from the publish command.
For everything else, no action is needed. However, if you want to keep the RID, add it to
the project file as follows: <RuntimeIdentifier>win-x64</RuntimeIdentifier> .
See also
Automatic RuntimeIdentifier for certain projects
CLI console output uses UTF-8
Article • 04/06/2023
If the DOTNET_CLI_UI_LANGUAGE or VSLANG environment variable is set, the .NET CLI
console output and input encoding changes to UTF-8, so that the code page can
change to UTF-8 as well. This new behavior allows characters from languages set by
those environment variables to be rendered correctly.
This change only affects Windows operating systems (the encoding was okay on other
platforms). Moreover, it only applies to Windows 10 and later versions where the UI
culture set by the user is non-English.
Previous behavior
Characters in certain languages, including Chinese, German, Japanese, and Russian,
would sometimes display as garbled characters or as ? in the console. For example:
Console
C:\>dotnet build
MSBuild version 17.3.0-preview[...] for .NET
???????????????...
New behavior
Starting in .NET 7 (version 7.0.3xx) and .NET 8, characters render correctly. Both the
encoding and the code page change. For example:
Console
C:\>dotnet build
MSBuild version 17.3.0-preview[...] for .NET
正在确定要还原的项目…
Versions of Windows older than Windows 10 1909 don't fully support UTF-8 and may
experience issues after this change. (Starting in .NET 8 Preview 3 and .NET 7.0.300 SDK,
the .NET SDK no longer changes the encoding to UTF-8 on these versions, by default. To
opt back into using UTF-8 even on Windows 10 versions that don't support it, use the
DOTNET_CLI_FORCE_UTF8_ENCODING environment variable.)
In addition, there was an existing bug
where the SDK can affect the encoding of other
commands and programs called in the same command prompt after the SDK has
finished execution. Now that the SDK more frequently changes the encoding, the impact
of this bug may increase. However, the bug was fixed in .NET 8 Preview 3 and .NET
7.0.300 SDK. For more information, see SDK no longer changes console encoding after
completion.
Version introduced
7.0.3xx .NET 8 Preview 1
Type of breaking change
This change can affect source compatibility and binary compatibility. It's also a
behavioral change.
Reason for change
Using the .NET CLI in non-English languages provided a poor experience.
Developers that weren't already using the VSLANG and DOTNET_CLI_UI_LANGUAGE variables
aren't impacted. The impact should be minimal, as this language setting wouldn't have
worked well in the first place due to garbled characters. Also, only developers using
Windows 10 or later might be impacted, most of which are likely using version 1909 or
later.
The legacy scenarios are already less likely to support the broken languages, so it's
unlikely you'd want to use another language that might expose this break anyway.
Recommended action
If you're using an older version of windows 10, upgrade to version 1909 or later.
If you want to use a legacy console or are facing build issues or others due to the
encoding change, unset VSLANG and DOTNET_CLI_UI_LANGUAGE to disable this
change.
See also
SDK no longer changes console encoding when finished
Version requirements for .NET 7 SDK
Article • 03/24/2023
Certain .NET SDK versions require newer versions of Visual Studio and MSBuild.
Version introduced
.NET SDK 7
Change description
The following table shows the minimum version of Visual Studio and MSBuild you need
to use the .NET 7.0.100 or 7.0.200 SDK.
NET SDK version
Minimum Visual Studio and MSBuild version
7.0.100
17.4
7.0.200
17.4
1In
addition, scenarios that use a source generator could fail when using a Visual Studio
or MSBuild version earlier than version 17.2.
For more information about minimum supported versions, see Targeting and support
rules.
Reason for change
Changes were made to features within the SDK that aren't compatible with previous
Visual Studio versions. In addition, it enables .NET SDK partners to have a reliable
minimum version of Visual Studio that they can expect to work against.
Recommended action
Upgrade your Visual Studio version to the required version. Alternatively, you can use a
global.json file to pin to an older standalone SDK install.
Affected APIs
N/A
See also
Targeting and support rules
SDK no longer calls
ResolvePackageDependencies
Article • 07/18/2023
Previously, the .NET SDK called the ResolvePackageDependencies target in order to
generate PackageDependencies and PackageDefinitions . However, that data was already
available from a different target. Instead, those two items are now added from
PreprocessPackageDependenciesDesignTime into the design-time build cache and the
prior target isn't called.
Version introduced
.NET SDK 7.0.200
Type of change
This change can affect source compatibility.
Previous behavior
An existing .NET SDK target was called to get information on packages that were already
available.
New behavior
Package information is added from PreprocessPackageDependenciesDesignTime into the
design-time build cache. If you depended on PackageDependencies and
PackageDefinitions in your build, you'll see build errors such as No dependencies
found.
Reason for change
In some situations, performance was particularly slow for the prior target. Solutions that
have large NuGet dependency graphs will see faster IntelliSense after solution loads,
branch switches, or when making solution-wide changes while using the Central
Package Management feature.
Recommended action
If your build depends on the previous behavior, add the
<EmitLegacyAssetsFileItems>true</EmitLegacyAssetsFileItems> property to your project
to return to the legacy behavior. We expect this to only impact a small number of users.
XML
<PropertyGroup>
<EmitLegacyAssetsFileItems>true</EmitLegacyAssetsFileItems>
</PropertyGroup>
BinaryFormatter serialization of custom
BuildEventArgs and ITaskItems removed
for .NET 7
Article • 05/12/2022
MSBuild in .NET 7 doesn't support serialization of custom BuildEventArgs -derived and
ITaskItem -derived types via the BinaryFormatter serializer.
Version introduced
MSBuild 17.4 (.NET SDK 7.0.100)
Old behavior
MSBuild used BinaryFormatter to preserve custom types that derived from
BuildEventArgs and ITaskItem across certain boundaries, most notably when running in a
multi-process environment.
New behavior
MSBuild will no longer support this mechanism, so code that used custom types derived
from BuildEventArgs and ITaskItem may fail.
Reason for change
BinaryFormatter was made obsolete in .NET 5 . Per this plan, all first-party code in the
dotnet GitHub organization must migrate away from its use by .NET 7. This change
impacts user-exposed functionality of MSBuild.
Recommended action
Engage with the MSBuild team on this GitHub discussion
about your specific use
cases and how you can migrate away from the TranslateDotNet mechanism.
Avoid returning custom derived types from tasks or when logging.
Side-by-side SDK installations
Article • 02/21/2023
If a preview .NET 7 SDK is installed alongside the general availability (GA) version of the
.NET 7 SDK, projects with workload dependencies such as
microsoft.net.workload.mono.toolchain may fail to build, load, or run. The error is
similar to:
The SDK resolver "Microsoft.DotNet.MSBuildSdkResolver" failed while attempting to
resolve the SDK "Microsoft.NET.Sdk". Exception:
"Microsoft.NET.Sdk.WorkloadManifestReader.WorkloadManifestCompositionExcepti
on: Workload definition 'wasm-tools' in manifest
'microsoft.net.workload.mono.toolchain'.
7 Note
This behavior was fixed in .NET SDK 7.0.101.
Version introduced
.NET 7
Previous behavior
Building, loading, or running an affected project worked fine.
New behavior
Building, loading, or running an affected project fails.
Type of breaking change
This change can affect source compatibility and binary compatibility.
Reason for change
.NET 7 preview SDKs are incompatible with the GA version because the mono.toolchain
workload was renamed.
Recommended action
Choose one of the following actions:
Uninstall any .NET 7 preview SDKs. For detailed instructions, see How to remove
the .NET Runtime and SDK. For example, on Windows, you can uninstall .NET
preview SDKs using Add or remove programs in Control panel. You can also use
the dotnet-core-uninstall tool
to uninstall preview SDKs.
For file-based installs, you can delete the folder %ProgramFiles%/dotnet/sdkmanifests/7.0.100/microsoft.net.workload.mono.toolchain.
Solution-level --output option no
longer valid for build-related commands
Article • 02/24/2023
In the 7.0.200 SDK, there was a change
to no longer accept the --output / -o option
when using a solution file with the following commands:
build
clean
pack
publish
store
test
vstest
This is because the semantics of the OutputPath property, which is controlled by the -output / -o option, aren't well defined for solutions. Projects built in this way will have
their output placed in the same directory, which is inconsistent and has led to a number
of user-reported issues.
This change was reduced to a warning level of severity in the 7.0.201 SDK, and pack was
removed from the list of commands that are affected.
Version introduced
.NET 7.0.200 SDK, reduced to a warning only in the 7.0.201 SDK.
Previous behavior
Previously, if you specified --output / -o when using a solution file, the output for all
built projects would be placed in the specified directory in an undefined and
inconsistent order.
New behavior
The dotnet CLI will error if the --output / -o option is used with a solution file. Starting
in the 7.0.201 SDK, a warning will be emitted instead, and in the case of dotnet pack no
warning or error will be generated.
Type of breaking change
This breaking change may require modifications to build scripts and continuous
integration pipelines. As a result it affects both binary and source compatibility.
Reason for change
This change was made because the semantics of the OutputPath property, which is
controlled by the --output / -o option, aren't well defined for solutions. Projects built in
this way will have their output placed in the same directory, which is inconsistent and
has led to a number of user-reported issues.
When a solution is built with the --output option, the OutputPath property is set to the
same value for all projects, which means that all projects will have their output placed in
the same directory. Depending on the complexity of the projects in the solution,
different and inconsistent results may occur. Let's take a look at some examples of
different solution shapes and how they are affected by a shared OutputPath .
Single project, single TargetFramework
Imagine a solution that contains a single project targeting a single TargetFramework ,
net7.0 . In this case, providing the --output option is equivalent to setting the
OutputPath property in the project file. During a build (or other commands, but let's
scope the discussion to build for now), all of the outputs for the project will be placed in
the specified directory.
Single project, multiple TargetFrameworks
Now imagine a solution that contains a single project with multiple TargetFrameworks ,
net6.0 and net7.0 . Because of multi-targeting, the project will be build twice, once for
each TargetFramework . For each of these 'inner' builds the OutputPath will be set to the
same value, and so the outputs for each of the inner builds will be placed in the same
directory. This means that whichever build completes last will overwrite the outputs of
the other build, and in a parallel-build system like MSBuild operates in by default, 'last'
is indeterminate.
Library => Console => Test, single TargetFramework
Now imagine a solution that contains a library project, a console project that references
the library project, and a test project that references the console project. All of these
projects target a single TargetFramework , net7.0 . In this case, the library project will be
built first, and then the console project will be built. The test project will be built last,
and will reference the console project. For each built project, the outputs of each build
will be copied into the directory specified by the OutputPath , and so the final directory
will contain assets from all three projects. This works for testing, but for publishing may
result in test assets being sent to production.
Library => Console => Test, multiple TargetFrameworks
Now take the same chain of projects and add a net6.0 TargetFramework build to them
in addition to the net7.0 build. The same problem as the single-project, multi-targeted
build occurs - inconsistent copying of TFM-specific assets to the specified directory.
Multiple apps
So far we have been looking at scenarios with a linear dependency graph - but many
solutions may contain multiple related applications. This means that multiple apps may
be built concurrently to the same output folder. If the apps include a dependency file
with the same name, then the build may intermittently fail when multiple projects try to
write to that file in the output path concurrently.
If multiple apps depend on different versions of a file, then even if the build succeeds,
which version of the file is copied to the output path may be non-deterministic. This can
happen when the projects depend (possibly transitively) on different versions of a
NuGet package. Within a single project, NuGet helps ensure that its dependencies
(including any transitive dependencies through NuGet packages and/or project
references) are unified to the same version. Because the unification is done within the
context of a single project and its dependent projects, this means it is possible to
resolve different versions of a package when two separate top-level projects are built. If
the project that depends on the higher version copies the dependency last, then often
the apps will run successfully. However, if the lower version is copied last, then the app
that was compiled against the higher version will fail to load the assembly at runtime.
Because the version that is copied can be non-deterministic, this can lead to sporadic,
unreliable builds where it is very difficult to diagnose the issue.
Other examples
For more examples of how this underlying error presents in practice, see the discussion
on dotnet/sdk#15607 .
Recommended action
The general recommendation is to perform the action that you previously took without
the --output / -o option, and then move the output to the desired location after the
command has completed. It's also possible to perform the action at a specific project
and still apply the --output / -o option, as that has more well-defined semantics.
If you want to maintain the existing behavior exactly, then you can use the --property
flag to set a MSBuild property to the desired directory. The property to use varies based
on the command:
Command
Property
Example
build
OutputPath
dotnet build --property:OutputPath=DESIRED_PATH
clean
OutputPath
dotnet clean --property:OutputPath=DESIRED_PATH
pack
PackageOutputPath
dotnet pack --property:PackageOutputPath=DESIRED_PATH
publish
PublishDir
dotnet publish --property:PublishDir=DESIRED_PATH
store
OutputPath
dotnet store --property:OutputPath=DESIRED_PATH
test
TestResultsDirectory
dotnet test --property:OutputPath=DESIRED_PATH
NOTE For best results, the DESIRED_PATH should be an absolute path. Relative paths will
be 'anchored' (i.e. made absolute) in ways that you may not expect, and may not work
the same with all commands.
Tool manifests in root folder
Article • 08/01/2023
.NET no longer looks for local tool manifest files in the root folder on Windows, unless
overridden via the DOTNET_TOOLS_ALLOW_MANIFEST_IN_ROOT environment variable. This
change does not impact Linux or macOS.
Previous behavior
Previously, .NET SDK local tools checked the root folder on all platforms when searching
for a tool manifest. The search continued from the current directory up the directory
tree to the root folder until it found a manifest. At each level, .NET searches for the tool
manifest, named dotnet-tools.json, in a .config subfolder. On a Windows system, if no
other tool manifest was found, the SDK ultimately looked for a tool manifest in
C:\.config\dotnet-tools.json.
New behavior
.NET no longer searches in the root folder of the current directory tree by default on
Windows, unless overridden via the DOTNET_TOOLS_ALLOW_MANIFEST_IN_ROOT environment
variable. DOTNET_TOOLS_ALLOW_MANIFEST_IN_ROOT is set to false by default.
Version introduced
.NET SDK 7.0.3xx
.NET SDK 7.0.1xx
.NET SDK 6.0.4xx
.NET SDK 6.0.3xx
.NET SDK 6.0.1xx
.NET SDK 3.1.4xx
Type of breaking change
This change is a behavioral change.
Reason for change
This change was made to address a security concern. Since all users can create files and
folders in the C:\ directory on Windows, low-privilege attackers can hijack the
C:\.config\dotnet-tools.json file. When an administrator runs a dotnet tool command, the
tool could potentially read malicious configuration information from the file and
download and run malicious tools.
Recommended action
To disable the new behavior, set the DOTNET_TOOLS_ALLOW_MANIFEST_IN_ROOT environment
variable to true or 1 .
See also
Tutorial: Install and use a .NET local tool using the .NET CLI
BinaryFormatter serialization APIs
produce compiler errors
Article • 05/17/2023
As part of the BinaryFormatter long-term deprecation plan , we continue to cull
BinaryFormatter functionality from our libraries and to wean developers off of the type.
Starting in .NET 7, calls to the following APIs produce compile-time errors across all C#
and Visual Basic project types:
System.Exception.SerializeObjectState event
BinaryFormatter.Serialize method
BinaryFormatter.Deserialize method
Formatter.Serialize(Stream, Object) method
Formatter.Deserialize(Stream) method
IFormatter.Serialize(Stream, Object) method
IFormatter.Deserialize(Stream) method
Previous behavior
Since .NET 5, using the affected Serialize and Deserialize methods produced a
compiler warning with ID SYSLIB0011 . For more information, see BinaryFormatter
serialization methods are obsolete and prohibited in ASP.NET apps (.NET 5).
Using the Exception.SerializeObjectState event did not produce an error.
New behavior
Starting in .NET 7, using any of the affected APIs in code produces a compiler error with
the same ID, SYSLIB0011 . Your project will be affected if it meets all of the following
criteria:
It's a C# or Visual Basic project.
It targets net7.0 or higher.
It directly calls one of the affected APIs.
It's not already suppressing the SYSLIB0011 warning code.
Version introduced
.NET 7
Type of breaking change
This change can affect source compatibility.
Reason for change
As part of the BinaryFormatter long-term deprecation plan , we continue to cull
BinaryFormatter functionality from our libraries and to wean developers off of the type.
Recommended action
The best course of action is to migrate away from BinaryFormatter due to its security
and reliability flaws. BinaryFormatter may be removed from .NET in a future release. The
.NET libraries team has already taken a stance that recent types such as System.Half and
System.DateOnly won't be compatible with BinaryFormatter .
If you must suppress the errors, you can do so by following the guidelines in the original
obsoletion article. You can also disable the error project-wide by setting a project
property that converts the error back to a warning (to match the .NET 5/6 behavior).
2 Warning
Setting this property might change host behavior. See
<EnableUnsafeBinaryFormatterSerialization> property.
C#
<PropertyGroup>
...
<EnableUnsafeBinaryFormatterSerialization>true</EnableUnsafeBinaryFormatterS
erialization>
</PropertyGroup>
7 Note
If your project compiles with "warnings as errors" enabled, compilation will still fail.
(This matches the behavior that shipped in the .NET 5 and .NET 6 SDKs.) If that's the
case, you'll still need to suppress the SYSLIB0011 warning in source or in your
project file's <NoWarn> element.
<EnableUnsafeBinaryFormatterSerialization> property
The <EnableUnsafeBinaryFormatterSerialization property was introduced in .NET 5.
With .NET 7, the behavior of this switch has changed to control both compilation and
host run-time behavior. The meaning of this switch differs based on the project type, as
described in the following table.
Type of
project
Property set to true
Property set to false
Property omitted
Library/shared
component1
The affected APIs are
obsolete as warning.
The affected APIs are
obsolete as error, and
(Same as for false .)
Compilation will succeed
calls from your code to
unless you have "warnings
those APIs will fail at
as errors" enabled for your
application or you've
compile time unless
the error is suppressed.
suppressed the SYSLIB0011
warning code.
Blazor and
MAUI
apps2
ASP.NET app
Calls to BinaryFormatter
Calls to
Calls to
will fail at run time.
BinaryFormatter will
BinaryFormatter will
fail at run time.
fail at run time.
The affected APIs are
The affected APIs are
(Same as for false .)
obsolete as warning.
Compilation will succeed
obsolete as error, and
calls from your code to
unless you have "warnings
as errors" enabled for your
application or you've
those APIs will fail at
compile time unless
the error is suppressed.
suppressed the SYSLIB0011
The runtime will forbid
calls to
BinaryFormatter ,
warning code. The runtime
will allow calls to
BinaryFormatter , regardless
of whether the call
originates from your code
or from a dependency that
you consume.
regardless of whether
the call originates from
your code or from a
dependency that you
consume.
Type of
project
Property set to true
Property set to false
Property omitted
Desktop apps
The affected APIs are
The affected APIs are
The affected APIs are
and all other
app types
obsolete as warning.
Compilation will succeed
unless you have "warnings
obsolete as error, and
calls from your code to
those APIs will fail at
obsolete as error, and
calls from your code to
those APIs will fail at
as errors" enabled for your
application or you've
suppressed the SYSLIB0011
compile time unless
the error is suppressed.
The runtime will forbid
compile time unless the
error is suppressed. The
runtime will allow calls
warning code. The runtime
calls to
to BinaryFormatter ,
will allow calls to
BinaryFormatter , regardless
BinaryFormatter ,
regardless of whether
the call originates from
your code or from a
of whether the call
originates from your code
or from a dependency that
you consume.
1
regardless of whether
the call originates from
your code or from a
dependency that you
consume.
dependency that you
consume.
Runtime policy is controlled by the app host. Calls into BinaryFormatter might still fail
at run time even if <EnableUnsafeBinaryFormatterSerialization> is set to true within
your library's project file. Libraries can't override the app host's runtime policy.
2
The Blazor and MAUI runtimes forbid calls to BinaryFormatter . Regardless of any value
you set for <EnableUnsafeBinaryFormatterSerialization> , the calls will fail at run time.
Don't call these APIs from Blazor or MAUI applications or from libraries intended to be
consumed by Blazor or MAUI apps.
Affected APIs
System.Exception.SerializeObjectState
System.Runtime.Serialization.Formatters.Binary.BinaryFormatter.Serialize
System.Runtime.Serialization.Formatters.Binary.BinaryFormatter.Deserialize
System.Runtime.Serialization.Formatter.Serialize(Stream, Object)
System.Runtime.Serialization.Formatter.Deserialize(Stream)
System.Runtime.Serialization.IFormatter.Serialize(Stream, Object)
System.Runtime.Serialization.IFormatter.Deserialize(Stream)
See also
dotnet/runtime issue 72132
BinaryFormatter serialization methods are obsolete (.NET 5)
SerializationFormat.Binary is obsolete (.NET 7)
BinaryFormatter disabled across most project types (.NET 8)
SerializationFormat.Binary is obsolete
Article • 05/17/2023
SerializationFormat.Binary is obsolete for DataTable and DataSet. Binary serialization
relies on BinaryFormatter, which is insecure. If you use SerializationFormat.Binary in your
code, obsoletion warning SYSLIB0038 will be generated at compile time.
In addition, an InvalidEnumArgumentException is thrown at run time if you:
Set DataSet.RemotingFormat or DataTable.RemotingFormat to
SerializationFormat.Binary.
Call one of the deserialization constructors for DataTable or DataSet with binary
data.
Previous behavior
Previously, DataTable and DataSet could be serialized and deserialized with their
RemotingFormat property set to SerializationFormat.Binary, which used BinaryFormatter
under the hood.
New behavior
Starting in .NET 7, if you attempt to serialize or deserialize DataTable and DataSet with
their RemotingFormat property set to SerializationFormat.Binary, an
InvalidEnumArgumentException is thrown.
Version introduced
.NET 7
Type of breaking change
This change can affect source compatibility and binary compatibility.
Reason for change
SerializationFormat.Binary is implemented via BinaryFormatter, which is insecure and
being obsoleted across the entire .NET stack.
Recommended action
If your code uses SerializationFormat.Binary, switch to using SerializationFormat.Xml or
use another method of serialization.
Otherwise, you can set the Switch.System.Data.AllowUnsafeSerializationFormatBinary
AppContext switch. This switch lets you opt in to allowing the use of
SerializationFormat.Binary, so that code can work as before. However, this switch will be
removed in .NET 8. For information about setting the switch, see AppContext for library
consumers.
Affected APIs
System.Data.SerializationFormat.Binary
DataSet.RemotingFormat
DataTable.RemotingFormat
DataSet(SerializationInfo, StreamingContext)
DataSet(SerializationInfo, StreamingContext, Boolean)
DataTable(SerializationInfo, StreamingContext)
See also
BinaryFormatter serialization methods are obsolete and prohibited in ASP.NET
apps
DataContractSerializer retains sign when
deserializing -0
Article • 11/08/2022
DataContractSerializer and DataContractJsonSerializer previously discarded the sign
when deserializing the input "-0" as a float or double. Both serializers have always done
the right thing when given "-0.0" as an input, but with an input of "-0", the sign was lost.
This behavior is both inconsistent and results in data loss. In addition, these serializers
write a value of negative zero out as "-0" during serialization.
Previous behavior
Previously, the negative sign was lost when deserializing "-0" (but not "-0.0") as a float or
double using DataContractSerializer.
New behavior
The negative sign is now preserved when deserializing "-0" as a float or double.
Version introduced
.NET 6.0.11 (servicing release)
.NET 7
Type of breaking change
This change can affect binary compatibility.
Reason for change
The previous behavior was inconsistent and resulted in data loss.
Recommended action
In most cases, no action is needed. If your code was affected by the bug, then this is a
good change. Or, you've already worked around the bug in a way that's unlikely to be
broken by this change.
Affected APIs
System.Runtime.Serialization.DataContractSerializer.ReadObject
System.Runtime.Serialization.Json.DataContractJsonSerializer.ReadObject
Deserialize Version type with leading or
trailing whitespace
Article • 11/08/2022
JsonSerializer now throws an exception while deserializing Version types that have
leading or trailing whitespace.
Previous behavior
Prior to .NET 7, deserializing Version types that have leading or trailing whitespace was
permitted.
New behavior
Started in .NET 7, JsonSerializer throws a FormatException when deserializing Version
types that have leading or trailing whitespace.
Version introduced
.NET 7
Type of breaking change
This change can affect binary compatibility.
Reason for change
.NET has optimized the implementation of the underlying Version converter. This
resulted in the implementation being made to align with the behavior for other
primitive types supported by System.Text.Json, for example, DateTime and Guid, which
also disallow leading and trailing spaces.
Recommended action
To get the old behavior back, add a custom converter for the Version type that permits
whitespace:
C#
internal sealed class VersionConverter : JsonConverter<Version>
{
public override Version Read(ref Utf8JsonReader reader, Type
typeToConvert, JsonSerializerOptions options)
{
string? versionString = reader.GetString();
if (Version.TryParse(versionString, out Version? result))
{
return result;
}
ThrowHelper.ThrowJsonException();
return null;
}
public override void Write(Utf8JsonWriter writer, Version value,
JsonSerializerOptions options)
{
writer.WriteStringValue(value.ToString());
}
}
Affected APIs
System.Text.Json.JsonSerializer.Deserialize
System.Text.Json.JsonSerializer.DeserializeAsync
JsonSerializerOptions copy constructor
includes JsonSerializerContext
Article • 11/08/2022
With the release of source generation in .NET 6, the JsonSerializerOptions copy
constructor was intentionally made to ignore its JsonSerializerContext state. This made
sense at the time since JsonSerializerContext was designed to have a 1:1 relationship
with JsonSerializerOptions instances. In .NET 7, IJsonTypeInfoResolver replaces
JsonSerializerContext to generalize the context, which removes the need for tight
coupling between JsonSerializerOptions and JsonSerializerContext. The copy constructor
now includes the IJsonTypeInfoResolver/JsonSerializerContext information, which could
manifest as a breaking change for some scenarios.
Previous behavior
In .NET 6, the following code serializes successfully. The MyContext configuration (which
doesn't support Poco2 ) is discarded by the copy constructor, and serialization succeeds
because the new options instance defaults to using reflection-based serialization.
C#
var options = new JsonSerializerOptions(MyContext.Default.Options);
JsonSerializer.Serialize(new Poco2(), options);
[JsonSerializable(typeof(Poco1))]
public partial class MyContext : JsonSerializerContext {}
public class Poco1 {}
public class Poco2 {}
New behavior
Starting in .NET 7, the same code as shown in the Previous behavior section throws an
InvalidOperationException. That's because the copy constructor now incorporates
MyContext metadata, which doesn't support Poco2 contracts.
Version introduced
.NET 7
Type of breaking change
This change can affect binary compatibility.
Reason for change
JsonSerializerContext was the only setting ignored by the copy constructor. This
behavior was surprising for some users.
Recommended action
If you depend on the .NET 6 behavior, you can manually unset the TypeInfoResolver
property to get back reflection-based contract resolution:
C#
var options = new JsonSerializerOptions(MyContext.Default.Options);
options.TypeInfoResolver = null; // Unset `MyContext.Default` as the
resolver for the options instance.
Affected APIs
System.Text.Json.JsonSerializerOptions.JsonSerializerOptions(JsonSerializerOptions)
Polymorphic serialization for object
types
Article • 11/08/2022
Using default configuration, System.Text.Json serializes values of type object using
polymorphism. This behavior becomes less consistent if you register a custom converter
for object . System.Text.Json has historically hardcoded polymorphism for root-level
object values but not for nested object values. Starting with .NET 7, this behavior has
changed so that custom converters never use polymorphism.
Previous behavior
Consider the following custom object converter:
C#
public class CustomObjectConverter : JsonConverter<object>
{
public override void Write(Utf8JsonWriter writer, object value,
JsonSerializerOptions options)
=> writer.WriteNumberValue(42);
public override object Read(ref Utf8JsonReader reader, Type
typeToConvert, JsonSerializerOptions options)
=> throw new NotImplementedException();
}
In previous versions, the following code serialized as 0. That's because the serializer used
polymorphism and ignored the custom converter.
C#
var options = new JsonSerializerOptions { Converters = { new
CustomObjectConverter() } };
JsonSerializer.Serialize<object>(0, options);
However, the following code serialized as 42 because the serializer honored the custom
converter.
C#
var options = new JsonSerializerOptions { Converters = { new
CustomObjectConverter() } };
JsonSerializer.Serialize<object[]>(new object[] { 0 }, options);
New behavior
Starting in .NET 7, using the custom object converter defined in the Previous behavior
section, the following code serializes as 42. That's because the serializer will always
consult the custom converter and not use polymorphism.
C#
var options = new JsonSerializerOptions { Converters = { new
CustomObjectConverter() } };
JsonSerializer.Serialize<object>(0, options);
Version introduced
.NET 7
Type of breaking change
This change can affect binary compatibility.
Reason for change
This change was made due to inconsistent serialization contracts for a type, depending
on whether it was being serialized as a root-level value or a nested value.
Recommended action
If desired, you can get back polymorphism for root-level values by invoking one of the
untyped serialization methods:
C#
var options = new JsonSerializerOptions { Converters = { new
CustomObjectConverter() } };
JsonSerializer.Serialize(0, inputType: typeof(int), options); // Serializes
as 0.
Affected APIs
System.Text.Json.JsonSerializer.Serialize<TValue>(TValue, JsonSerializerOptions)
System.Text.Json.JsonSerializer.Serialize<TValue>(Stream, TValue,
JsonSerializerOptions)
System.Text.Json.JsonSerializer.Serialize<TValue>(Utf8JsonWriter, TValue,
JsonSerializerOptions)
System.Text.Json.JsonSerializer.SerializeAsync<TValue>(Stream, TValue,
JsonSerializerOptions, CancellationToken)
System.Text.Json source generator
fallback
Article • 11/08/2022
When using one of the JsonSerializer methods that accepts JsonSerializerOptions, the
System.Text.Json source generator will no longer implicitly fall back to reflection-based
serialization for unrecognized types.
Previous behavior
Consider the following source generation example in .NET 6:
C#
JsonSerializer.Serialize(new Poco2(), typeof(Poco2), MyContext.Default);
[JsonSerializable(typeof(Poco1))]
public partial class MyContext : JsonSerializerContext {}
public class Poco1 { }
public class Poco2 { }
Since MyContext does not include Poco2 in its serializable types, the serialization will
correctly fail with the following exception:
Output
System.InvalidOperationException:
'Metadata for type 'Poco2' was not provided to the serializer. The
serializer method used does not
support reflection-based creation of serialization-related type metadata. If
using source generation,
ensure that all root types passed to the serializer have been indicated with
'JsonSerializableAttribute',
along with any types that might be serialized polymorphically.
Now consider the following call, which tries to serialize the same type ( MyContext ) using
the JsonSerializerOptions instance constructed by the source generator:
C#
JsonSerializer.Serialize(new Poco2(), MyContext.Default.Options);
The options instance silently incorporates the default reflection-based contract resolver
as a fallback mechanism, and as such, the type serializes successfully—using reflection.
The same fallback logic applies to JsonSerializerOptions.GetConverter(Type) for options
instances that are attached to a JsonSerializerContext. The following statement will
return a converter using the built-in reflection converter:
C#
JsonConverter converter =
MyContext.Default.Options.GetConverter(typeof(Poco2));
New behavior
Starting in .NET 7, the following call fails with the same exception
(InvalidOperationException) as when using the JsonSerializerContext overload:
C#
JsonSerializer.Serialize(new Poco2(), MyContext.Default.Options);
In addition, the following statement will fail with a NotSupportedException:
C#
JsonConverter converter =
MyContext.Default.Options.GetConverter(typeof(Poco2));
Version introduced
.NET 7
Type of breaking change
This change can affect binary compatibility.
Reason for change
The previous behavior violates the principle of least surprise and ultimately defeats the
purpose of source generation. With the release of a feature that allows you to customize
the JSON serialization contracts of your types
, you have the ability to fine tune the
sources of your contract metadata. With this in mind, silently introducing alternative
sources becomes even less desirable.
Recommended action
You might depend on the previous behavior, either intentionally or unintentionally. The
recommended course of action is to update your JsonSerializerContext definition so that
it includes all type dependencies:
C#
[JsonSerializable(typeof(Poco1))]
[JsonSerializable(typeof(Poco2))]
public partial class MyContext : JsonSerializerContext {}
This will let your application take full advantage of the benefits of source generation,
including trim safety.
In certain cases, however, making such a change might not be practical or possible. Even
though it's not recommended, there are a couple of ways you can re-enable reflection
fallback in your source-generated serializer.
Use a custom contract resolver
You can use the new contract customization feature to build a custom contract resolver
that falls back to reflection-based resolution where required:
C#
var options = new JsonSerializerOptions
{
TypeInfoResolver = JsonTypeInfoResolver.Combine(MyContext.Default, new
DefaultJsonTypeInfoResolver());
}
JsonSerializer.Serialize(new Poco2(), options); // Contract resolution falls
back to the default reflection-based resolver.
options.GetConverter(typeof(Poco2)); // Returns the reflection-based
converter.
Use an AppContext switch
Starting in .NET 7, you can re-enable reflection fallback globally using the provided
AppContext compatibility switch. Add the following entry to your application's project
file to re-enable reflection fallback for all source-generated contexts in your app. For
more information on using AppContext switches, see the article on .NET runtime
configuration settings.
XML
<ItemGroup>
<RuntimeHostConfigurationOption
Include="System.Text.Json.Serialization.EnableSourceGenReflectionFallback"
Value="true" />
</ItemGroup>
Affected APIs
System.Text.Json.JsonSerializerOptions.GetConverter(Type)
System.Text.Json.JsonSerializer.Serialize(Stream, Object, Type,
JsonSerializerOptions)
System.Text.Json.JsonSerializer.Serialize(Object, Type, JsonSerializerOptions)
System.Text.Json.JsonSerializer.Serialize(Utf8JsonWriter, Object, Type,
JsonSerializerOptions)
System.Text.Json.JsonSerializer.Serialize<TValue>(TValue, JsonSerializerOptions)
System.Text.Json.JsonSerializer.Serialize<TValue>(Stream, TValue,
JsonSerializerOptions)
System.Text.Json.JsonSerializer.Serialize<TValue>(Utf8JsonWriter, TValue,
JsonSerializerOptions)
System.Text.Json.JsonSerializer.SerializeAsync(Stream, Object, Type,
JsonSerializerOptions, CancellationToken)
System.Text.Json.JsonSerializer.SerializeAsync<TValue>(Stream, TValue,
JsonSerializerOptions, CancellationToken)
XmlSecureResolver is obsolete
Article • 11/08/2022
The method System.Xml.XmlSecureResolver.GetEntity(Uri, String, Type) unconditionally
throws an XmlException at run time. If your application utilizes XmlSecureResolver and
you attempt to resolve an XML resource through it, resolution will fail with an exception.
Additionally, the entire System.Xml.XmlSecureResolver type is obsolete. All references to
this type will result in a SYSLIB0047 warning at build time. If you've enabled warnings as
errors, this will cause a build break if your application references XmlSecureResolver.
C#
using System.Xml;
// Compiler warning SYSLIB0047: XmlSecureResolver type is obsolete.
XmlResolver resolver = new XmlSecureResolver(
resolver: new XmlUrlResolver(),
securityUrl: "https://www.example.com/");
// Call to XmlSecureResolver.GetEntity below throws XmlException at run
time.
object entity = resolver.GetEntity(
absoluteUri: new Uri("https://www.example.com/some-entity"),
role: null,
ofObjectToReturn: null);
Previous behavior
In .NET Framework, XmlSecureResolver.GetEntity(Uri, String, Type) constructs a Code
Access Security (CAS) sandbox to restrict the external XML resource resolution process.
If policy is violated, a SecurityException is thrown.
In .NET Core 3.1, and .NET 6, XmlSecureResolver.GetEntity(Uri, String, Type) doesn't
restrict external XML resource resolution at all. External resource resolution is allowed to
proceed with no limitations.
New behavior
Starting in .NET 7, XmlSecureResolver.GetEntity(Uri, String, Type) unconditionally throws
an XmlException. It does not construct a CAS sandbox and does not attempt to resolve
the external resource.
Version introduced
.NET 7
Type of breaking change
This change can affect source compatibility and binary compatibility.
Reason for change
This change improves the security of the .NET ecosystem. This obsoletion moves the
behavior of XmlSecureResolver from fail-dangerous (always perform resolution) to failsafe (never perform resolution) when running on .NET 7 or later.
Recommended action
Consider instead using the newly introduced static property
XmlResolver.ThrowingResolver. This property provides an XmlResolver instance that
forbids external resource resolution.
C#
using System.Xml;
// BAD: Do not use XmlSecureResolver.
// XmlResolver resolver = new XmlSecureResolver(
//
resolver: new XmlUrlResolver(),
//
securityUrl: "https://www.example.com/");
// GOOD: Use XmlResolver.ThrowingResolver instead.
XmlResolver resolver = XmlResolver.ThrowingResolver;
Affected APIs
System.Xml.XmlSecureResolver
System.Xml.XmlSecureResolver.GetEntity(Uri, String, Type)
Some APIs throw
ArgumentNullException (.NET 7)
Article • 05/13/2022
Some APIs now validate input parameters and throw an ArgumentNullException where
previously they threw a NullReferenceException, if invoked with null input arguments.
Previous behavior
In previous .NET versions, the affected APIs throw a NullReferenceException if invoked
with an argument that's null .
New behavior
Starting in .NET 7, the affected APIs throw an ArgumentNullException if invoked with an
argument that's null .
Change category
This change affects binary compatibility.
Reason for change
Throwing ArgumentNullException conforms to .NET Runtime behavior. It provides a
better debug experience by clearly communicating which argument caused the
exception.
Version introduced
.NET 7
Recommended action
Review and, if necessary, update your code to prevent passing null input
arguments to the affected APIs.
If your code handles NullReferenceException, replace or add an additional handler
for ArgumentNullException.
Affected APIs
The following table lists the affected APIs and specific parameters.
Method/property
Parameter
name
Change
version
ComboBox.ChildAccessibleObject(ComboBox, IntPtr)
owner
Preview 1
ControlPaint.CreateHBitmap16Bit(Bitmap, Color)
bitmap
Preview 1
ControlPaint.CreateHBitmapColorMask(Bitmap, IntPtr)
bitmap
Preview 1
DataGridViewEditingControlShowingEventArgs(Control,
control or
Preview 1
DataGridViewCellStyle)
cellStyle
ToolStripArrowRenderEventArgs(Graphics, ToolStripItem,
Rectangle, Color, ArrowDirection)
g
Preview 1
ToolStripContentPanelRenderEventArgs(Graphics,
ToolStripContentPanel)
g or
Preview 1
ToolStripItemRenderEventArgs(Graphics, ToolStripItem)
g or item
Preview 1
ToolStripPanelRenderEventArgs(Graphics, ToolStripPanel)
g or
Preview 1
contentPanel
toolStripPanel
ListView.CheckedIndexCollection(ListView)
owner
Preview 5
Windows Forms obsoletions and warnings
(.NET 7)
Article • 09/14/2022
Some Windows Forms APIs have been marked as obsolete, starting in .NET 7. Other APIs aren't
obsolete but will cause a compile-time warning if you reference them.
Previous behavior
In previous .NET versions, these APIs can be used without any build warning.
New behavior
In .NET 7 and later versions, use of these APIs produces a compile-time warning or error with a
custom diagnostic ID. The use of custom diagnostic IDs allows you to suppress the warnings
individually instead of blanket-suppressing all obsoletion warnings.
The following table lists the custom diagnostic IDs and their corresponding warning messages.
Diagnostic
Description
Severity
ID
WFDEV001
Version
introduced
Casting to/from IntPtr is unsafe. Use WParamInternal , LParamInternal , or
Warning
Preview 1
ResultInternal instead.
WFDEV002
System.Windows.Forms.DomainUpDown.DomainUpDownAccessibleObject
is no longer used to provide accessible support for DomainUpDown
controls. Use AccessibleObject instead.
Warning
RC 1
WFDEV003
System.Windows.Forms.DomainUpDown.DomainItemAccessibleObject is
no longer used to provide accessible support for DomainUpDown items.
Use AccessibleObject instead.
Warning
RC 1
Version introduced
.NET 7
Type of breaking change
These obsoletions and warnings can affect source compatibility.
Recommended action
Follow the specific guidance provided for the each diagnostic ID using the URL link
provided on the warning.
If necessary, you can suppress the warning using the custom WFDEVxxx diagnostic ID
value.
Affected APIs
WFDEV001
System.Windows.Forms.Message.WParam
System.Windows.Forms.Message.LParam
System.Windows.Forms.Message.Result
WFDEV002
System.Windows.Forms.DomainUpDown.DomainUpDownAccessibleObject
WFDEV003
System.Windows.Forms.DomainUpDown.DomainItemAccessibleObject
See also
Obsolete Windows Forms features in .NET 7+
Breaking changes in .NET 6
Article • 07/28/2023
If you're migrating an app to .NET 6, the breaking changes listed here might affect you.
Changes are grouped by technology area, such as ASP.NET Core or Windows Forms.
This article indicates whether each breaking change is binary compatible or source
compatible:
Binary compatible - Existing binaries will load and execute successfully without
recompilation, and the run-time behavior won't change.
Source compatible - Source code will compile successfully without changes when
targeting the new runtime or using the new SDK or component.
ASP.NET Core
Title
Binary
compatible
Source
compatible
ActionResult<T> sets StatusCode to 200
✔️
❌
AddDataAnnotationsValidation method made
obsolete
✔️
❌
Assemblies removed from
Microsoft.AspNetCore.App shared framework
❌
✔️
Blazor: Parameter name changed in
RequestImageFileAsync method
✔️
❌
Blazor: WebEventDescriptor.EventArgsType
property replaced
❌
❌
Blazor: Byte array interop
✔️
❌
Changed MessagePack library in
❌
✔️
✔️
❌
EndpointName metadata not set automatically
✔️
❌
Identity: Default Bootstrap version of UI changed
❌
❌
Kestrel: Log message attributes changed
✔️
❌
@microsoft/signalr-protocol-msgpack
ClientCertificate property doesn't trigger
renegotiation for HttpSys
Introduced
Preview 1
Preview 6
RC 2
Title
Binary
compatible
Source
compatible
Microsoft.AspNetCore.Http.Features split
❌
✔️
Middleware: HTTPS Redirection Middleware
throws exception on ambiguous HTTPS ports
✔️
❌
Middleware: New Use overload
✔️
❌
Preview 4
Minimal API renames in RC 1
❌
❌
RC 1
Minimal API renames in RC 2
❌
❌
RC 2
MVC doesn't buffer IAsyncEnumerable types when
✔️
❌
Preview 4
Nullable reference type annotations changed
✔️
❌
Obsoleted and removed APIs
✔️
❌
PreserveCompilationContext not configured by
default
❌
✔️
Razor: Compiler no longer produces a Views
assembly
✔️
❌
Preview 3
Razor: Logging ID changes
❌
✔️
RC1
Razor: RazorEngine APIs marked obsolete
✔️
❌
Preview 1
SignalR: Java Client updated to RxJava3
❌
✔️
Preview 4
TryParse and BindAsync methods are validated
❌
❌
RC 2
using System.Text.Json
Introduced
Preview 1
Containers
Title
Default console logger formatting in
container images
Binary
compatible
Source
compatible
✔️
❌
Introduced
Servicing
6.0.6
For information on other breaking changes for containers in .NET 6, see .NET 6
Container Release Notes
.
Core .NET libraries
Title
Binary
Source
compatible
compatible
API obsoletions with non-default diagnostic IDs
✔️
❌
Preview 1
Changes to nullable reference type annotations
✔️
❌
Preview 1-2
Conditional string evaluation in Debug methods
✔️
❌
RC 1
Environment.ProcessorCount behavior on Windows
✔️
❌
Preview 2
EventSource callback behavior
✔️
✔️
Servicing
File.Replace on Unix throws exceptions to match
Windows
✔️
❌
Preview 7
FileStream locks files with shared lock on Unix
❌
✔️
Preview 1
FileStream no longer synchronizes file offset with OS
❌
❌
Preview 4
FileStream.Position updates after ReadAsync or
❌
❌
Preview 4
New diagnostic IDs for obsoleted APIs
✔️
❌
Preview 5
New nullable annotation in
AssociatedMetadataTypeTypeDescriptionProvider
✔️
❌
RC 2
New System.Linq.Queryable method overloads
✔️
❌
Preview 3-4
Older framework versions dropped from package
❌
✔️
Preview 5
Parameter names changed
✔️
❌
Preview 1
Parameter names in Stream-derived types
✔️
❌
Preview 1
Partial and zero-byte reads in DeflateStream,
GZipStream, and CryptoStream
✔️
❌
Preview 6
Set timestamp on read-only file on Windows
❌
✔️
Servicing
Standard numeric format parsing precision
✔️
❌
Preview 2
Static abstract members in interfaces
❌
✔️
Preview 7
StringBuilder.Append overloads and evaluation
❌
✔️
RC 1
❌
✔️
Preview 4
WriteAsync completes
order
Strong-name APIs throw
PlatformNotSupportedException
Introduced
6.0.2
Title
Binary
compatible
Source
compatible
❌
❌
Preview 7
System.Security.SecurityContext is marked obsolete
✔️
❌
RC 1
Task.FromResult may return singleton
❌
✔️
Preview 1
Unhandled exceptions from a BackgroundService
✔️
❌
Preview 4
System.Drawing.Common only supported on
Windows
Introduced
Cryptography
Title
CreateEncryptor methods throw exception for
incorrect feedback size
Binary
compatible
Source
compatible
❌
✔️
Introduced
Preview 7
Deployment
Title
Binary compatible
Source compatible
✔️
✔️
x86 host path on 64-bit Windows
Introduced
Servicing release
Entity Framework Core
Breaking changes in EF Core 6
Extensions
Title
Binary
Source
Introduced
compatible
compatible
AddProvider checks for non-null provider
✔️
❌
RC 1
FileConfigurationProvider.Load throws
InvalidDataException
✔️
❌
RC 1
Repeated XML elements include index
❌
✔️
Resolving disposed ServiceProvider throws
✔️
❌
RC 1
Title
Binary
Source
compatible
compatible
Introduced
exception
Globalization
Title
Binary
compatible
Source
compatible
Culture creation and case mapping in
globalization-invariant mode
Introduced
Preview 7
Interop
Title
Binary compatible
Source compatible
❌
✔️
Static abstract members in interfaces
Introduced
Preview 7
JIT compiler
Title
Coerce call arguments according to
ECMA-335
Binary
compatible
Source
compatible
✔️
✔️
Introduced
Preview 1
Networking
Title
Binary
compatible
Source
compatible
Port removed from SPN for Kerberos and
Negotiate
❌
✔️
RC 1
WebRequest, WebClient, and ServicePoint
are obsolete
✔️
❌
Preview 1
SDK
Introduced
Title
Binary
compatible
Source
compatible
-p option for dotnet run is deprecated
✔️
❌
Preview 6
C# code in templates not supported by earlier
versions
✔️
✔️
Preview 7
EditorConfig files implicitly included
✔️
❌
Generate apphost for macOS
✔️
❌
Preview 6
Generate error for duplicate files in publish
output
❌
✔️
Preview 1
GetTargetFrameworkProperties and
GetNearestTargetFramework removed from
ProjectReference protocol
❌
✔️
Preview 1
Install location for x64 emulated on Arm64
✔️
❌
RC 2
MSBuild no longer supports calling GetType()
Introduced
RC 1
OutputType not automatically set to WinExe
✔️
❌
RC 1
Publish ReadyToRun with --no-restore requires
changes
✔️
❌
6.0.100
runtimeconfig.dev.json file not generated
❌
✔️
6.0.100
RuntimeIdentifier warning if self-contained is
✔️
❌
RC 1
Tool manifests in root folder
✔️
✔️
6.0.4xx,
6.0.3xx,
6.0.1xx
Version requirements for .NET 6 SDK
✔️
✔️
6.0.300
.version file includes build version
✔️
✔️
6.0.401
Write reference assemblies to
IntermediateOutputPath
❌
✔️
6.0.200
unspecified
Serialization
Title
Binary
Source
compatible
compatible
DataContractSerializer retains sign when
deserializing -0
❌
✔️
Servicing
6.0.11
Default serialization format for TimeSpan
❌
✔️
Servicing
6.0.2
IAsyncEnumerable serialization
✔️
❌
Preview 4
JSON source-generation API refactoring
❌
✔️
RC 2
JsonNumberHandlingAttribute on collection
properties
❌
✔️
RC 1
New JsonSerializer source generator
❌
✔️
Preview 6
overloads
Introduced
Windows Forms
Title
Binary
compatible
Source
compatible
C# templates use application bootstrap
✔️
❌
RC 1
Selected TableLayoutSettings properties throw
InvalidEnumArgumentException
❌
✔️
Preview 1
DataGridView-related APIs now throw
❌
✔️
Preview 4
ListViewGroupCollection methods throw new
InvalidOperationException
❌
✔️
RC 2
NotifyIcon.Text maximum text length increased
❌
✔️
Preview 1
ScaleControl called only when needed
✔️
❌
Servicing
6.0.101
Some APIs throw ArgumentNullException
❌
✔️
Preview 1-4
TreeNodeCollection.Item throws exception if
❌
✔️
Preview 1
InvalidOperationException
node is assigned elsewhere
XML and XSLT
Introduced
Title
XmlDocument.XmlResolver nullability
change
XNodeReader.GetAttribute behavior for
invalid index
See also
What's new in .NET 6
Binary
compatible
Source
compatible
Introduced
❌
✔️
RC 1
✔️
❌
Preview 2
ActionResult<T> sets StatusCode to 200
Article • 05/25/2022
When returning a T in an MVC/API controller action that declares the return type as
ActionResult<TValue>, the ObjectResult.StatusCode is always set to 200, except when
the T is a ProblemDetails.
This change can cause unexpected behavior in some scenarios where you set the status
code manually, since previously the ObjectResult.StatusCode was null . Also, an action
filter could be affected by this change if it expects a null value instead of 200.
Version introduced
ASP.NET Core 6.0
Previous behavior
Previously, a controller's action that returns T and sets Response.StatusCode manually
generated the specified response status code. For example, the following controller's
action will generate a 202 Accepted response.
C#
// Generates a 202 Accepted response
public ActionResult<Model> Get()
{
Response.StatusCode = StatusCodes.Status202Accepted;
return new Model();
}
New behavior
Now, the same controller's action that returns T and sets Response.StatusCode manually
always generates a 200 OK response.
C#
// Generates a 200 OK response
public ActionResult<Model> Get()
{
Response.StatusCode = StatusCodes.Status202Accepted;
return new Model();
}
Type of breaking change
This change can affect source compatibility.
Reason for change
Returning a status code of 200 OK is documented since ASP.NET Core 3.1. However, it
keeps StatusCode as null and eventually generates a 200 OK response only because it's
the default. Since the default internal behavior could change, we decided to avoid
relying on the default and to explicitly set StatusCode to the expected 200 OK .
Recommended action
If your code sets the status code manually and is broken by this change, you'll need to
change your controller action. For example, the following code snippet sets a status
code of 202 and is broken by this change.
C#
public ActionResult<Model> Get()
{
Response.StatusCode = StatusCodes.Status202Accepted;
return new Model();
}
To retain the desired behavior of a 202 status code, the following code snippets show
some options.
C#
public ActionResult<Model> Get()
{
return Accepted(new Model());
}
// or
public ActionResult<Model> Get()
{
return StatusCode(StatusCodes.Status202Accepted, new Model());
}
// or
public Model Get()
{
Response.StatusCode = StatusCodes.Status202Accepted;
return new Model();
}
Affected APIs
MVC/API controller actions
See also
ActionResult<T> type
AddDataAnnotationsValidation method
made obsolete
Article • 09/15/2021
The extension method
EditContextDataAnnotationsExtensions.AddDataAnnotationsValidation(EditContext) is
marked as obsolete starting in ASP.NET Core 6. Developers should use the new
extension method
EditContextDataAnnotationsExtensions.EnableDataAnnotationsValidation instead.
The only difference between these two APIs is their return value:
C#
EditContext AddDataAnnotationsValidation(this EditContext editContext) { ...
}
IDisposable EnableDataAnnotationsValidation(this EditContext editContext) {
... }
Version introduced
ASP.NET Core 6.0
Old behavior
The older API, AddDataAnnotationsValidation(EditContext), returns its EditContext (as a
kind of fluent API).
New behavior
The new API, EnableDataAnnotationsValidation , returns an IDisposable whose disposal
can be used to remove the data-annotations validation support from the EditContext .
Reason for change
There are cases where it's desirable to remove the data-annotations validation support
after adding it. This was not possible with the older API because there was no place to
store the internal event subscriptions. The new API returns an object that holds the state
necessary to remove data-annotations validation support on disposal.
Recommended action
Most applications don't need to be changed. The direct use of these extension methods
is a rare and advanced case. If your app uses the <DataAnnotationsValidator>
component instead of calling this method directly, it doesn't need to be changed.
However, if you do call editContext.AddDataAnnotationsValidation() , then replace that
call with editContext.EnableDataAnnotationsValidation() . Optionally, capture the new
returned IDisposable object and dispose it later if you want to undo the effects of the
call.
Affected APIs
Microsoft.AspNetCore.Components.Forms.EditContextDataAnnotationsExtensions.
AddDataAnnotationsValidation(EditContext)
Assemblies removed from
Microsoft.AspNetCore.App shared
framework
Article • 09/15/2021
The following two assemblies were removed from the ASP.NET Core targeting pack:
System.Security.Permissions
System.Windows.Extensions
In addition, the following assemblies were removed from the ASP.NET Core runtime
pack:
Microsoft.Win32.SystemEvents
System.Drawing.Common
System.Security.Permissions
System.Windows.Extensions
Version introduced
ASP.NET Core 6.0
Old behavior
Applications could use APIs provided by these libraries by referencing the
Microsoft.AspNetCore.App shared framework.
New behavior
If you use APIs from the affected assemblies without having a PackageReference in your
project file, you might see run-time errors. For example, an application that uses
reflection to access APIs from one of these assemblies without adding an explicit
reference to the package will have run-time errors. The PackageReference ensures that
the assemblies are present as part of the application output.
For discussion, see https://github.com/dotnet/aspnetcore/issues/31007 .
Reason for change
This change was introduced to reduce the size of the ASP.NET Core shared framework.
Recommended action
To continue using these APIs in your project, add a PackageReference. For example:
XML
<PackageReference Include="System.Security.Permissions" Version="6.0.0" />
Affected APIs
System.Security.Permissions
System.Media
System.Security.Cryptography.X509Certificates.X509Certificate2UI
System.Xaml.Permissions.XamlAccessLevel
Blazor: Parameter name changed in
RequestImageFileAsync method
Article • 12/02/2021
The RequestImageFileAsync method's maxWith parameter was renamed from maxWith to
maxWidth .
Version introduced
ASP.NET Core 6.0
Old behavior
The parameter name is spelled maxWith .
New behavior
The parameter name is spelled maxWidth .
Reason for change
The original parameter name was a typographical error.
Recommended action
If you're using named parameters in the RequestImageFile API, update the maxWith
parameter name to maxWidth . Otherwise, no change is necessary.
Affected APIs
BrowserFileExtensions.RequestImageFileAsync
Blazor:
WebEventDescriptor.EventArgsType
property replaced
Article • 09/15/2021
The WebEventDescriptor class is part of Blazor's internal protocol for communicating
events from JavaScript into .NET. This class isn't typically used by app code, but rather
by platform authors.
Starting in ASP.NET Core 6.0, the EventArgsType property on WebEventDescriptor is
being replaced by a new EventName property. This change is unlikely to affect any app
code, as it's a low-level platform implementation detail.
Version introduced
ASP.NET Core 6.0
Old behavior
In ASP.NET Core 5.0 and earlier, the property EventArgsType describes a nonstandard,
Blazor-specific category name for groups of DOM event types. For example, the click
and mousedown events were both mapped to an EventArgsType value of mouse . Similarly,
cut , copy , and paste events are mapped to an EventArgsType value of clipboard .
These category names are used to determine the .NET type to use for deserializing the
incoming event arguments data.
New behavior
Starting in ASP.NET Core 6.0, the new property EventName only specifies the original
event's name. For example, click , mousedown , cut , copy , or paste . There's no longer a
need to supply a Blazor-specific category name. For that reason, the old property
EventArgsType is removed.
Reason for change
In pull request dotnet/aspnetcore#29993
, support for custom event arguments
classes was introduced. As part of this support, the framework no longer relies on all
events fitting into a predefined set of categories. The framework now only needs to
know the original event name.
Recommended action
App code should be unaffected and doesn't need to change.
If building a custom Blazor rendering platform, you may need to update the mechanism
for dispatching events into the Renderer . Replace any hardcoded rules about event
categories with simpler logic that supplies the original, raw event name.
Affected APIs
WebEventDescriptor.EventArgsType
Blazor: Byte-array interop
Article • 03/25/2023
Blazor now supports optimized byte-array interop, which avoids encoding and decoding
byte-arrays into Base64 and facilitates a more efficient interop process. This applies to
both Blazor Server and Blazor WebAssembly.
Version introduced
ASP.NET Core 6.0
Receive byte array in JavaScript from .NET
Old behavior
TypeScript
function receivesByteArray(data) {
// Previously, data was a Base64-encoded string representing the byte
array.
}
New behavior
TypeScript
function receivesByteArray(data) {
// Data is a Uint8Array (no longer requires processing the Base64
encoding).
}
Reason for change
This change was made to create a more efficient interop mechanism for byte arrays.
Recommended action
Receive byte array in JavaScript from .NET
Consider this .NET interop, where you call into JavaScript passing a byte array:
C#
var bytes = new byte[] { 1, 5, 7 };
await _jsRuntime.InvokeVoidAsync("receivesByteArray", bytes);
In the preceding code example, you'd treat the incoming parameter in JavaScript as a
byte array instead of a Base64-encoded string.
Return byte array from JavaScript to .NET
If .NET expects a byte[] , JavaScript should provide a Uint8Array . It's still possible to
provide a Base64-encoded array using btoa , however that is less performant.
For example, if you have the following code, then you should provide a Uint8Array from
JavaScript that's not Base64-encoded:
C#
var bytes = await _jsRuntime.InvokeAsync<byte[]>
("someJSMethodReturningAByteArray");
ClientCertificate property no longer
triggers renegotiation for HttpSys
Article • 09/15/2021
The HttpContext.Connection.ClientCertificate property no longer triggers TLS
renegotiations for HttpSys.
Version introduced
ASP.NET Core 6.0
Old behavior
Setting HttpSysOptions.ClientCertificateMethod =
ClientCertificateMethod.AllowRenegotiation allowed renegotiation to be triggered by
both HttpContext.Connection.ClientCertificate and
HttpContext.Connection.GetClientCertificateAsync .
New behavior
Setting HttpSysOptions.ClientCertificateMethod =
ClientCertificateMethod.AllowRenegotiation allows renegotiation to be triggered only
by HttpContext.Connection.GetClientCertificateAsync .
HttpContext.Connection.ClientCertificate returns the current certificate if available,
but does not renegotiate with the client to request the certificate.
Reason for change
When implementing the same features for Kestrel, it became clear that applications
need to be able to check the state of the client certificate before triggering a
renegotiation. For issues like the request body conflicting with the renegotiation,
checking the state enables the following usage pattern to deal with the issue:
C#
if (connection.ClientCertificate == null)
{
await BufferRequestBodyAsync();
await connection.GetClientCertificateAsync();
}
Recommended action
Apps that use delayed client-certificate negotiation should call
GetClientCertificateAsync(CancellationToken) to trigger renegotiation.
Affected APIs
Microsoft.AspNetCore.Server.HttpSys.HttpSysOptions.ClientCertificateMethod
Microsoft.AspNetCore.Http.ConnectionInfo.ClientCertificate
Microsoft.AspNetCore.Http.ConnectionInfo.GetClientCertificateAsync(CancellationT
oken)
See also
dotnet/aspnetcore issue number 34124
EndpointName metadata not set
automatically
Article • 10/27/2021
Behavior that was introduced in .NET 6 RC 1 to automatically set IEndpointNameMetadata
for endpoints has been reverted. IEndpointNameMetadata is no longer set automatically
to avoid issues with duplicate endpoint names.
Version introduced
ASP.NET Core 6 RC 2
Previous behavior
In ASP.NET Core 6 RC 1, IEndpointNameMetadata was automatically set for endpoints that
referenced a method group. For example, the following code produced an endpoint for
/foo with EndpointName set to GetFoo .
C#
app.MapGet("/foo", GetFoo);
New behavior
Starting in ASP.NET Core 6 RC 2, IEndpointNameMetadata is not automatically set. The
following code does not generate any IEndpointNameMetadata .
C#
app.MapGet("/foo", GetFoo);
Type of breaking change
This change can affect source compatibility.
Reason for change
The behavior of automatically setting endpoint name metadata was not robust and
resulted in issues where the same name was set for different endpoints. For more
information, see dotnet/aspnetcore#36487
.
Recommended action
We recommend that you manually set IEndpointNameMetadata using the WithName
extension method to set the metadata.
C#
app.MapGet("/foo", GetFoo).WithName("GetFoo");
Affected APIs
N/A
Identity: Default Bootstrap version of UI
changed
Article • 02/18/2022
Starting in ASP.NET Core 6.0, Identity UI defaults to using version 5 of Bootstrap .
ASP.NET Core 3.0 to 5.0 used version 4 of Bootstrap.
Version introduced
ASP.NET Core 6.0
Behavior
AddDefaultIdentity<TUser>(IServiceCollection) calls the internal private method
TryResolveUIFramework
. TryResolveUIFramework reads the UIFramework from the
application assembly. The UIFramework version defaults to:
Bootstrap 5 for the .NET 6 SDK
Bootstrap 4 for the .NET Core 3.1 and .NET 5 SDK
Template-created ASP.NET Core 3.1 and 5.0 apps contain Bootstrap 4 in
wwwroot\lib\bootstrap. Template-created ASP.NET Core 6 apps use Bootstrap 5. When
an ASP.NET Core 3.1 or 5.0 app is migrated to .NET 6, the application detects
UIFramework version 5, while wwwroot\lib\bootstrap contains version 4. This version
mismatch renders the Identity templates incorrectly.
Reason for change
Bootstrap 5 was released during the ASP.NET Core 6.0 timeframe.
Recommended action
Apps that are impacted by this change use the default Identity UI and have added it in
Startup.ConfigureServices as shown in the following code:
C#
services.AddDefaultIdentity<IdentityUser>()
Take one of the following actions:
Add the MSBuild property IdentityUIFrameworkVersion in the project file and
specify Bootstrap 4:
XML
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<IdentityUIFrameworkVersion>Bootstrap4</IdentityUIFrameworkVersion>
</PropertyGroup>
The preceding markup sets the UIFramework version to Bootstrap 4, the same
Bootstrap version as used in ASP.NET Core 3.1 and 5.0.
Rename or delete the wwwroot\lib\bootstrap folder and replace it with the
wwwroot\lib\bootstrap folder from an ASP.NET Core 6 template-generated app.
The Identity templates work with this change but apps using Bootstrap may need
to refer to the Bootstrap 5 migration guide .
Affected APIs
AddDefaultIdentity<TUser>(IServiceCollection)
Kestrel: Log message attributes changed
Article • 09/15/2021
Kestrel log messages have associated IDs and names. These attributes uniquely identify
different kinds of log messages. Some of those IDs and names were incorrectly
duplicated. This duplication problem is fixed in ASP.NET Core 6.0.
Version introduced
ASP.NET Core 6.0
Old behavior
The following table shows the state of the affected log messages before ASP.NET Core
6.0.
Message description
Name
ID
HTTP/2 connection closed log messages
Http2ConnectionClosed
36
HTTP/2 frame sending log messages
Http2FrameReceived
37
New behavior
The following table shows the state of the affected log messages in ASP.NET Core 6.0.
Message description
Name
ID
HTTP/2 connection closed log messages
Http2ConnectionClosed
48
HTTP/2 frame sending log messages
Http2FrameSending
49
Reason for change
Log IDs and names should be unique so different message types can be identified.
Recommended action
If you have code or configuration that references the old IDs and names, update those
references to use the new IDs and names.
Changed MessagePack library in
@microsoft/signalr-protocol-msgpack
Article • 09/15/2021
The @microsoft/signalr-protocol-msgpack
npm package now references
@msgpack/msgpack instead of msgpack5 . Additionally, the available options that can
optionally be passed into the MessagePackHubProtocol have changed. The
MessagePackOptions.disableTimestampEncoding and MessagePackOptions.forceFloat64
properties were removed, and some new options were added.
For discussion, see https://github.com/dotnet/aspnetcore/issues/30471 .
Version introduced
ASP.NET Core 6.0
Old behavior
In previous versions, you must include three script references to use the MessagePack
Hub Protocol in the browser:
HTML
<script src="~/lib/signalr/signalr.js"></script>
<script src="~/lib/msgpack5/msgpack5.js"></script>
<script src="~/lib/signalr/signalr-protocol-msgpack.js"></script>
New behavior
Starting in ASP.NET Core 6, you only need two script references to use the MessagePack
Hub Protocol in the browser:
HTML
<script src="~/lib/signalr/signalr.js"></script>
<script src="~/lib/signalr/signalr-protocol-msgpack.js"></script>
Instead of the msgpack5 package, the @msgpack/msgpack package is downloaded to your
node_modules directory if you want to use it directly in your app.
Finally, MessagePackOptions has new, additional properties, and the
disableTimestampEncoding and forceFloat64 properties are removed.
Reason for change
This change was made to reduce asset size, make it simpler to consume the package,
and add more customizability.
Recommended action
If you were previously using msgpack5 in your app, you'll need to add a direct reference
to the library in your package.json file.
Affected APIs
The following APIs were removed:
MessagePackOptions.disableTimestampEncoding
MessagePackOptions.forceFloat64
Microsoft.AspNetCore.Http.Features
split
Article • 12/02/2021
Microsoft.AspNetCore.Http.Features has been split into the following two assemblies:
Microsoft.AspNetCore.Http.Features
Microsoft.Extensions.Features
For discussion, see GitHub issue dotnet/aspnetcore#32307
.
Version introduced
ASP.NET Core 6.0
Old behavior
Microsoft.AspNetCore.Http.Features 5.0 shipped both in the ASP.NET shared framework
and as a NuGet package. Microsoft.AspNetCore.Http.Features 5.0 targeted .NET 4.6.1,
.NET Standard 2.0, and .NET 5.
New behavior
Microsoft.AspNetCore.Http.Features 6.0 ships only in the ASP.NET shared framework,
not as a NuGet package. It targets .NET 6 only.
Microsoft.Extensions.Features 6.0 ships in both the ASP.NET shared framework and as a
NuGet package. It targets .NET 4.6.1, .NET Standard 2.0, and .NET 6.
The following types have been moved to the new Microsoft.Extensions.Features
assembly:
IFeatureCollection
FeatureCollection
FeatureReference<T>
FeatureReferences<TCache>
These types are still in the Microsoft.AspNetCore.Http.Features namespace, and type
forwards have been added for compatibility.
Reason for change
This change was introduced for two reasons:
Allows the core types to be shared more broadly across components.
Allows the remaining Http-specific components in
Microsoft.AspNetCore.Http.Features to take advantage of new runtime and
language features.
Recommended action
When upgrading to ASP.NET Core 6.0, remove any packages references for
Microsoft.AspNetCore.Http.Features. Add a package reference for
Microsoft.Extensions.Features only if required.
For class libraries that need to consume the types from
Microsoft.AspNetCore.Http.Features, add a FrameworkReference instead:
XML
<ItemGroup>
<FrameworkReference Include="Microsoft.AspNetCore.App" />
</ItemGroup>
For more information about adding the framework reference, see Use the ASP.NET Core
shared framework.
Libraries with out of date references may encounter a TypeLoadException or the
following error:
Error CS0433 The type 'IFeatureCollection' exists in both
'Microsoft.AspNetCore.Http.Features, Version=5.0.0.0, Culture=neutral,
PublicKeyToken=adb9793829ddae60' and 'Microsoft.Extensions.Features,
Version=6.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60'
To resolve the error, add a FrameworkReference to Microsoft.AspNetCore.App to any of
the affected projects.
For questions, see dotnet/aspnetcore#32307
.
Affected APIs
Microsoft.AspNetCore.Http.Features.IFeatureCollection
Microsoft.AspNetCore.Http.Features.FeatureCollection
Microsoft.AspNetCore.Http.Features.FeatureReference<T>
Microsoft.AspNetCore.Http.Features.FeatureReferences<TCache>
Middleware: HTTPS Redirection
Middleware throws exception on
ambiguous HTTPS ports
Article • 09/15/2021
In ASP.NET Core 6.0, the HTTPS Redirection Middleware throws an exception of type
InvalidOperationException when it finds multiple HTTPS ports in the server
configuration. The exception's message contains the text "Cannot determine the https
port from IServerAddressesFeature, multiple values were found. Set the desired port
explicitly on HttpsRedirectionOptions.HttpsPort."
For discussion, see GitHub issue dotnet/aspnetcore#29222
.
Version introduced
ASP.NET Core 6.0
Old behavior
When the HTTPS Redirection Middleware isn't explicitly configured with a port, it
searches IServerAddressesFeature during the first request to determine the HTTPS port
to which it should redirect.
If there are no HTTPS ports or multiple distinct ports, it's unclear which port should be
used. The middleware logs a warning and disables itself. HTTP requests are processed
normally.
New behavior
When the HTTPS Redirection Middleware isn't explicitly configured with a port, it
searches IServerAddressesFeature during the first request to determine the HTTPS port
to which it should redirect.
If there are no HTTPS ports, the middleware still logs a warning and disables itself. HTTP
requests are processed normally. This behavior supports:
Development scenarios without HTTPS.
Hosted scenarios in which TLS is terminated before reaching the server.
If there are multiple distinct ports, it's unclear which port should be used. The
middleware throws an exception and fails the HTTP request.
Reason for change
This change prevents potentially sensitive data from being served over unencrypted
HTTP connections when HTTPS is known to be available.
Recommended action
To enable HTTPS redirection when the server has multiple distinct HTTPS ports, you
must specify one port in the configuration. For more information, see Port configuration.
If you don't need the HTTPS Redirection Middleware in your app, remove
UseHttpsRedirection from Startup.cs.
If you need to select the correct HTTPS port dynamically, provide feedback in GitHub
issue dotnet/aspnetcore#21291
.
Affected APIs
HttpsPolicyBuilderExtensions.UseHttpsRedirection
Middleware: New Use overload
Article • 12/02/2021
A new overload of app.Use has been introduced. If you call app.Use but never call the
next middleware, you'll now get compiler error CS0121:
The call is ambiguous between the following methods or properties:
'UseExtensions.Use(IApplicationBuilder, Func<HttpContext, Func, Task>)' and
'UseExtensions.Use(IApplicationBuilder, Func<HttpContext, RequestDelegate, Task>)'
To resolve the error, use app.Run instead of app.Use .
For discussion, see GitHub issue dotnet/aspnetcore#32020
Version introduced
ASP.NET Core 6.0
Old behavior
C#
app.Use(async (context, next) =>
{
await next();
});
or
C#
app.Use(async (context, next) =>
{
await SomeAsyncWork();
// next not called...
});
New behavior
You can now pass context to the next delegate:
.
C#
app.Use(async (context, next) =>
{
await next(context);
});
Use app.Run when your middleware never calls next :
C#
app.Run(async (context) =>
{
await SomeAsyncWork();
// next never called
});
Reason for change
The previous Use method allocates two objects per request. The new overload avoids
these allocations with a small change to how you invoke the next middleware.
Recommended action
If you get a compile error, it means you are calling app.Use without using the next
delegate. Switch to app.Run to fix the error.
Affected APIs
None.
Minimal API renames in RC 1
Article • 06/07/2022
Some APIs were renamed to improve the consistency of type names and to remove
"minimal" and "action" from the API names.
Version introduced
ASP.NET Core 6.0 RC 1
Old and new behavior
The Microsoft.AspNetCore.Builder.MinimalActionEndpointConventionBuilder class
was renamed to
Microsoft.AspNetCore.Builder.DelegateEndpointConventionBuilder .
7 Note
This class was renamed again in RC 2 to
Microsoft.AspNetCore.Builder.RouteHandlerBuilder.
The Microsoft.AspNetCore.Builder.MinimalActionEndpointRouteBuilderExtensions
class was renamed to
Microsoft.AspNetCore.Builder.DelegateEndpointRouteBuilderExtensions .
7 Note
This class was merged with
Microsoft.AspNetCore.Builder.EndpointRouteBuilderExtensions in RC 2.
The RequestDelegate parameter to Map , MapGet , MapPost , MapPut , MapDelete ,
MapMethod , MapFallback , and RequestDelegateFactory.Create(Delegate,
RequestDelegateFactoryOptions) was renamed from action to handler .
Change category
This change affects binary compatibility and source compatibility.
Reason for change
This change was made to improve the consistency of type names and to remove
"minimal" and "action" from the API names.
Recommended action
Recompile any projects built with an earlier SDK. For most projects, this should be all
that's necessary.
If your code references any of these type or parameter names directly by name, updated
the code to reflect the new names.
Affected APIs
Microsoft.AspNetCore.Builder.MinimalActionEndpointConventionBuilder
Microsoft.AspNetCore.Builder.MinimalActionEndpointRouteBuilderExtensions
Microsoft.AspNetCore.Builder.MinimalActionEndpointRouteBuilderExtensions.Map(
)
Microsoft.AspNetCore.Builder.MinimalActionEndpointRouteBuilderExtensions.MapG
et()
Microsoft.AspNetCore.Builder.MinimalActionEndpointRouteBuilderExtensions.MapP
ost()
Microsoft.AspNetCore.Builder.MinimalActionEndpointRouteBuilderExtensions.MapP
ut()
Microsoft.AspNetCore.Builder.MinimalActionEndpointRouteBuilderExtensions.MapDe
lete()
Microsoft.AspNetCore.Builder.MinimalActionEndpointRouteBuilderExtensions.MapMe
thod()
Microsoft.AspNetCore.Builder.MinimalActionEndpointRouteBuilderExtensions.MapFa
llback()
Microsoft.AspNetCore.Http.RequestDelegateFactory.Create(Delegate action,
RequestDelegateFactoryOptions? options = null)
Minimal API renames in RC 2
Article • 10/27/2021
To improve the consistency of type names, two classes were renamed, and one class was
removed and its methods merged into the existing EndpointRouteBuilderExtensions
class.
Version introduced
ASP.NET Core 6.0 RC 2
Old and new behavior
The Microsoft.AspNetCore.Builder.DelegateEndpointConventionBuilder class was
renamed to Microsoft.AspNetCore.Builder.RouteHandlerBuilder.
The
Microsoft.AspNetCore.Http.OpenApiDelegateEndpointConventionBuilderExtensions
class was renamed to
Microsoft.AspNetCore.Http.OpenApiRouteHandlerBuilderExtensions.
The Microsoft.AspNetCore.Builder.DelegateEndpointRouteBuilderExtensions class
was removed and all of its methods were merged into the existing
Microsoft.AspNetCore.Builder.EndpointRouteBuilderExtensions class.
Change category
This change affects binary compatibility and source compatibility.
Reason for change
This change was made to improve the consistency of type names. Now that there is a
new RouteHandlerOptions class, we wanted to replace DelegateEndpoint with
RouteHandler .
Recommended action
Recompile any projects built with an earlier SDK. For most projects, this should be all
that's necessary.
If your code references any of these type names directly by name, updated the code to
reflect the new names.
Affected APIs
Microsoft.AspNetCore.Builder.DelegateEndpointConventionBuilder
Microsoft.AspNetCore.Http.OpenApiDelegateEndpointConventionBuilderExtensions
Microsoft.AspNetCore.Builder.DelegateEndpointRouteBuilderExtensions
MVC doesn't buffer IAsyncEnumerable
types when using System.Text.Json
Article • 12/02/2021
In ASP.NET Core 5, MVC added support for output formatting IAsyncEnumerable<T>
types by buffering the sequence in memory and formatting the buffered collection. In
ASP.NET Core 6, when formatting using System.Text.Json, MVC no longer buffers
IAsyncEnumerable<T> instances. Instead, MVC relies on the support that
System.Text.Json added for these types.
In most cases, the absence of buffering would not be observable by the application.
However, some scenarios may have inadvertently relied on the buffering semantics to
correctly serialize. For example, returning an IAsyncEnumerable<T> that's backed by an
Entity Framework query on a type with lazy-loaded properties can result in concurrent
query execution, which might not be supported by the provider.
This change does not affect output formatting using Newtonsoft.Json or with XMLbased formatters.
Version introduced
ASP.NET Core 6.0
Old behavior
IAsyncEnumerable<T> instances returned from an MVC action as a value to be
formatted using ObjectResult or a JsonResult are buffered before being serialized as a
synchronous collection.
New behavior
When formatting using System.Text.Json, MVC no longer buffers IAsyncEnumerable<T>
instances.
Reason for change
System.Text.Json added support for streaming IAsyncEnumerable<T> types. This allows
for a smaller memory footprint during serialization.
Recommended action
If your application requires buffering, consider manually buffering the
IAsyncEnumerable<T> object:
C#
// Before
public IActionResult Get()
{
return Ok(dbContext.Blogs);
}
// After
public async Task<IActionResult> Get()
{
return Ok(await dbContext.Blogs.ToListAsync());
}
Nullable reference type annotations
changed
Article • 09/15/2021
This issue represents a work-in-progress. All breaking changes to nullability
annotations will be aggregated into this issue throughout the course of ASP.NET Core
6.0.
Starting in ASP.NET Core 5.0, nullability annotations have been applied to parts of the
code. From the outset of this effort, mistakes were expected
in these annotations and
fixes would need to be made. In ASP.NET Core 6.0, some previously applied annotations
are being updated. Some of these changes are considered source breaking changes. The
changes lead to the APIs being incompatible or more restrictive. The updated APIs may
result in build-time warnings when used in projects that have nullable reference types
enabled.
For discussion, see GitHub issue dotnet/aspnetcore#27564
.
Version introduced
ASP.NET Core 6.0
Old behavior
The affected APIs have incorrect nullable reference type annotations. Build warnings are
either absent or incorrect.
New behavior
New build warnings are produced. Incorrect build warnings are no longer produced for
the affected APIs.
Reason for change
Through feedback and further testing, the nullable annotations for the affected APIs
were determined to be inaccurate. The updated annotations now correctly represent the
nullability contracts for the APIs.
Recommended action
Update code calling these APIs to reflect the revised nullability contracts.
Affected APIs
ParameterView.FromDictionary
Renderer.DispatchEventAsync
RenderTreeEdit.RemovedAttributeName
AuthenticationSchemeOptions.ForwardDefaultSelector
RangeConditionHeaderValue.RangeConditionHeaderValue
IConnectionListener.AcceptAsync
IApplicationDiscriminator.Discriminator
DataProtectionOptions.ApplicationDiscriminator
AuthenticatedEncryptorFactory.CreateEncryptorInstance
CngCbcAuthenticatedEncryptorFactory.CreateEncryptorInstance
CngGcmAuthenticatedEncryptorFactory.CreateEncryptorInstance
IAuthenticatedEncryptorFactory.CreateEncryptorInstance
ManagedAuthenticatedEncryptorFactory.CreateEncryptorInstance
IKey.CreateEncryptor
KeyManagementOptions.AuthenticatedEncryptorConfiguration
KeyManagementOptions.XmlEncryptor
KeyManagementOptions.XmlRepository
ICertificateResolver.ResolveCertificate
DataProtectionUtilityExtensions.GetApplicationUniqueIdentifier
FileSystemXmlRepository.DefaultKeyStorageDirectory
RegistryXmlRepository.DefaultRegistryKey
CertificateResolver.ResolveCertificate
Endpoint.Endpoint
Endpoint.RequestDelegate
RouteValueDictionary.TryAdd
LinkGenerator.GetUriByAddress
IFeatureCollection.Set
FeatureCollection.Set
IFeatureCollection.Get
ITicketStore.RetrieveAsync
IFeatureCollection.Get<TFeature>()
IFeatureCollection.Set<TFeature>(TFeature)
FeatureCollection.Set<TFeature>(TFeature)
ModelStateDictionary.SetModelValue(String, Object, String)
ModelStateDictionary.Item[String]
ClientValidatorItem.ClientValidatorItem
ClientValidatorItem.Validator
Endpoint.Endpoint
RouteValueDictionary.TryAdd(String, Object)>
LinkGenerator.GetUriByAddress<TAddress>(TAddress, RouteValueDictionary,
String, HostString, PathString, FragmentString, LinkOptions)
IApplicationDiscriminator.Discriminator
DataProtectionOptions.ApplicationDiscriminator
AuthenticatedEncryptorFactory.CreateEncryptorInstance(IKey)
CngCbcAuthenticatedEncryptorFactory.CreateEncryptorInstance(IKey)
CngGcmAuthenticatedEncryptorFactory.CreateEncryptorInstance(IKey)
IAuthenticatedEncryptorFactory.CreateEncryptorInstance(IKey)
ManagedAuthenticatedEncryptorFactory.CreateEncryptorInstance(IKey)
IKey.CreateEncryptor()
KeyManagementOptions.AuthenticatedEncryptorConfiguration
KeyManagementOptions.XmlEncryptor
KeyManagementOptions.XmlRepository
ICertificateResolver.ResolveCertificate(String)
DataProtectionUtilityExtensions.GetApplicationUniqueIdentifier(IServiceProvider)
FileSystemXmlRepository.DefaultKeyStorageDirectory
RegistryXmlRepository.DefaultRegistryKey
CertificateResolver.ResolveCertificate(String)
IConnectionListener.AcceptAsync(CancellationToken)
AuthenticationSchemeOptions.ForwardDefaultSelector
RangeConditionHeaderValue.RangeConditionHeaderValue(EntityTagHeaderValue)
IHttpContextFeature.HttpContext
CompletionMessage.WithError
CompletionMessage.WithResult
HubMethodInvocationMessage.Arguments
HubMethodInvocationMessage.HubMethodInvocationMessage(String, String,
Object[])
HubMethodInvocationMessage.HubMethodInvocationMessage(String, String,
Object[], String[])
InvocationMessage.InvocationMessage(String, Object[])
InvocationMessage.InvocationMessage(String, String, Object[])
InvocationMessage.InvocationMessage(String, String, Object[], String[])
StreamInvocationMessage.StreamInvocationMessage(String, String, Object[])
StreamInvocationMessage.StreamInvocationMessage(String, String, Object[],
String[])
IHubProtocol.TryParseMessage(ReadOnlySequence<Byte>, IInvocationBinder,
HubMessage)
DefaultHubLifetimeManager<THub>.SendAllAsync(String, Object[],
CancellationToken)
DefaultHubLifetimeManager<THub>.SendAllExceptAsync(String, Object[],
IReadOnlyList<String>, CancellationToken)
DefaultHubLifetimeManager<THub>.SendConnectionAsync(String, String, Object[],
CancellationToken)
DefaultHubLifetimeManager<THub>.SendConnectionsAsync(IReadOnlyList<String
>, String, Object[], CancellationToken)
DefaultHubLifetimeManager<THub>.SendGroupAsync(String, String, Object[],
CancellationToken)
DefaultHubLifetimeManager<THub>.SendGroupExceptAsync(String, String,
Object[], IReadOnlyList<String>, CancellationToken)
DefaultHubLifetimeManager<THub>.SendGroupsAsync(IReadOnlyList<String>,
String, Object[], CancellationToken)
DefaultHubLifetimeManager<THub>.SendUserAsync(String, String, Object[],
CancellationToken)
DefaultHubLifetimeManager<THub>.SendUsersAsync(IReadOnlyList<String>,
String, Object[], CancellationToken)
DefaultHubLifetimeManager<THub>.SendAllAsync(String, Object[],
CancellationToken)
DefaultHubLifetimeManager<THub>.SendAllExceptAsync(String, Object[],
IReadOnlyList<String>, CancellationToken)
DefaultHubLifetimeManager<THub>.SendConnectionAsync(String, String, Object[],
CancellationToken)
DefaultHubLifetimeManager<THub>.SendConnectionsAsync(IReadOnlyList<String
>, String, Object[], CancellationToken)
DefaultHubLifetimeManager<THub>.SendGroupAsync(String, String, Object[],
CancellationToken)
DefaultHubLifetimeManager<THub>.SendGroupExceptAsync(String, String,
Object[], IReadOnlyList<String>, CancellationToken)
DefaultHubLifetimeManager<THub>.SendGroupsAsync(IReadOnlyList<String>,
String, Object[], CancellationToken)
DefaultHubLifetimeManager<THub>.SendUserAsync(String, String, Object[],
CancellationToken)
DefaultHubLifetimeManager<THub>.SendUsersAsync(IReadOnlyList<String>,
String, Object[], CancellationToken)
IClientProxy.SendCoreAsync
HubConnectionContext.User
QueryHelpers.ParseNullableQuery(String)
QueryHelpers.ParseQuery(String)
See also
Nullable reference type annotation changes in core .NET libraries
Obsoleted and removed APIs
Article • 12/10/2022
Several APIs have been either removed or marked as obsolete.
Version introduced
ASP.NET Core 6.0
Old behavior
In ASP.NET Core 5.0 and previous versions, the APIs weren't removed or obsolete.
New behavior
The APIs are removed or obsoleted.
Reason for change
The APIs are either no longer used or don't function anymore.
Recommended action
Use the recommended replacement APIs.
Affected APIs
Removed
Microsoft.AspNetCore.Http.Connections.NegotiateProtocol.ParseResponse. Use
NegotiateProtocol.ParseResponse(ReadOnlySpan<Byte>) instead.
Removed Microsoft.AspNetCore.SignalR.HubInvocationContext. Use
HubInvocationContext.HubInvocationContext(HubCallerContext, IServiceProvider,
Hub, MethodInfo, IReadOnlyList<Object>) instead.
Removed Microsoft.AspNetCore.Http.Features.IHttpBufferingFeature. Use
Microsoft.AspNetCore.Http.Features.IHttpResponseBodyFeature instead.
Removed Microsoft.AspNetCore.Http.Features.IHttpSendFileFeature. Use
Microsoft.AspNetCore.Http.Features.IHttpResponseBodyFeature instead.
Removed argument-less constructor of
Microsoft.AspNetCore.StaticFiles.StaticFileResponseContext. Use
StaticFileResponseContext.StaticFileResponseContext(HttpContext, IFileInfo)
instead.
Removed the constructor
Microsoft.AspNetCore.Mvc.Infrastructure.ObjectResultExecutor. Use
ObjectResultExecutor.ObjectResultExecutor(OutputFormatterSelector,
IHttpResponseStreamWriterFactory, ILoggerFactory, IOptions<MvcOptions>)
instead.
Removed
Microsoft.AspNetCore.Mvc.ModelBinding.Validation.ValidationVisitor.AllowShortCir
cuitingValidationWhenNoValidatorsArePresent.
Removed Microsoft.AspNetCore.Mvc.ViewFeatures.ViewComponentResultExecutor.
Use
ViewComponentResultExecutor.ViewComponentResultExecutor(IOptions<MvcView
Options>, ILoggerFactory, HtmlEncoder, IModelMetadataProvider,
ITempDataDictionaryFactory, IHttpResponseStreamWriterFactory) instead.
Obsoleted CompatibilityVersion
PreserveCompilationContext not
configured by default
Article • 09/15/2021
PreserveCompilationContext is an MSBuild property that causes .NET Core projects to
emit additional content to the application's dependency (.deps) file about how the app
was compiled. This is primarily used to support run-time compilation scenarios.
Prior to .NET 6, PreserveCompilationContext was set to true for all apps that target the
Razor (Microsoft.NET.Sdk.Razor) and Web (Microsoft.NET.Sdk.Web) SDKs. Starting in
.NET 6, this property is no longer configured by default. However, packages such as
Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation configure this property as
required.
Version introduced
ASP.NET Core 6.0
Old behavior
The dependency file contains compilation context.
New behavior
The dependency file no longer contains compilation context.
Reason for change
This change improves build performance and startup time, and reduces the size of
ASP.NET Core's build output.
Recommended action
If your app requires this feature and does not reference a package that configures the
property, add the PreserveCompilationContext property to your project file.
XML
<PropertyGroup>
<PreserveCompilationContext>true</PreserveCompilationContext>
</PropertyGroup>
Affected APIs
None.
Razor: Compiler no longer produces a
Views assembly
Article • 09/08/2022
The Razor compiler no longer produces a separate Views.dll file that contains the
CSHTML views defined in an application.
Version introduced
ASP.NET Core 6.0
Old behavior
In previous versions, the Razor compiler utilizes a two-step compilation process that
produces two files:
A main AppName.dll assembly that contains application types.
An AppName.Views.dll assembly that contains the generated views that are defined
in the app. Generated view types are public and under the AspNetCore
namespace.
New behavior
Both views and application types are included in a single AppName.dll assembly. View
types have the accessibility modifiers internal and sealed and are included under the
AspNetCoreGeneratedDocument namespace.
Reason for change
Removing the two-step compilation process:
Improves build performance for applications that use Razor views.
Allows Razor views to participate in the "hot reload" experience for Visual Studio.
Recommended action
None.
Affected APIs
None.
Razor: Logging ID changes
Article • 09/15/2021
Razor Pages log messages have associated IDs and names. These are used to uniquely
identify different kinds of log messages. Some of those IDs were incorrectly duplicated.
This .NET 6 change corrects the duplication.
Version introduced
ASP.NET Core 6.0 RC1
Old and new behavior
Event name
Previous event ID
New event ID
ExecutedHandlerMethod
102
108
ExecutingImplicitHandlerMethod
103
107
ExecutedImplicitHandlerMethod
104
109
NotMostEffectiveFilter
1
4
Change category
This change affects binary compatibility.
Reason for change
Log IDs should be unique so different message types can be identified.
Recommended action
If you have code or configuration that references the old IDs, update those references to
use the new IDs.
Affected APIs
N/A.
Razor: RazorEngine APIs marked
obsolete
Article • 07/15/2022
Types related to the Microsoft.AspNetCore.Razor.Language.RazorEngine type in Blazor
have been marked as obsolete.
Version introduced
ASP.NET Core 6.0
Old behavior
The RazorEngine APIs weren't obsolete.
New behavior
The RazorEngine APIs are obsolete.
Reason for change
The RazorEngine type has been deprecated as compatibility can't be guaranteed.
Recommended action
Don't use RazorEngine APIs in your code.
Affected APIs
Microsoft.AspNetCore.Mvc.Razor.Extensions.InjectDirective.Register
Microsoft.AspNetCore.Mvc.Razor.Extensions.ModelDirective.Register
Microsoft.AspNetCore.Mvc.Razor.Extensions.PageDirective.Register
Microsoft.AspNetCore.Razor.Language.Extensions.FunctionsDirective.Register
Microsoft.AspNetCore.Razor.Language.Extensions.InheritsDirective.Register
Microsoft.AspNetCore.Razor.Language.Extensions.SectionDirective.Register
Microsoft.AspNetCore.Razor.Language.IRazorEngineBuilder
SignalR: Java Client updated to RxJava3
Article • 12/02/2021
The SignalR Java Client is now RxJava3.
Version introduced
ASP.NET Core 6.0
Old behavior
The RxJava package reference in the library was RxJava2.
New behavior
The RxJava package reference in the library is now RxJava3. For information about what
changed in RxJava, see What's different in 3.0 .
Reason for change
The previous dependency (RxJava2) is no longer maintained. Support for RxJava2 ended
in February 2021.
Recommended action
If you were using RxJava2 in your app or library, you might need to update to RxJava3.
For more information, see What's different in 3.0 .
Affected APIs
None.
TryParse and BindAsync methods are
validated
Article • 09/23/2021
ASP.NET Core now validates TryParse and BindAsync methods on parameter types for
Map* methods. If no valid method is found, ASP.NET Core looks for invalid methods and
throws an exception at startup if one is found. The exception helps to avoid unexpected
behavior by alerting you that your method signature may be incorrect.
Version introduced
ASP.NET Core 6.0 RC 2
Previous behavior
In previous versions of ASP.NET Core 6, if a TryParse or BindAsync method has an
invalid signature, no exception was thrown, and the framework tried to bind JSON from
the body.
C#
// Todo.TryParse is not in a valid format.
// Will try to bind from body as JSON instead.
app.MapPost("/endpoint", (Todo todo) => todo.Item);
public class Todo
{
public string Item { get; set; }
public static bool TryParse(string value) => true;
}
New behavior
If ASP.NET Core finds a public TryParse or BindAsync method that doesn't match the
expected syntax, an exception is thrown on startup. The previous example produces an
error similar to:
txt
TryParse method found on Todo with incorrect format. Must be a static method
with format
bool TryParse(string, IFormatProvider, out Todo)
bool TryParse(string, out Todo)
but found
Boolean TryParse(System.String)
Type of breaking change
This change can affect binary compatibility and source compatibility.
Reason for change
This change was made so that developers are made aware of BindAsync and TryParse
methods that have an invalid format. Previously, the framework would fall back to
assuming the parameter is JSON from the body. This assumption can result in
unexpected behavior.
Recommended action
If your type has a BindAsync or TryParse method with different syntax for a reason
other than parameter binding, you'll now encounter an exception at startup. To avoid
this behavior, there are multiple strategies available:
Change your BindAsync or TryParse method to be internal or private .
Add a new BindAsync or TryParse method that has the syntax the framework
looks for—invalid methods are ignored if a valid one is found.
Mark your parameter as [FromBody] .
Affected APIs
RequestDelegateFactory.Create()
All IEndpointRouteBuilder.Map*() methods, for example, app.MapGet() and
app.MapPost()
Default console logger formatting in
container images
Article • 06/24/2022
The default console formatter that's configured in aspnet containers has changed.
Previous behavior
In previous servicing releases of .NET 6, aspnet container images were configured with
the Logging__Console__FormatterName environment variable set to Json . This resulted in
console output formatted similarly to the following:
txt
{"EventId":0,"LogLevel":"Information","Category":"Microsoft.Hosting.Lifetime
","Message":"Now listening on: http://localhost:7000/","State":
{"Message":"Now listening on:
http://localhost:7000/","address":"http://localhost:7000/","
{OriginalFormat}":"Now listening on: {address}"}}
{"EventId":0,"LogLevel":"Information","Category":"Microsoft.Hosting.Lifetime
","Message":"Now listening on: http://localhost:7001/","State":
{"Message":"Now listening on:
http://localhost:7001/","address":"http://localhost:7001/","
{OriginalFormat}":"Now listening on: {address}"}}
{"EventId":0,"LogLevel":"Information","Category":"Microsoft.Hosting.Lifetime
","Message":"Now listening on: http://localhost:7002/","State":
{"Message":"Now listening on:
http://localhost:7002/","address":"http://localhost:7002/","
{OriginalFormat}":"Now listening on: {address}"}}
{"EventId":0,"LogLevel":"Information","Category":"Microsoft.Hosting.Lifetime
","Message":"Application started. Press Ctrl\u002BC to shut down.","State":
{"Message":"Application started. Press Ctrl\u002BC to shut down.","
{OriginalFormat}":"Application started. Press Ctrl\u002BC to shut down."}}
{"EventId":0,"LogLevel":"Information","Category":"Microsoft.Hosting.Lifetime
","Message":"Hosting environment: Development","State":{"Message":"Hosting
environment: Development","envName":"Development","
{OriginalFormat}":"Hosting environment: {envName}"}}
{"EventId":0,"LogLevel":"Information","Category":"Microsoft.Hosting.Lifetime
","Message":"Content root path: C:\\source\\temp\\web50","State":
{"Message":"Content root path:
C:\\source\\temp\\web50","contentRoot":"C:\\source\\temp\\web50","
{OriginalFormat}":"Content root path: {contentRoot}"}}
New behavior
Starting in .NET 6.0.5, aspnet container images have the
Logging__Console__FormatterName environment variable unset by default. This results in
simple, multiline, human-readable console output similar to the following:
txt
warn: Microsoft.AspNetCore.Server.HttpSys.MessagePump[37]
Overriding address(es) ''. Binding to endpoints added to UrlPrefixes
instead.
info: Microsoft.Hosting.Lifetime[0]
Now listening on: http://localhost:7000/
info: Microsoft.Hosting.Lifetime[0]
Now listening on: http://localhost:7001/
info: Microsoft.Hosting.Lifetime[0]
Now listening on: http://localhost:7002/
info: Microsoft.Hosting.Lifetime[0]
Application started. Press Ctrl+C to shut down.
info: Microsoft.Hosting.Lifetime[0]
Hosting environment: Development
info: Microsoft.Hosting.Lifetime[0]
Content root path: C:\source\temp\web50
Version introduced
.NET 6.0.5 (May 2022 servicing)
Type of breaking change
This change can affect source compatibility.
Reason for change
When the change to use JSON formatting was introduced in the .NET 6 GA release, it
broke many scenarios that relied on the original, simple formatting as described in
dotnet/dotnet-docker#2725
.
Recommended action
If you want to continue using the JSON formatting, you can configure your container to
use it by setting the Logging__Console__FormatterName environment variable value to
Json .
Affected APIs
None.
See also
dotnet/dotnet-docker#3706
API obsoletions with non-default
diagnostic IDs (.NET 6)
Article • 06/07/2022
Some APIs have been marked as obsolete, starting in .NET 6. This breaking change is
specific to APIs that have been marked as obsolete with a custom diagnostic ID.
Suppressing the default obsoletion diagnostic ID, which is CS0618 for the C# compiler,
does not suppress the warnings that the compiler generates when these APIs are used.
Change description
In previous .NET versions, these APIs can be used without any build warning. In .NET 6
and later versions, use of these APIs produces a compile-time warning or error with a
custom diagnostic ID. The use of custom diagnostic IDs allows you to suppress the
obsoletion warnings individually instead of blanket-suppressing all obsoletion warnings.
The following table lists the custom diagnostic IDs and their corresponding warning
messages for obsoleted APIs.
Diagnostic
ID
Description
Severity
SYSLIB0013
Uri.EscapeUriString(String) can corrupt the Uri string in some cases.
Consider using Uri.EscapeDataString(String) for query string
components instead.
Warning
SYSLIB0014
WebRequest, HttpWebRequest, ServicePoint, and WebClient are
Warning
obsolete. Use HttpClient instead.
SYSLIB0015
DisablePrivateReflectionAttribute has no effect in .NET 6+.
Warning
SYSLIB0016
Use the Graphics.GetContextInfo overloads that accept arguments for
better performance and fewer allocations.
Warning
SYSLIB0017
Strong-name signing is not supported and throws
PlatformNotSupportedException.
Warning
SYSLIB0018
Reflection-only loading is not supported and throws
PlatformNotSupportedException.
Warning
SYSLIB0019
The System.Runtime.InteropServices.RuntimeEnvironment members
SystemConfigurationFile, GetRuntimeInterfaceAsIntPtr(Guid, Guid), and
GetRuntimeInterfaceAsObject(Guid, Guid) are no longer supported and
Warning
throw PlatformNotSupportedException.
Diagnostic
ID
Description
Severity
SYSLIB0020
JsonSerializerOptions.IgnoreNullValues is obsolete. To ignore null values
when serializing, set DefaultIgnoreCondition to
Warning
JsonIgnoreCondition.WhenWritingNull.
SYSLIB0021
Derived cryptographic types are obsolete. Use the Create method on
Warning
the base type instead.
SYSLIB0022
The Rijndael and RijndaelManaged types are obsolete. Use Aes instead.
Warning
SYSLIB0023
RNGCryptoServiceProvider is obsolete. To generate a random number,
use one of the RandomNumberGenerator static methods instead.
Warning
SYSLIB0024
Creating and unloading AppDomains is not supported and throws an
Warning
exception.
SYSLIB0025
SuppressIldasmAttribute has no effect in .NET 6+.
Warning
SYSLIB0026
X509Certificate and X509Certificate2 are immutable. Use the
appropriate constructor to create a new certificate.
Warning
SYSLIB0027
PublicKey.Key is obsolete. Use the appropriate method to get the public
key, such as GetRSAPublicKey().
Warning
SYSLIB0028
X509Certificate2.PrivateKey is obsolete. Use the appropriate method to
get the private key, such as
Warning
RSACertificateExtensions.GetRSAPrivateKey(X509Certificate2), or use the
X509Certificate2.CopyWithPrivateKey(ECDiffieHellman) method to create
a new instance with a private key.
SYSLIB0029
ProduceLegacyHmacValues is obsolete. Producing legacy HMAC values is
Warning
no longer supported.
SYSLIB0030
HMACSHA1 always uses the algorithm implementation provided by the
Warning
platform. Use a constructor without the useManagedSha1 parameter.
SYSLIB0031
CryptoConfig.EncodeOID(String) is obsolete. Use the ASN.1 functionality
Warning
provided in System.Formats.Asn1.
SYSLIB0032
Recovery from corrupted process state exceptions is not supported;
Warning
HandleProcessCorruptedStateExceptionsAttribute is ignored.
SYSLIB0033
Rfc2898DeriveBytes.CryptDeriveKey(String, String, Int32, Byte[]) is
Warning
obsolete and is not supported. Use
PasswordDeriveBytes.CryptDeriveKey(String, String, Int32, Byte[]) instead.
SYSLIB0034
CmsSigner(CspParameters) is obsolete. Use an alternative constructor
instead.
Warning
Diagnostic
Description
Severity
SignerInfo.ComputeCounterSignature() is obsolete. Use the overload
Warning
ID
SYSLIB0035
that accepts a CmsSigner instead.
Version introduced
.NET 6
Recommended action
Follow the specific guidance provided for the each diagnostic ID using the URL link
provided on the warning.
Warnings or errors for these obsoletions can't be suppressed using the standard
diagnostic ID for obsolete types or members; use the custom SYSLIBxxxx
diagnostic ID value instead.
Affected APIs
SYSLIB0013
System.Uri.EscapeUriString(String)
SYSLIB0014
System.Net.WebRequest
System.Net.HttpWebRequest
System.Net.ServicePoint
System.Net.WebClient
SYSLIB0015
System.Runtime.CompilerServices.DisablePrivateReflectionAttribute
SYSLIB0016
System.Drawing.Graphics.GetContextInfo()
SYSLIB0017
System.Reflection.AssemblyName.KeyPair
System.Reflection.StrongNameKeyPair
SYSLIB0018
System.Reflection.Assembly.ReflectionOnlyLoad
System.Reflection.Assembly.ReflectionOnlyLoadFrom(String)
System.Type.ReflectionOnlyGetType(String, Boolean, Boolean)
SYSLIB0019
System.Runtime.InteropServices.RuntimeEnvironment.SystemConfigurationFile
System.Runtime.InteropServices.RuntimeEnvironment.GetRuntimeInterfaceAsIntPtr(
Guid, Guid)
System.Runtime.InteropServices.RuntimeEnvironment.GetRuntimeInterfaceAsObjec
t(Guid, Guid)
SYSLIB0020
System.Text.Json.JsonSerializerOptions.IgnoreNullValues
SYSLIB0021
System.Security.Cryptography.AesCryptoServiceProvider
System.Security.Cryptography.AesManaged
System.Security.Cryptography.DESCryptoServiceProvider
System.Security.Cryptography.MD5CryptoServiceProvider
System.Security.Cryptography.RC2CryptoServiceProvider
System.Security.Cryptography.SHA1CryptoServiceProvider
System.Security.Cryptography.SHA1Managed
System.Security.Cryptography.SHA256Managed
System.Security.Cryptography.SHA256CryptoServiceProvider
System.Security.Cryptography.SHA384Managed
System.Security.Cryptography.SHA384CryptoServiceProvider
System.Security.Cryptography.SHA512Managed
System.Security.Cryptography.SHA512CryptoServiceProvider
System.Security.Cryptography.TripleDESCryptoServiceProvider
SYSLIB0022
System.Security.Cryptography.Rijndael
System.Security.Cryptography.RijndaelManaged
SYSLIB0023
RNGCryptoServiceProvider
SYSLIB0024
System.AppDomain.CreateDomain(String)
System.AppDomain.Unload(AppDomain)
SYSLIB0025
System.Runtime.CompilerServices.SuppressIldasmAttribute
SYSLIB0026
X509Certificate()
X509Certificate.Import
X509Certificate2()
X509Certificate2.Import
SYSLIB0027
PublicKey.Key
SYSLIB0028
X509Certificate2.PrivateKey
SYSLIB0029
HMACSHA384.ProduceLegacyHmacValues
HMACSHA512.ProduceLegacyHmacValues
SYSLIB0030
HMACSHA1(Byte[], Boolean)
SYSLIB0031
CryptoConfig.EncodeOID(String)
SYSLIB0032
System.Runtime.ExceptionServices.HandleProcessCorruptedStateExceptionsAttribut
e
SYSLIB0033
Rfc2898DeriveBytes.CryptDeriveKey(String, String, Int32, Byte[])
SYSLIB0034
CmsSigner(CspParameters)
SYSLIB0035
ComputeCounterSignature()
See also
API obsoletions with non-default diagnostic IDs (.NET 7)
API obsoletions with non-default diagnostic IDs (.NET 5)
Obsolete features in .NET 5+
Conditional string evaluation in Debug
methods
Article • 10/27/2021
It's common to use interpolated strings as assert messages, for example:
C#
Debug.Assert(result != x, $"Unexpected result {result}");
However, in previous versions, this causes a string to be created for the message,
including formatting result, on every call, even if the condition is true . And the typical
use for asserts is that they're about a condition that should always be true.
C# 10 adds support for better string interpolation , including the ability to target
custom "handlers" in addition to strings. In .NET 6, the Debug class has new overloads of
Assert, WriteIf, and WriteLineIf that utilize this functionality to conditionally evaluate the
interpolated string formatting items only if the message is required. The C# compiler will
prefer these new overloads. If the formatting items were mutating state and the
program was relying on those mutations to be visible even if the assert didn't fire, you
could observe a difference in behavior.
Previous behavior
In the following code, r.ToString() would always be invoked.
C#
Debug.Assert(true, $"{r.ToString()}");
New behavior
In the following code, r.ToString() will never be invoked, because the message is only
needed if the condition is false .
C#
Debug.Assert(true, $"{r.ToString()}");
Version introduced
6.0 RC 1
Type of breaking change
This change can affect source compatibility.
Reason for change
This change was introduced to improve performance.
Recommended action
Interpolated strings used with Debug methods should not mutate shared state. (These
methods are also conditional on the DEBUG compilation constant.) If, for some reason,
it's critical to maintain the old behavior, add a (string) cast before the interpolated
string. This cast forces the compiler to bind to the existing overload and ensures that the
string is always materialized.
Affected APIs
System.Diagnostics.Debug.Assert(Boolean, String)
System.Diagnostics.Debug.Assert(Boolean, String, String)
System.Diagnostics.Debug.Assert(Boolean, String, String, Object[])
System.Diagnostics.Debug.WriteIf(Boolean, String)
System.Diagnostics.Debug.WriteIf(Boolean, String, String)
System.Diagnostics.Debug.WriteIf(Boolean, Object, String)
System.Diagnostics.Debug.WriteLineIf(Boolean, String)
System.Diagnostics.Debug.WriteLineIf(Boolean, String, String)
System.Diagnostics.Debug.WriteLineIf(Boolean, Object, String)
Environment.ProcessorCount behavior
on Windows
Article • 09/15/2021
On Windows, the Environment.ProcessorCount property now respects process affinity
and the job object's hard limit on CPU utilization.
Change description
In previous .NET versions, the Environment.ProcessorCount property on Windows
returns the number of logical processors on the machine. The property ignores process
affinity and the job object's hard limit on CPU utilization. That Windows behavior is
inconsistent with the behavior on Unix-based operating systems, where those limits are
respected.
Starting with .NET 6, the behavior of Environment.ProcessorCount on Windows is
consistent with the behavior on Unix-based operating system. In general,
Environment.ProcessorCount returns the minimum of:
The number of logical processors on the machine.
If the process is running with CPU affinity, the number of processors that the
process is affinitized to.
If the process is running with a CPU utilization limit, the CPU utilization limit
rounded up to the next whole number.
The following table shows how the value of Environment.ProcessorCount changes from
.NET 5 to .NET 6 on a machine with eight logical processors:
Environment
.NET 5
.NET 6
Process affinitized to two logical processors (Windows)
8
2
Process affinitized to two logical processors (Unix)
2
2
CPU utilization limited to the equivalent of two logical processors (Windows)
8
2
CPU utilization limited to the equivalent of two logical processors (Unix)
2
2
Version introduced
6.0
Reason for change
This property is frequently used to determine the parallelism factor for a process. We've
observed that not limiting the property's value based on affinitization and CPU
utilization limit can lead to worse performance.
Recommended action
Review code that uses Environment.ProcessorCount to scale down the parallelism factor
based on application or system configuration. Even if the code takes the process's
affinity mask or the job object's CPU utilization limit into account, it may end up using
lower parallelism than intended.
Review code that expects Environment.ProcessorCount to return the total number of
logical processors on the machine, for example, to display it to a user. Instead, you can
use a PInvoke call to the GetSystemInfo or GetNativeSystemInfo Win32 APIs.
If code performs worse as a result of this change, you can use the
DOTNET_PROCESSOR_COUNT environment variable to override the number of processors
thought to be available by the .NET runtime and reported by the
Environment.ProcessorCount property. For example, if you set DOTNET_PROCESSOR_COUNT
to 4, Environment.ProcessorCount will disregard any process affinity and CPU utilization
limit and return 4. To mimic .NET 5 behavior, set the environment variable to
%NUMBER_OF_PROCESSORS% .
Affected APIs
System.Environment.ProcessorCount
EventSource callback behavior
Article • 06/16/2023
For an EventCommand.Disable, the EventSource is now marked as disabled before the
callback is issued.
Previous behavior
Previously, the EventSource.OnEventCommand(EventCommandEventArgs) callback was
issued for an EventCommand.Disable prior to setting m_eventSourceEnabled=false .
This meant that EventSource.IsEnabled() returned true in the
OnEventCommand(EventCommandEventArgs) callback for a user EventSource, even if
the command led to the EventSource being disabled. The callback happened after the
ability to dispatch events was turned off though, so even if an EventSource tried to fire
an event, it wasn't written.
New behavior
Now, the EventSource is marked as disabled before the callback is issued for an
EventCommand.Disable.
Version introduced
.NET 6 servicing
.NET 7 servicing
Type of breaking change
This change is a behavioral change.
Reason for change
This change was necessary to support multiple EventCounter instances. The ability to
have multiple instances has been requested by multiple customers.
In addition, EventCommand.Enable has always issued a consistent view:
EventSource.IsEnabled() accurately reports the enabled status, and EventSource can
write events from the OnEventCommand callback. This change makes the
EventCommand.Disable behavior consistent with EventCommand.Enable .
Recommended action
It's unlikely that there's a scenario where the previous behavior is desired, and there's no
way to revert the behavior.
Affected APIs
System.Diagnostics.Tracing.EventCommand.Disable
File.Replace on Unix throws exceptions
to match Windows implementation
Article • 05/04/2022
The behavior of File.Replace on Unix-based operating systems has changed. The
exceptions it throws now match those that are thrown by the Windows implementation.
Previous behavior
On Unix, with .NET 5, the File.Replace method:
Throws IOException with the message Is a directory when sourceFileName is a
file and destinationFileName is a directory.
Throws IOException with the message Not a directory when sourceFileName is a
directory and destinationFileName is a file.
Silently succeeds when both sourceFileName and destinationFileName point to the
same file or directory.
New behavior
On Unix, with .NET 6, the File.Replace method:
Throws UnauthorizedAccessException with the message The specified path
<path> is not a path , when either sourceFileName or destinationFileName exists
and is not a file, or when both sourceFileName and destinationFileName point to
the same existing directory.
Throws IOException with the message The source <sourceFileName> and
destination <destinationFileName> are the same file when sourceFileName and
destinationFileName point to the same existing file.
Version introduced
.NET 6
Type of breaking change
This change can affect source compatibility.
Reason for change
This change was made to ensure that File.Replace throws the same exceptions for the
same reasons across platforms.
Recommended action
If you invoke File.Replace on Unix inside a try catch block, make sure to now also catch
UnauthorizedAccessException. Also, be aware of the new behaviors that are caught.
Affected APIs
System.IO.File.Replace
FileStream locks files with shared lock
on Unix
Article • 11/03/2021
On Unix, if you open a file using FileStream with FileAccess.Read permissions only, and
then call FileStream.Lock(Int64, Int64) to lock a region of the file, the operation will now
succeed. It succeeds because the runtime locks the file with a shared or read lock
instead of a write lock.
C#
using (FileStream fs = File.OpenRead("testfile")) // Opening with
FileAccess.Read only
{
fs.Lock((long) 3, (long) 1); // Attempting to lock a region of the readonly file
}
There's no change to the behavior on Windows, where the operation always succeeded.
Previous behavior
On Unix, if you opened a file using a FileStream with read permissions only, and then
called FileStream.Lock(Int64, Int64) to lock a region of the file, the runtime attempted to
lock the file with a write lock. That resulted in an UnauthorizedAccessException and the
message "Access to the path is denied".
New behavior
Starting in .NET 6, if you open a file using a FileStream with read permissions only on
Unix, and then call FileStream.Lock(Int64, Int64) to lock a region of the file, the runtime
locks the file with a read lock (also known as a shared lock).
Version introduced
.NET 6 RC 1
Type of breaking change
This change can affect binary compatibility.
Reason for change
FileStream.Lock(Int64, Int64) is the API that lets users lock a specific region of a file.
There's no API that lets you choose the underlying locking method, so
FileStream.Lock(Int64, Int64) should correctly determine the appropriate locking method
for the file permissions.
Recommended action
Prior to .NET 6, to be able to lock the file, you had to do one of the following:
Check if the code was being executed in Windows or the FileStream was opened
with FileAccess.Write permission.
Wrap the FileStream.Lock(Int64, Int64) call with a try catch to capture the
UnauthorizedAccessException.
If you used one of these workarounds, you can now remove them.
Affected APIs
System.IO.FileStream.Lock(Int64, Int64)
FileStream no longer synchronizes file
offset with OS
Article • 11/04/2022
To improve performance, FileStream no longer synchronizes the file offset with the
operating system.
Change description
In previous .NET versions, FileStream synchronizes the file offset with the Windows
operating system (OS) when it reads or writes to a file. It synchronizes the offset by
calling SetFilePointer, which is an expensive system call. Starting in .NET 6, FileStream no
longer synchronizes the file offset, and instead just keeps the offset in memory.
FileStream.Position always returns the current offset, but if you obtain the file handle
from FileStream.SafeFileHandle and query the OS for the current file offset using a
system call, the offset value will be 0.
The following code shows how the file offset differs between previous .NET versions and
.NET 6.
C#
[DllImport("kernel32.dll")]
private static extern bool SetFilePointerEx(SafeFileHandle hFile, long
liDistanceToMove, out long lpNewFilePointer, uint dwMoveMethod);
byte[] bytes = new byte[10_000];
string path = Path.Combine(Path.GetTempPath(), Path.GetTempFileName());
using (FileStream fs = new FileStream(path, FileMode.Create,
FileAccess.ReadWrite, FileShare.None, bufferSize: 4096, useAsync: true))
{
SafeFileHandle handle = fs.SafeFileHandle;
await fs.WriteAsync(bytes, 0, bytes.Length);
Console.WriteLine(fs.Position); // 10000 in all versions
if (SetFilePointerEx(handle, 0, out long currentOffset, 1 /* get current
offset */))
{
Console.WriteLine(currentOffset); // 10000 in .NET 5, 0 in .NET 6
}
}
Version introduced
.NET 6
Reason for change
This change was introduced to improve the performance of asynchronous reads and
writes and to address the following issues:
Win32 FileStream will issue a seek on every ReadAsync call
FileStream.Windows useAsync WriteAsync calls blocking APIs
With this change, ReadAsync operations are up to two times faster, and WriteAsync
operations are up to five times faster.
Recommended action
Modify any code that relied on the offset being synchronized.
To enable the .NET 5 behavior in .NET 6, specify an AppContext switch or an
environment variable. By setting the switch to true , you opt out of all performance
improvements made to FileStream in .NET 6.
JSON
{
"configProperties": {
"System.IO.UseNet5CompatFileStream": true
}
}
Windows Command Prompt
set DOTNET_SYSTEM_IO_USENET5COMPATFILESTREAM=1
7 Note
This switch is only available in .NET 6. It was removed in .NET 7.
Affected APIs
None.
See also
SetFilePointer function
SetFilePointerEx function
FileStream.Position updates after
ReadAsync or WriteAsync completes
Article • 11/04/2022
FileStream.Position is now updated after ReadAsync or WriteAsync completes.
Change description
In previous .NET versions on Windows, FileStream.Position is updated after the
asynchronous read or write operation starts. Starting in .NET 6, FileStream.Position is
updated after those operations complete.
The following code shows how the value of FileStream.Position differs between previous
.NET versions and .NET 6.
C#
byte[] bytes = new byte[10_000];
string path = Path.Combine(Path.GetTempPath(), Path.GetTempFileName());
using (FileStream fs = new FileStream(path, FileMode.Create,
FileAccess.ReadWrite, FileShare.None, bufferSize: 4096, useAsync: true))
{
Task[] writes = new Task[3];
writes[0] = fs.WriteAsync(bytes, 0, bytes.Length);
Console.WriteLine(fs.Position); // 10000 in .NET 5, 0 in .NET 6
writes[1] = fs.WriteAsync(bytes, 0, bytes.Length);
Console.WriteLine(fs.Position); // 20000 in .NET 5, 0 in .NET 6
writes[2] = fs.WriteAsync(bytes, 0, bytes.Length);
Console.WriteLine(fs.Position); // 30000 in .NET 5, 0 in .NET 6
await Task.WhenAll(writes);
Console.WriteLine(fs.Position);
}
Version introduced
.NET 6
Reason for change
// 30000 in all versions
FileStream has never been thread-safe, but until .NET 6, .NET has tried to support
multiple concurrent calls to its asynchronous methods (ReadAsync and WriteAsync) on
Windows.
This change was introduced to allow for 100% asynchronous file I/O with FileStream and
to fix the following issues:
FileStream.FlushAsync ends up doing synchronous writes
Win32 FileStream turns async reads into sync reads
Now, when buffering is enabled (that is, the bufferSize argument that's passed to the
FileStream constructor is greater than 1), every ReadAsync and WriteAsync operation is
serialized.
Recommended action
Modify any code that relied on the position being set before operations
completed.
To enable the .NET 5 behavior in .NET 6, specify an AppContext switch or an
environment variable. By setting the switch to true , you opt out of all performance
improvements made to FileStream in .NET 6.
JSON
{
"configProperties": {
"System.IO.UseNet5CompatFileStream": true
}
}
Windows Command Prompt
set DOTNET_SYSTEM_IO_USENET5COMPATFILESTREAM=1
7 Note
This switch is only available in .NET 6. It was removed in .NET 7.
Affected APIs
System.IO.FileStream.Position
New diagnostic IDs for obsoleted APIs
Article • 12/02/2021
Previously, a few APIs were obsoleted without using custom diagnostic IDs. Starting in
.NET 6, those APIs report as obsolete using different, custom diagnostic IDs. If you
suppressed warnings for usage of those APIs through CS0618, modify the suppressions
to use the new diagnostic IDs, which are SYSLIB0003, SYSLIB0019, and SYSLIB0020.
Change description
The following table shows the old and new diagnostic IDs for the listed obsolete API.
API
Previous
diagnostic ID
New
diagnostic ID
Thread.GetCompressedStack()
CS0618
SYSLIB0003
Thread.SetCompressedStack(CompressedStack)
CS0618
SYSLIB0003
RuntimeEnvironment.SystemConfigurationFile
CS0618
SYSLIB0019
RuntimeEnvironment.GetRuntimeInterfaceAsIntPtr(Guid,
Guid)
CS0618
SYSLIB0019
RuntimeEnvironment.GetRuntimeInterfaceAsObject(Guid,
Guid)
CS0618
SYSLIB0019
JsonSerializerOptions.IgnoreNullValues
CS0618
SYSLIB0020
Version introduced
.NET 6
Reason for change
Starting in .NET 5, obsoletions are intended to use custom diagnostic ID values to allow
fine-grained suppression of the warnings. This yields a better experience when the
obsolete APIs need to remain referenced. The obsoletions affected here should have
had custom diagnostic ID values applied when the APIs were originally marked as
[Obsolete] .
Recommended action
If the SYSLIB0003, SYSLIB0019, or SYSLIB0020 diagnostic IDs are produced from your
build, review the usage of the affected APIs. If possible, avoid using those APIs and refer
to the messages and documentation for alternatives. If you need to retain the references
to the obsolete APIs and suppress the diagnostics, suppress the warnings using the new
diagnostic IDs instead of CS0618.
Affected APIs
System.Threading.Thread.GetCompressedStack()
System.Threading.Thread.SetCompressedStack(CompressedStack)
System.Runtime.InteropServices.RuntimeEnvironment.SystemConfigurationFile
System.Runtime.InteropServices.RuntimeEnvironment.GetRuntimeInterfaceAsIntPtr(
Guid, Guid)
System.Runtime.InteropServices.RuntimeEnvironment.GetRuntimeInterfaceAsObjec
t(Guid, Guid)
System.Text.Json.JsonSerializerOptions.IgnoreNullValues
See also
SYSLIB0003: Code access security is not supported
SYSLIB0019: Some RuntimeEnvironment APIs are obsolete
SYSLIB0020: IgnoreNullValues is obsolete
CS0618
New nullable annotation in
AssociatedMetadataTypeTypeDescriptionProvider
Article • 09/23/2021
In .NET 6, a nullability annotation has been added to
AssociatedMetadataTypeTypeDescriptionProvider.GetTypeDescriptor(Type, Object). This method was
previously unannotated for nullable reference types.
Previous behavior
The affected method was treated as "oblivious" with regard to reference type nullability.
New behavior
The parameters of the affected method are now annotated with accurate nullability conditions.
Version introduced
6.0 RC 2
Type of breaking change
This change can affect source compatibility.
Reason for change
This method had dependencies on other APIs that were previously unannotated. The dependencies have since
been annotated, allowing this API to also be annotated. This change completes our nullable reference type
annotations for the shared framework libraries.
Recommended action
Update code that calls this method to reflect the revised nullability contract.
Affected APIs
API
What
changed
System.ComponentModel.DataAnnotations.AssociatedMetadataTypeTypeDescriptionProvider.GetTypeDescriptor(Type,
Object)
instance
parameter
type is
nullable
New System.Linq.Queryable method
overloads
Article • 12/02/2021
Additional public method overloads have been added to System.Linq.Queryable as part
of the new features implemented in https://github.com/dotnet/runtime/pull/47231 . If
your reflection code isn't sufficiently robust when looking up methods, these additions
can break your query provider implementations.
Change description
In .NET 6, new overloads were added to the methods listed in the Affected APIs section.
Reflection code, such as that shown in the following example, may break as a result of
these additions:
C#
typeof(System.Linq.Queryable)
.GetMethods(BindingFlags.Public | BindingFlags.Static)
.Where(m => m.Name == "ElementAt")
.Single();
This reflection code will now throw an InvalidOperationException with a message similar
to: Sequence contains more than one element.
Version introduced
.NET 6
Reason for change
The new overloads were added to extend the LINQ Queryable API.
Recommended action
If you're a query-provider library author, ensure that your reflection code is tolerant of
method overload additions. For example, use a Type.GetMethod overload that explicitly
accepts the method's parameter types.
Affected APIs
New overloads were added for the following Queryable extension methods:
System.Linq.Queryable.ElementAt
System.Linq.Queryable.ElementAtOrDefault
System.Linq.Queryable.Take
System.Linq.Queryable.Min
System.Linq.Queryable.Max
System.Linq.Queryable.FirstOrDefault
System.Linq.Queryable.LastOrDefault
System.Linq.Queryable.SingleOrDefault
System.Linq.Queryable.Zip
Changes to nullable reference type
annotations
Article • 12/02/2021
In .NET 6, some nullability annotations in the .NET libraries have changed.
Change description
In previous .NET versions, some nullable reference type annotations are incorrect, and
build warnings are either absent or incorrect. Starting in .NET 6, some annotations that
were previously applied have been updated. New build warnings will be produced and
incorrect build warnings will no longer be produced for the affected APIs.
Some of these changes are considered to be breaking because they can lead to new
build-time warnings. When you migrate to .NET 6, code that references these APIs will
need to be updated.
Other changes that aren't considered to be breaking are also documented on this page.
Any code that references the updated APIs may benefit from removing operators or
pragmas that are no longer necessary.
Version introduced
6.0
Type of breaking change
This change can affect source compatibility.
Reason for change
Starting in .NET Core 3.0, nullability annotations were applied to the .NET libraries. From
the outset of the effort, mistakes in these annotations were anticipated. Through
feedback and further testing, the nullable annotations for the affected APIs were
determined to be inaccurate. The updated annotations correctly represent the nullability
contracts for the APIs.
Recommended action
Update code that calls these APIs to reflect the revised nullability contracts.
Affected APIs
The following table lists the affected APIs:
API
What changed
Breaking or
nonbreaking
ISite.Container
Property type is
nullable
Breaking
XContainer.Add(Object[])
Parameter type is
Nonbreaking
nullable
XContainer.AddFirst(Object[])
Parameter type is
Nonbreaking
nullable
XContainer.ReplaceNodes(Object[])
Parameter type is
Nonbreaking
nullable
XDocument(Object[])
Parameter type is
Nonbreaking
nullable
XDocument(XDeclaration, Object[])
Parameter type is
nullable
Nonbreaking
XElement(XName, Object[])
Second parameter
type is nullable
Nonbreaking
XElement.ReplaceAll(Object[])
Parameter type is
nullable
Nonbreaking
XElement.ReplaceAttributes(Object[])
Parameter type is
nullable
Nonbreaking
XNode.AddAfterSelf(Object[])
Parameter type is
nullable
Nonbreaking
XNode.AddBeforeSelf(Object[])
Parameter type is
nullable
Nonbreaking
XNode.ReplaceWith(Object[])
Parameter type is
nullable
Nonbreaking
XStreamingElement(XName, Object)
Second parameter
type is nullable
Nonbreaking
API
What changed
Breaking or
nonbreaking
XStreamingElement(XName, Object[])
Second parameter
Nonbreaking
type is nullable
XStreamingElement.Add(Object[])
Parameter type is
Nonbreaking
nullable
HttpClient.PatchAsync
content parameter
Nonbreaking
type is nullable
HttpClient.PostAsync
content parameter
Nonbreaking
type is nullable
HttpClient.PutAsync
content parameter
Nonbreaking
type is nullable
MethodCallExpression.Update(Expression,
First parameter type is
IEnumerable<Expression>)
nullable
Expression<TDelegate>.Update(Expression,
Return type is not
IEnumerable<ParameterExpression>)
nullable
IDataRecord.GetBytes(Int32, Int64, Byte[], Int32, Int32)
buffer parameter type
Nonbreaking
Nonbreaking
Breaking
is nullable
IDataRecord.GetChars(Int32, Int64, Char[], Int32, Int32)
buffer parameter type
Breaking
is nullable
DbDataRecord.GetBytes(Int32, Int64, Byte[], Int32, Int32)
buffer parameter type
Breaking
is nullable
DbDataRecord.GetChars(Int32, Int64, Char[], Int32,
buffer parameter type
Int32)
is nullable
System.Net.HttpListenerContext.AcceptWebSocketAsync
subProtocol
Breaking
Nonbreaking
parameter type is
nullable
Methods that override Object.Equals(Object) and many
others that return bool
ImmutableArray<T>.Equals(Object)
[NotNullWhen(true)]
Breaking
added to first nullable
parameter
NotNullWhen(true) was
Breaking
added to the obj
parameter
BitVector32.Equals(Object)
NotNullWhen(true) was
added to the o
parameter
Breaking
API
What changed
Breaking or
nonbreaking
BitVector32.Section.Equals(Object)
NotNullWhen(true) was
Breaking
added to the o
parameter
BlobContentId.Equals(Object)
NotNullWhen(true) was
Breaking
added to the obj
parameter
BlobHandle.Equals(Object)
NotNullWhen(true) was
Breaking
added to the obj
parameter
CustomDebugInformationHandle.Equals(Object)
NotNullWhen(true) was
Breaking
added to the obj
parameter
DocumentNameBlobHandle.Equals(Object)
NotNullWhen(true) was
Breaking
added to the obj
parameter
EntityHandle.Equals(Object)
NotNullWhen(true) was
Breaking
added to the obj
parameter
GuidHandle.Equals(Object)
NotNullWhen(true) was
Breaking
added to the obj
parameter
Handle.Equals(Object)
NotNullWhen(true) was
Breaking
added to the obj
parameter
ImportScopeHandle.Equals(Object)
NotNullWhen(true) was
Breaking
added to the obj
parameter
LocalConstantHandle.Equals(Object)
NotNullWhen(true) was
Breaking
added to the obj
parameter
NamespaceDefinitionHandle.Equals(Object)
NotNullWhen(true) was
Breaking
added to the obj
parameter
SequencePoint.Equals(Object)
NotNullWhen(true) was
added to the obj
parameter
Breaking
API
What changed
Breaking or
nonbreaking
SignatureHeader.Equals(Object)
NotNullWhen(true) was
Breaking
added to the obj
parameter
EditAndContinueLogEntry.Equals(Object)
NotNullWhen(true) was
Breaking
added to the obj
parameter
LabelHandle.Equals(Object)
NotNullWhen(true) was
Breaking
added to the obj
parameter
Label.Equals(Object)
NotNullWhen(true) was
Breaking
added to the obj
parameter
OpCode.Equals(Object)
NotNullWhen(true) was
Breaking
added to the obj
parameter
DateOnly.Equals(System.Object)
NotNullWhen(true) was
Breaking
added to the value
parameter
TimeOnly.Equals(System.Object)
NotNullWhen(true) was
Breaking
added to the value
parameter
Pointer.Equals(Object)
NotNullWhen(true) was
added to the obj
parameter
See also
Attributes for null-state static analysis
Nullable reference type annotation changes in ASP.NET Core
Breaking
Older framework versions dropped from
package
Article • 09/16/2022
Starting with .NET 6, the core libraries packages can no longer be installed into projects
whose target framework is older than:
.NET Framework 4.6.1
.NET Core 3.1
.NET Standard 2.0
Change description
Previously, you were able to upgrade the packages to the latest version, even if you
were using them from a framework older than .NET Framework 4.6.1, .NET Core 3.1, or
.NET Standard 2.0. Starting in .NET 6, if you're referencing an impacted package from an
earlier framework, you can no longer update the referenced package to the latest
version.
Version introduced
.NET 6
Reason for change
Continuing to build for all frameworks increases the complexity and size of a package. In
the past, .NET solved this issue by building only for current frameworks and harvesting
binaries for older frameworks. Harvesting means that during build, the earlier version of
the package is downloaded and the binaries are extracted.
While consuming a harvested binary means that you can always update without
worrying that a framework is dropped, it also means that you don't get any bug fixes or
new features. In other words, harvested assets can't be serviced. That's hidden from you
because you're able to keep updating the package to a later version even though you're
consuming the same old binary that's no longer updated.
Starting with .NET 6, .NET no longer performs any form of harvesting to ensure that all
shipped assets can be serviced.
Recommended action
If your project is maintained but not evolving, simply don't upgrade the impacted
packages. This is generally not a huge take back because you're already consuming a
frozen binary.
If your project is evolving, upgrade it to a later framework version, such as:
.NET Framework 4.6.1
.NET Core 3.1
.NET Standard 2.0
Affected APIs
The following packages no longer ship old frameworks:
Microsoft.Extensions.DependencyModel
Microsoft.Win32.Registry.AccessControl
Microsoft.Win32.SystemEvents
System.Collections.Immutable
System.ComponentModel.Annotations
System.ComponentModel.Composition
System.ComponentModel.Composition.Registration
System.Composition.AttributedModel
System.Composition.Convention
System.Composition.Hosting
System.Composition.Runtime
System.Composition.TypedParts
System.Data.Odbc
System.Data.OleDb
System.Diagnostics.DiagnosticSource
System.Diagnostics.EventLog
System.Diagnostics.PerformanceCounter
System.DirectoryServices
System.DirectoryServices.AccountManagement
System.DirectoryServices.Protocols
System.Drawing.Common
System.IO.Packaging
System.IO.Pipelines
System.Management
System.Net.Http.WinHttpHandler
System.Net.WebSockets.WebSocketProtocol
System.Numerics.Tensors
System.Reflection.Context
System.Reflection.Metadata
System.Reflection.MetadataLoadContext
System.Runtime.Caching
System.Runtime.CompilerServices.Unsafe
System.Security.Cryptography.Cng
System.Security.Cryptography.OpenSsl
System.Security.Cryptography.Pkcs
System.Security.Cryptography.ProtectedData
System.Security.Permissions
System.ServiceProcess.ServiceController
System.Speech
System.Text.Encoding.CodePages
System.Text.Encodings.Web
System.Threading.AccessControl
System.Threading.Channels
System.Threading.Tasks.Dataflow
System.Windows.Extensions
The following packages will no longer be updated because their implementation is now
part of the .NET 6 platform:
Microsoft.Win32.Registry
System.ComponentModel.Annotations
System.IO.FileSystem.AccessControl
System.IO.Pipes.AccessControl
System.Security.AccessControl
System.Security.Cryptography.Cng
System.Security.Cryptography.OpenSsl
System.Security.Principal.Windows
Parameter names changed in .NET 6
Article • 12/02/2021
Some parameter names have changed to be consistent between reference and implementation assemblies. Most
of the changes are in the reference assemblies, but a handful are in the implementation assemblies.
Previous behavior
Some reference assembly parameter names were different to their corresponding parameters in the
implementation assembly. This can cause problems while using named arguments and reflection.
New behavior
In .NET 6, these mismatched parameter names were updated to be consistent across the reference and
implementation assemblies.
The following table shows the APIs and parameter names that changed.
API
Old parameter
name
New parameter name
Where
changed
Attribute.GetCustomAttributes(MemberInfo, Type)
type
attributeType
Reference and
implementation
assembly
Attribute.GetCustomAttributes(MemberInfo, Type, Boolean)
type
attributeType
Reference and
implementation
assembly
Strings.InStr(Int32, String, String, CompareMethod)
StartPos
Start
Reference
assembly
SortedList<TKey,TValue>.ICollection.CopyTo(Array, Int32)
arrayIndex
index
Reference
assembly
Vector.Narrow
source1 ,
low , high
Reference
assembly
source2
Vector.Widen
dest1 , dest2
low , high
Reference
assembly
StreamWriter.WriteLine(ReadOnlySpan<Char>)
value
buffer
Implementation
assembly
FileStream.BeginRead(Byte[], Int32, Int32, AsyncCallback, Object)
array ,
buffer , count
Implementation
assembly
buffer , count
Implementation
assembly
numBytes
FileStream.BeginWrite(Byte[], Int32, Int32, AsyncCallback, Object)
array ,
numBytes
MemoryStream.Read(Span<Byte>)
destination
buffer
Reference
assembly
MemoryStream.ReadAsync(Memory<Byte>, CancellationToken)
destination
buffer
Reference
assembly
MemoryStream.Write(ReadOnlySpan<Byte>)
source
buffer
Reference
assembly
API
Old parameter
name
New parameter name
Where
changed
MemoryStream.WriteAsync(ReadOnlyMemory<Byte>,
CancellationToken)
source
buffer
Reference
assembly
UnmanagedMemoryStream.Read(Span<Byte>)
destination
buffer
Reference
assembly
UnmanagedMemoryStream.Write(ReadOnlySpan<Byte>)
source
buffer
Reference
assembly
SignerInfo.AddUnsignedAttribute(AsnEncodedData)
asnEncodedData
unsignedAttribute
Reference
assembly
SignerInfo.RemoveUnsignedAttribute(AsnEncodedData)
asnEncodedData
unsignedAttribute
Reference
assembly
Rfc3161TimestampRequest.ProcessResponse(ReadOnlyMemory<Byte>,
source
responseBytes
Implementation
Int32)
assembly
Rfc3161TimestampToken.TryDecode(ReadOnlyMemory<Byte>,
Rfc3161TimestampToken, Int32)
source
encodedBytes
Implementation
assembly
Rfc3161TimestampTokenInfo.Rfc3161TimestampTokenInfo(Oid, Oid,
ReadOnlyMemory<Byte>, ReadOnlyMemory<Byte>, DateTimeOffset,
Nullable<Int64>, Boolean, Nullable<ReadOnlyMemory<Byte>>,
Nullable<ReadOnlyMemory<Byte>>, X509ExtensionCollection)
tsaName
timestampAuthorityName
Implementation
assembly
Rfc3161TimestampTokenInfo.TryDecode(ReadOnlyMemory<Byte>,
Rfc3161TimestampTokenInfo, Int32)
|
PrincipalPermission.Equals(Object)
o
obj
Reference
assembly
UrlMembershipCondition.Equals(Object)
o
obj
Reference
assembly
DBDataPermission(DBDataPermission)
dataPermission
permission
Implementation
assembly
DBDataPermission(DBDataPermissionAttribute)
attribute
permissionAttribute
Implementation
assembly
DBDataPermission(PermissionState, Boolean)
blankPassword
allowBlankPassword
Implementation
assembly
DBDataPermission.FromXml(SecurityElement)
elem
securityElement
Implementation
assembly
DBDataPermission.Union(IPermission)
other
target
Implementation
assembly
Reason for change
In cases where the reference assembly parameter names were changed, the new names were deemed more
appropriate or readable and minimally breaking.
In cases where the names of runtime parameters were changed to gain consistency across platforms or with
reference assemblies, the runtime implementation now matches the public API and documentation for the
method.
Version introduced
.NET 6
Recommended action
If you encounter a compiler error due to a parameter name change, update the parameter name accordingly.
If you use run-time reflection to inspect methods and took a dependency on the parameter names, update the
code to use the new parameter names.
Affected APIs
Microsoft.VisualBasic.Strings.InStr(Int32, String, String, CompareMethod)
System.Attribute.GetCustomAttributes(MemberInfo, Type)
System.Attribute.GetCustomAttributes(MemberInfo, Type, Boolean)
System.Collections.Generic.SortedList<TKey,TValue>.System.Collections.ICollection.CopyTo(Array, Int32)
System.IO.StreamWriter.WriteLine(ReadOnlySpan<Char>)
System.IO.FileStream.BeginRead(Byte[], Int32, Int32, AsyncCallback, Object)
System.IO.FileStream.BeginWrite(Byte[], Int32, Int32, AsyncCallback, Object)
System.IO.MemoryStream.Read(Span<Byte>)
System.IO.MemoryStream.ReadAsync(Memory<Byte>, CancellationToken)
System.IO.MemoryStream.Write(ReadOnlySpan<Byte>)
System.IO.MemoryStream.WriteAsync(ReadOnlyMemory<Byte>, CancellationToken)
System.IO.UnmanagedMemoryStream.Read(Span<Byte>)
System.IO.UnmanagedMemoryStream.Write(ReadOnlySpan<Byte>)
System.Numerics.Vector.Narrow
System.Numerics.Vector.Widen
System.Security.Cryptography.Pkcs.Rfc3161TimestampRequest.ProcessResponse(ReadOnlyMemory<Byte>,
Int32)
System.Security.Cryptography.Pkcs.Rfc3161TimestampToken.TryDecode(ReadOnlyMemory<Byte>,
Rfc3161TimestampToken, Int32)
System.Security.Cryptography.Pkcs.Rfc3161TimestampTokenInfo.Rfc3161TimestampTokenInfo(Oid, Oid,
ReadOnlyMemory<Byte>, ReadOnlyMemory<Byte>, DateTimeOffset, Nullable<Int64>, Boolean,
Nullable<ReadOnlyMemory<Byte>>, Nullable<ReadOnlyMemory<Byte>>, X509ExtensionCollection)
System.Security.Cryptography.Pkcs.Rfc3161TimestampTokenInfo.TryDecode(ReadOnlyMemory<Byte>,
Rfc3161TimestampTokenInfo, Int32)
System.Security.Cryptography.Pkcs.SignerInfo.AddUnsignedAttribute(AsnEncodedData)
System.Security.Cryptography.Pkcs.SignerInfo.RemoveUnsignedAttribute(AsnEncodedData)
System.Security.Permissions.PrincipalPermission.Equals(Object)
System.Security.Policy.UrlMembershipCondition.Equals(Object)
DBDataPermission(DBDataPermission)
DBDataPermission(DBDataPermissionAttribute)
DBDataPermission(PermissionState, Boolean)
System.Data.Common.DBDataPermission.FromXml(SecurityElement)
System.Data.Common.DBDataPermission.Union(IPermission)
See also
Some parameters in Stream-derived types are renamed
Some parameters in Stream-derived
types are renamed
Article • 10/04/2022
In .NET 6, some parameters of methods on types derived from System.IO.Stream have
been renamed to match the base class.
Change description
In previous .NET versions, several types derived from Stream override methods but use
different parameter names than those used by the base type. For example, the byte
array parameter of DeflateStream.Read(Byte[], Int32, Int32) is named array while the
corresponding argument in the base class method is named buffer .
In .NET 6, all types that derive from System.IO.Stream that had mismatched parameter
names have been brought into conformance with the base type by using the same
parameter names as the base type.
Version introduced
.NET 6
Reason for change
There are several reasons for the change:
If an invalid argument was passed and an exception was thrown, that exception
might have contained the base parameter's name or the derived parameter's
name, depending on the implementation. Since the caller may have been using a
reference typed as the base or as the derived type, it's impossible for the argument
name in the exception to always be correct.
Having different parameter names makes it harder to consistently validate
behavior across all Stream implementations.
.NET 6 adds a public method on Stream for validating arguments, and that method
needs to have a consistent parameter name to use.
Recommended action
The effect of this breaking change is minimal:
For existing binaries, its impact is limited to code that uses reflection to examine
the names of parameters on the affected derived types.
For source code, its impact is limited to code that uses named parameters to
invoke methods on the derived stream type using a variable typed as that derived
type.
In both cases, the recommended action is to consistently use the base parameter name.
Affected APIs
System.IO.BufferedStream.Read(Byte[], Int32, Int32)
System.IO.BufferedStream.Write(Byte[], Int32, Int32)
System.IO.Compression.DeflateStream.BeginWrite(Byte[], Int32, Int32,
AsyncCallback, Object)
System.IO.Compression.DeflateStream.Read(Byte[], Int32, Int32)
System.IO.Compression.DeflateStream.ReadAsync(Byte[], Int32, Int32,
CancellationToken)
System.IO.Compression.DeflateStream.Write(Byte[], Int32, Int32)
System.IO.Compression.DeflateStream.WriteAsync(Byte[], Int32, Int32,
CancellationToken)
System.IO.Compression.GZipStream.BeginRead(Byte[], Int32, Int32, AsyncCallback,
Object)
System.IO.Compression.GZipStream.BeginWrite(Byte[], Int32, Int32, AsyncCallback,
Object)
System.IO.Compression.GZipStream.Read(Byte[], Int32, Int32)
System.IO.Compression.GZipStream.ReadAsync(Byte[], Int32, Int32,
CancellationToken)
System.IO.Compression.GZipStream.Write(Byte[], Int32, Int32)
System.IO.Compression.GZipStream.WriteAsync(Byte[], Int32, Int32,
CancellationToken)
System.IO.FileStream.BeginRead(Byte[], Int32, Int32, AsyncCallback, Object)
System.IO.FileStream.BeginWrite(Byte[], Int32, Int32, AsyncCallback, Object)
System.IO.FileStream.Read(Byte[], Int32, Int32)
System.IO.FileStream.Write(Byte[], Int32, Int32)
System.Net.Sockets.NetworkStream.BeginRead(Byte[], Int32, Int32, AsyncCallback,
Object)
System.Net.Sockets.NetworkStream.BeginWrite(Byte[], Int32, Int32, AsyncCallback,
Object)
System.Net.Sockets.NetworkStream.Read(Byte[], Int32, Int32)
System.Net.Sockets.NetworkStream.ReadAsync(Byte[], Int32, Int32,
CancellationToken)
System.Net.Sockets.NetworkStream.Write(Byte[], Int32, Int32)
System.Net.Sockets.NetworkStream.WriteAsync(Byte[], Int32, Int32,
CancellationToken)
See also
Parameter names changed in .NET 6
Partial and zero-byte reads in
DeflateStream, GZipStream, and
CryptoStream
Article • 11/08/2021
DeflateStream, GZipStream, and CryptoStream diverged from typical Stream.Read and
Stream.ReadAsync behavior in two ways:
They didn't complete the read operation until either the buffer passed to the read
operation was completely filled or the end of the stream was reached.
As wrapper streams, they didn't delegate zero-length buffer functionality to the
stream they wrap.
This change addresses both of these issues.
Old behavior
When Stream.Read or Stream.ReadAsync was called on one of the affected stream types
with a buffer of length N , the operation would not complete until:
N bytes had been read from the stream, or
The underlying stream they wrap returned 0 from a call to its read, indicating no
more data is available.
Also, when Stream.Read or Stream.ReadAsync was called with a buffer of length 0, the
operation would succeed immediately, sometimes without doing a zero-length read on
the stream it wraps.
New behavior
Starting in .NET 6, when Stream.Read or Stream.ReadAsync is called on one of the
affected stream types with a buffer of length N , the operation completes when:
At least one byte has been read from the stream, or
The underlying stream they wrap returns 0 from a call to its read, indicating no
more data is available.
Also, when Stream.Read or Stream.ReadAsync is called with a buffer of length 0, the
operation succeeds once a call with a non-zero buffer would succeed.
Version introduced
6.0
Reason for change
The streams might not have returned from a read operation even if data had been
successfully read. This meant they couldn't readily be used in any bidirectional
communication situation where messages smaller than the buffer size were being used.
This could lead to deadlocks: the application is unable to read the data from the stream
that's necessary to continue the operation. It could also lead to arbitrary slowdowns,
with the consumer unable to process available data while waiting for additional data to
arrive.
Also, in highly scalable applications, it's common to use zero-byte reads as a way of
delaying buffer allocation until a buffer is needed. An application can issue a read with
an empty buffer, and when that read completes, data should soon be available to
consume. The application can then issue the read again, this time with a buffer to
receive the data. By delegating to the wrapped stream if no already decompressed or
transformed data is available, these streams now inherit any such behavior of the
streams they wrap.
Recommended action
In general, code should:
Not make any assumptions about a stream Read or ReadAsync operation reading
as much as was requested. The call returns the number of bytes read, which may
be less than what was requested. If an application depends on the buffer being
completely filled before progressing, it can perform the read in a loop to regain
the behavior.
C#
int totalRead = 0;
while (totalRead < buffer.Length)
{
int bytesRead = stream.Read(buffer.Slice(totalRead));
if (bytesRead == 0) break;
totalRead += bytesRead;
}
Expect that a stream Read or ReadAsync call may not complete until at least a byte
of data is available for consumption (or the stream reaches its end), regardless of
how many bytes were requested. If an application depends on a zero-byte read
completing immediately without waiting, it can check the buffer length itself and
skip the call entirely:
C#
int bytesRead = 0;
if (!buffer.IsEmpty)
{
bytesRead = stream.Read(buffer);
}
Affected APIs
System.IO.Compression.DeflateStream.Read
System.IO.Compression.DeflateStream.ReadAsync
System.IO.Compression.DeflateStream.BeginRead(Byte[], Int32, Int32,
AsyncCallback, Object)
System.IO.Compression.GZipStream.Read
System.IO.Compression.GZipStream.ReadAsync
System.IO.Compression.GZipStream.BeginRead(Byte[], Int32, Int32, AsyncCallback,
Object)
System.Security.Cryptography.CryptoStream.Read
System.Security.Cryptography.CryptoStream.ReadAsync
System.Security.Cryptography.CryptoStream.BeginRead(Byte[], Int32, Int32,
AsyncCallback, Object)
Set timestamp on read-only file on
Windows
Article • 01/12/2022
Setting the timestamp on a file with the read-only attribute now succeeds on Windows
and no longer throws an exception.
Old behavior
Prior to .NET 6 servicing releases, setting the timestamp on a read-only file on Windows
resulted in an UnauthorizedAccessException.
New behavior
Starting in .NET 6.0.2, setting the timestamp on a read-only file on Windows succeeds.
Version introduced
.NET 6.0.2 (servicing release)
Type of breaking change
This change can affect binary compatibility.
Reason for change
Customers gave feedback that they expected setting the timestamp on a read-only file
to succeed. This change also makes the Windows behavior consistent with Linux. Finally,
the behavior was unintentional, caused by a bug.
Recommended action
It is unlikely that existing code expects setting the timestamp on a read-only file to fail.
However, if your code expects it to fail, add a check for the read-only attribute using
File.GetAttributes(String) before attempting to set the timestamp.
Affected APIs
System.IO.File.SetCreationTime(String, DateTime)
System.IO.File.SetCreationTimeUtc(String, DateTime)
System.IO.File.SetLastAccessTime(String, DateTime)
System.IO.File.SetLastAccessTimeUtc(String, DateTime)
System.IO.File.SetLastWriteTime(String, DateTime)
System.IO.File.SetLastWriteTimeUtc(String, DateTime)
Standard numeric format parsing
precision
Article • 09/08/2022
.NET now supports greater precision values when formatting numbers as strings using
ToString and TryFormat .
7 Note
The maximum precision was changed again in .NET 7. For more information, see
Maximum precision for numeric format strings.
Change description
When formatting numbers as strings, the precision specifier in the format string
represents the number of digits in the resulting string. Depending on the format
specifier, which is the character at the beginning of the string, the precision can
represent the total number of digits, the number of significant digits, or the number of
decimal digits.
In previous .NET versions, the standard numeric format parsing logic is limited to a
precision of 99 or less. Some numeric types have more precision, but ToString(string
format) does not expose it correctly. If you specify a precision greater than 99, for
example, 32.ToString("C100") , the format string is interpreted as a custom numeric
format string instead of "currency with precision 100". In custom numeric format strings,
characters are interpreted as character literals. In addition, a format string that contains
an invalid format specifier is interpreted differently depending on the precision value.
H99 throws a FormatException for the invalid format specifier, while H100 is interpreted
as a custom numeric format string.
Starting in .NET 6, .NET supports precision up to Int32.MaxValue. A format string that
consists of a format specifier with any number of digits is interpreted as a standard
numeric format string with precision. A FormatException is thrown for either or both of
the following conditions:
The format specifier character is not a standard format specifier.
The precision is greater than Int32.MaxValue.
This change was implemented in the parsing logic that affects all numeric types.
The following table shows the behavior changes for various format strings.
Format
string
Previous behavior
.NET 6+ behavior
C2
Denotes currency with two decimal
digits
Denotes currency with two decimal digits (no
change)
C100
Denotes custom numeric format string
that prints "C100"
Denotes currency with 100 decimal digits
H99
Throws FormatException due to invalid
standard format specifier "H"
Throws FormatException due to invalid
standard format specifier "H" (no change)
H100
Denotes custom numeric format string
Throws FormatException due to invalid
standard format specifier "H"
Version introduced
.NET 6
Reason for change
This change corrects unexpected behavior when using higher precision for numeric
format parsing.
Recommended action
In most cases, no action is necessary and the correct precision will be shown in the
resulting strings.
However, if you want to revert to the previous behavior where the format specifier is
interpreted as a literal character when the precision is greater than 99, you can wrap that
character in single quotes or escape it with a backslash. For example, in previous .NET
versions, 42.ToString("G999") returns G999 . To maintain that behavior, change the
format string to "'G'999" or "\\G999" . This will work on .NET Framework, .NET Core,
and .NET 5+.
The following format strings will continue to be interpreted as custom numeric format
strings:
Start with any character that is not an ASCII alphabetical character, for example, $
or è .
Start with an ASCII alphabetical character that's not followed by an ASCII digit, for
example, A$ .
Start with an ASCII alphabetical character, followed by an ASCII digit sequence, and
then any character that is not an ASCII digit character, for example, A12A .
Affected APIs
This change was implemented in the parsing logic that affects all numeric types.
System.Numerics.BigInteger.ToString(String)
System.Numerics.BigInteger.ToString(String, IFormatProvider)
System.Numerics.BigInteger.TryFormat(Span<Char>, Int32, ReadOnlySpan<Char>,
IFormatProvider)
System.Int32.ToString(String)
System.Int32.ToString(String, IFormatProvider)
System.Int32.TryFormat(Span<Char>, Int32, ReadOnlySpan<Char>,
IFormatProvider)
System.UInt32.ToString(String)
System.UInt32.ToString(String, IFormatProvider)
System.UInt32.TryFormat(Span<Char>, Int32, ReadOnlySpan<Char>,
IFormatProvider)
System.Byte.ToString(String)
System.Byte.ToString(String, IFormatProvider)
System.Byte.TryFormat(Span<Char>, Int32, ReadOnlySpan<Char>,
IFormatProvider)
System.SByte.ToString(String)
System.SByte.ToString(String, IFormatProvider)
System.SByte.TryFormat(Span<Char>, Int32, ReadOnlySpan<Char>,
IFormatProvider)
System.Int16.ToString(String)
System.Int16.ToString(String, IFormatProvider)
System.Int16.TryFormat(Span<Char>, Int32, ReadOnlySpan<Char>,
IFormatProvider)
System.UInt16.ToString(String)
System.UInt16.ToString(String, IFormatProvider)
System.UInt16.TryFormat(Span<Char>, Int32, ReadOnlySpan<Char>,
IFormatProvider)
System.Int64.ToString(String)
System.Int64.ToString(String, IFormatProvider)
System.Int64.TryFormat(Span<Char>, Int32, ReadOnlySpan<Char>,
IFormatProvider)
System.UInt64.ToString(String)
System.UInt64.ToString(String, IFormatProvider)
System.UInt64.TryFormat(Span<Char>, Int32, ReadOnlySpan<Char>,
IFormatProvider)
System.Half.ToString(String)
System.Half.ToString(String, IFormatProvider)
System.Half.TryFormat(Span<Char>, Int32, ReadOnlySpan<Char>, IFormatProvider)
System.Single.ToString(String)
System.Single.ToString(String, IFormatProvider)
System.Single.TryFormat(Span<Char>, Int32, ReadOnlySpan<Char>,
IFormatProvider)
System.Double.ToString(String)
System.Double.ToString(String, IFormatProvider)
System.Double.TryFormat(Span<Char>, Int32, ReadOnlySpan<Char>,
IFormatProvider)
System.Decimal.ToString(String)
System.Decimal.ToString(String, IFormatProvider)
System.Decimal.TryFormat(Span<Char>, Int32, ReadOnlySpan<Char>,
IFormatProvider)
See also
Standard numeric format strings
Character literals in custom format strings
Maximum precision for numeric format strings (.NET 7)
Static abstract members declared in
interfaces
Article • 09/29/2022
.NET 6 previews a new feature where static interface members can be marked as
abstract . This feature involves several changes to the ECMA 335 spec to allow
intermediate language (IL) metadata patterns that were previously considered illegal.
For more information, see dotnet/runtime#49558 .
Old behavior
If a static interface was marked as abstract :
The C# compiler generated error CS0112.
Tools and other compilers generated illegal IL metadata.
New behavior
Starting in .NET 6, static interface members can be marked as abstract and will compile
successfully. In addition, the IL metadata patterns that are generated are now
considered legal due to changes in the ECMA 335 spec.
The implementation of static abstract interface members is provided by types that
implement the interface.
7 Note
For .NET 6, you must enable preview features in your project to be able to mark an
interface member as static abstract .
Since this is a newly legal IL pattern, existing tooling may incorrectly process the
associated metadata and have unexpected behavior. It's likely that tooling will
encounter the new metadata pattern, because interfaces with static abstract members
now appear on the primitive types, for example, System.Int32.
Version introduced
.NET 6
Type of breaking change
This change can affect binary compatibility.
Reason for change
This change was introduced because there was no way to abstract over static members
and write generalized code that applies across types that define those static members.
This was particularly problematic for member kinds that only exist in a static form, for
example, operators.
Recommended action
Update any tooling that consumes .NET binaries or C# source code to account for the
new concept of static abstract interface members, including those that now exist on
the .NET primitive types.
Affected APIs
N/A
See also
Tutorial: Explore static virtual members in interfaces
StringBuilder.Append overloads and
evaluation order
Article • 10/15/2021
C# 10 adds support for better string interpolation , including the ability to target
custom "handlers" in addition to strings. StringBuilder takes advantage of this with new
overloads of Append and AppendLine that accept a custom interpolated string handler.
Existing calls to these methods may now start binding to the new overloads. In general,
behavior is identical but with improved performance. For example, rather than a string
first being created and then that string appended, the individual components of the
interpolated string are appended directly to the builder. However, this can change the
evaluation order of objects used as format items, which can manifest as a difference in
behavior.
Previous behavior
In previous versions, a call to:
C#
stringBuilder.Append($"{a} {b}");
compiled to the equivalent of:
C#
stringBuilder.Append(string.Format("{0} {1}", a, b));
This means a is evaluated, then b is evaluated, then a string is created from the results
of those evaluations, and then that string is appended to the builder.
New behavior
Starting in .NET 6, a call to:
C#
stringBuilder.Append($"{a} {b}");
compiles to the equivalent of:
C#
var handler = new StringBuilder.AppendInterpolatedStringHandler(1, 2,
stringBuilder);
handler.AppendFormatted(a);
handler.AppendLiteral(" ");
handler.AppendFormatted(b);
stringBuilder.Append(ref handler);
This means a is evaluated and appended to the builder, and then b is evaluated and
appended to the builder.
If, for example, either a or b is itself the builder, as shown in the following code, the
new evaluation order can result in different behavior at run time.
C#
stringBuilder.Append($"{a} {stringBuilder}");
Version introduced
6.0 RC 1
Type of breaking change
This change can affect source compatibility.
Reason for change
It's common for developers to pass interpolated strings to StringBuilder, as it's more
convenient than manually splitting the string up and calling StringBuilder.Append for
each part. These new overloads enable the concise syntax and most of the performance
of doing the individual calls.
Recommended action
In most cases where StringBuilder.Append and StringBuilder.AppendLine are used, you
won't notice a functional difference. If you find a difference that proves to be
problematic, you can restore the previous behavior by adding a cast to (string) prior
to the interpolated string. For example:
C#
stringBuilder.Append((string)$"{a} {b}")
This is not recommended, however, unless it's actually required for compatibility.
Affected APIs
System.Text.StringBuilder.Append
System.Text.StringBuilder.AppendLine
See also
String Interpolation in C# 10 and .NET 6
Strong-name APIs throw
PlatformNotSupportedException
Article • 07/22/2022
A few APIs that aren't supported in .NET/.NET Core but didn't do anything when
accessed have been changed to now throw a PlatformNotSupportedException at run
time. Previously, using these APIs would eventually result in a run-time exception further
along; the exception is now thrown when the type is instantiated or first accessed.
Previous behavior
In previous versions, calling AssemblyName.KeyPair or StrongNameKeyPair(Byte[]) was a
no-op. Calling StrongNameKeyPair(FileStream) read the stream but otherwise did
nothing.
New behavior
Starting in .NET 6, each of the three affected APIs throws a
PlatformNotSupportedException at run time.
Version introduced
.NET 6
Type of breaking change
This change can affect binary compatibility.
Reason for change
Previously, an application that called the API compiled and ran, but as soon as the
instance was used in any code path, it threw a run-time exception. To make it more
explicit that this scenario is unsupported, the exception-throwing logic was moved into
the instance constructor. In case no instances are created, the exception is also thrown
in public entry points that return this type, that is, AssemblyName.KeyPair.
Recommended action
Strong-name signing is not supported in .NET/.NET Core, and there is no workaround.
7 Note
.NET Core/5+ never checks signatures in its runtime. However, if you're targeting
cross-platform libraries (for example, a basic auth package that targets .NET
Standard 2.0, so it runs on .NET Framework too), then strong-naming is a good idea
for cross-runtime compatibility. .NET Framework continues to enforce strong
naming if the calling app is strong-named. You can strong-name assemblies in all
versions of .NET using the Sn.exe tool. For more information, see Strong name
signing .
Affected APIs
StrongNameKeyPair(FileStream)
StrongNameKeyPair(Byte[])
System.Reflection.AssemblyName.KeyPair
See also
How to: Sign an assembly with a strong name
System.Drawing.Common only
supported on Windows
Article • 07/21/2023
The System.Drawing.Common
NuGet package is now attributed as a Windows-
specific library. The platform analyzer emits warning at compile time when compiling for
non-Windows operating systems.
On non-Windows operating systems, unless you set a runtime configuration switch, a
TypeInitializationException exception is thrown with PlatformNotSupportedException as
the inner exception.
Old behavior
Prior to .NET 6, using the System.Drawing.Common package did not produce any
compile-time warnings, and no run-time exceptions were thrown.
New behavior
Starting in .NET 6, the platform analyzer emits compile-time warnings when referencing
code is compiled for non-Windows operating systems. In addition, the following runtime exception is thrown unless you set a configuration option:
System.TypeInitializationException : The type initializer for 'Gdip' threw
an exception.
---- System.PlatformNotSupportedException : System.Drawing.Common is
not supported on non-Windows platforms. See
https://aka.ms/systemdrawingnonwindows for more information.
Stack Trace:
at
System.Drawing.SafeNativeMethods.Gdip.GdipCreateBitmapFromFile(String
filename, IntPtr& bitmap)
/_/src/libraries/System.Drawing.Common/src/System/Drawing/Bitmap.cs(42,0):
at System.Drawing.Bitmap..ctor(String filename, Boolean useIcm)
/_/src/libraries/System.Drawing.Common/src/System/Drawing/Bitmap.cs(25,0):
at System.Drawing.Bitmap..ctor(String filename)
/_/src/libraries/System.Resources.ResourceManager/tests/ResourceManagerTests
.cs(270,0): at
System.Resources.Tests.ResourceManagerTests.EnglishImageResourceData()+MoveN
ext()
/_/src/libraries/System.Linq/src/System/Linq/Select.cs(136,0): at
System.Linq.Enumerable.SelectEnumerableIterator`2.MoveNext()
----- Inner Stack Trace ----/_/src/libraries/System.Drawing.Common/src/System/Drawing/LibraryResolver.cs
(31,0): at System.Drawing.LibraryResolver.EnsureRegistered()
/_/src/libraries/System.Drawing.Common/src/System/Drawing/GdiplusNative.Unix
.cs(65,0): at System.Drawing.SafeNativeMethods.Gdip.PlatformInitialize()
/_/src/libraries/System.Drawing.Common/src/System/Drawing/Gdiplus.cs(27,0):
at System.Drawing.SafeNativeMethods.Gdip..cctor()
Version introduced
.NET 6
Type of breaking change
This change can affect source compatibility and binary compatibility.
Reason for change
Because System.Drawing.Common was designed to be a thin wrapper over Windows
technologies, its cross-platform implementation is subpar.
libgdiplus is the main provider of the cross-platform implementation of
System.Drawing.Common on the native side. libgdiplus is effectively a reimplementation
of the parts of Windows that System.Drawing.Common depends on. That implementation
makes libgdiplus a non-trivial component. It's around 30,000 lines of C code that's
largely untested, and it lacks a lot of functionality. libgdiplus also has numerous
external dependencies for image processing and text rendering, such as cairo , pango ,
and other native libraries. Those dependencies make maintaining and shipping the
component even more challenging. Since the inclusion of the Mono cross-platform
implementation, we have redirected numerous issues to libgdiplus that never got
fixed. In comparison, other external dependencies we have taken, such as icu or
openssl , are high-quality libraries. It's not viable to get libgdiplus to the point where
its feature set and quality is on par with the rest of the .NET stack.
From analysis of NuGet packages, we've observed that System.Drawing.Common is used
cross-platform mostly for image manipulation, such as QR code generators and text
rendering. We haven't noticed heavy graphics usage, as our cross-platform graphics
support is incomplete. The usages we see of System.Drawing.Common in non-Windows
environments are typically well supported with SkiaSharp and ImageSharp.
System.Drawing.Common will continue to evolve only in the context of Windows Forms
and GDI+.
Recommended action
To use these APIs for cross-platform apps, migrate to one of the following libraries:
SkiaSharp
ImageSharp
(tiered license)
Microsoft.Maui.Graphics
Alternatively, you can enable support for non-Windows platforms in .NET 6 by setting
the System.Drawing.EnableUnixSupport runtime configuration switch to true in the
runtimeconfig.json file.
runtimeconfig.template.json template file:
JSON
{
"configProperties": {
"System.Drawing.EnableUnixSupport": true
}
}
[appname].runtimeconfig.json output file:
JSON
{
"runtimeOptions": {
"configProperties": {
"System.Drawing.EnableUnixSupport": true
}
}
}
7 Note
This configuration switch was added to give cross-platform apps that depend
heavily on this package time to migrate to more modern libraries. However,
non-Windows bugs will not be fixed.
This switch is only available in .NET 6 and was removed in .NET 7. For more
information, see System.Drawing.Common config switch removed.
Affected APIs
System.Drawing namespace:
Bitmap
Brush
Brushes
BufferedGraphics
BufferedGraphicsContext
Font
FontFamily
FontConverter
Graphics
Icon
IconConverter
Image
ImageAnimator
Pen
Pens
Region
SolidBrush
StringFormat
SystemBrushes
SystemFonts
SystemIcons
SystemPens
TextureBrush
System.Drawing.Drawing2D namespace:
AdjustableArrowCap
CustomLineCap
GraphicsPath
GraphicsPathIterator
GraphicsState
HatchBrush
LinearGradientBrush
Matrix
PathGradientBrush
System.Drawing.Imaging namespace:
Encoder
EncoderParameter
EncoderParameters
ImageAttributes
ImageCodecInfo
ImageFormat
Metafile
MetafileHeader
PlayRecordCallback
System.Drawing.Printing namespace:
PageSettings
PreviewPageInfo
PrintController
PrintDocument
PrinterSettings
PrintEventArgs
PrintEventHandler
PrintPageEventArgs
PrintPageEventHandler
System.Drawing.Text namespace:
FontCollection
InstalledFontCollection
PrivateFontCollection
See also
System.Drawing.Common only supported on Windows - dotnet/designs
System.Security.SecurityContext is
marked obsolete
Article • 10/12/2021
SecurityContext is marked obsolete with a custom diagnostic ID. Using any
SecurityContext APIs generates warning SYSLIB0003 at compile time.
7 Note
Suppressing the default obsoletion diagnostic ID, which is CS0618 for the C#
compiler, does not suppress the warnings that the compiler generates when these
APIs are used.
Old behavior
Prior to .NET 6, the SecurityContext type was not marked obsolete, however, all of its
public members throw a PlatformNotSupportedException at run time.
New behavior
Starting in .NET 6, the SecurityContext is marked obsolete.
Version introduced
.NET 6 RC 1
Type of breaking change
This change can affect source compatibility.
Reason for change
All of the public members of SecurityContext throw a PlatformNotSupportedException
at run time. The SecurityContext is part of Code access security (CAS), which is an
unsupported legacy technology.
Recommended action
Remove any use of these APIs from your code.
Affected APIs
System.Security.SecurityContext
See also
Most code access security APIs are obsolete
Task.FromResult may return singleton
Article • 12/02/2021
Task.FromResult<TResult>(TResult) may now return a cached Task<TResult> instance
rather than always creating a new instance.
Old behavior
In previous versions, Task.FromResult<TResult>(TResult) would always allocate a new
Task<TResult>, regardless of the type of T or the result value.
New behavior
For some T types and some result values, Task.FromResult<TResult>(TResult) may
return a cached singleton object rather than allocating a new object. For example, it is
likely that every call to Task.FromResult(true) will return the same already-completed
Task<bool> object.
Version introduced
.NET 6
Type of breaking change
This change can affect binary compatibility.
Reason for change
Many developers expected Task.FromResult<TResult>(TResult) to behave similarly to
asynchronous methods, which already performed such caching. Developers that knew
about the allocation behavior often maintained their own cache to avoid the
performance cost of always allocating for these commonly used values. For example:
C#
private static readonly Task<bool> s_trueTask = Task.FromResult(true);
Now, such custom caches are no longer required for values such as Boolean and small
Int32 values.
Recommended action
Unless you're using reference equality to check whether one Task instance is the same
as another Task instance, you should not be impacted by this change. If you are using
such reference equality and need to continue this checking, use the following code to
be guaranteed to always get back a unique Task<TResult> instance:
C#
private static Task<T> NewInstanceFromResult<T>(T result)
{
var tcs = new TaskCompletionSource<T>();
tcs.TrySetResult(result);
return tcs.Task;
}
7 Note
This pattern is much less efficient than just using Task.FromResult(result) , and
should be avoided unless you really need it.
Affected APIs
System.Threading.Tasks.Task.FromResult<TResult>(TResult)
Unhandled exceptions from a
BackgroundService
Article • 05/12/2022
In previous versions, when a BackgroundService throws an unhandled exception, the
exception is lost and the service appears unresponsive. .NET 6 fixes this behavior by
logging the exception and stopping the host.
Change description
In previous .NET versions, when an exception is thrown from a
BackgroundService.ExecuteAsync(CancellationToken) override, the host continues to run,
and no message is logged.
Starting in .NET 6, when an exception is thrown from a
BackgroundService.ExecuteAsync(CancellationToken) override, the exception is logged
to the current ILogger. By default, the host is stopped when an unhandled exception is
encountered.
Version introduced
.NET 6
Reason for change
The new behavior is consistent with the way other app models behave when unhandled
exceptions are encountered. It's also confusing to developers when their
BackgroundService encounters an error, but nothing is logged. The best default
behavior is to stop the host, because unhandled exceptions shouldn't be ignored. They
indicate a problem that needs attention.
Recommended action
If you prefer to keep the previous behavior of allowing an unhandled exception in a
BackgroundService to not stop the Host, you can set
HostOptions.BackgroundServiceExceptionBehavior to
BackgroundServiceExceptionBehavior.Ignore.
C#
Host.CreateBuilder(args)
.ConfigureServices(services =>
{
services.Configure<HostOptions>(hostOptions =>
{
hostOptions.BackgroundServiceExceptionBehavior =
BackgroundServiceExceptionBehavior.Ignore;
});
});
Affected APIs
Microsoft.Extensions.Hosting.IHost.StartAsync(CancellationToken)
Microsoft.Extensions.Hosting.BackgroundService.ExecuteAsync(CancellationToken)
CreateEncryptor methods throw
exception for incorrect feedback size
Article • 12/02/2021
The CreateEncryptor and CreateDecryptor methods for AesCng and TripleDESCng now
throw a CryptographicException when the object instance is being used with a CNG
persisted (or named) key for Cipher Feedback (CFB) mode, with a feedback size other
than eight (CFB8).
Previous behavior
Previously, these classes allowed CFB128 ( AesCng ) or CFB64 ( TripleDESCng ) to be
selected. However, if the key was a persisted key, then the computation was always
done as if CFB8 was selected.
New behavior
The CreateEncryptor and CreateDecryptor methods throw a CryptographicException
when both of the following conditions are met:
CFB128 or CFB64 mode is selected (that is, SymmetricAlgorithm.FeedbackSize is
set to 128 or 64).
The instance is backed by a persisted key.
Version introduced
.NET 6
Type of breaking change
This change can affect binary compatibility.
Reason for change
This change was introduced to indicate that the requested work cannot be performed
correctly.
Recommended action
If you encounter this exception, consider switching from CFB128 or CFB64 to CFB8.
Making that switch will produce results compatible with the behavior in previous
releases.
Affected APIs
System.Security.Cryptography.AesCng.CreateEncryptor()
System.Security.Cryptography.AesCng.CreateDecryptor()
System.Security.Cryptography.TripleDESCng.CreateEncryptor()
System.Security.Cryptography.TripleDESCng.CreateDecryptor()
x86 host path on 64-bit Windows
Article • 06/13/2023
The x86 versions of .NET installers for Windows have been modified to no longer add
the x86 host location (Program Files (x86)\dotnet) to the PATH environment variable on
64-bit Windows systems.
With this change, if the x86 host location was added to PATH by a prior version of .NET,
the x86 versions of .NET installers and .NET updates will remove it on upgrade.
This change affects .NET Core 3.1, .NET 6, .NET 7, and future versions.
This change only affects the dotnet host. It doesn't affect 32-bit/x86 application hosts,
like myapp.exe. Those hosts will continue to find the x86 runtime correctly (assuming it's
installed).
Previous behavior
The x86 host location was added to PATH , even on x64/Arm64 systems. Depending on
which .NET architecture installer was run first, a user's machine could have either the
native (x64/Arm64) or x86 host listed first in PATH .
New behavior
Going forward, the x86 host location is only added to the PATH environment variable on
x86 systems and will be removed on upgrade of .NET or Visual Studio on any x64 and
arm64 systems.
Version introduced
.NET 7
Reason for change
Currently, the x86 host location is added to PATH , even on x64/Arm64 systems.
Depending on which .NET architecture installer is run first, a user's machine could have
either the native (x64/Arm64) or the x86 host as the first location in the PATH list. This
ambiguity causes problems with the initial .NET installation and during .NET servicing
events. Any of these installation scenarios can modify the order of .NET hosts in PATH ,
making it non-deterministic. There's a high chance of behavior regression of the .NET
runtime.
This change streamlines the dotnet host experience on Windows 64-bit systems. Only
64-bit hosts will be available in the system's PATH environment variable: the x64 host on
x64 systems and the Arm64 host on Arm64 systems. We removed the ambiguity in the
order of dotnet hosts in PATH , and only one host will be present.
Recommended action
If you need the x86 host in the PATH environment variable on x64/Arm64 systems, add
the host location to PATH manually.
Affected APIs
None.
Breaking changes in EF Core 6.0
Article • 01/30/2023
The following API and behavior changes have the potential to break existing
applications updating to EF Core 6.0.
Target Framework
EF Core 6.0 targets .NET 6. Applications targeting older .NET, .NET Core, and .NET
Framework versions will need to target .NET 6 to use EF Core 6.0.
Summary
Breaking change
Impact
Nested optional dependents sharing a table and with no required properties cannot be
saved
High
Changing the owner of an owned entity now throws an exception
Medium
Azure Cosmos DB: Related entity types are discovered as owned
Medium
SQLite: Connections are pooled
Medium
Many-to-many relationships without mapped join entities are now scaffolded
Medium
Cleaned up mapping between DeleteBehavior and ON DELETE values
Low
In-memory database validates required properties do not contain nulls
Low
Removed last ORDER BY when joining for collections
Low
DbSet no longer implements IAsyncEnumerable
Low
TVF return entity type is also mapped to a table by default
Low
Check constraint name uniqueness is now validated
Low
Added IReadOnly Metadata interfaces and removed extension methods
Low
IExecutionStrategy is now a singleton service
Low
SQL Server: More errors are considered transient
Low
Azure Cosmos DB: More characters are escaped in 'id' values
Low
Some Singleton services are now Scoped
Low*
Breaking change
Impact
New caching API for extensions that add or replace services
Low*
New snapshot and design-time model initialization procedure
Low
OwnedNavigationBuilder.HasIndex returns a different type now
Low
DbFunctionBuilder.HasSchema(null) overrides [DbFunction(Schema = "schema")]
Low
Pre-initialized navigations are overridden by values from database queries
Low
Unknown enum string values in the database are not converted to the enum default
when queried
Low
DbFunctionBuilder.HasTranslation now provides the function arguments as
Low
IReadOnlyList rather than IReadOnlyCollection
Default table mapping is not removed when the entity is mapped to a table-valued
Low
function
dotnet-ef targets .NET 6
Low
IModelCacheKeyFactory implementations may need to be updated to handle design-
Low
time caching
* These changes are of particular interest to authors of database providers and
extensions.
High-impact changes
Nested optional dependents sharing a table and with no
required properties are disallowed
Tracking Issue #24558
Old behavior
Models with nested optional dependents sharing a table and with no required
properties were allowed, but could result in data loss when querying for the data and
then saving again. For example, consider the following model:
C#
public class Customer
{
public int Id { get; set; }
public string Name { get; set; }
public ContactInfo ContactInfo { get; set; }
}
[Owned]
public class ContactInfo
{
public string Phone { get; set; }
public Address Address { get; set; }
}
[Owned]
public class Address
{
public string House { get; set; }
public string Street { get; set; }
public string City { get; set; }
public string Postcode { get; set; }
}
None of the properties in ContactInfo or Address are required, and all these entity
types are mapped to the same table. The rules for optional dependents (as opposed to
required dependents) say that if all of the columns for ContactInfo are null, then no
instance of ContactInfo will be created when querying for the owner Customer .
However, this also means that no instance of Address will be created, even if the
Address columns are non-null.
New behavior
Attempting to use this model will now throw the following exception:
System.InvalidOperationException: Entity type 'ContactInfo' is an optional
dependent using table sharing and containing other dependents without any
required non shared property to identify whether the entity exists. If all nullable
properties contain a null value in database then an object instance won't be created
in the query causing nested dependent's values to be lost. Add a required property
to create instances with null values for other properties or mark the incoming
navigation as required to always create an instance.
This prevents data loss when querying and saving data.
Why
Using models with nested optional dependents sharing a table and with no required
properties often resulted in silent data loss.
Mitigations
Avoid using optional dependents sharing a table and with no required properties. There
are three easy ways to do this:
1. Make the dependents required. This means that the dependent entity will always
have a value after it is queried, even if all its properties are null. For example:
C#
public class Customer
{
public int Id { get; set; }
public string Name { get; set; }
[Required]
public Address Address { get; set; }
}
Or:
C#
modelBuilder.Entity<Customer>(
b =>
{
b.OwnsOne(e => e.Address);
b.Navigation(e => e.Address).IsRequired();
});
2. Make sure that the dependent contains at least one required property.
3. Map optional dependents to their own table, instead of sharing a table with the
principal. For example:
C#
modelBuilder.Entity<Customer>(
b =>
{
b.ToTable("Customers");
b.OwnsOne(e => e.Address, b =>
b.ToTable("CustomerAddresses"));
});
The problems with optional dependents and examples of these mitigations are included
in the documentation for What's new in EF Core 6.0.
Medium-impact changes
Changing the owner of an owned entity now throws an
exception
Tracking Issue #4073
Old behavior
It was possible to reassign an owned entity to a different owner entity.
New behavior
This action will now throw an exception:
The property '{entityType}.{property}' is part of a key and so cannot be modified or
marked as modified. To change the principal of an existing entity with an identifying
foreign key, first delete the dependent and invoke 'SaveChanges', and then
associate the dependent with the new principal.
Why
Even though we don't require key properties to exist on an owned type, EF will still
create shadow properties to be used as the primary key and the foreign key pointing to
the owner. When the owner entity is changed it causes the values of the foreign key on
the owned entity to change, and since they are also used as the primary key this results
in the entity identity to change. This isn't yet fully supported in EF Core and was only
conditionally allowed for owned entities, sometimes resulting in the internal state
becoming inconsistent.
Mitigations
Instead of assigning the same owned instance to a new owner you can assign a copy
and delete the old one.
Azure Cosmos DB: Related entity types are discovered as
owned
Tracking Issue #24803
What's new: Default to implicit ownership
Old behavior
As in other providers, related entity types were discovered as normal (non-owned) types.
New behavior
Related entity types will now be owned by the entity type on which they were
discovered. Only the entity types that correspond to a DbSet<TEntity> property will be
discovered as non-owned.
Why
This behavior follows the common pattern of modeling data in Azure Cosmos DB of
embedding related data into a single document. Azure Cosmos DB does not natively
support joining different documents, so modeling related entities as non-owned has
limited usefulness.
Mitigations
To configure an entity type to be non-owned call modelBuilder.Entity<MyEntity>();
SQLite: Connections are pooled
Tracking Issue #13837
What's new: Default to implicit ownership
Old behavior
Previously, connections in Microsoft.Data.Sqlite were not pooled.
New behavior
Starting in 6.0, connections are now pooled by default. This results in database files
being kept open by the process even after the ADO.NET connection object is closed.
Why
Pooling the underlying connections greatly improves the performance of opening and
closing ADO.NET connection objects. This is especially noticeable for scenarios where
opening the underlying connection is expensive as in the case of encryption, or in
scenarios where there are a lot of short-lived connection to the database.
Mitigations
Connection pooling can be disabled by adding Pooling=False to a connection string.
Some scenarios (like deleting the database file) may now encounter errors stating that
the file is still in use. You can manually clear the connection pool before performing
operations of the file using SqliteConnection.ClearPool() .
C#
SqliteConnection.ClearPool(connection);
File.Delete(databaseFile);
Many-to-many relationships without mapped join entities
are now scaffolded
Tracking Issue #22475
Old behavior
Scaffolding (reverse engineering) a DbContext and entity types from an existing
database always explicitly mapped join tables to join entity types for many-to-many
relationships.
New behavior
Simple join tables containing only two foreign key properties to other tables are no
longer mapped to explicit entity types, but are instead mapped as a many-to-many
relationship between the two joined tables.
Why
Many-to-many relationships without explicit join types were introduced in EF Core 5.0
and are a cleaner, more natural way to represent simple join tables.
Mitigations
There are two mitigations. The preferred approach is to update code to use the manyto-many relationships directly. It is very rare that the join entity type needs to be used
directly when it contains only two foreign keys for the many-to-many relationships.
Alternately, the explicit join entity can be added back to the EF model. For example,
assuming a many-to-many relationship between Post and Tag , add back the join type
and navigations using partial classes:
C#
public partial class PostTag
{
public int PostsId { get; set; }
public int TagsId { get; set; }
public virtual Post Posts { get; set; }
public virtual Tag Tags { get; set; }
}
public partial class Post
{
public virtual ICollection<PostTag> PostTags { get; set; }
}
public partial class Tag
{
public virtual ICollection<PostTag> PostTags { get; set; }
}
Then add configuration for the join type and navigations to a partial class for the
DbContext:
C#
public partial class DailyContext
{
partial void OnModelCreatingPartial(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Post>(entity =>
{
entity.HasMany(d => d.Tags)
.WithMany(p => p.Posts)
.UsingEntity<PostTag>(
l => l.HasOne<Tag>(e => e.Tags).WithMany(e =>
e.PostTags).HasForeignKey(e => e.TagsId),
r => r.HasOne<Post>(e => e.Posts).WithMany(e =>
e.PostTags).HasForeignKey(e => e.PostsId),
j =>
{
j.HasKey("PostsId", "TagsId");
j.ToTable("PostTag");
});
});
}
}
Finally, remove the generated configuration for the many-to-many relationship from the
scaffolded context. This is needed because the scaffolded join entity type must be
removed from the model before the explicit type can be used. This code will need to be
removed each time the context is scaffolded, but because the code above is in partial
classes it will persist.
Note that with this configuration, the join entity can be used explicitly, just like in
previous versions of EF Core. However, the relationship can also be used as a many-tomany relationship. This means that updating the code like this can be a temporary
solution while the rest of the code is updated to use the relationship as a many-to-many
in the natural way.
Low-impact changes
Cleaned up mapping between DeleteBehavior and ON
DELETE values
Tracking Issue #21252
Old behavior
Some of the mappings between a relationship's OnDelete() behavior and the foreign
keys' ON DELETE behavior in the database were inconsistent in both Migrations and
Scaffolding.
New behavior
The following table illustrates the changes for Migrations.
OnDelete()
ON DELETE
NoAction
NO ACTION
ClientNoAction
NO ACTION
OnDelete()
ON DELETE
Restrict
RESTRICT
Cascasde
CASCADE
ClientCascade
RESTRICT NO ACTION
SetNull
SET NULL
ClientSetNull
RESTRICT NO ACTION
The changes for Scaffolding are as follows.
ON DELETE
OnDelete()
NO ACTION
ClientSetNull
RESTRICT
ClientSetNull Restrict
CASCADE
Cascade
SET NULL
SetNull
Why
The new mappings are more consistent. The default database behavior of NO ACTION is
now preferred over the more restrictive and less performant RESTRICT behavior.
Mitigations
The default OnDelete() behavior of optional relationships is ClientSetNull. Its mapping
has changed from RESTRICT to NO ACTION. This may cause a lot of operations to be
generated in your first migration added after upgrading to EF Core 6.0.
You can choose to either apply these operations or manually remove them from the
migration since they have no functional impact on EF Core.
SQL Server doesn't support RESTRICT, so these foreign keys were already created using
NO ACTION. The migration operations will have no affect on SQL Server and are safe to
remove.
In-memory database validates required properties do not
contain nulls
Tracking Issue #10613
Old behavior
The in-memory database allowed saving null values even when the property was
configured as required.
New behavior
The in-memory database throws a Microsoft.EntityFrameworkCore.DbUpdateException
when SaveChanges or SaveChangesAsync is called and a required property is set to null.
Why
The in-memory database behavior now matches the behavior of other databases.
Mitigations
The previous behavior (i.e. not checking null values) can be restored when configuring
the in-memory provider. For example:
C#
protected override void OnConfiguring(DbContextOptionsBuilder
optionsBuilder)
{
optionsBuilder
.UseInMemoryDatabase("MyDatabase", b => b.EnableNullChecks(false));
}
Removed last ORDER BY when joining for collections
Tracking Issue #19828
Old behavior
When performing SQL JOINs on collections (one-to-many relationships), EF Core used
to add an ORDER BY for each key column of the joined table. For example, loading all
Blogs with their related Posts was done via the following SQL:
SQL
SELECT [b].[BlogId], [b].[Name], [p].[PostId], [p].[BlogId], [p].[Title]
FROM [Blogs] AS [b]
LEFT JOIN [Post] AS [p] ON [b].[BlogId] = [p].[BlogId]
ORDER BY [b].[BlogId], [p].[PostId]
These orderings are necessary for proper materialization of the entities.
New behavior
The very last ORDER BY for a collection join is now omitted:
SQL
SELECT [b].[BlogId], [b].[Name], [p].[PostId], [p].[BlogId], [p].[Title]
FROM [Blogs] AS [b]
LEFT JOIN [Post] AS [p] ON [b].[BlogId] = [p].[BlogId]
ORDER BY [b].[BlogId]
An ORDER BY for the Post's ID column is no longer generated.
Why
Every ORDER BY imposes additional work at the database side, and the last ordering
isn't necessary for EF Core's materialization needs. Data shows that removing this last
ordering can produce a significant performance improvement in some scenarios.
Mitigations
If your application expects joined entities to be returned in a particular order, make that
explicit by adding a LINQ OrderBy operator to your query.
DbSet no longer implements IAsyncEnumerable
Tracking Issue #24041
Old behavior
DbSet<TEntity>, which is used to execute queries on DbContext, used to implement
IAsyncEnumerable<T>.
New behavior
DbSet<TEntity> no longer directly implements IAsyncEnumerable<T>.
Why
DbSet<TEntity> was originally made to implement IAsyncEnumerable<T> mainly in
order to allow direct enumeration on it via the foreach construct. Unfortunately, when a
project also references System.Linq.Async
in order to compose async LINQ operators
client-side, this resulted in an ambiguous invocation error between the operators
defined over IQueryable<T> and those defined over IAsyncEnumerable<T> . C# 9 added
extension GetEnumerator support for foreach loops, removing the original main reason
to reference IAsyncEnumerable .
The vast majority of DbSet usages will continue to work as-is, since they compose LINQ
operators over DbSet , enumerate it, etc. The only usages broken are those which
attempt to cast DbSet directly to IAsyncEnumerable .
Mitigations
If you need to refer to a DbSet<TEntity> as an IAsyncEnumerable<T>, call
DbSet<TEntity>.AsAsyncEnumerable to explicitly cast it.
TVF return entity type is also mapped to a table by
default
Tracking Issue #23408
Old behavior
An entity type was not mapped to a table by default when used as a return type of a
TVF configured with HasDbFunction.
New behavior
An entity type used as a return type of a TVF retains the default table mapping.
Why
It isn't intuitive that configuring a TVF removes the default table mapping for the return
entity type.
Mitigations
To remove the default table mapping, call ToTable(EntityTypeBuilder, String):
C#
modelBuilder.Entity<MyEntity>().ToTable((string?)null));
Check constraint name uniqueness is now validated
Tracking Issue #25061
Old behavior
Check constraints with the same name were allowed to be declared and used on the
same table.
New behavior
Explicitly configuring two check constraints with the same name on the same table will
now result in an exception. Check constraints created by a convention will be assigned a
unique name.
Why
Most databases don't allow two check constraints with the same name to be created on
the same table, and some require them to be unique even across tables. This would
result in exception being thrown when applying a migration.
Mitigations
In some cases, valid check constraint names might be different due to this change. To
specify the desired name explicitly, call HasName:
C#
modelBuilder.Entity<MyEntity>().HasCheckConstraint("CK_Id", "Id > 0", c =>
c.HasName("CK_MyEntity_Id"));
Added IReadOnly Metadata interfaces and removed
extension methods
Tracking Issue #19213
Old behavior
There were three sets of metadata interfaces: IModel, IMutableModel and
IConventionModel as well as extension methods.
New behavior
A new set of IReadOnly interfaces has been added, e.g. IReadOnlyModel. Extension
methods that were previously defined for the metadata interfaces have been converted
to default interface methods.
Why
Default interface methods allow the implementation to be overridden, this is leveraged
by the new run-time model implementation to offer better performance.
Mitigations
These changes shouldn't affect most code. However, if you were using the extension
methods via the static invocation syntax, it would need to be converted to instance
invocation syntax.
IExecutionStrategy is now a singleton service
Tracking Issue #21350
New behavior
IExecutionStrategy is now a singleton service. This means that any added state in custom
implementations will remain between executions and the delegate passed to
ExecutionStrategy will only be executed once.
Why
This reduced allocations on two hot paths in EF.
Mitigations
Implementations deriving from ExecutionStrategy should clear any state in
OnFirstExecution().
Conditional logic in the delegate passed to ExecutionStrategy should be moved to a
custom implementation of IExecutionStrategy.
SQL Server: More errors are considered transient
Tracking Issue #25050
New behavior
The errors listed in the issue above are now considered transient. When using the
default (non-retrying) execution strategy, these errors will now be wrapped in an
addition exception instance.
Why
We continue to gather feedback from both users and SQL Server team on which errors
should be considered transient.
Mitigations
To change the set of errors that are considered transient, use a custom execution
strategy that could be derived from SqlServerRetryingExecutionStrategy - Connection
Resiliency - EF Core.
Azure Cosmos DB: More characters are escaped in 'id'
values
Tracking Issue #25100
Old behavior
In EF Core 5, only '|' was escaped in id values.
New behavior
In EF Core 6, '/' , '\' , '?' and '#' are also escaped in id values.
Why
These characters are invalid, as documented in Resource.Id. Using them in id will cause
queries to fail.
Mitigations
You can override the generated value by setting it before the entity is marked as Added :
C#
var entry = context.Attach(entity);
entry.Property("__id").CurrentValue = "MyEntity|/\\?#";
entry.State = EntityState.Added;
Some Singleton services are now Scoped
Tracking Issue #25084
New behavior
Many query services and some design-time services that were registered as Singleton
are now registered as Scoped .
Why
The lifetime had to be changed to allow a new feature - DefaultTypeMapping - to affect
queries.
The design-time services lifetimes have been adjusted to match the run-time services
lifetimes to avoid errors when using both.
Mitigations
Use TryAdd to register EF Core services using the default lifetime. Only use
TryAddProviderSpecificServices for services that are not added by EF.
New caching API for extensions that add or replace
services
Tracking Issue #19152
Old behavior
In EF Core 5, GetServiceProviderHashCode returned long and was used directly as part
of the cache key for the service provider.
New behavior
GetServiceProviderHashCode now returns int and is only used to calculate the hash
code of the cache key for the service provider.
Also, ShouldUseSameServiceProvider needs to be implemented to indicate whether the
current object represents the same service configuration and thus can use the same
service provider.
Why
Just using a hash code as part of the cache key resulted in occasional collisions that
were hard to diagnose and fix. The additional method ensures that the same service
provider is used only when appropriate.
Mitigations
Many extensions don't expose any options that affect registered services and can use
the following implementation of ShouldUseSameServiceProvider:
C#
private sealed class ExtensionInfo : DbContextOptionsExtensionInfo
{
public ExtensionInfo(IDbContextOptionsExtension extension)
: base(extension)
{
}
...
public override bool
ShouldUseSameServiceProvider(DbContextOptionsExtensionInfo other)
=> other is ExtensionInfo;
}
Otherwise, additional predicates should be added to compare all relevant options.
New snapshot and design-time model initialization
procedure
Tracking Issue #22031
Old behavior
In EF Core 5, specific conventions needed to be invoked before the snapshot model was
ready to be used.
New behavior
IModelRuntimeInitializer was introduced to hide some of the required steps, and a runtime model was introduced that doesn't have all the migrations metadata, so the
design-time model should be used for model diffing.
Why
IModelRuntimeInitializer abstracts away the model finalization steps, so these can now
be changed without further breaking changes for the users.
The optimized run-time model was introduced to improve run-time performance. It has
several optimizations, one of which is removing metadata that is not used at run-time.
Mitigations
The following snippet illustrates how to check whether the current model is different
from the snapshot model:
C#
var snapshotModel = migrationsAssembly.ModelSnapshot?.Model;
if (snapshotModel is IMutableModel mutableModel)
{
snapshotModel = mutableModel.FinalizeModel();
}
if (snapshotModel != null)
{
snapshotModel = context.GetService<IModelRuntimeInitializer>
().Initialize(snapshotModel);
}
var hasDifferences = context.GetService<IMigrationsModelDiffer>
().HasDifferences(
snapshotModel?.GetRelationalModel(),
context.GetService<IDesignTimeModel>().Model.GetRelationalModel());
This snippet shows how to implement IDesignTimeDbContextFactory<TContext> by
creating a model externally and calling UseModel:
C#
internal class MyDesignContext : IDesignTimeDbContextFactory<MyContext>
{
public TestContext CreateDbContext(string[] args)
{
var optionsBuilder = new DbContextOptionsBuilder();
optionsBuilder.UseSqlServer(Configuration.GetConnectionString("DB"));
var modelBuilder =
SqlServerConventionSetBuilder.CreateModelBuilder();
CustomizeModel(modelBuilder);
var model = modelBuilder.Model.FinalizeModel();
var serviceContext = new MyContext(optionsBuilder.Options);
model = serviceContext.GetService<IModelRuntimeInitializer>
().Initialize(model);
return new MyContext(optionsBuilder.Options);
}
}
OwnedNavigationBuilder.HasIndex returns a different type
now
Tracking Issue #24005
Old behavior
In EF Core 5, HasIndex returned IndexBuilder<TEntity> where TEntity is the owner
type.
New behavior
HasIndex now returns IndexBuilder<TDependentEntity> , where TDependentEntity is the
owned type.
Why
The returned builder object wasn't typed correctly.
Mitigations
Recompiling your assembly against the latest version of EF Core will be enough to fix
any issues caused by this change.
DbFunctionBuilder.HasSchema(null) overrides
[DbFunction(Schema = "schema")]
Tracking Issue #24228
Old behavior
In EF Core 5, calling HasSchema with null value didn't store the configuration source,
thus DbFunctionAttribute was able to override it.
New behavior
Calling HasSchema with null value now stores the configuration source and prevents
the attribute from overriding it.
Why
Configuration specified with the ModelBuilder API should not be overridable by data
annotations.
Mitigations
Remove the HasSchema call to let the attribute configure the schema.
Pre-initialized navigations are overridden by values from
database queries
Tracking Issue #23851
Old behavior
Navigation properties set to an empty object were left unchanged for tracking queries,
but were overwritten for non-tracking queries. For example, consider the following
entity types:
C#
public class Foo
{
public int Id { get; set; }
public Bar Bar { get; set; } = new(); // Don't do this.
}
public class Bar
{
public int Id { get; set; }
}
A no-tracking query for Foo including Bar set Foo.Bar to the entity queried from the
database. For example, this code:
C#
var foo = context.Foos.AsNoTracking().Include(e => e.Bar).Single();
Console.WriteLine($"Foo.Bar.Id = {foo.Bar.Id}");
Printed Foo.Bar.Id = 1 .
However, the same query run for tracking didn't overwrite Foo.Bar with the entity
queried from the database. For example, this code:
C#
var foo = context.Foos.Include(e => e.Bar).Single();
Console.WriteLine($"Foo.Bar.Id = {foo.Bar.Id}");
Printed Foo.Bar.Id = 0 .
New behavior
In EF Core 6.0, the behavior of tracking queries now matches that of no-tracking queries.
This means that both this code:
C#
var foo = context.Foos.AsNoTracking().Include(e => e.Bar).Single();
Console.WriteLine($"Foo.Bar.Id = {foo.Bar.Id}");
And this code:
C#
var foo = context.Foos.Include(e => e.Bar).Single();
Console.WriteLine($"Foo.Bar.Id = {foo.Bar.Id}");
Print Foo.Bar.Id = 1 .
Why
There are two reasons for making this change:
1. To ensure that tracking and no-tracking queries have consistent behavior.
2. When a database is queried it is reasonable to assume that the application code
wants to get back the values that are stored in the database.
Mitigations
There are two mitigations:
1. Do not query for objects from the database that should not be included in the
results. For example, in the code snippets above, do not Include Foo.Bar if the
Bar instance should not be returned from the database and included in the results.
2. Set the value of the navigation after querying from the database. For example, in
the code snippets above, call foo.Bar = new() after running the query.
Also, consider not initializing related entity instances to default objects. This implies that
the related instance is a new entity, not saved to the database, with no key value set. If
instead the related entity does exist in the database, then the data in code is
fundamentally at odds with the data stored in the database.
Unknown enum string values in the database are not
converted to the enum default when queried
Tracking Issue #24084
Old behavior
Enum properties can be mapped to string columns in the database using
HasConversion<string>() or EnumToStringConverter . This results in EF Core converting
string values in the column to matching members of the .NET enum type. However, if
the string value did not match and enum member, then the property was set to the
default value for the enum.
New behavior
EF Core 6.0 now throws an InvalidOperationException with the message "Cannot
convert string value ' {value} ' from the database to any value in the mapped
' {enumType} ' enum."
Why
Converting to the default value can result in database corruption if the entity is later
saved back to the database.
Mitigations
Ideally, ensure that the database column only contains valid values. Alternately,
implement a ValueConverter with the old behavior.
DbFunctionBuilder.HasTranslation now provides the
function arguments as IReadOnlyList rather than
IReadOnlyCollection
Tracking Issue #23565
Old behavior
When configuring translation for a user-defined function using HasTranslation method,
the arguments to the function were provided as IReadOnlyCollection<SqlExpression> .
New behavior
In EF Core 6.0, the arguments are now provided as IReadOnlyList<SqlExpression> .
Why
IReadOnlyList allows to use indexers, so the arguments are now easier to access.
Mitigations
None. IReadOnlyList implements IReadOnlyCollection interface, so the transition
should be straightforward.
Default table mapping is not removed when the entity is
mapped to a table-valued function
Tracking Issue #23408
Old behavior
When an entity was mapped to a table-valued function, its default mapping to a table
was removed.
New behavior
In EF Core 6.0, the entity is still mapped to a table using default mapping, even if it's also
mapped to table-valued function.
Why
Table-valued functions which return entities are often used either as a helper or to
encapsulate an operation returning a collection of entities, rather than as a strict
replacement of the entire table. This change aims to be more in line with the likely user
intention.
Mitigations
Mapping to a table can be explicitly disabled in the model configuration:
C#
modelBuilder.Entity<MyEntity>().ToTable((string)null);
dotnet-ef targets .NET 6
Tracking Issue #27787
Old behavior
The dotnet-ef command has targeted .NET Core 3.1 for a while now. This allowed you to
use newer version of the tool without installing newer versions of the .NET runtime.
New behavior
In EF Core 6.0.6, the dotnet-ef tool now targets .NET 6. You can still use the tool on
projects targeting older versions of .NET and .NET Core, but you'll need to install the
.NET 6 runtime in order to run the tool.
Why
The .NET 6.0.200 SDK updated the behavior of dotnet tool install on osx-arm64 to
create an osx-x64 shim for tools targeting .NET Core 3.1. In order to maintain a working
default experience for dotnet-ef, we had to update it to target .NET 6.
Mitigations
To run dotnet-ef without installing the .NET 6 runtime, you can install an older version of
the tool:
.NET CLI
dotnet tool install dotnet-ef --version 3.1.*
IModelCacheKeyFactory implementations may need to be
updated to handle design-time caching
Tracking Issue #25154
Old behavior
IModelCacheKeyFactory did not have an option to cache the design-time model
separately from the runtime model.
New behavior
IModelCacheKeyFactory has a new overload that allows the design-time model to be
cached separately from the runtime model. Not implementing this method may result in
an exception similar to:
System.InvalidOperationException: 'The requested configuration is not stored in the
read-optimized model, please use 'DbContext.GetService<IDesignTimeModel>
().Model'.'
Why
Implementation of compiled models required separation of the design-time (used when
building the model) and runtime (used when executing queries, etc.) models. If the
runtime code needs access to design-time information, then the design-time model
must be cached.
Mitigations
Implement the new overload. For example:
C#
public object Create(DbContext context, bool designTime)
=> context is DynamicContext dynamicContext
? (context.GetType(), dynamicContext.UseIntProperty, designTime)
: (object)context.GetType();
AddProvider checks for non-null
provider
Article • 11/08/2021
Microsoft.Extensions.Logging.LoggerFactory implements ILoggerFactory with an
AddProvider(ILoggerProvider) method. null providers aren't accepted and will cause
an ArgumentNullException to be thrown.
Version introduced
6.0 RC 1
Previous behavior
Previously, AddProvider(ILoggerProvider) did not perform any validation of the
provider argument. As such, the method considered null to be a "valid" provider and
added it to the collection of providers.
New behavior
Starting in .NET 6, null providers aren't accepted, and AddProvider(ILoggerProvider)
throws an ArgumentNullException if the logging provider argument is null . For
example, the following code throws an ArgumentNullException:
C#
var factory = new LoggerFactory();
((ILoggerFactory)factory).AddProvider(null));
Type of breaking change
This change can affect source compatibility.
Reason for change
The previous behavior caused some operations inside the class to unnecessarily throw
NullReferenceException exceptions. For example, the LoggerFactory.Dispose() method
will capture the exception and do nothing.
Recommended action
Ensure you're not passing a null provider to AddProvider(ILoggerProvider).
Affected APIs
Microsoft.Extensions.Logging.LoggerFactory.AddProvider(ILoggerProvider)
FileConfigurationProvider.Load throws
InvalidDataException
Article • 11/08/2021
When Load() fails to load a file, it throws an InvalidDataException. If the file or directory
doesn't exist, it throws a DirectoryNotFoundException or FileNotFoundException.
Version introduced
6.0 RC 1
Previous behavior
When loading failed, Load() did not throw an InvalidDataException.
New behavior
Starting in .NET 6, Load() throws an InvalidDataException if a file fails to load. In
addition, the exception message includes the file path that failed to load.
Type of breaking change
This change can affect source compatibility.
Reason for change
This change improves the debugging experience. When a file fails to load, it's helpful to
know which file failed to load.
Recommended action
If you are catching specific exceptions when calling Load(), make sure to also catch
InvalidDataException.
Affected APIs
Microsoft.Extensions.Configuration.FileConfigurationProvider.Load()
Repeated XML elements include index
Article • 10/05/2022
When Microsoft.Extensions.Configuration.Xml is used to read an XML document that
has repeated XML elements without a Name attribute, the Configuration entries created
with these repeated elements now have an index appended to their configuration path.
Version introduced
.NET 6
Previous behavior
Consider the following XML snippets that show repeated elements without a
distinguishing Name attribute.
XML
<settings>
<Data ConnectionString="TestConnectionString" />
<Data Provider="MySql" />
</settings>
XML
<configuration>
<Level1>
<Level2 Key1="Value1" />
<Level2 Key2="Value2" />
</Level1>
</configuration>
The configurations created from these XML files were:
txt
Data:ConnectionString = TestConnectionString
Data:Provider = MySql
and
txt
Level1:Level2:Key1 = Value1
Level1:Level2:Key2 = Value2
respectively.
New behavior
The configurations created from the XML files in the Previous behavior section are now:
txt
Data:0:ConnectionString = TestConnectionString
Data:1:Provider = MySql
and
txt
Level1:Level2:0:Key1 = Value1
Level1:Level2:1:Key2 = Value2
respectively.
Type of breaking change
This change can affect binary compatibility.
Reason for change
This change was introduced to fully support repeated XML elements that don't have a
Name attribute. The previous behavior only allowed for repeated elements to set unique
values (using attributes or subelements). If repeated XML elements had the same
attribute, an exception was thrown.
Recommended action
To get the original behavior, you can update your XML to collapse the two attributes
into the same element. For example:
XML
<configuration>
<Level1>
<Level2 Key1="Value1" Key2="Value2" />
</Level1>
</configuration>
Alternatively, you can update your code to expect indices (such as 0, 1, 2) in the
IConfiguration keys:
C#
configRoot.GetSection("Level1:Level2")
becomes
C#
configRoot.GetSection("Level1:Level2:0")
Affected APIs
Microsoft.Extensions.Configuration.XmlConfigurationExtensions
Microsoft.Extensions.Configuration.Xml.XmlStreamConfigurationProvider
Microsoft.Extensions.Configuration.Xml.XmlConfigurationSource
See also
XML configuration provider
Resolving disposed ServiceProvider
throws exception
Article • 11/10/2021
When a service is resolved after the service provider has been disposed, the affected
methods now throw an ObjectDisposedException instead of causing a deadlock.
Version introduced
6.0 RC 1
Previous behavior
Previously, in the rare case that an application resolved a service after the service
provider was disposed, it led to a deadlock.
New behavior
Starting in .NET 6, an ObjectDisposedException is thrown when a service is resolved after
the service provider has been disposed, and there's no deadlock.
Type of breaking change
This change can affect source compatibility.
Reason for change
This change was introduced to fix the deadlock scenario.
Recommended action
Catch ObjectDisposedException when calling any of the affected APIs.
Affected APIs
System.IServiceProvider.GetService(Type)
Microsoft.Extensions.DependencyInjection.ISupportRequiredService.GetRequiredSe
rvice(Type)
Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRe
quiredService(IServiceProvider, Type)
Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRe
quiredService<T>(IServiceProvider)
Culture creation and case mapping in
globalization-invariant mode
Article • 01/27/2022
This breaking change affects globalization-invariant mode
in two ways:
Previously, .NET allowed any culture to be created in globalization-invariant mode,
as long as the culture name conformed to BCP-47
. However, the invariant
culture data was used instead of the real culture data. Starting in .NET 6, an
exception is thrown if you create any culture other than the invariant culture in
globalization-invariant mode.
Previously, globalization-invariant mode only supported case mapping for ASCII
characters. Starting in .NET 6, globalization-invariant mode provides full casemapping support for all Unicode-defined characters. Case mapping is used in
operations such as string comparisons, string searches, and upper or lower casing
strings.
Globalization-invariant mode
is used for apps that don't required any globalization
support. That is, the app runs without access to culture-specific data and behavior.
Globalization-invariant mode is enabled by default on some Docker containers, for
example, Alpine containers.
Old behavior
In previous .NET versions when globalization-invariant mode is enabled:
If an app creates a culture that's not the invariant culture, the operation succeeds
but the returned culture always use the invariant culture data instead of the real
culture data.
Case mapping was performed only for ASCII characters. For example:
C#
if ("Á".Equals("á", StringComparison.CurrentCultureIgnoreCase)) //
Evaluates to false.
New behavior
Starting in .NET 6 when globalization-invariant mode is enabled:
If an app attempts to create a culture that's not the invariant culture, a
CultureNotFoundException exception is thrown.
Case mapping is performed for all Unicode-defined characters. For example:
C#
if ("Á".Equals("á", StringComparison.CurrentCultureIgnoreCase)) //
Evaluates to true.
Version introduced
.NET 6
Reason for change
The culture-creation change was introduced to more easily diagnose culture-related
problems. Some users are unaware that their apps are running in an environment where
globalization-invariant mode is enabled. They may experience unexpected behavior and
don't make the association with globalization-invariant mode, so it's hard to diagnose
the issue.
The full case-mapping support was introduced for better usability and experience in
globalization-invariant mode.
Recommended action
In most cases, no action is needed. However, if you desire the previous culture-creation
behavior, you can set a runtime configuration option to allow creation of any culture in
globalization-invariant mode. For more information, see Predefined cultures.
Affected APIs
CultureInfo
System.Globalization.CultureInfo.CreateSpecificCulture(String)
System.Globalization.CultureInfo.GetCultureInfo
RegionInfo
Any APIs that perform string casing, comparison, or searching
See also
.NET globalization invariant mode
Invariant, neutral, and specific cultures
Static abstract members declared in
interfaces
Article • 09/29/2022
.NET 6 previews a new feature where static interface members can be marked as
abstract . This feature involves several changes to the ECMA 335 spec to allow
intermediate language (IL) metadata patterns that were previously considered illegal.
For more information, see dotnet/runtime#49558 .
Old behavior
If a static interface was marked as abstract :
The C# compiler generated error CS0112.
Tools and other compilers generated illegal IL metadata.
New behavior
Starting in .NET 6, static interface members can be marked as abstract and will compile
successfully. In addition, the IL metadata patterns that are generated are now
considered legal due to changes in the ECMA 335 spec.
The implementation of static abstract interface members is provided by types that
implement the interface.
7 Note
For .NET 6, you must enable preview features in your project to be able to mark an
interface member as static abstract .
Since this is a newly legal IL pattern, existing tooling may incorrectly process the
associated metadata and have unexpected behavior. It's likely that tooling will
encounter the new metadata pattern, because interfaces with static abstract members
now appear on the primitive types, for example, System.Int32.
Version introduced
.NET 6
Type of breaking change
This change can affect binary compatibility.
Reason for change
This change was introduced because there was no way to abstract over static members
and write generalized code that applies across types that define those static members.
This was particularly problematic for member kinds that only exist in a static form, for
example, operators.
Recommended action
Update any tooling that consumes .NET binaries or C# source code to account for the
new concept of static abstract interface members, including those that now exist on
the .NET primitive types.
Affected APIs
N/A
See also
Tutorial: Explore static virtual members in interfaces
Coerce call arguments according to
ECMA-335
Article • 07/12/2022
ECMA-335
(Table III.9: Signature Matching) describes which implicit conversions are
supported for call arguments. This change adds checking for the supported conversions.
Version introduced
6.0
Change description
In previous .NET versions, the just-in-time (JIT) compiler does not coerce call arguments
according to ECMA-335. This leads to undefined behavior on some platforms. For
example, on x86, passing a long value as an int register leaves the register undefined.
Starting in .NET 6, if implicit conversion is not allowed, then the JIT compiler throws an
InvalidProgramException. There are two conversion cases that are still allowed:
int8 -> nint on 64-bit platform (because it's used often and doesn't lead to bad
code)
byref -> nint
Reason for change
The previous behavior caused silent, bad code generation on some platforms, including
Arm64 Apple.
Recommended action
If you updated to .NET 6 and your app throws InvalidProgramException exceptions
because of this change, use an explicit conversion for the affected argument or fix the
callee declaration.
Affected APIs
None.
Port removed from SPN for Kerberos
and Negotiate
Article • 11/08/2021
When using HttpClient with Kerberos or Negotiate authentication, non-default ports are
no longer included in service principal names (SPN) to look up services. This new .NET 6
behavior is consistent with .NET Core 3.1 and earlier versions.
Previous behavior
If you connected to a service on a non-default port, .NET 5 included a port component
when constructing the SPN to look up the service.
New behavior
Starting in .NET 6, by default, the SPN is not constructed with a port component, even
for non-default ports.
Version introduced
6.0 RC 1
Type of breaking change
This change can affect binary compatibility.
Reason for change
We want to bring back the behavior from .NET Core 1.0 - 3.1 that customers had started
to depend on.
Recommended action
If you need to preserve .NET 5 behavior, you can set the app context switch
System.Net.Http.UsePortInSpn or the environment variable
DOTNET_SYSTEM_NET_HTTP_USEPORTINSPN to true .
Affected APIs
System.Net.Http.HttpClient behavior
WebRequest, WebClient, and
ServicePoint are obsolete
Article • 11/17/2021
WebRequest, WebClient, and ServicePoint classes are marked as obsolete and generate
a SYSLIB0014 warning at compile time.
Version introduced
6.0
Change description
WebRequest, WebClient, and ServicePoint classes were added to .NET Core in version
2.0 for backward compatibility. However, they introduced several runtime breaking
changes, for example, WebRequest.GetRequestStream allocates memory for the whole
response, and WebClient.CancelAsync doesn't always cancel immediately.
Starting in .NET 6, the WebRequest, WebClient, and ServicePoint classes are deprecated.
The classes are still available, but they're not recommended for new development. To
reduce the number of analyzer warnings, only construction methods are decorated with
the ObsoleteAttribute attribute.
Recommended action
Use the System.Net.Http.HttpClient class instead.
For FTP, since HttpClient doesn't support it, we recommend using a third-party library.
Affected APIs
WebRequest
HttpWebRequest
FtpWebRequest
WebClient
ServicePoint
-p option for dotnet run is deprecated
Article • 12/02/2021
-p is deprecated as an abbreviation for --project , and using -p generates a warning.
This warning comes from the CLI parser, so it won't generally cause failures when
warnings are treated as errors. However, if your process wraps MSBuild or CI and checks
for the text "Warning", the warning will appear in that check.
Version introduced
.NET SDK 6.0.100
Old behavior
In previous .NET versions, -p indicated --project .
New behavior
Starting in .NET 6, passing -p to dotnet run results in a warning that it is deprecated
and to use the full --project option instead. Despite the warning, -p is still a valid
abbreviation for --project .
Reason for change
We are deprecating -p because of the close relationship dotnet run has with dotnet
build and dotnet publish . This breaking change is the first step in aligning
abbreviations for these commands. For more information, see Spec for resolving '-p' in
'dotnet run' .
Recommended action
If you encounter the new warning, use --project . If you have a project argument that
includes an = and you use the -p abbreviation, the option will be interpreted as -property .
Review any scripts that use dotnet run where you might overlook the warning if -p is
used.
If you have any scripts that are using dotnet run and process the output, you could
encounter a break. dotnet run typically doesn't output anything of its own if there are
no errors, so you only get the output of the program that's being run. If you have a
script or other program that wraps dotnet run and parses the output, the warning
would be unexpected text and may cause a failure.
Affected APIs
N/A
See also
Spec for resolving '-p' in 'dotnet run'
C# code in templates not supported by
earlier versions
Article • 11/29/2022
Starting in .NET 6, the project templates that ship with the .NET SDK use the latest C#
language features. The following language features are used in template-generated
code or enabled by default in the project:
Top-level statements
Global using directives
File-scoped namespaces
Target-typed new expressions
Nullable reference types
Async Main return values
Some of the latest C# language features are not supported by previous target
frameworks, so you might experience issues in the following scenarios:
When you use a .NET 6 template and then change the target framework to a
previous version.
When you use a .NET 6 template and then multi-target by adding a previous target
framework version.
Version introduced
.NET SDK 6.0.100
Old behavior
In previous .NET versions, you can change the target framework to a previous version or
add an additional version without having to change the C# code created by the project
template.
New behavior
Starting in .NET 6, when you change or add a target framework, you may need to
change the C# code generated by the template to avoid using unsupported language
features. The project won't build without these changes. The compiler errors and
warnings usually guide you on how to change the generated code to make it
compatible with new target framework.
Change category
This change affects source compatibility.
Reason for change
We want the project templates to be synchronized with the latest language features. The
main usage scenario of targeting the latest framework uses the latest C# language
features.
Recommended action
If you encounter compile-time errors and warnings when you retarget to an earlier
framework version, use that information to guide you in changing the generated code
to make it compatible with the target framework you selected.
To avoid compile-time errors when targeting an older framework, follow these
suggestions:
Avoid changing the target framework to a previous version. Instead, select the
target framework you want during project creation in Visual Studio or at the
command line by using dotnet new <templateName> --framework
<targetFramework> .
When creating a multi-target project, select the lowest target-framework version
when you create the project. Add the additional target frameworks after the
project is created.
Affected APIs
N/A
EditorConfig files implicitly included
Article • 01/24/2022
Roslyn analyzers added support for parsing and respecting .editorconfig file options
before the compiler added support for these files. To work around this limitation,
.editorconfig files had to be included as AdditionalFiles project items. Now that the
compiler implicitly includes .editorconfig files in a project, you'll get an error if you
include them as AdditionalFiles project items.
Version introduced
.NET 6
Previous behavior
.editorconfig files could be included as AdditionalFiles project items.
New behavior
Starting with the .NET 6 SDK, you'll get the following error at compile time if you include
an .editorconfig file as an AdditionalFiles project item:
error AD0001: Analyzer [...] threw an exception of type
'System.InvalidOperationException' with message 'Passing '.editorconfig' files as
additional files is no longer needed. It will be implicitly discovered (if the file is in the
project's directory or any ancestor directory), or it should be converted into a
'globalconfig'.
7 Note
This warning only appears in .NET 6. It was removed in .NET 7.
Change category
This change may affect source compatibility.
Reason for change
The compiler now supports .editorconfig files, and they're implicitly included in a project
if the file is in the project directory or an ancestor directory.
Recommended action
If the .editorconfig file is in the project directory or an ancestor directory, remove
the <AdditionalFiles> item for the .editorconfig file from your project file.
Otherwise, convert the .editorconfig file to a Global AnalyzerConfig file, and change
the AdditionalFiles item to a GlobalAnalyzerConfigFiles item in your project file.
Affected APIs
N/A
Generate apphost for macOS
Article • 12/11/2021
When building on macOS, the .NET SDK now produces an executable for app projects.
Since .NET Core 3.0, the .NET SDK has created an executable for app projects when
building on non-macOS operating systems. However, it did not create an executable on
macOS since we weren't signing this binary. That resulted in an app that was recognized
as dangerous by the OS, which made it hard for the user to run it. The .NET 6 SDK can
sign the app executable, so it now produces the executable by default.
Version introduced
.NET SDK 6.0.100
Old behavior
The apphost executable was not generated by default. You could explicitly ask the SDK
to generate an executable by setting the UseAppHost property to true .
New behavior
When you run dotnet build or dotnet publish , the apphost is now generated by
default and is signed using the native command-line codesign, making it easier for users
to execute the binary.
Reason for change
We implemented the necessary changes in the HostModel to be able to code-sign
executables.
Recommended action
If your app targets macOS and you don't want the apphost to be generated, set the
UseAppHost property to false to prevent the SDK from generating this file. This setting
does not affect dotnet tool install , which always generates an executable.
Affected APIs
N/A
Generate error for duplicate files in
publish output
Article • 12/02/2021
The .NET SDK generates a new error ( NETSDK1152 ) in cases where files from different
source paths would be copied to the same file path in the publish output. This can
happen when a project and its project references include a file with the same name
that's included in the publish output.
Version introduced
.NET SDK 6.0.100
Old behavior
Both files were copied to the same destination. The second file to be copied overwrote
the first file, and which file "won" was mostly arbitrary.
In some cases, the build failed. For example, when trying to create a single-file app, the
bundler failed with an ArgumentException, as shown in the following build output:
shell
C:\Program
Files\dotnet\sdk\5.0.403\Sdks\Microsoft.NET.Sdk\targets\Microsoft.NET.Publis
h.targets(962,5): error MSB4018: The "GenerateBundle" task failed
unexpectedly. [C:\repro\repro.csproj]
C:\Program
Files\dotnet\sdk\5.0.403\Sdks\Microsoft.NET.Sdk\targets\Microsoft.NET.Publis
h.targets(962,5): error MSB4018: System.ArgumentException: Invalid input
specification: Found multiple entries with the same BundleRelativePath
[C:\repro\repro.csproj]
C:\Program
Files\dotnet\sdk\5.0.403\Sdks\Microsoft.NET.Sdk\targets\Microsoft.NET.Publis
h.targets(962,5): error MSB4018: at
Microsoft.NET.HostModel.Bundle.Bundler.GenerateBundle(IReadOnlyList`1
fileSpecs) [C:\repro\repro.csproj]
New behavior
Starting in .NET 6, MSBuild removes duplicate files that are copied to the publish folder
if both the source and destination are the same. If there are any remaining duplicates, a
NETSDK1152 error is generated and lists the files that are duplicated.
Reason for change
Duplicate files in the publish output sometimes caused build breaks or unpredictable
behavior.
Recommended action
Ideally, you should update your project to avoid situations where multiple files with
the same name are copied to the publish output. The error message includes the
name of the duplicate file. Some causes for duplicate files include:
An ASP.NET Core project that references an ASP.NET Core web service, and each
has its own appsettings.json file.
A project item where CopyToOutputDirectory is unnecessarily set to Always .
Binary log files
can be useful for finding the cause of the duplicated files.
Alternatively, you can set the ErrorOnDuplicatePublishOutputFiles property to
false .
Affected APIs
N/A
GetTargetFrameworkProperties and
GetNearestTargetFramework removed
from ProjectReference protocol
Article • 12/02/2021
The GetTargetFrameworkProperties target and GetNearestTargetFramework task have
been removed from the .NET SDK.
Version introduced
.NET SDK 6.0.100
Old behavior
The GetTargetFrameworkProperties target and the GetNearestTargetFramework task were
available but not used. However, custom MSBuild logic could take a dependency on
them.
New behavior
The GetTargetFrameworkProperties target and the GetNearestTargetFramework task no
longer exist.
Reason for change
The GetTargetFrameworkProperties target and the GetNearestTargetFramework task were
deprecated in MSBuild 15.5, and .NET 6 removes them completely.
Recommended action
Use GetTargetFrameworks instead. For more information, see Targets required to be
referenceable .
Affected APIs
N/A
Install location for x64 emulated on
Arm64
Article • 07/15/2022
We've moved the install location of the x64 version of the .NET SDK and runtime on
Arm64 hardware.
Previously, the x64 and Arm64 versions installed to the same location, leading to a
broken state. In addition, the PATH environment variable value was being set for both
installations, so depending on install order, you might have an unexpected version of
the dotnet command used by default.
Version introduced
.NET 6 RC 2
Previous behavior
In previous versions, both the Arm64 and x64 versions of .NET SDK and runtime installed
to the same location on Arm64 hardware:
macOS: /usr/local/share/dotnet
Windows: %ProgramFiles%\dotnet
This worked if only one version was installed, but was completely broken if both were
installed.
New behavior
In .NET 6, the x64 version of .NET installs to a subfolder named x64 on Arm64 hardware:
macOS: /usr/local/share/dotnet/x64
Windows: %ProgramFiles%\dotnet\x64
For more information, see Install location .
Change category
This change may affect source compatibility.
Reason for change
Without this change, the x64 and Arm64 versions of the .NET SDK and .NET runtime
install to the same location on Arm64 hardware. This leads to being in a completely
broken state. This change allows customers to develop for both x64 and Arm64 at the
same time.
Recommended action
If you need to use the x64 version of the dotnet command, manually add that file path
to the PATH environment variable.
Affected APIs
N/A
See also
x64 emulation model
MSBuild no longer supports calling
GetType()
Article • 09/29/2021
MSBuild 17 no longer supports calling the GetType() instance method within property
functions. This method allowed unpredictable code execution during evaluation and
could cause Visual Studio hangs.
Version introduced
.NET SDK 6.0.100-rc1
Previous behavior
GetType() calls in MSBuild property functions would execute and sometimes caused
unpredictable behavior in Visual Studio.
New behavior
Starting with the .NET 6 SDK, if you call GetType() in an MSBuild property function,
you'll see the following compile-time error during project evaluation:
The function "GetType" on type "System.String" is not available for execution as an
MSBuild property function.
Change category
This change affects source compatibility.
Reason for change
This functionality was not documented or commonly used. It caused performance and
reliability issues with project loading, especially in Visual Studio.
The only known common use of this pattern was in the CBT system
deprecated.
, which has been
Recommended action
Replace any calls to GetType() with alternative MSBuild logic.
Affected APIs
N/A
.NET can't be installed to custom
location
Article • 08/08/2023
You can no longer change the installation path of .NET with the Windows Installer
package. To install .NET to a different path, use the dotnet-install scripts.
Version introduced
.NET 6
Old behavior
Previously, you could set DOTNET_HOME prior to running the Windows MSI installer to
install to a location other than Program Files\dotnet.
New behavior
Starting in .NET 6, DOTNET_HOME is ignored and the SDK and runtime will always install
under Program Files\dotnet on Windows. This impacts all .NET installers, including all
three runtimes, the hosting bundle, and the SDK installer. It also impacts all
architectures, even though the driver of the change was ARM64 support.
Reason for change
To support SxS architecture installs on ARM64, the x64 version of dotnet must be
installed to a location known to the ARM64 dotnet. This means that the native
architecture version of dotnet goes in Program Files\dotnet. And on ARM64, the x64
version is installed to Program Files\dotnet\x64, so it can be found when multiple
platforms are targeted.
Recommended action
To install to a custom location, use install scripts instead.
Affected APIs
N/A
OutputType not changed from Exe to
WinExe for Windows Forms and WPF
projects
Article • 09/15/2021
In .NET 5, a change was made to automatically change OutputType from Exe to WinExe
for WPF and Windows Forms apps. In .NET 6, we are reverting that change and
OutputType will no longer be changed by the SDK.
Version introduced
.NET 6 RC 1
Previous behavior
If a project targeted .NET 5 or higher, OutputType was set to Exe , and UseWindowsForms
or UseWPF was set to true , then the .NET SDK would change OutputType to WinExe .
New behavior
OutputType is no longer changed from what's in the project file.
XML
<PropertyGroup>
<OutputType>Exe</OutputType>
</PropertyGroup>
Change category
This change may affect source compatibility.
Reason for change
The .NET 5 change was intended to simplify .NET MAUI apps, so that OutputType
wouldn't need to be conditioned on the target framework. However:
Automatically inferring OutputType broke user expectations and frustrated
developers. For more information, see dotnet/sdk#16563
and its linked issues.
.NET MAUI apps will use WinUI by default, not Windows Forms or WPF, so the
automatic inference doesn't even apply to .NET MAUI apps.
Recommended action
If you relied on the fact that OutputType was changed from Exe to WinExe , you should
explicitly set it to WinExe in the project file.
If you were impacted by the previous breaking change and had to set
DisableWinExeOutputInference in order to disable the logic that was added in .NET 5,
you can remove that property now.
Affected APIs
N/A
See also
OutputType set to WinExe for WPF and WinForms apps
Publishing a ReadyToRun project with -no-restore requires changes to the
restore
Article • 05/17/2022
If you publish a project with -p:PublishReadyToRun=true in addition to --no-restore , the
project will only build with packages that were restored in a prior dotnet restore
operation. In .NET 5, this process worked and resulted in a crossgen'd binary. In .NET 6,
this same process will fail with the NETSDK1095 error.
Version introduced
.NET 6
Previous behavior
In previous versions, the crossgen application was packaged with the runtime. As a
result, the crossgen process was able to run on your application regardless of if the
project had been restored or not. It was a common practice to separate dotnet restore
from dotnet publish , adding --no-restore to the publish command to ensure that no
additional network accesses occurred.
New behavior
In .NET 6, dotnet restore followed by dotnet publish -p:PublishReadyToRun=true --norestore will fail with the NETSDK1095 error. This is because the crossgen binary is now
shipped as a separate NuGet package, and so needs to be part of the restore operation
for publishing to succeed.
Reason for change
The crossgen binary is not required for many workloads, so it was split out from the
main SDK. It is typically acquired on-demand, and the publishing MSBuild targets now
handle this acquisition by adding the package to the list of packages to be restored.
Recommended action
If you want to maintain an isolated publish experience, tell the restore step that
you'll be publishing ReadyToRun. Add -p:PublishReadyToRun=true to your restore
command line as well.
Or, remove --no-restore from your publish command line to allow the publish
command to restore crossgen as well.
runtimeconfig.dev.json file not
generated
Article • 10/13/2022
The .NET SDK no longer generates the runtimeconfig.dev.json file by default. This file was
not used in most scenarios, and finding and loading the file added small amounts of
performance overhead.
Previous behavior
An [Appname].runtimeconfig.dev.json file was automatically generated at compile time.
New behavior
The [Appname].runtimeconfig.dev.json file is no longer generated by default.
Version introduced
.NET SDK 6.0.100
Change category
This change may affect binary compatibility.
Reason for change
Not generating the file provides a small but measurable performance improvement.
Recommended action
This change should not affect most customers. However, if you still require this file, add
<GenerateRuntimeConfigDevFile>true</GenerateRuntimeConfigDevFile> to your project.
For more information, see GenerateRuntimeConfigDevFile.
Affected APIs
N/A
RuntimeIdentifier warning if selfcontained is unspecified
Article • 06/02/2022
If you specify a RuntimeIdentifier in your project file or use the -r option with dotnet ,
the .NET SDK defaults the build, publish, and run outputs to be self-contained
applications. The default without specifying a RuntimeIdentifier is to have a
framework-dependent application. This change introduces a new warning (NETSDK1179)
if you specify a RuntimeIdentifier without specifying whether the application is selfcontained.
Version introduced
.NET 6 RC 1
Previous behavior
In previous versions, specifying a RuntimeIdentifier would silently change the
application from a framework-dependent application to a self-contained application.
New behavior
In .NET 6, if you specify a RuntimeIdentifier without specifying whether the application
is self-contained, you'll get the following warning:
warning NETSDK1179: One of '--self-contained' or '--no-self-contained' options are
required when '--runtime' is used.
For example, the following command will generate the warning:
.NET CLI
dotnet publish -r win-x86
Change category
This change may affect source compatibility.
Reason for change
The default without specifying a RuntimeIdentifier is to generate a frameworkdependent application. This default caused confusion for many customers. The purpose
of adding the warning is to:
Warn customers of the behavior change to default to a framework-dependent app.
Encourage customers to specifically choose the type of application they want to
build.
Prepare customers for possibly changing the behavior in .NET 7 to default to
framework-dependent.
Recommended action
Specify a Boolean value in your project file for SelfContained .
Or, add --self-contained with a value to your build or publish command.
Affected APIs
N/A
See also
dotnet build command
dotnet publish command
Tool manifests in root folder
Article • 08/01/2023
.NET no longer looks for local tool manifest files in the root folder on Windows, unless
overridden via the DOTNET_TOOLS_ALLOW_MANIFEST_IN_ROOT environment variable. This
change does not impact Linux or macOS.
Previous behavior
Previously, .NET SDK local tools checked the root folder on all platforms when searching
for a tool manifest. The search continued from the current directory up the directory
tree to the root folder until it found a manifest. At each level, .NET searches for the tool
manifest, named dotnet-tools.json, in a .config subfolder. On a Windows system, if no
other tool manifest was found, the SDK ultimately looked for a tool manifest in
C:\.config\dotnet-tools.json.
New behavior
.NET no longer searches in the root folder of the current directory tree by default on
Windows, unless overridden via the DOTNET_TOOLS_ALLOW_MANIFEST_IN_ROOT environment
variable. DOTNET_TOOLS_ALLOW_MANIFEST_IN_ROOT is set to false by default.
Version introduced
.NET SDK 7.0.3xx
.NET SDK 7.0.1xx
.NET SDK 6.0.4xx
.NET SDK 6.0.3xx
.NET SDK 6.0.1xx
.NET SDK 3.1.4xx
Type of breaking change
This change is a behavioral change.
Reason for change
This change was made to address a security concern. Since all users can create files and
folders in the C:\ directory on Windows, low-privilege attackers can hijack the
C:\.config\dotnet-tools.json file. When an administrator runs a dotnet tool command, the
tool could potentially read malicious configuration information from the file and
download and run malicious tools.
Recommended action
To disable the new behavior, set the DOTNET_TOOLS_ALLOW_MANIFEST_IN_ROOT environment
variable to true or 1 .
See also
Tutorial: Install and use a .NET local tool using the .NET CLI
Version requirements for .NET 6 SDK
Article • 05/03/2022
Starting with the .NET SDK 6.0.300, the .NET SDK no longer loads in version 16.11 or
earlier of Visual Studio or MSBuild.
Version introduced
.NET SDK 6.0.300
Old behavior
The .NET SDK would load in the 16.10 and 16.11 versions of Visual Studio and MSBuild.
New behavior
The .NET SDK can only be used with version 17.0 or later of Visual Studio and MSBuild.
In addition, any scenarios that use a source generator could fail when using a Visual
Studio or MSBuild version earlier than version 17.2.
Reason for change
Changes were made to features within the SDK that aren't compatible with Visual Studio
version 16.11.
Recommended action
Upgrade to Visual Studio 2022 version 17.0 or later. It's recommended that you use
version 17.2 preview builds.
Affected APIs
N/A
.version file includes build version
Article • 09/20/2022
The .NET SDK installation folder (under dotnet/sdk/<version>) contains a .version file.
This file includes the following data:
The hash for the commit for that version of the .NET SDK.
The stable version.
The runtime identifier (RID) of the .NET SDK.
This change adds the specific build number from the product's build system.
For example, the file now includes information similar to the following:
txt
baae4cac8b288405720786a3bcd35ee5a6086f1b
6.0.401
win-x64
6.0.401-servicing.22414.4
Previous behavior
The dotnet/sdk/<version>.version file contained three values:
Git commit hash
Simplified version
RID
New behavior
The dotnet/sdk/<version>.version file contains four values:
Git commit hash
Simplified Version
RID
Precise build version
Version introduced
.NET SDK 6.0.401
Reason for change
This change was made to enable internal testing of the dotnet-install script. The
simpler, short build version cannot be used by the dotnet-install script prior to
release.
Recommended action
No action is necessary unless you have tooling that parses the .version file. If you do,
you'll need to account for a fourth entry in the file.
Write reference assemblies to
intermediate output
Article • 05/21/2022
The .NET SDK now writes reference assemblies to the IntermediateOutputPath instead of
the OutDir by default. This change removes these build-time-only artifacts from outputs
that you require at run time.
Version introduced
.NET SDK 6.0.200
Old behavior
Since reference assemblies were added, the .NET SDK has written reference assemblies
to the ref directory in the OutDir directory of the compilation. In .NET 5, the
ProduceReferenceAssembly property was introduced and defaulted to true for net5.0
and later applications. With this change, many applications began implicitly generating
reference assemblies in the OutDir directory.
New behavior
Now, reference assemblies are written to the refint directory of the
IntermediateOutputPath directory by default, like many other intermediate artifacts.
Reason for change
Reference assemblies are generally not run-time assets, and so don't belong in the
OutDir directory by default.
Recommended action
If you have custom build logic and you need to manipulate the reference assemblies,
use the TargetRefPath property to get the correct path.
If an external system requires the reference assembly in OutDir , set the MSBuild
property ProduceReferenceAssemblyInOutDir to true in your project file.
DataContractSerializer retains sign when
deserializing -0
Article • 11/08/2022
DataContractSerializer and DataContractJsonSerializer previously discarded the sign
when deserializing the input "-0" as a float or double. Both serializers have always done
the right thing when given "-0.0" as an input, but with an input of "-0", the sign was lost.
This behavior is both inconsistent and results in data loss. In addition, these serializers
write a value of negative zero out as "-0" during serialization.
Previous behavior
Previously, the negative sign was lost when deserializing "-0" (but not "-0.0") as a float or
double using DataContractSerializer.
New behavior
The negative sign is now preserved when deserializing "-0" as a float or double.
Version introduced
.NET 6.0.11 (servicing release)
.NET 7
Type of breaking change
This change can affect binary compatibility.
Reason for change
The previous behavior was inconsistent and resulted in data loss.
Recommended action
In most cases, no action is needed. If your code was affected by the bug, then this is a
good change. Or, you've already worked around the bug in a way that's unlikely to be
broken by this change.
Affected APIs
System.Runtime.Serialization.DataContractSerializer.ReadObject
System.Runtime.Serialization.Json.DataContractJsonSerializer.ReadObject
Default serialization format for
TimeSpan
Article • 09/22/2022
System.Text.Json added support for TimeSpan in .NET 6 GA, however this change did not
include support for source generators. In .NET 6 servicing release 6.0.2, System.Text.Json
includes support for source-generator serialization of TimeSpan values. This support
changes the default serialization format for TimeSpan values in source generators.
Previous behavior
In .NET 6 GA, source generators serialize TimeSpan values by outputting all public
properties of the type, which is the default serialization behavior for objects:
JSON
{"days":0,"hours":0,"milliseconds":0,"minutes":0,"seconds":1,"ticks":1000000
0,"totalDays":1.1574074074074073E05,"totalHours":0.0002777777777777778,"totalMilliseconds":1000,"totalMinutes
":0.016666666666666666,"totalSeconds":1}
New behavior
In servicing release .NET 6.0.2, source generators serialize TimeSpan values in the
following format, which is consistent with the reflection-based serializer format:
JSON
"00:00:01"
Version introduced
.NET 6.0.2 (servicing release)
Type of breaking change
This change may affect binary compatibility.
Reason for change
System.Text.Json source generation is a new feature, and its serialization behavior should
be as consistent as possible with the reflection-based serializer. This change simplifies
migration to source generators.
Recommended action
It's unlikely for users to depend on the current TimeSpan serialization format, as it
redundantly outputs all public properties of the type (which is the default serialization
behavior for objects), and it doesn't roundtrip.
If you do depend on the existing behavior, the recommended course of action is to
author a custom converter that outputs the needed properties from TimeSpan:
C#
public class TimeSpanConverter : JsonConverter<TimeSpan>
{
public void WriteValue(Utf8JsonWriter writer, TimeSpan value,
JsonSerializerOptions options)
{
writer.WriteStartObject();
writer.WriteNumber("days", value.Days);
writer.WriteNumber("hours", value.Hours);
/* insert any needed properties here */
writer.WriteEndObject();
}
}
Affected APIs
System.Text.Json.JsonSerializer.Serialize
See also
How to use source generation in System.Text.Json
System.Text.Json IAsyncEnumerable
serialization
Article • 09/22/2022
System.Text.Json now supports serializing and deserializing of IAsyncEnumerable<T>
instances.
Previous behavior
In previous versions, System.Text.Json serialized IAsyncEnumerable<T> instances as
empty JSON objects ( {} ). Deserialization failed with a JsonException.
New behavior
Asynchronous serialization methods now enumerate any IAsyncEnumerable<T>
instances in an object graph and then serialize them as JSON arrays. Synchronous
serializations methods do not support IAsyncEnumerable<T> serialization and throw a
NotSupportedException.
Version introduced
.NET 6
Type of breaking change
This change can affect source compatibility. If you retarget to .NET 6, you won't get any
compile-time errors, but you may encounter run-time serialization behavior differences.
Reason for change
This change was introduced to add support for streaming IAsyncEnumerable<T>
responses in ASP.NET Core MVC.
Recommended action
Check if your serialization models contain types that implement IAsyncEnumerable<T>
and determine if emitting the enumeration in the JSON output is desirable. You can
disable IAsyncEnumerable<T> serialization in one of the following ways:
Attach a JsonIgnoreAttribute to the property containing the
IAsyncEnumerable<T>.
Define a custom converter factory that serializes IAsyncEnumerable<T> instances
as empty JSON objects.
Affected APIs
System.Text.Json.JsonSerializer.SerializeAsync
System.Text.Json.JsonSerializer.Serialize
See also
System.Text.Json support for IAsyncEnumerable
JSON source-generation API refactoring
Article • 09/22/2022
The APIs that the output of the JSON source generator calls have been refactored. The
refactoring makes them easier to extend with new features in the future. Projects that
explicitly use the JSON source generator and were compiled with .NET 6 RC 1 or earlier
will fail with run-time exceptions when run on the .NET 6 RC 2 runtime.
Previous behavior
Projects that were compiled using the .NET 6 RC 1 or earlier version of the
System.Text.Json source generator and library run as expected.
New behavior
Projects that were compiled using the .NET 6 RC 1 version of the System.Text.Json
source generator and library fail when run against the .NET 6 RC 2 runtime. Projects that
are recompiled with the RC 2 SDK work as expected.
Version introduced
6.0 RC 2
Type of breaking change
This change can affect binary compatibility.
Reason for change
This change was introduced to make it easier to extend the source-generator
implementation with features in the future. For more information, see
dotnet/runtime#59243 .
Recommended action
Recompile your app using the RC 2 SDK.
Affected APIs
System.Text.Json.Serialization.JsonSerializerContext
System.Text.Json.Serialization.JsonSerializableAttribute
System.Text.Json.Serialization.JsonSourceGenerationOptionsAttribute
System.Text.Json.Serialization.Metadata (not intended for direct use)
See also
How to use source generation in System.Text.Json
JsonNumberHandlingAttribute on
collection properties
Article • 12/02/2021
A minor breaking change was introduced in .NET 6 with regard to the
JsonNumberHandlingAttribute attribute. If you apply the attribute to a property that's a
collection of non-number values and attempt to serialize or deserialize the property, an
InvalidOperationException is thrown. The attribute is only valid for properties that are
collections of number types, for example:
C#
[JsonNumberHandling(JsonNumberHandling.AllowReadingFromString |
JsonNumberHandling.WriteAsString)]
public List<int> MyList { get; set; }
Previous behavior
Although it was ignored during serialization, JsonNumberHandlingAttribute could be
applied to properties that were collections of non-number types. For example:
C#
[JsonNumberHandling(JsonNumberHandling.AllowReadingFromString |
JsonNumberHandling.WriteAsString)]
public List<MyClass> MyList { get; set; }
New behavior
Starting in .NET 6, if you apply JsonNumberHandlingAttribute to a property that's a
collection of non-number values and attempt to serialize or deserialize the property, an
InvalidOperationException is thrown.
Version introduced
.NET 6
Type of breaking change
This change can affect binary compatibility.
Reason for change
This change was a side effect of a performance optimization for the number handling
feature.
Recommended action
Remove the JsonNumberHandlingAttribute attribute from incompatible collection
properties.
Affected APIs
All of the System.Text.Json.JsonSerializer serialization and deserialization methods.
New JsonSerializer source generator
overloads
Article • 12/02/2021
The System.Text.Json source generator feature added new overloads to JsonSerializer
that accept pre-generated type information via JsonTypeInfo<T> or
JsonSerializerContext. These overloads provide a performance optimization over preexisting overloads that take JsonSerializerOptions instances and perform run-time
reflection. All of these parameter types are reference types for which you can pass null .
The following example shows the method-signature patterns for both approaches:
Pre-existing reflection/ JsonSerializerOptions -based overloads:
C#
public static string JsonSerializer.Serialize<T>(T value,
JsonSerializerOptions? options = null);
public static string JsonSerializer.Serialize(object value, Type type,
JsonSerializerOptions? options = null);
public static T JsonSerializer.Deserialize<T>(string json,
JsonSerializerOptions? options = null);
public static T JsonSerializer.Deserialize(string json, Type type,
JsonSerializerOptions? options = null);
New source-generator/ JsonTypeInfo / JsonSerializerContext -based overloads:
C#
public static string JsonSerializer.Serialize<T>(T value, JsonTypeInfo<T>
jsonTypeInfo);
public static string JsonSerializer.Serialize(object value, Type type,
JsonSerializerContext jsonSerializerContext);
public static T JsonSerializer.Deserialize<T>(string json, JsonTypeInfo<T>
jsonTypeInfo);
public static object JsonSerializer.Deserialize(string json, Type type,
JsonSerializerContext jsonSerializerContext);
Previous behavior
You could write code that passed null as the value for the JsonSerializerOptions
parameter, and it compiled and ran successfully.
C#
entity.Property(e => e.Value).HasConversion(v =>
JsonSerializer.Serialize(v,null), v => JsonSerializer.Deserialize(v, null));
New behavior
The new source-generator methods in .NET 6 can introduce compiler ambiguity if you
pass null for the JsonSerializerOptions parameter. For example, you might see the
following error message:
The call is ambiguous between the following methods or properties:
'JsonSerializer.Serialize(TValue, JsonSerializerOptions?)' and
'JsonSerializer.Serialize(TValue, JsonTypeInfo)
Version introduced
.NET 6
Type of breaking change
This change can affect source compatibility.
Reason for change
New overloads were added to the serializer as a performance optimization. For more
information, see Try the new System.Text.Json source generator .
Recommended action
Update your code in a manner that disambiguates the intended overload, such as
performing an explicit cast to the intended target. For example, you can change the
example in the Previous behavior section as follows:
C#
entity.Property(e => e.Value).HasConversion(v => JsonSerializer.Serialize(v,
(JsonSerializerOptions)null), v => JsonSerializer.Deserialize(v,
(JsonSerializerOptions)null));
Other workarounds include:
Omitting the optional parameter JsonSerializerOptions? options = null .
Using named arguments.
However, you can't omit optional parameters or use named arguments in a lambda
expression.
Affected APIs
All of the System.Text.Json.JsonSerializer methods.
Some APIs throw
ArgumentNullException
Article • 12/02/2021
Some APIs now validate input parameters and throw an ArgumentNullException where
previously they threw a NullReferenceException, if invoked with null input arguments.
Change description
In previous .NET versions, the affected APIs throw a NullReferenceException if invoked
with an argument that's null .
Starting in .NET 6, the affected APIs throw an ArgumentNullException if invoked with an
argument that's null .
Change category
This change affects binary compatibility.
Reason for change
Throwing ArgumentNullException conforms to .NET Runtime behavior. It provides a
better debug experience by clearly communicating which argument caused the
exception.
Version introduced
.NET 6
Recommended action
Review and, if necessary, update your code to prevent passing null input
arguments to the affected APIs.
If your code handles NullReferenceException, replace or add an additional handler
for ArgumentNullException.
Affected APIs
The following table lists the affected APIs and specific parameters:
Method/property
Parameter name
System.Windows.Forms.TreeNodeCollection.Item[Int32]
index
DrawTreeNodeEventArgs(Graphics, TreeNode, Rectangle, TreeNodeStates)
graphics
DataGridViewRowStateChangedEventArgs(DataGridViewRow,
dataGridViewRow
DataGridViewElementStates)
DataGridViewColumnStateChangedEventArgs(DataGridViewColumn,
dataGridViewColumn
DataGridViewElementStates)
See also
TreeNodeCollection.Item throws exception if node is assigned elsewhere
C# templates use application bootstrap
Article • 10/29/2021
In line with related changes in .NET workloads, Windows Forms templates for C# have
been updated to support global using directives, file-scoped namespaces, and nullable
reference types. Because a typical Windows Forms app consist of multiple types split
across multiple files, for example, Form1.cs and Form1.Designer.cs, top-level statements
are notably absent from the Windows Forms templates. However, the updated
templates do include application bootstrap code. This can cause incompatibility if you
target an earlier version of .NET.
Version introduced
.NET 6 RC 1
Old behavior
A Windows Forms application entry point looked like this:
C#
using
using
using
using
using
System;
System.Collections.Generic;
System.Linq;
System.Threading.Tasks;
System.Windows.Forms;
namespace MyApp
{
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
Application.SetHighDpiMode(HighDpiMode.SystemAware);
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form1());
}
}
}
New behavior
The new application entry point for a .NET 6+ application looks like this:
C#
namespace MyApp;
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
ApplicationConfiguration.Initialize();
Application.Run(new Form1());
}
}
ApplicationConfiguration.Initialize() is an ephemeral API produced by the Roslyn
compiler (via source generators). This method emits the same calls that the original
templates had. You can configure the behavior of this API by setting the following
MSBuild properties:
ApplicationDefaultFont
ApplicationHighDpiMode
ApplicationUseCompatibleTextRendering
ApplicationVisualStyles
If you don't explicitly configure any properties, the following code is executed at run
time:
C#
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
// ApplicationConfiguration.Initialize() will emit the following
calls:
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.SetHighDpiMode(HighDpiMode.SystemAware);
Application.Run(new Form1());
}
}
Change category
This change affects source compatibility.
Reason for change
The application bootstrap feature:
Allows the Windows Forms designer to render the design surface in the preferred
font.
Reduces boilerplate code in the templates.
Recommended action
If the same source is used to build an application that targets multiple TFMs, you can do
one of the following:
Replace the ApplicationConfiguration.Initialize(); call with the original code
(and lose the designer support for Application.SetDefaultFont API).
Use #if...#endif directives, for example:
C#
#if NET6_0_OR_GREATER
ApplicationConfiguration.Initialize();
#else
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.SetHighDpiMode(HighDpiMode.SystemAware);
#endif
Affected APIs
N/A
DataGridView-related APIs now throw
InvalidOperationException
Article • 12/02/2021
Some APIs related to DataGridView now throw an InvalidOperationException if the
object's DataGridViewCell.DataGridViewCellAccessibleObject.Owner value is null .
Change description
In previous .NET versions, the affected APIs throw a NullReferenceException when they
are invoked and the Owner property value is null . Starting in .NET 6, these APIs throw
an InvalidOperationException instead of a NullReferenceException if the Owner property
value is null when they're invoked.
Change category
This change affects binary compatibility.
Reason for change
Throwing an InvalidOperationException conforms to the behavior of the .NET runtime. It
also improves the debugging experience by clearly communicating the invalid property.
Version introduced
.NET 6
Recommended action
Review your code and, if necessary, update it to prevent constructing the affected types
with the Owner property as null .
Affected APIs
The following table lists the affected properties and methods:
System.Windows.Forms.DataGridViewTopLeftHeaderCell.DataGridViewTopLeftHead
erCellAccessibleObject.Bounds
System.Windows.Forms.DataGridViewTopLeftHeaderCell.DataGridViewTopLeftHead
erCellAccessibleObject.DefaultAction
System.Windows.Forms.DataGridViewTopLeftHeaderCell.DataGridViewTopLeftHead
erCellAccessibleObject.Name
System.Windows.Forms.DataGridViewTopLeftHeaderCell.DataGridViewTopLeftHead
erCellAccessibleObject.Navigate(AccessibleNavigation)
System.Windows.Forms.DataGridViewTopLeftHeaderCell.DataGridViewTopLeftHead
erCellAccessibleObject.State
See also
DataGridView-related APIs throw InvalidOperationException (.NET 5)
ListViewGroupCollection methods throw
new InvalidOperationException
Article • 11/13/2021
Previously, an InvalidOperationException was thrown if ListViewGroupCollection
methods were invoked on a ListView in virtual mode and the Handle had already been
created. Starting in .NET 6, these ListViewGroupCollection methods now only check if
the ListView is in virtual mode. If it is, they throw an InvalidOperationException with a
more descriptive message.
Previous behavior
Consider the following code that adds a ListViewGroup to a ListView:
C#
ListViewGroup group1 = new ListViewGroup
{
Header = "CollapsibleGroup1",
CollapsedState = ListViewGroupCollapsedState.Expanded
};
listView.Groups.Add(group1);
This code produced an InvalidOperationException with the following message:
When the ListView is in virtual mode, you cannot enumerate through the ListView
items collection using an enumerator or call GetEnumerator. Use the ListView items
indexer instead and access an item by index value.
New behavior
The same code from the Previous behavior section produces an
InvalidOperationException with the following message:
You cannot add groups to the ListView groups collection when the ListView is in
virtual mode.
Change category
This change affects binary compatibility.
Reason for change
The new InvalidOperationException message is more understandable. In addition, it
closes a workaround where the developer could add a ListViewGroup to the ListView
before the Handle was created.
Version introduced
.NET 6 RC 2
Recommended action
Review and, if necessary, update your code so that it doesn't add a ListViewGroup
to a ListView in virtual mode.
If your code handles InvalidOperationException exceptions, you may need to
update the message to reflect that the ListView is in virtual mode.
Affected APIs
System.Windows.Forms.ListViewGroupCollection.Add
System.Windows.Forms.ListViewGroupCollection.AddRange
System.Windows.Forms.ListViewGroupCollection.Insert(Int32, ListViewGroup)
NotifyIcon.Text maximum text length
increased
Article • 12/02/2021
The maximum text length allowed for the NotifyIcon.Text property increased from 63 to
127.
Change description
In previous .NET versions, the maximum text length allowed for the NotifyIcon.Text
property is 63 characters. Starting in .NET 6, the maximum allowed text length is 127
characters. In any version, an ArgumentException is thrown when you attempt to set a
value that's longer than the limit.
Change category
This change affects binary compatibility.
Reason for change
The maximum allowed text length was increased to be in line with the underlying
Windows API. The Windows API was updated in Windows 2000, but due to compatibility
reasons, the size limit for this property wasn't updated in .NET Framework.
Version introduced
.NET 6
Recommended action
Review your code and relax any existing guard conditions, if necessary.
Affected APIs
System.Windows.Forms.NotifyIcon.Text
ScaleControl called only when needed
Article • 02/16/2022
Scaling is usually needed only when an application is running in SystemAware or
PerMonitorV2 mode and the monitor has custom DPI settings that differ from the
machine where the app was designed. In these scenarios, the Windows Forms runtime
calculates the scale factor, based on custom DPI settings of the monitor, and calls
ScaleControl(SizeF, BoundsSpecified) with the new scale factor. To improve performance,
ScaleControl is now called only when the calculated scale factor is something other
than 1.0F (that is, scaling is needed). This change can break your app if it overrides
ScaleControl and performs any custom actions in the override.
Version introduced
.NET 6 servicing 6.0.101
Old behavior
In .NET 6 GA release and earlier versions, the virtual, public API ScaleControl(SizeF,
BoundsSpecified) was called every time PerformAutoScale() was called on the container
control of the application. That is, the method was called every time there is a layout or
font change, regardless of whether scaling was needed.
New behavior
Starting in .NET 6 servicing releases, ScaleControl(SizeF, BoundsSpecified) is called only
when there's a need to scale the form or control. The Windows Forms runtime calculates
the scale factor based on the custom DPI settings of the monitor and the DPI settings of
the monitor on which the application was designed. ScaleControl(SizeF,
BoundsSpecified) is called only if the scale factor indicates that scaling is required.
Change category
This change affects source compatibility.
Reason for change
This change was made to improve performance and avoid unnecessary layouts.
Recommended action
Check if your code performs any custom, non-scaling actions in these overridable
methods.
Affected APIs
System.Windows.Forms.Form.ScaleControl(SizeF, BoundsSpecified)
Selected TableLayoutSettings properties
throw InvalidEnumArgumentException
Article • 12/02/2021
Selected TableLayoutSettings properties now throw an InvalidEnumArgumentException
if you attempt to assign an incorrect value.
Change description
In previous .NET versions, these properties throw an ArgumentOutOfRangeException if
you attempt to assign an incorrect value. Starting in .NET 6, these properties throw an
InvalidEnumArgumentException in such cases.
Change category
This change affects binary compatibility.
Reason for change
Throwing InvalidEnumArgumentException is in line with the existing Windows Forms API
in similar situations. Throwing this exception also provides developers with a better
debug experience.
Version introduced
.NET 6
Recommended action
Update the code to prevent assigning incorrect values.
If necessary, handle an InvalidEnumArgumentException when accessing these APIs.
Affected APIs
System.Windows.Forms.TableLayoutPanel.CellBorderStyle
System.Windows.Forms.TableLayoutPanel.GrowStyle
TreeNodeCollection.Item throws
exception if node is assigned elsewhere
Article • 12/02/2021
TreeNodeCollection.Item[Int32] throws an ArgumentException if the node being
assigned is already bound to another TreeView or to this TreeView at a different index.
Change description
In previous .NET versions, you can assign a tree node to a collection even if it's already
bound to a TreeView. This can lead to duplicated nodes. Starting in .NET 6,
TreeNodeCollection.Item[Int32] throws an ArgumentException if the node being
assigned is already bound to another TreeView or to this TreeView at a different index.
Change category
This change affects binary compatibility.
Reason for change
Validating the input parameter is consistent with the behavior of other
TreeNodeCollection APIs.
Version introduced
.NET 6
Recommended action
Make sure to unbind a TreeNode before assigning it to the collection.
Affected APIs
System.Windows.Forms.TreeNodeCollection.Item[Int32]
XmlDocument.XmlResolver nullability
change
Article • 11/08/2021
The nullability for XmlDocument.XmlResolver has changed in .NET 6. The property setter
now accepts a nullable reference.
Previous behavior
The setter for XmlDocument.XmlResolver was annotated as not accepting null :
C#
public virtual System.Xml.XmlResolver XmlResolver { set { } }
New behavior
The setter for XmlDocument.XmlResolver is annotated as accepting null :
C#
public virtual System.Xml.XmlResolver? XmlResolver { set { } }
Version introduced
6.0 RC 1
Type of breaking change
This change can affect binary compatibility if you've overridden this property in a
derived type.
Reason for change
The non-nullable annotation was incorrect.
Recommended action
If you have overridden this property in a derived type, you'll need to update your code
to handle null when setting the property value.
Affected APIs
System.Xml.XmlDocument.XmlResolver
XNodeReader.GetAttribute behavior for
invalid index
Article • 12/02/2021
XNodeReader is an internal class, but it's accessible through the XmlReader class if you
call XNode.CreateReader. All XmlReader implementations except XNodeReader threw an
ArgumentOutOfRangeException for an invalid index in the GetAttribute(Int32) method.
With this change, XNodeReader.GetAttribute(int) now also throws an
ArgumentOutOfRangeException for an invalid index.
Old behavior
XNodeReader.GetAttribute(int) returned null if the index was invalid.
New behavior
XNodeReader.GetAttribute(int) throws an ArgumentOutOfRangeException if the index
is invalid.
Version introduced
.NET 6
Type of breaking change
This change can affect source compatibility.
Reason for change
XmlReader.GetAttribute(int) is well documented, and XNodeReader was not behaving as
documented. It's behavior for invalid indices was also inconsistent with other XmlReader
implementations.
Recommended action
To avoid an invalid index:
Call XmlReader.AttributeCount to retrieve the number of attributes in the current
node.
Then, pass a value of range 0..XmlReader.AttributeCount-1 to
XmlReader.GetAttribute(Int32).
Affected APIs
System.Xml.XmlReader.GetAttribute(Int32)
Breaking changes in .NET 5
Article • 03/18/2023
If you're migrating an app to .NET 5, the breaking changes listed here might affect you.
Changes are grouped by technology area, such as ASP.NET Core or cryptography.
This article indicates whether each breaking change is binary compatible or source
compatible:
Binary compatible - Existing binaries will load and execute successfully without
recompilation, and the run-time behavior won't change.
Source compatible - Source code will compile successfully without changes when
targeting the new runtime or using the new SDK or component.
ASP.NET Core
Title
Binary
compatible
Source
compatible
ASP.NET Core apps deserialize quoted numbers
✔️
❌
AzureAD.UI and AzureADB2C.UI APIs obsolete
✔️
❌
BinaryFormatter serialization methods are obsolete
✔️
❌
Resource in endpoint routing is HttpContext
✔️
❌
Microsoft-prefixed Azure integration packages removed
❌
✔️
Blazor: Route precedence logic changed in Blazor apps
✔️
❌
Blazor: Updated browser support
✔️
✔️
Blazor: Insignificant whitespace trimmed by compiler
✔️
❌
Blazor: JSObjectReference and JSInProcessObjectReference types
are internal
✔️
❌
Blazor: Target framework of NuGet packages changed
❌
✔️
Blazor: ProtectedBrowserStorage feature moved to shared
framework
✔️
❌
Blazor: RenderTreeFrame readonly public fields are now properties
❌
✔️
Blazor: Updated validation logic for static web assets
❌
✔️
Title
Binary
compatible
Source
compatible
Cryptography APIs not supported on browser
❌
✔️
Extensions: Package reference changes
❌
✔️
Kestrel and IIS BadHttpRequestException types are obsolete
✔️
❌
HttpClient instances created by IHttpClientFactory log integer
✔️
❌
HttpSys: Client certificate renegotiation disabled by default
✔️
❌
IIS: UrlRewrite middleware query strings are preserved
✔️
❌
Kestrel: Configuration changes detected by default
✔️
❌
Kestrel: Default supported TLS protocol versions changed
✔️
❌
Kestrel: HTTP/2 disabled over TLS on incompatible Windows
✔️
✔️
Kestrel: Libuv transport marked as obsolete
✔️
❌
Obsolete properties on ConsoleLoggerOptions
✔️
❌
ResourceManagerWithCultureStringLocalizer class and
✔️
❌
Pubternal APIs removed
✔️
❌
Obsolete constructor removed in request localization middleware
✔️
❌
Middleware: Database error page marked as obsolete
✔️
❌
Exception handler middleware throws original exception
✔️
✔️
ObjectModelValidator calls a new overload of Validate
✔️
❌
Cookie name encoding removed
✔️
❌
IdentityModel NuGet package versions updated
❌
✔️
SignalR: MessagePack Hub Protocol options type changed
✔️
❌
SignalR: MessagePack Hub Protocol moved
✔️
❌
UseSignalR and UseConnections methods removed
✔️
❌
CSV content type changed to standards-compliant
✔️
❌
status codes
versions
WithCulture interface member removed
Code analysis
Title
Binary compatible
Source compatible
CA1416 warning
✔️
❌
CA1417 warning
✔️
❌
CA1831 warning
✔️
❌
CA2013 warning
✔️
❌
CA2014 warning
✔️
❌
CA2015 warning
✔️
❌
CA2200 warning
✔️
❌
CA2247 warning
✔️
❌
Core .NET libraries
Title
Binary
Source
compatible
compatible
Assembly-related API changes for single-file publishing
❌
✔️
BinaryFormatter serialization methods are obsolete
✔️
❌
Code access security APIs are obsolete
✔️
❌
CreateCounterSetInstance throws
✔️
❌
Default ActivityIdFormat is W3C
❌
✔️
Environment.OSVersion returns the correct version
❌
✔️
FrameworkDescription's value is .NET not .NET Core
✔️
❌
GAC APIs are obsolete
✔️
❌
Hardware intrinsic IsSupported checks
❌
✔️
IntPtr and UIntPtr implement IFormattable
✔️
❌
LastIndexOf handles empty search strings
❌
✔️
URI paths with non-ASCII characters on Unix
❌
✔️
InvalidOperationException
Title
Binary
compatible
Source
compatible
API obsoletions with non-default diagnostic IDs
✔️
❌
Obsolete properties on ConsoleLoggerOptions
✔️
❌
Complexity of LINQ OrderBy.First
❌
✔️
OSPlatform attributes renamed or removed
✔️
❌
Microsoft.DotNet.PlatformAbstractions package
removed
❌
✔️
PrincipalPermissionAttribute is obsolete
✔️
❌
Parameter name changes from preview versions
✔️
❌
Parameter name changes in reference assemblies
✔️
❌
Remoting APIs are obsolete
❌
✔️
Order of Activity.Tags list is reversed
✔️
❌
SSE and SSE2 comparison methods handle NaN
✔️
❌
Thread.Abort is obsolete
✔️
❌
Uri recognition of UNC paths on Unix
❌
✔️
UTF-7 code paths are obsolete
✔️
❌
Behavior change for Vector2.Lerp and Vector4.Lerp
✔️
❌
Vector<T> throws NotSupportedException
❌
✔️
Binary
Source
compatible
compatible
Cryptography APIs not supported on browser
❌
✔️
Cryptography.Oid is init-only
✔️
❌
Default TLS cipher suites on Linux
❌
✔️
Create() overloads on cryptographic abstractions are
✔️
❌
✔️
❌
Cryptography
Title
obsolete
Default FeedbackSize value changed
Entity Framework Core
Breaking changes in EF Core 5.0
Globalization
Title
Binary
Source
compatible
compatible
Use ICU libraries on Windows
❌
✔️
StringInfo and TextElementEnumerator are UAX29compliant
❌
✔️
Unicode category changed for Latin-1 characters
✔️
❌
TextInfo.ListSeparator values changed
✔️
❌
Interop
Title
Binary
Source
compatible
compatible
Support for WinRT is removed
❌
✔️
Casting RCW to InterfaceIsIInspectable throws
exception
❌
✔️
No A/W suffix probing on non-Windows platforms
❌
✔️
Networking
Title
Binary compatible
Source compatible
Cookie path handling conforms to RFC 6265
✔️
❌
LocalEndPoint is updated after calling SendToAsync
✔️
❌
MulticastOption.Group doesn't accept null
✔️
❌
Streams allow successive Begin operations
❌
✔️
WinHttpHandler removed from .NET runtime
❌
✔️
SDK
Title
Binary
compatible
Source
compatible
Directory.Packages.props files imported by default
❌
✔️
✔️
Error generated when executable project references
mismatched executable
✔️
❌
NETCOREAPP3_1 preprocessor symbol not defined
✔️
❌
OutputType set to WinExe
❌
✔️
PublishDepsFilePath behavior change
❌
✔️
TargetFramework change from netcoreapp to net
❌
✔️
WinForms and WPF apps use Microsoft.NET.Sdk
❌
✔️
FrameworkReference replaced with
WindowsSdkPackageVersion for Windows SDK
Security
Title
Binary compatible
Source compatible
Code access security APIs are obsolete
✔️
❌
PrincipalPermissionAttribute is obsolete
✔️
❌
UTF-7 code paths are obsolete
✔️
❌
Serialization
Title
Binary
compatible
Source
compatible
BinaryFormatter.Deserialize rewraps exceptions
✔️
❌
JsonSerializer.Deserialize requires single-character string
✔️
❌
ASP.NET Core apps deserialize quoted numbers
✔️
❌
JsonSerializer.Serialize throws ArgumentNullException
✔️
❌
Title
Binary
Source
compatible
compatible
Non-public, parameterless constructors not used for
deserialization
✔️
❌
Options are honored when serializing key-value pairs
✔️
❌
Windows Forms
Title
Binary compatible
Source compatible
Native code can't access Windows Forms objects
✔️
❌
OutputType set to WinExe
❌
✔️
DataGridView doesn't reset custom fonts
✔️
❌
Methods throw ArgumentException
✔️
❌
Methods throw ArgumentNullException
✔️
❌
Properties throw ArgumentOutOfRangeException
✔️
❌
TextFormatFlags.ModifyString is obsolete
✔️
❌
DataGridView APIs throw InvalidOperationException
✔️
❌
WinForms apps use Microsoft.NET.Sdk
❌
✔️
Removed status bar controls
✔️
❌
WPF
Title
Binary compatible
Source compatible
OutputType set to WinExe
❌
✔️
WPF apps use Microsoft.NET.Sdk
❌
✔️
See also
What's new in .NET 5
ASP.NET Core apps allow deserializing
quoted numbers
Article • 09/15/2021
Starting in .NET 5, ASP.NET Core apps use the default deserialization options as specified
by JsonSerializerDefaults.Web. The Web set of options includes setting NumberHandling
to JsonNumberHandling.AllowReadingFromString. This change means that ASP.NET
Core apps will successfully deserialize numbers that are represented as JSON strings
instead of throwing an exception.
Change description
In .NET Core 3.0 - 3.1, JsonSerializer throws a JsonException during deserialization if it
encounters a quoted number in a JSON payload. The quoted numbers are used to map
with number properties in object graphs. In .NET Core 3.0 - 3.1, numbers are only read
from JsonTokenType.Number tokens.
Starting in .NET 5, quoted numbers in JSON payloads are considered valid, by default,
for ASP.NET Core apps. No exception is thrown during deserialization of quoted
numbers.
 Tip
There is no behavior change for the default, standalone JsonSerializer or
JsonSerializerOptions.
This is technically not a breaking change, since it makes a scenario more
permissive instead of more restrictive (that is, it succeeds in coercing a
number from a JSON string instead of throwing an exception). However, since
this is a significant behavioral change that affects many ASP.NET Core apps, it
is documented here.
The HttpClientJsonExtensions.GetFromJsonAsync and
HttpContentJsonExtensions.ReadFromJsonAsync extension methods also use
the Web set of serialization options.
Version introduced
5.0
Reason for change
Multiple users have requested an option for more permissive number handling in
JsonSerializer. This feedback indicates that many JSON producers (for example, services
across the web) emit quoted numbers. By allowing quoted numbers to be read
(deserialized), .NET apps can successfully parse these payloads, by default, in web
contexts. The configuration is exposed via JsonSerializerDefaults.Web so that you can
specify the same options across different application layers, for example, client, server,
and shared.
Recommended action
If this change is disruptive, for example, if you depend on the strict number handling for
validation, you can re-enable the previous behavior. Set the
JsonSerializerOptions.NumberHandling option to JsonNumberHandling.Strict.
For ASP.NET Core MVC and web API apps, you can configure the option in Startup by
using the following code:
C#
services.AddControllers()
.AddJsonOptions(options => options.JsonSerializerOptions.NumberHandling =
JsonNumberHandling.Strict);
Affected APIs
System.Text.Json.JsonSerializer.Deserialize
System.Text.Json.JsonSerializer.DeserializeAsync
Authentication: AzureAD.UI and
AzureADB2C.UI APIs and packages
marked obsolete
Article • 09/15/2021
In ASP.NET Core 2.1, integration with Azure Active Directory (Azure AD) and Azure
Active Directory B2C (Azure AD B2C) authentication is provided by the
Microsoft.AspNetCore.Authentication.AzureAD.UI
and
Microsoft.AspNetCore.Authentication.AzureADB2C.UI
packages. The functionality
provided by these packages is based on the Azure AD v1.0 endpoint.
In ASP.NET Core 5.0 and later, integration with Azure AD and Azure AD B2C
authentication is provided by the Microsoft.Identity.Web
package. This package is
based on the Microsoft Identity Platform, which is formerly known as the Azure AD v2.0
endpoint. Consequently, the old APIs in the
Microsoft.AspNetCore.Authentication.AzureAD.UI and
Microsoft.AspNetCore.Authentication.AzureADB2C.UI packages were deprecated.
For discussion, see GitHub issue dotnet/aspnetcore#25807
.
Version introduced
5.0 Preview 8
Old behavior
The APIs weren't marked as obsolete.
New behavior
The APIs are marked as obsolete.
Reason for change
The Azure AD and Azure AD B2C authentication functionality was migrated to Microsoft
Authentication Library (MSAL) APIs that are provided by Microsoft.Identity.Web .
Recommended action
Follow the Microsoft.Identity.Web API guidance for web apps
and web APIs .
Affected APIs
Microsoft.AspNetCore.Authentication.AzureADAuthenticationBuilderExtensions
Microsoft.AspNetCore.Authentication.AzureAD.UI.AzureADDefaults
Microsoft.AspNetCore.Authentication.AzureAD.UI.AzureADOptions
Microsoft.AspNetCore.Authentication.AzureADB2CAuthenticationBuilderExtensions
Microsoft.AspNetCore.Authentication.AzureADB2C.UI.AzureADB2CDefaults
Microsoft.AspNetCore.Authentication.AzureADB2C.UI.AzureADB2COptions
BinaryFormatter serialization methods
are obsolete and prohibited in ASP.NET
apps
Article • 05/17/2023
Serialize and Deserialize methods on BinaryFormatter, Formatter, and IFormatter are
now obsolete as warning. Additionally, BinaryFormatter serialization is prohibited by
default for ASP.NET apps.
7 Note
In .NET 7, the affected APIs are obsolete as error. For more information, see
BinaryFormatter serialization APIs produce compiler errors.
Change description
Due to security vulnerabilities in BinaryFormatter, the following methods are now
obsolete and produce a compile-time warning with ID SYSLIB0011 . Additionally, in
ASP.NET Core 5.0 and later apps, they will throw a NotSupportedException, unless the
web app has re-enabled BinaryFormatter functionality.
BinaryFormatter.Serialize
BinaryFormatter.Deserialize
The following serialization methods are also obsolete and produce warning SYSLIB0011 ,
but have no behavioral changes:
Formatter.Serialize(Stream, Object)
Formatter.Deserialize(Stream)
IFormatter.Serialize(Stream, Object)
IFormatter.Deserialize(Stream)
Version introduced
5.0
Reason for change
These methods are marked obsolete as part of an effort to wind down usage of
BinaryFormatter within the .NET ecosystem.
Recommended action
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.
C#
// 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.
XML
<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.
XML
<PropertyGroup>
<TargetFramework>net5.0</TargetFramework>
<!-- Warning: Setting the following switch is *NOT* recommended in
web apps. -->
<EnableUnsafeBinaryFormatterSerialization>true</EnableUnsafeBinaryForma
tterSerialization>
</PropertyGroup>
For more information about recommended actions, see Resolving BinaryFormatter
obsoletion and disablement errors.
Affected APIs
System.Runtime.Serialization.Formatters.Binary.BinaryFormatter.Serialize
System.Runtime.Serialization.Formatters.Binary.BinaryFormatter.Deserialize
System.Runtime.Serialization.Formatter.Serialize(Stream, Object)
System.Runtime.Serialization.Formatter.Deserialize(Stream)
System.Runtime.Serialization.IFormatter.Serialize(Stream, Object)
System.Runtime.Serialization.IFormatter.Deserialize(Stream)
See also
SerializationFormat.Binary is obsolete (.NET 7)
BinaryFormatter serialization APIs produce compiler errors (.NET 7)
BinaryFormatter disabled across most project types (.NET 8)
Authorization: Resource in endpoint
routing is HttpContext
Article • 12/04/2021
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
ASP.NET Core 5.0
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.
Recommended action
If your app uses the endpoint resource, call GetEndpoint on the HttpContext to continue
accessing the endpoint.
You can revert to the old behavior with SetSwitch. For example:
C#
AppContext.SetSwitch(
"Microsoft.AspNetCore.Authorization.SuppressUseHttpContextAsAuthorizationRes
ource",
isEnabled: true);
Affected APIs
None
Azure: Microsoft-prefixed Azure
integration packages removed
Article • 09/15/2021
The following Microsoft.* packages that provide integration between ASP.NET Core and
Azure SDKs aren't included in ASP.NET Core 5.0:
Microsoft.Extensions.Configuration.AzureKeyVault , which integrates Azure Key Vault
into the Configuration system.
Microsoft.AspNetCore.DataProtection.AzureKeyVault , which integrates Azure Key
Vault into the ASP.NET Core Data Protection system.
Microsoft.AspNetCore.DataProtection.AzureStorage , which integrates Azure Blob
Storage into the ASP.NET Core Data Protection system.
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.
Recommended action
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.
Affected APIs
AzureKeyVaultConfigurationExtensions.AddAzureKeyVault
AzureDataProtectionBuilderExtensions.ProtectKeysWithAzureKeyVault
AzureDataProtectionBuilderExtensions.PersistKeysToAzureBlobStorage
Blazor: Route precedence logic changed
in Blazor apps
Article • 09/15/2021
A bug in the Blazor routing implementation affected how the precedence of routes was
determined. This bug affects catch-all routes or routes with optional parameters within
your Blazor app.
Version introduced
5.0.1
Old behavior
With the erroneous behavior, routes with lower precedence are considered and matched
over routes with higher precedence. For example, the {*slug} route is matched before
/customer/{id} .
New behavior
The current behavior more closely matches the routing behavior defined in ASP.NET
Core apps. The framework determines the route precedence for each segment first. The
route's length is used only as a second criteria to break ties.
Reason for change
The original behavior is considered a bug in the implementation. As a goal, the routing
system in Blazor apps should behave the same way as the routing system in the rest of
ASP.NET Core.
Recommended action
If upgrading from previous versions of Blazor to 5.x, use the PreferExactMatches
attribute on the Router component. This attribute can be used to opt into the correct
behavior. For example:
razor
<Router AppAssembly="@typeof(Program).Assembly" PreferExactMatches="true">
When PreferExactMatches is set to true , route matching prefers exact matches over
wildcards.
Affected APIs
None
Blazor: Updated browser support
Article • 09/15/2021
ASP.NET Core 5.0 introduces new Blazor features , some of which are incompatible
with older browsers. The list of browsers supported by Blazor in ASP.NET Core 5.0 has
been updated accordingly.
For discussion, see GitHub issue dotnet/aspnetcore#26475
.
Version introduced
5.0
Old behavior
Blazor Server supports Microsoft Internet Explorer 11 with sufficient polyfills. Blazor
Server and Blazor WebAssembly are also functional in Microsoft Edge Legacy
.
New behavior
Blazor Server in ASP.NET Core 5.0 isn't supported with Microsoft Internet Explorer 11.
Blazor Server and Blazor WebAssembly aren't fully functional in Microsoft Edge Legacy.
Reason for change
New Blazor features in ASP.NET Core 5.0 are incompatible with these older browsers,
and use of these older browsers is diminishing. For more information, see the following
resources:
Windows support for Microsoft Edge Legacy is also ending on March 9, 2021
Microsoft 365 apps and services will end support for Microsoft Internet Explorer 11
by August 17, 2021
Recommended action
Upgrade from these older browsers to the new, Chromium-based Microsoft Edge
Blazor apps that need to support these older browsers, use ASP.NET Core 3.1. The
supported browsers list for Blazor in ASP.NET Core 3.1 hasn't changed and is
documented at Supported platforms.
. For
Affected APIs
None
Blazor: Insignificant whitespace
trimmed from components at compile
time
Article • 02/25/2022
Starting with ASP.NET Core 5.0, the Razor compiler omits insignificant whitespace in
Razor components (.razor files) at compile time. For discussion, see issue
dotnet/aspnetcore#23568
.
Version introduced
5.0
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 Razor component code:
razor
<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:
razor
<foo
bar="baz"
/>
Whitespace isn't preserved. The pre-rendered output is:
razor
<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.
Recommended action
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 whitespace: 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.
Affected APIs
None
Blazor: JSObjectReference and
JSInProcessObjectReference types
changed to internal
Article • 09/15/2021
The new Microsoft.JSInterop.JSObjectReference and
Microsoft.JSInterop.JSInProcessObjectReference types introduced in ASP.NET Core 5.0
RC1 have been marked as internal .
Version introduced
5.0 RC2
Old behavior
A JSObjectReference can be obtained from a JavaScript interop call via IJSRuntime . For
example:
C#
var jsObjectReference = await JSRuntime.InvokeAsync<JSObjectReference>(...);
New behavior
JSObjectReference uses the internal access modifier. The public IJSObjectReference
interface must be used instead. For example:
C#
var jsObjectReference = await JSRuntime.InvokeAsync<IJSObjectReference>
(...);
JSInProcessObjectReference was also marked as internal and was replaced by
IJSInProcessObjectReference .
Reason for change
The change makes the JavaScript interop feature more consistent with other patterns
within Blazor. IJSObjectReference is analogous to IJSRuntime in that it serves a similar
purpose and has similar methods and extensions.
Recommended action
Replace occurrences of JSObjectReference and JSInProcessObjectReference with
IJSObjectReference and IJSInProcessObjectReference , respectively.
Affected APIs
Microsoft.JSInterop.JSObjectReference
Microsoft.JSInterop.JSInProcessObjectReference
Blazor: Target framework of NuGet
packages changed
Article • 11/08/2021
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
( <TargetFramework>net5.0</TargetFramework> ). To better align with the target framework
change, the following Blazor packages no longer target .NET Standard 2.1:
Microsoft.AspNetCore.Components
Microsoft.AspNetCore.Components.Authorization
Microsoft.AspNetCore.Components.Forms
Microsoft.AspNetCore.Components.Web
Microsoft.AspNetCore.Components.WebAssembly
Microsoft.AspNetCore.Components.WebAssembly.Authentication
Microsoft.JSInterop
Microsoft.JSInterop.WebAssembly
Microsoft.Authentication.WebAssembly.Msal
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.
Recommended action
Blazor 3.2 WebAssembly projects should target .NET 5 as part of updating their package
references to 5.x.x. Libraries that reference one of these packages can either target .NET
5 or multi-target depending on their requirements.
Affected APIs
None
Blazor: ProtectedBrowserStorage
feature moved to shared framework
Article • 09/15/2021
As part of the ASP.NET Core 5.0 RC2 release, the ProtectedBrowserStorage feature
moved to the ASP.NET Core shared framework.
Version introduced
5.0 RC2
Old behavior
In ASP.NET Core 5.0 Preview 8, the feature is available as a part of the
Microsoft.AspNetCore.Components.Web.Extensions
package but was only usable in
Blazor WebAssembly.
In ASP.NET Core 5.0 RC1, the feature is available as part of the
Microsoft.AspNetCore.Components.ProtectedBrowserStorage
package, which
references the Microsoft.AspNetCore.App shared framework.
New behavior
In ASP.NET Core 5.0 RC2, a NuGet package reference is no longer needed to reference
and use the feature.
Reason for change
The move to the shared framework is a better fit for the user experience customers
expect.
Recommended action
If upgrading from ASP.NET Core 5.0 RC1, complete the following steps:
1. Remove the Microsoft.AspNetCore.Components.ProtectedBrowserStorage package
reference from the project.
2. Replace using Microsoft.AspNetCore.Components.ProtectedBrowserStorage; with
using Microsoft.AspNetCore.Components.Server.ProtectedBrowserStorage; .
3. Remove the call to AddProtectedBrowserStorage from your Startup class.
If upgrading from ASP.NET Core 5.0 Preview 8, complete the following steps:
1. Remove the Microsoft.AspNetCore.Components.Web.Extensions package reference
from the project.
2. Replace using Microsoft.AspNetCore.Components.Web.Extensions; with using
Microsoft.AspNetCore.Components.Server.ProtectedBrowserStorage; .
3. Remove the call to AddProtectedBrowserStorage from your Startup class.
Affected APIs
None
Blazor: RenderTreeFrame readonly
public fields have become properties
Article • 02/25/2022
In ASP.NET Core 3.0 and 3.1, the RenderTreeFrame struct exposed various readonly
public fields, including FrameType, Sequence, and others. In ASP.NET Core 5.0 RC1 and
later versions, all the readonly public fields changed to readonly public properties.
This change won't affect many developers because:
Any app or library that simply uses .razor files (or even manual RenderTreeBuilder
calls) to define its components wouldn't be referencing this type directly.
The RenderTreeFrame type itself is regarded as an implementation detail, not
intended for use outside of the framework. ASP.NET Core 3.0 and later includes an
analyzer that issues compiler warnings if the type is being used directly.
Even if you reference RenderTreeFrame directly, this change is binary-breaking but
not source-breaking. That is, your existing source code will compile and behave
properly. You'll only encounter an issue if compiling against a .NET Core 3.x
framework and then running those binaries against the .NET 5 or a later
framework.
For discussion, see GitHub issue dotnet/aspnetcore#25727
.
Version introduced
5.0 RC1
Old behavior
Public members on RenderTreeFrame are defined as fields. For example,
renderTreeFrame.Sequence and renderTreeFrame.ElementName .
New behavior
Public members on RenderTreeFrame are defined as properties with the same names as
before. For example, renderTreeFrame.Sequence and renderTreeFrame.ElementName .
If older precompiled code hasn't been recompiled since this change, it may throw an
exception similar to MissingFieldException: Field not found:
'Microsoft.AspNetCore.Components.RenderTree.RenderTreeFrame.FrameType'.
Reason for change
This change was necessary to implement high-impact performance improvements in
Razor component rendering in ASP.NET Core 5.0. The same levels of safety and
encapsulation are maintained.
Recommended action
Most Blazor developers are unaffected by this change. The change is more likely to
affect library and package authors, but only in rare cases. Specifically, if you're
developing:
An app and using ASP.NET Core 3.x or upgrading to 5.0 RC1 or later, you don't
need to change your own code. However, if you depend on a library that upgraded
to account for this change, then you need to update to a newer version of that
library.
A library and want to support only ASP.NET Core 5.0 RC1 or later, no action is
needed. Just ensure that your project file declares a <TargetFramework> value of
net5.0 or a later version.
A library and want to support both ASP.NET Core 3.x and 5.0, determine whether
your code reads any RenderTreeFrame members. For example, evaluating
someRenderTreeFrame.FrameType .
Most libraries won't read RenderTreeFrame members, including libraries that
contain .razor components. In this case, no action is needed.
However, if your library does that, you'll need to multi-target to support both
netstandard2.1 and net5.0 . Apply the following changes in your project file:
Replace the existing <TargetFramework> element with
<TargetFrameworks>netstandard2.0;net5.0</TargetFrameworks> .
Use a conditional Microsoft.AspNetCore.Components package reference to
account for both versions you wish to support. For example:
XML
<PackageReference Include="Microsoft.AspNetCore.Components"
Version="3.0.0" Condition="'$(TargetFramework)' ==
'netstandard2.0'" />
<PackageReference Include="Microsoft.AspNetCore.Components"
Version="5.0.0-rc.1.*" Condition="'$(TargetFramework)' !=
'netstandard2.0'" />
For further clarification, see this diff showing how @jsakamoto already upgraded the
Toolbelt.Blazor.HeadElement library
.
Affected APIs
Microsoft.AspNetCore.Components.RenderTree.RenderTreeFrame
Blazor: Updated validation logic for
static web assets
Article • 09/15/2021
There was an issue in conflict validation for static web assets in ASP.NET Core 3.1 and
Blazor WebAssembly 3.2. The issue:
Prevented proper conflict detection between the host assets and assets from Razor
Class Libraries (RCLs) and Blazor WebAssembly apps.
Mostly affects Blazor WebAssembly apps, since by default, static web assets in
RCLs are served under the _content/$(PackageId) prefix.
Version introduced
5.0
Old behavior
During development, an RCL's static web assets could be silently overridden with host
project assets on the same host path. Consider an RCL that has defined a static web
asset to be served at /folder/file.txt. If the host placed a file at wwwroot/folder/file.txt, the
file on the server silently overrode the file on the RCL or Blazor WebAssembly app.
New behavior
ASP.NET Core correctly detects when this issue happens. It informs you, the user, of the
conflict so that you can take the appropriate action.
Reason for change
Static web assets weren't intended to be overridable by files on the wwwroot host of the
project. Allowing for the overriding of those files could lead to errors that are difficult to
diagnose. The result could be undefined behavior changes in published apps.
Recommended action
By default, there's no reason for an RCL file to conflict with a file on the host. RCL files
are prefixed with _content/${PackageId} . Blazor WebAssembly files are placed at the
root of the host URL space, which makes conflicts easier. For example, Blazor
WebAssembly apps contain a favicon.ico file that the host might also include in its
wwwroot folder.
If the conflict's source is an RCL file, it often means code is copying assets from the
library into the project's wwwroot folder. Writing code to copy files defeats a primary
goal of static web assets. This goal is fundamental to get updates on the browser when
the contents are updated without having to trigger a new compilation.
You may choose to preserve this behavior and maintain the file on the host. To do so,
remove the file from the list of static web assets with a custom MSBuild target.
To use the RCL's file or the Blazor WebAssembly app's file instead of the host project's
file, remove the file from the host project.
Affected APIs
None
System.Security.Cryptography APIs not
supported on Blazor WebAssembly
Article • 09/15/2021
System.Security.Cryptography APIs throw a PlatformNotSupportedException at run time
when run on a browser.
Change description
In previous .NET versions, most of the System.Security.Cryptography APIs aren't
available to Blazor WebAssembly apps. Starting in .NET 5, Blazor WebAssembly apps
target the full .NET 5 API surface area, however, not all .NET 5 APIs are supported due to
browser sandbox constraints. In .NET 5 and later versions, the unsupported
System.Security.Cryptography APIs throw a PlatformNotSupportedException when
running on WebAssembly.
 Tip
The Platform compatibility analyzer will flag any calls to the affected APIs when
you build a project that supports the browser platform. This analyzer runs by
default in .NET 5 and later apps.
Reason for change
Microsoft is unable to ship OpenSSL as a dependency in the Blazor WebAssembly
configuration. We attempted to work around this by trying to integrate with the
browser's SubtleCrypto API. Unfortunately, it required significant API changes that made
it too hard to integrate.
Version introduced
5.0
Recommended action
There are no good workarounds to suggest at this time.
Affected APIs
All System.Security.Cryptography APIs except the following:
System.Security.Cryptography.RandomNumberGenerator
System.Security.Cryptography.IncrementalHash
System.Security.Cryptography.SHA1
System.Security.Cryptography.SHA256
System.Security.Cryptography.SHA384
System.Security.Cryptography.SHA512
System.Security.Cryptography.SHA1Managed
System.Security.Cryptography.SHA256Managed
System.Security.Cryptography.SHA384Managed
System.Security.Cryptography.SHA512Managed
Extensions: Package reference changes
affecting some NuGet packages
Article • 09/15/2021
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.
Recommended action
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
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.
Affected APIs
None
HTTP: Kestrel and IIS
BadHttpRequestException types marked
obsolete and replaced
Article • 09/15/2021
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.
Recommended action
Replace usages of Microsoft.AspNetCore.Server.Kestrel.BadHttpRequestException and
Microsoft.AspNetCore.Server.IIS.BadHttpRequestException with
Microsoft.AspNetCore.Http.BadHttpRequestException .
Affected APIs
Microsoft.AspNetCore.Server.IIS.BadHttpRequestException
Microsoft.AspNetCore.Server.Kestrel.BadHttpRequestException
HTTP: HttpClient instances created by
IHttpClientFactory log integer status
codes
Article • 09/15/2021
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:
Output
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:
Output
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.
Recommended action
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:
LoggingHttpMessageHandlerBuilderFilter
LoggingHttpMessageHandler
LoggingScopeHttpMessageHandler
HttpHeadersLogValue
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:
C#
public void ConfigureServices(IServiceCollection services)
{
// Other service registrations go first. Code omitted for brevity.
// Place the following after all AddHttpClient registrations.
services.RemoveAll<IHttpMessageHandlerBuilderFilter>();
services.AddSingleton<IHttpMessageHandlerBuilderFilter,
MyLoggingHttpMessageHandlerBuilderFilter>();
}
Affected APIs
System.Net.Http.HttpClient
HttpSys: Client certificate renegotiation
disabled by default
Article • 09/15/2021
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
.
Recommended action
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.
Affected APIs
ConnectionInfo.ClientCertificate
ConnectionInfo.GetClientCertificateAsync
HttpSysOptions.ClientCertificateMethod
IIS: UrlRewrite middleware query strings
are preserved
Article • 12/04/2021
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
Old behavior
Consider the following rewrite rule:
XML
<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.
Recommended action
If the behavior of removing the query string is preferred, set the action element to
appendQueryString="false" .
Affected APIs
IISUrlRewriteOptionsExtensions.AddIISUrlRewrite
Kestrel: Configuration changes at run
time detected by default
Article • 09/15/2021
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:
C#
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.
Recommended action
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:
C#
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.
Affected APIs
GenericHostBuilderExtensions.ConfigureWebHostDefaults
Kestrel: Default supported TLS protocol
versions changed
Article • 09/15/2021
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.
Recommended action
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:
C#
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.
Affected APIs
HttpsConnectionAdapterOptions.SslProtocols
Kestrel: HTTP/2 disabled over TLS on
incompatible Windows versions
Article • 12/04/2021
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
Old behavior
The following table outlines the behavior when HTTP/2 over TLS is configured.
Protocols
Http2
Http1AndHttp2
Windows 7,
Windows 8,
Windows 8.1,
Windows 10,
Windows Server
2008 R2,
or earlier
Windows
Server 2012
Windows Server
2012 R2
Windows
Server 2016,
or newer
Throw
Error during TLS
handshake *
No error
NotSupportedException
Error during TLS
handshake
Downgrade to Http1
Downgrade to
Error during TLS
handshake *
No error
Http1
* 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
Windows 8,
Windows Server
Windows 8.1,
Windows Server
Windows
10,
2008 R2,
or earlier
2012
2012 R2
Windows
Server
2016,
or newer
Http2
Throw
Throw
Throw
NotSupportedException
NotSupportedException
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.
Recommended action
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:
C#
AppContext.SetSwitch("Microsoft.AspNetCore.Server.Kestrel.EnableWindows81Htt
p2", 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.
Affected APIs
None
Kestrel: Libuv transport marked as
obsolete
Article • 06/29/2022
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 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 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.
Recommended action
Discontinue use of the Libuv package
and extension methods.
Affected APIs
WebHostBuilderLibuvExtensions
WebHostBuilderLibuvExtensions.UseLibuv
Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.LibuvTransportOptions
Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.LibuvTransportOptions.Thread
Count
Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.LibuvTransportOptions.NoDela
y
Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.LibuvTransportOptions.MaxWr
iteBufferSize
Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.LibuvTransportOptions.MaxRe
adBufferSize
Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.LibuvTransportOptions.Bac
klog
Obsolete properties on
ConsoleLoggerOptions
Article • 09/15/2021
The Microsoft.Extensions.Logging.Console.ConsoleLoggerFormat type and some
properties on ConsoleLoggerOptions are now obsolete.
Change description
Starting in .NET 5, the Microsoft.Extensions.Logging.Console.ConsoleLoggerFormat type
and several properties on ConsoleLoggerOptions are obsolete. The obsolete properties
are:
ConsoleLoggerOptions.DisableColors
ConsoleLoggerOptions.IncludeScopes
ConsoleLoggerOptions.TimestampFormat
ConsoleLoggerOptions.UseUtcTimestamp
ConsoleLoggerOptions.Format
With the introduction of new formatters, these properties are now available on the
individual formatters.
Reason for change
The Format property is an enumeration type, which cannot represent a custom
formatter.
The remaining properties were set on ConsoleLoggerOptions and applied to both of the
built-in formats for console logs. However, with the introduction of a new formatter API,
it makes more sense for formatting to be represented on the formatter-specific options.
This change provides better separation between the logger and logger formatters.
Version introduced
5.0
Recommended action
Use the new ConsoleLoggerOptions.FormatterName property in place of the
ConsoleLoggerOptions.Format property. For example:
C#
loggingBuilder.AddConsole(options =>
{
options.FormatterName = ConsoleFormatterNames.Systemd;
});
There are several differences between FormatterName and Format:
Format has only two possible options: Default and Systemd .
FormatterName is case insensitive and can be any string. The reserved, built-in
names are Simple , Systemd , and Json (.NET 5 and later).
"Format": "Systemd" maps to "FormatterName": "Systemd" .
"Format": "Default" maps to "FormatterName": "Simple" .
For the DisableColors, IncludeScopes, TimestampFormat, and UseUtcTimestamp
properties, use the corresponding property on the new ConsoleFormatterOptions,
JsonConsoleFormatterOptions, or SimpleConsoleFormatterOptions types instead.
For example, the corresponding setting for ConsoleLoggerOptions.DisableColors is
SimpleConsoleFormatterOptions.ColorBehavior.
Previous code:
C#
loggingBuilder.AddConsole(options =>
{
options.DisableColors = true;
});
New code:
C#
loggingBuilder.AddSimpleConsole(options =>
{
options.ColorBehavior = LoggerColorBehavior.Disabled;
});
The following two JSON snippets show how the configuration file changes. Old
configuration file:
JSON
{
"Logging": {
"LogLevel": {
"Default": "None",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
},
"Console": {
"LogLevel": {
"Default": "Information"
},
"Format": "Systemd",
"IncludeScopes": true,
"TimestampFormat": "HH:mm:ss",
"UseUtcTimestamp": true
}
},
"AllowedHosts": "*"
}
New configuration file:
JSON
{
"Logging": {
"LogLevel": {
"Default": "None",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
},
"Console": {
"LogLevel": {
"Default": "Information"
},
"FormatterName": "Systemd",
"FormatterOptions": {
"IncludeScopes": true,
"TimestampFormat": "HH:mm:ss",
"UseUtcTimestamp": true
}
}
},
"AllowedHosts": "*"
}
Affected APIs
Microsoft.Extensions.Logging.Console.ConsoleLoggerOptions.DisableColors
Microsoft.Extensions.Logging.Console.ConsoleLoggerOptions.IncludeScopes
Microsoft.Extensions.Logging.Console.ConsoleLoggerOptions.TimestampFormat
Microsoft.Extensions.Logging.Console.ConsoleLoggerOptions.UseUtcTimestamp
Microsoft.Extensions.Logging.Console.ConsoleLoggerOptions.Format
Localization:
ResourceManagerWithCultureStringLoc
alizer class and WithCulture interface
member removed
Article • 12/04/2021
The ResourceManagerWithCultureStringLocalizer class and WithCulture method were
removed in .NET 5.
For context, see aspnet/Announcements#346
and dotnet/aspnetcore#3324
discussion on this change, see dotnet/aspnetcore#7756
. For
.
Version introduced
5.0
Old behavior
The ResourceManagerWithCultureStringLocalizer class and the
ResourceManagerStringLocalizer.WithCulture method are obsolete in .NET Core 3.0 and
later.
New behavior
The ResourceManagerWithCultureStringLocalizer class and the
ResourceManagerStringLocalizer.WithCulture method have been removed in .NET 5. 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.
Recommended action
Stop using the ResourceManagerWithCultureStringLocalizer class and the
ResourceManagerStringLocalizer.WithCulture method.
Affected APIs
ResourceManagerWithCultureStringLocalizer
ResourceManagerStringLocalizer.WithCulture
Localization: "Pubternal" APIs removed
Article • 09/15/2021
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.
Recommended action
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
.
Affected APIs
Microsoft.Extensions.Localization.Internal.AssemblyWrapper
Microsoft.Extensions.Localization.Internal.IResourceStringProvider
ResourceManagerStringLocalizer.ResourceManagerStringLocalizer
Localization: Obsolete constructor
removed in request localization
middleware
Article • 09/15/2021
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.
Recommended action
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.
Affected APIs
RequestLocalizationMiddleware.ctor(RequestDelegate,
IOptions<RequestLocalizationOptions>)
Middleware: Database error page
marked as obsolete
Article • 09/15/2021
The DatabaseErrorPageMiddleware and its associated extension methods were marked
as obsolete in ASP.NET Core 5.0. The middleware and extension methods will be
removed in ASP.NET Core 6.0. The functionality will instead be provided by
DatabaseDeveloperPageExceptionFilter and its extension methods.
For discussion, see the GitHub issue at dotnet/aspnetcore#24987
.
Version introduced
5.0 RC 1
Old behavior
DatabaseErrorPageMiddleware and its associated extension methods weren't obsolete.
New behavior
DatabaseErrorPageMiddleware and its associated extension methods are obsolete.
Reason for change
DatabaseErrorPageMiddleware was migrated to an extensible API for the developer
exception page. For more information on the extensible API, see GitHub issue
dotnet/aspnetcore#8536
.
Recommended action
Complete the following steps:
1. Stop using DatabaseErrorPageMiddleware in your project. For example, remove the
UseDatabaseErrorPage method call from Startup.Configure :
C#
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDatabaseErrorPage();
}
}
2. Add the developer exception page to your project. For example, call the
UseDeveloperExceptionPage method in Startup.Configure :
C#
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
}
3. Add the Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore
NuGet package
to the project file.
4. Add the database developer page exception filter to the services collection. For
example, call the AddDatabaseDeveloperPageExceptionFilter method in
Startup.ConfigureServices :
C#
public void ConfigureServices(IServiceCollection services)
{
services.AddDatabaseDeveloperPageExceptionFilter();
}
Affected APIs
Microsoft.AspNetCore.Builder.DatabaseErrorPageExtensions.UseDatabaseErrorPage
Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore.DatabaseErrorPageMiddle
ware
Middleware: Exception Handler
Middleware throws original exception if
handler not found
Article • 09/15/2021
Before ASP.NET Core 5.0, the Exception Handler Middleware executes the configured
exception handler when an exception has occurred. If the exception handler, configured
via ExceptionHandlingPath, can't be found, an HTTP 404 response is produced. The
response is misleading in that it:
Seems to be a user error.
Obscures the fact that an exception occurred on the server.
To address the misleading error in ASP.NET Core 5.0, the ExceptionHandlerMiddleware
throws the original exception if the exception handler can't be found. As a result, an
HTTP 500 response is produced by the server. The response will be easier to examine in
the server logs when debugging the error that occurred.
For discussion, see GitHub issue dotnet/aspnetcore#25288
.
Version introduced
5.0 RC 1
Old behavior
The Exception Handler Middleware produces an HTTP 404 response if the configured
exception handler can't be found.
New behavior
The Exception Handler Middleware throws the original exception if the configured
exception handler can't be found.
Reason for change
The HTTP 404 error doesn't make it obvious that an exception occurred on the server.
This change produces an HTTP 500 error to make it obvious that:
The problem isn't caused by a user error.
An exception was encountered on the server.
Recommended action
There are no API changes. All existing apps will continue to compile and run. The
exception thrown is handled by the server. For example, the exception is converted to an
HTTP 500 error response by Kestrel or HTTP.sys.
Affected APIs
None
MVC: ObjectModelValidator calls a new
overload of ValidationVisitor.Validate
Article • 11/08/2021
In ASP.NET Core 5.0, an overload of the ValidationVisitor.Validate was added. The new
overload accepts the top-level model instance that contains properties:
diff
bool Validate(ModelMetadata metadata, string key, object model, bool
alwaysValidateAtTopLevel);
+ bool Validate(ModelMetadata metadata, string key, object model, bool
alwaysValidateAtTopLevel, object container);
ObjectModelValidator invokes this new overload of ValidationVisitor to perform
validation. This new overload is pertinent if your validation library integrates with
ASP.NET Core MVC's model validation system.
For discussion, see GitHub issue dotnet/aspnetcore#26020
.
Version introduced
5.0
Old behavior
ObjectModelValidator invokes the following overload during model validation:
C#
ValidationVisitor.Validate(ModelMetadata metadata, string key, object model,
bool alwaysValidateAtTopLevel)
New behavior
ObjectModelValidator invokes the following overload during model validation:
C#
ValidationVisitor.Validate(ModelMetadata metadata, string key, object model,
bool alwaysValidateAtTopLevel, object container)
Reason for change
This change was introduced to support validators, such as CompareAttribute, that rely
on inspection of other properties.
Recommended action
Validation frameworks that rely on ObjectModelValidator to invoke the existing overload
of ValidationVisitor must override the new method when targeting .NET 5 or later:
C#
public class MyCustomValidationVisitor : ValidationVisitor
{
+ public override bool Validate(ModelMetadata metadata, string key, object
model, bool alwaysValidateAtTopLevel, object container)
+ {
+
...
}
Affected APIs
ValidationVisitor.Validate
Security: Cookie name encoding
removed
Article • 09/15/2021
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.
Recommended action
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.
Affected APIs
HttpRequest.Cookies
HttpResponse.Cookies
Microsoft.Owin.IOwinRequest.Cookies
Microsoft.Owin.IOwinResponse.Cookies
Security: IdentityModel NuGet package
versions updated
Article • 09/15/2021
The following packages were updated to version 6.6.0:
Microsoft.IdentityModel.Logging
Microsoft.IdentityModel.Protocols.OpenIdConnect
Microsoft.IdentityModel.Protocols.WsFederation
System.IdentityModel.Tokens.Jwt
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.
Recommended action
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.
Affected APIs
None
SignalR: MessagePack Hub Protocol
options type changed
Article • 09/15/2021
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:
C#
services.AddSignalR()
.AddMessagePackProtocol(options =>
{
options.FormatterResolvers.Add(MessagePack.Resolvers.StandardResolver.Instan
ce);
});
And replace the options as follows:
C#
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:
C#
services.AddSignalR()
.AddMessagePackProtocol(options =>
{
options.SerializerOptions =
options.SerializeOptions.WithResolver(MessagePack.Resolvers.StandardResolver
.Instance);
});
And replace the options as follows:
C#
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.
Recommended action
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.
Affected APIs
Microsoft.AspNetCore.SignalR.MessagePackHubProtocolOptions
SignalR: MessagePack Hub Protocol
moved to MessagePack 2.x package
Article • 09/15/2021
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.
Recommended action
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 .
Affected APIs
Microsoft.AspNetCore.SignalR.MessagePackHubProtocolOptions
SignalR: UseSignalR and
UseConnections methods removed
Article • 09/15/2021
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.
Recommended action
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:
C#
app.UseSignalR(routes =>
{
routes.MapHub<SomeHub>("/path");
});
New code:
C#
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-tono change needed.
Affected APIs
ConnectionsAppBuilderExtensions.UseConnections
SignalRAppBuilderExtensions.UseSignalR
ConnectionsRouteBuilder.MapConnections
ConnectionsRouteBuilder.MapConnectionHandler
HubRouteBuilder.MapHub
Static files: CSV content type changed to
standards-compliant
Article • 09/15/2021
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.
Recommended action
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:
C#
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.
Affected APIs
Microsoft.AspNetCore.StaticFiles.FileExtensionContentTypeProvider
Warning CA1416: Platform compatibility
Article • 05/13/2022
.NET code analyzer rule CA1416 is enabled, by default, starting in .NET 5. It produces a
build warning for calls to platform-specific APIs from call sites that don't verify the
operating system.
Change description
Starting in .NET 5, the .NET SDK includes .NET source code analyzers. Several of these
rules are enabled, by default, including CA1416. If your project contains code that
violates this rule and is configured to treat warnings as errors, this change could break
your build. Rule CA1416 informs you when you're using platform-specific APIs from
places where the platform context isn't verified.
Rule CA1416, the platform compatibility analyzer, works hand-in-hand with some other
features that are new in .NET 5. .NET 5 introduces the SupportedOSPlatformAttribute
and UnsupportedOSPlatformAttribute, which let you specify the platforms that an API is
or isn't supported on. In the absence of these attributes, an API is assumed to be
supported on all platforms. These attributes have been applied to platform-specific APIs
in the core .NET libraries.
In projects that target platforms for which APIs that they use aren't available, rule
CA1416 flags any platform-specific API call where the platform context isn't verified.
Most of the APIs that are now decorated with the SupportedOSPlatformAttribute and
UnsupportedOSPlatformAttribute attributes throw PlatformNotSupportedException
exceptions when they're invoked on an unsupported operating system. Now that these
APIs are marked as platform-specific, rule CA1416 helps you prevent run-time
PlatformNotSupportedException exceptions by adding OS checks to your call sites.
Examples
The Console.Beep(Int32, Int32) method is only supported on Windows and is
decorated with [SupportedOSPlatform("windows")] . The following code will
produce a CA1416 warning at build time if the project targets net5.0 (cross
platform). But this code won't warn if the project targets Windows ( net5.0windows ) and the GenerateAssemblyInfo is enabled for the project. For actions you
can take to avoid the warning, see Recommended action.
C#
public void PlayCMajor()
{
Console.Beep(261, 1000);
}
The Image.FromFile(String) method is not supported in the browser and is
decorated with [UnsupportedOSPlatform("browser")] . The following code will
produce a CA1416 warning at build time if the project supports the browser
platform.
C#
public void CreateImage()
{
Image newImage = Image.FromFile("SampImag.jpg");
}
 Tip
Blazor WebAssembly projects and Razor class library projects include
browser support automatically.
To manually add the browser as a supported platform for your project, add
the following entry to your project file:
XML
<ItemGroup>
<SupportedPlatform Include="browser" />
</ItemGroup>
Version introduced
5.0
Recommended action
Ensure that platform-specific APIs are only called when the code is running on an
appropriate platform. You can check the current operating system using one of the
Is<Platform> methods in the System.OperatingSystem class, for example,
OperatingSystem.IsWindows(), before calling a platform-specific API.
You can use one of the Is<Platform> methods in the condition of an if statement:
C#
public void PlayCMajor()
{
if (OperatingSystem.IsWindows())
{
Console.Beep(261, 1000);
}
}
Or, if you don't want the overhead of an additional if statement at run time, call
Debug.Assert(Boolean) instead:
C#
public void PlayCMajor()
{
Debug.Assert(OperatingSystem.IsWindows());
Console.Beep(261, 1000);
}
If you're authoring a library, you can mark your API as platform-specific. In this case, the
burden of checking requirements falls on your callers. You can mark specific methods or
types or an entire assembly.
C#
[SupportedOSPlatform("windows")]
public void PlayCMajor()
{
Console.Beep(261, 1000);
}
If you don't want to fix all your call sites, you can choose one of the following options to
suppress the warning:
To suppress rule CA1416, you can do so using #pragma or the NoWarn compiler
flag, or by setting the rule's severity to none in an .editorconfig file.
C#
public void PlayCMajor()
{
#pragma warning disable CA1416
Console.Beep(261, 1000);
#pragma warning restore CA1416
}
To disable code analysis completely, set EnableNETAnalyzers to false in your
project file. For more information, see EnableNETAnalyzers.
Affected APIs
For Windows platform:
All APIs listed at
https://github.com/dotnet/designs/blob/main/accepted/2020/windows-specificapis/windows-specific-apis.md .
System.Security.Cryptography.DSAOpenSsl
System.Security.Cryptography.ECDiffieHellmanOpenSsl
System.Security.Cryptography.ECDsaOpenSsl
System.Security.Cryptography.RSAOpenSsl
For Blazor WebAssembly platform:
All APIs listed at https://github.com/dotnet/runtime/issues/41087
See also
CA1416: Validate platform compatibility
Platform compatibility analyzer
.
Warning CA1417: OutAttribute on string
parameter for P/Invoke
Article • 11/08/2021
.NET code analyzer rule CA1417 is enabled, by default, starting in .NET 5. It produces a
build warning for any Platform Invoke (P/Invoke) method definitions where a String
parameter is passed by value and marked with OutAttribute.
Change description
Starting in .NET 5, the .NET SDK includes .NET source code analyzers. Several of these
rules are enabled, by default, including CA1417. If your project contains code that
violates this rule and is configured to treat warnings as errors, this change could break
your build.
Rule CA1417 flags P/Invoke method definitions where a String parameter is marked with
the OutAttribute attribute and is passed by value. For example:
C#
[DllImport("MyLibrary")]
private static extern void PIMethod([Out] string s);
The .NET runtime maintains a table, called the intern pool, that contains a single
reference to each unique literal string in a program. If an interned string marked with
OutAttribute is passed by value to a P/Invoke method, the runtime can be destabilized.
For more information about string interning, see the remarks for String.Intern(String).
Version introduced
5.0
Recommended action
If you need to marshal modified string data back to the caller, pass the string by
reference instead.
C#
[DllImport("MyLibrary")]
private static extern void PIMethod(out string s);
If you don't need to marshal modified string data back to the caller, simply remove
the OutAttribute.
C#
[DllImport("MyLibrary")]
private static extern void PIMethod(string s);
For more information, see CA1417.
To disable code analysis completely, set EnableNETAnalyzers to false in your
project file. For more information, see EnableNETAnalyzers.
Affected APIs
Not detectable via API analysis.
Warning CA1831: Use AsSpan instead of
Range-based indexers for string
Article • 11/08/2021
.NET code analyzer rule CA1831 is enabled, by default, starting in .NET 5. It produces a
build warning for any code where a Range-based indexer is used on a string, but no
copy was intended.
Change description
Starting in .NET 5, the .NET SDK includes .NET source code analyzers. Several of these
rules are enabled, by default, including CA1831. If your project contains code that
violates this rule and is configured to treat warnings as errors, this change could break
your build.
Rule CA1831 finds instances where a Range-based indexer is used on a string, but no
copy was intended. If the Range-based indexer is used directly on a string to produce an
implicit cast, then an unnecessary copy of the requested portion of the string is created.
For example:
C#
ReadOnlySpan<char> slice = str[1..3];
CA1831 suggests using the Range-based indexer on a span of the string, instead. For
example:
C#
ReadOnlySpan<char> slice = str.AsSpan()[1..3];
Version introduced
5.0
Recommended action
To correct your code and avoid unnecessary allocations, call AsSpan(String) or
AsMemory(String) before using the Range-based indexer. For example:
C#
ReadOnlySpan<char> slice = str.AsSpan()[1..3];
If you don't want to change your code, you can disable the rule by setting its
severity to suggestion or none . For more information, see Configure code analysis
rules.
To disable code analysis completely, set EnableNETAnalyzers to false in your
project file. For more information, see EnableNETAnalyzers.
Affected APIs
System.Range
Warning CA2013: Do not use
ReferenceEquals with value types
Article • 11/08/2021
.NET code analyzer rule CA2013 is enabled, by default, starting in .NET 5. It produces a
build warning for any code where ReferenceEquals(Object, Object) is used to compare
one or more value types for equality.
Change description
Starting in .NET 5, the .NET SDK includes .NET source code analyzers. Several of these
rules are enabled, by default, including CA2013. If your project contains code that
violates this rule and is configured to treat warnings as errors, this change could break
your build.
Rule CA2013 finds instances where ReferenceEquals(Object, Object) is used to compare
one or more value types for equality. Comparing value types for equality in this way can
lead to incorrect results, because the values are boxed before they're compared.
ReferenceEquals(Object, Object) will return false even if the compared values represent
the same instance of a value type.
Version introduced
5.0
Recommended action
Change the code to use an appropriate equality operator, such as == . You should
not suppress this warning.
To disable code analysis completely, set EnableNETAnalyzers to false in your
project file. For more information, see EnableNETAnalyzers.
Affected APIs
System.Object.ReferenceEquals(Object, Object)
Warning CA2014: Do not use stackalloc
in loops
Article • 11/08/2021
.NET code analyzer rule CA2014 is enabled, by default, starting in .NET 5. It produces a
build warning for any C# code where a stackalloc expression is used inside a loop.
Change description
Starting in .NET 5, the .NET SDK includes .NET source code analyzers. Several of these
rules are enabled, by default, including CA2014. If your project contains code that
violates this rule and is configured to treat warnings as errors, this change could break
your build.
Rule CA2014 looks for C# code where a stackalloc expression is used inside a loop.
stackalloc allocates memory from the current stack frame. The memory isn't released
until the current method call returns, which can lead to stack overflows. Because you
can't catch stack overflow exceptions, the app will terminate in case of stack overflow.
Version introduced
5.0
Recommended action
Avoid using stackalloc inside loops. Allocate the memory block outside the loop
and reuse it inside the loop. For more information, see CA2014.
To disable code analysis completely, set EnableNETAnalyzers to false in your
project file. For more information, see EnableNETAnalyzers.
Affected APIs
Not detectable via API analysis.
Warning CA2015: Do not define
finalizers for types derived from
MemoryManager<T>
Article • 11/08/2021
.NET code analyzer rule CA2015 is enabled, by default, starting in .NET 5. It produces a
build warning for any types that derive from MemoryManager<T> that define a finalizer.
Change description
Starting in .NET 5, the .NET SDK includes .NET source code analyzers. Several of these
rules are enabled, by default, including CA2015. If your project contains code that
violates this rule and is configured to treat warnings as errors, this change could break
your build.
Rule CA2015 flags types that derive from MemoryManager<T> that define a finalizer.
Adding a finalizer to a type that derives from MemoryManager<T> is likely an indication
of a bug. It suggests that a native resource that could have been obtained in a Span<T>
is getting cleaned up, potentially while it's still in use by the Span<T>.
Version introduced
5.0
Recommended action
Remove the finalizer definition. For more information, see CA2015.
To disable code analysis completely, set EnableNETAnalyzers to false in your
project file. For more information, see EnableNETAnalyzers.
Affected APIs
Not detectable via API analysis.
Warning CA2200: Rethrow to preserve
stack details
Article • 11/08/2021
.NET code analyzer rule CA2200 is enabled, by default, starting in .NET 5. It produces a
build warning for any catch blocks that rethrow an exception and the exception is
explicitly specified in the throw statement.
Change description
Starting in .NET 5, the .NET SDK includes .NET source code analyzers. Several of these
rules are enabled, by default, including CA2200. If your project contains code that
violates this rule and is configured to treat warnings as errors, this change could break
your build.
Rule CA2200 flags code where exceptions are rethrown and the exception variable is
specified in the throw statement. When an exception is thrown, part of the information
it carries is the stack trace. The stack trace is a list of the method call hierarchy that starts
with the method that throws the exception and ends with the method that catches the
exception. If an exception is rethrown by specifying the exception in the throw
statement, the stack trace restarts at the current method and the list of method calls
between the original method that threw the exception and the current method is lost. To
keep the original stack trace information with the exception, use the throw statement
without specifying the exception.
The following code snippet does not produce a warning for rule CA2200. The
commented line would trigger a violation, however.
C#
catch (ArithmeticException e)
{
// throw e;
throw;
}
Version introduced
5.0
Recommended action
Rethrow exceptions without specifying the exception explicitly. For more
information, see CA2200.
To disable code analysis completely, set EnableNETAnalyzers to false in your
project file. For more information, see EnableNETAnalyzers.
Affected APIs
Not detectable via API analysis.
Warning CA2247: Argument to
TaskCompletionSource constructor
should be TaskCreationOptions value
Article • 11/08/2021
.NET code analyzer rule CA2247 is enabled, by default, starting in .NET 5. It produces a
build warning for calls to the TaskCompletionSource<TResult> constructor that pass an
argument of type TaskContinuationOptions.
Change description
Starting in .NET 5, the .NET SDK includes .NET source code analyzers. Several of these
rules are enabled, by default, including CA2247. If your project contains code that
violates this rule and is configured to treat warnings as errors, this change could break
your build.
Rule CA2247 finds calls to the TaskCompletionSource<TResult> constructor that pass an
argument of type TaskContinuationOptions. The TaskCompletionSource<TResult> type
has a constructor that accepts a TaskCreationOptions value, and another constructor
that accepts an Object. If you accidentally pass a TaskContinuationOptions value instead
of a TaskCreationOptions value, the constructor with the Object parameter is called at
run time. Your code will compile and run but won't have the intended behavior.
Version introduced
5.0
Recommended action
Replace the TaskContinuationOptions argument with the corresponding
TaskCreationOptions value. Do not suppress this warning, since it almost always
highlights a bug in your code. For more information, see CA2247.
To disable code analysis completely, set EnableNETAnalyzers to false in your
project file. For more information, see EnableNETAnalyzers.
Affected APIs
TaskCompletionSource<TResult>(Object)
Assembly-related API behavior changes
for single-file publishing format
Article • 09/15/2021
Multiple APIs related to an assembly's file location have behavior changes when they're
invoked in a single-file publishing format.
Change description
In single-file publishing for .NET 5 and later versions, bundled assemblies are loaded
from memory instead of extracted to disk. For single-file published apps, this means that
certain location-related APIs return different values on .NET 5 and later than on previous
versions of .NET. The changes are as follows:
API
Previous versions
.NET 5 and later
Assembly.Location
Returns extracted DLL file
path
Returns empty string for bundled
assemblies
Assembly.CodeBase
Returns extracted DLL file
path
Throws exception for bundled
assemblies
Assembly.GetFile(String)
Returns null for
bundled assemblies
Throws exception for bundled
assemblies
Environment.GetCommandLineArgs()
Value is the name of the
Value is the name of the host
[0]
entry point DLL
executable
AppContext.BaseDirectory
Value is the temporary
extraction directory
Value is the containing directory
of the host executable
Version introduced
5.0
Recommended action
Avoid dependencies on the file location of assemblies when publishing as a single file.
Affected APIs
Assembly.Location
Assembly.CodeBase
Assembly.GetFile(String)
Environment.GetCommandLineArgs()
AppContext.BaseDirectory
Most code access security APIs are
obsolete
Article • 11/08/2021
Most code access security (CAS)-related types in .NET are now obsolete as warning. This
includes CAS attributes, such as SecurityPermissionAttribute, CAS permission objects,
such as SocketPermission, EvidenceBase-derived types, and other supporting APIs.
Change description
In .NET Framework 2.x - 4.x, CAS attributes and APIs can influence the course of code
execution, including ensuring that CAS-demand stack walks succeed or fail.
C#
// In .NET Framework, the attribute causes CAS stack walks
// to terminate successfully when this permission is demanded.
[SocketPermission(SecurityAction.Assert, Host = "contoso.com", Port =
"443")]
public void DoSomething()
{
// open a socket to contoso.com:443
}
In .NET Core 2.x - 3.x, the runtime does not honor CAS attributes or CAS APIs. The
runtime ignores attributes on method entry, and most programmatic APIs have no
effect.
C#
// The .NET Core runtime ignores the following attribute.
[SocketPermission(SecurityAction.Assert, Host = "contoso.com", Port =
"443")]
public void DoSomething()
{
// open a socket to contoso.com:443
}
Additionally, programmatic calls to expansive APIs ( Assert ) always succeed, while
programmatic calls to restrictive APIs ( Deny , PermitOnly ) always throw an exception at
run time. (PrincipalPermission is an exception to this rule. See the Recommended action
section below.)
C#
public void DoAssert()
{
// The line below has no effect at run time.
new SocketPermission(PermissionState.Unrestricted).Assert();
}
public void DoDeny()
{
// The line below throws PlatformNotSupportedException at run time.
new SocketPermission(PermissionState.Unrestricted).Deny();
}
In .NET 5 and later versions, most CAS-related APIs are obsolete and produce compiletime warning SYSLIB0003 .
C#
[SocketPermission(SecurityAction.Assert, Host = "contoso.com", Port =
"443")] // warning SYSLIB0003
public void DoSomething()
{
new SocketPermission(PermissionState.Unrestricted).Assert(); // warning
SYSLIB0003
new SocketPermission(PermissionState.Unrestricted).Deny(); // warning
SYSLIB0003
}
This is a compile-time only change. There is no run-time change from previous versions
of .NET Core. Methods that perform no operation in .NET Core 2.x - 3.x will continue to
perform no operation at run time in .NET 5 and later. Methods that throw
PlatformNotSupportedException in .NET Core 2.x - 3.x will continue to throw a
PlatformNotSupportedException at run time in .NET 5 and later.
Reason for change
Code access security (CAS) is an unsupported legacy technology. The infrastructure to
enable CAS exists only in .NET Framework 2.x - 4.x, but is deprecated and not receiving
servicing or security fixes.
Due to CAS's deprecation, the supporting infrastructure was not brought forward to
.NET Core or .NET 5+. However, the APIs were brought forward so that apps could
cross-compile against .NET Framework and .NET Core. This led to "fail open" scenarios,
where some CAS-related APIs exist and are callable but perform no action at run time.
This can lead to security issues for components that expect the runtime to honor CAS-
related attributes or programmatic API calls. To better communicate that the runtime
doesn't respect these attributes or APIs, we have obsoleted the majority of them in .NET
5.0.
Version introduced
5.0
Recommended action
If you're asserting any security permission, remove the attribute or call that asserts
the permission.
C#
// REMOVE the attribute below.
[SecurityPermission(SecurityAction.Assert, ControlThread = true)]
public void DoSomething()
{
}
public void DoAssert()
{
// REMOVE the line below.
new
SecurityPermission(SecurityPermissionFlag.ControlThread).Assert();
}
If you're denying or restricting (via PermitOnly ) any permission, contact your
security advisor. Because CAS attributes are not honored by the .NET 5+ runtime,
your application could have a security hole if it incorrectly relies on the CAS
infrastructure to restrict access to these methods.
C#
// REVIEW the attribute below; could indicate security vulnerability.
[SecurityPermission(SecurityAction.Deny, ControlThread = true)]
public void DoSomething()
{
}
public void DoPermitOnly()
{
// REVIEW the line below; could indicate security vulnerability.
new
SecurityPermission(SecurityPermissionFlag.ControlThread).PermitOnly();
}
If you're demanding any permission (except PrincipalPermission), remove the
demand. All demands will succeed at run time.
C#
// REMOVE the attribute below; it will always succeed.
[SecurityPermission(SecurityAction.Demand, ControlThread = true)]
public void DoSomething()
{
}
public void DoDemand()
{
// REMOVE the line below; it will always succeed.
new
SecurityPermission(SecurityPermissionFlag.ControlThread).Demand();
}
If you're demanding PrincipalPermission, consult the guidance for
PrincipalPermissionAttribute is obsolete as error. That guidance applies for both
PrincipalPermission and PrincipalPermissionAttribute.
If you absolutely must disable these warnings (which is not recommended), you
can suppress the SYSLIB0003 warning in code.
C#
#pragma warning disable SYSLIB0003 // disable the warning
[SecurityPermission(SecurityAction.Demand, ControlThread = true)]
#pragma warning restore SYSLIB0003 // re-enable the warning
public void DoSomething()
{
}
public void DoDemand()
{
#pragma warning disable SYSLIB0003 // disable the warning
new
SecurityPermission(SecurityPermissionFlag.ControlThread).Demand();
#pragma warning restore SYSLIB0003 // re-enable the warning
}
You can also suppress the warning in your project file. Doing so disables the
warning for all source files within the project.
XML
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net5.0</TargetFramework>
<!-- NoWarn below suppresses SYSLIB0003 project-wide -->
<NoWarn>$(NoWarn);SYSLIB0003</NoWarn>
</PropertyGroup>
</Project>
7 Note
Suppressing SYSLIB0003 disables only the CAS-related obsoletion warnings. It
does not disable any other warnings or change the behavior of the .NET 5+
runtime.
Security
Affected APIs
System.AppDomain.PermissionSet
System.Configuration.ConfigurationPermission
System.Configuration.ConfigurationPermissionAttribute
System.Data.Common.DBDataPermission
System.Data.Common.DBDataPermissionAttribute
System.Data.Odbc.OdbcPermission
System.Data.Odbc.OdbcPermissionAttribute
System.Data.OleDb.OleDbPermission
System.Data.OleDb.OleDbPermissionAttribute
System.Data.OracleClient.OraclePermission
System.Data.OracleClient.OraclePermissionAttribute
System.Data.SqlClient.SqlClientPermission
System.Data.SqlClient.SqlClientPermissionAttribute
System.Diagnostics.EventLogPermission
System.Diagnostics.EventLogPermissionAttribute
System.Diagnostics.PerformanceCounterPermission
System.Diagnostics.PerformanceCounterPermissionAttribute
System.DirectoryServices.DirectoryServicesPermission
System.DirectoryServices.DirectoryServicesPermissionAttribute
System.Drawing.Printing.PrintingPermission
System.Drawing.Printing.PrintingPermissionAttribute
System.Net.DnsPermission
System.Net.DnsPermissionAttribute
System.Net.Mail.SmtpPermission
System.Net.Mail.SmtpPermissionAttribute
System.Net.NetworkInformation.NetworkInformationPermission
System.Net.NetworkInformation.NetworkInformationPermissionAttribute
System.Net.PeerToPeer.Collaboration.PeerCollaborationPermission
System.Net.PeerToPeer.Collaboration.PeerCollaborationPermissionAttribute
System.Net.PeerToPeer.PnrpPermission
System.Net.PeerToPeer.PnrpPermissionAttribute
System.Net.SocketPermission
System.Net.SocketPermissionAttribute
System.Net.WebPermission
System.Net.WebPermissionAttribute
System.Runtime.InteropServices.AllowReversePInvokeCallsAttribute
System.Security.CodeAccessPermission
System.Security.HostProtectionException
System.Security.IPermission
System.Security.IStackWalk
System.Security.NamedPermissionSet
System.Security.PermissionSet
System.Security.Permissions.CodeAccessSecurityAttribute
System.Security.Permissions.DataProtectionPermission
System.Security.Permissions.DataProtectionPermissionAttribute
System.Security.Permissions.DataProtectionPermissionFlags
System.Security.Permissions.EnvironmentPermission
System.Security.Permissions.EnvironmentPermissionAccess
System.Security.Permissions.EnvironmentPermissionAttribute
System.Security.Permissions.FileDialogPermission
System.Security.Permissions.FileDialogPermissionAccess
System.Security.Permissions.FileDialogPermissionAttribute
System.Security.Permissions.FileIOPermission
System.Security.Permissions.FileIOPermissionAccess
System.Security.Permissions.FileIOPermissionAttribute
System.Security.Permissions.GacIdentityPermission
System.Security.Permissions.GacIdentityPermissionAttribute
System.Security.Permissions.HostProtectionAttribute
System.Security.Permissions.HostProtectionResource
System.Security.Permissions.IUnrestrictedPermission
System.Security.Permissions.IsolatedStorageContainment
System.Security.Permissions.IsolatedStorageFilePermission
System.Security.Permissions.IsolatedStorageFilePermissionAttribute
System.Security.Permissions.IsolatedStoragePermission
System.Security.Permissions.IsolatedStoragePermissionAttribute
System.Security.Permissions.KeyContainerPermission
System.Security.Permissions.KeyContainerPermissionAccessEntry
System.Security.Permissions.KeyContainerPermissionAccessEntryCollection
System.Security.Permissions.KeyContainerPermissionAccessEntryEnumerator
System.Security.Permissions.KeyContainerPermissionAttribute
System.Security.Permissions.KeyContainerPermissionFlags
System.Security.Permissions.MediaPermission
System.Security.Permissions.MediaPermissionAttribute
System.Security.Permissions.MediaPermissionAudio
System.Security.Permissions.MediaPermissionImage
System.Security.Permissions.MediaPermissionVideo
System.Security.Permissions.PermissionSetAttribute
System.Security.Permissions.PermissionState
System.Security.Permissions.PrincipalPermission
System.Security.Permissions.PrincipalPermissionAttribute
System.Security.Permissions.PublisherIdentityPermission
System.Security.Permissions.PublisherIdentityPermissionAttribute
System.Security.Permissions.ReflectionPermission
System.Security.Permissions.ReflectionPermissionAttribute
System.Security.Permissions.ReflectionPermissionFlag
System.Security.Permissions.RegistryPermission
System.Security.Permissions.RegistryPermissionAccess
System.Security.Permissions.RegistryPermissionAttribute
System.Security.Permissions.ResourcePermissionBase
System.Security.Permissions.ResourcePermissionBaseEntry
System.Security.Permissions.SecurityAction
System.Security.Permissions.SecurityAttribute
System.Security.Permissions.SecurityPermission
System.Security.Permissions.SecurityPermissionAttribute
System.Security.Permissions.SecurityPermissionFlag
System.Security.Permissions.SiteIdentityPermission
System.Security.Permissions.SiteIdentityPermissionAttribute
System.Security.Permissions.StorePermission
System.Security.Permissions.StorePermissionAttribute
System.Security.Permissions.StorePermissionFlags
System.Security.Permissions.StrongNameIdentityPermission
System.Security.Permissions.StrongNameIdentityPermissionAttribute
System.Security.Permissions.StrongNamePublicKeyBlob
System.Security.Permissions.TypeDescriptorPermission
System.Security.Permissions.TypeDescriptorPermissionAttribute
System.Security.Permissions.TypeDescriptorPermissionFlags
System.Security.Permissions.UIPermission
System.Security.Permissions.UIPermissionAttribute
System.Security.Permissions.UIPermissionClipboard
System.Security.Permissions.UIPermissionWindow
System.Security.Permissions.UrlIdentityPermission
System.Security.Permissions.UrlIdentityPermissionAttribute
System.Security.Permissions.WebBrowserPermission
System.Security.Permissions.WebBrowserPermissionAttribute
System.Security.Permissions.WebBrowserPermissionLevel
System.Security.Permissions.ZoneIdentityPermission
System.Security.Permissions.ZoneIdentityPermissionAttribute
System.Security.Policy.ApplicationTrust.ApplicationTrust(PermissionSet,
IEnumerable<StrongName>)
System.Security.Policy.ApplicationTrust.FullTrustAssemblies
System.Security.Policy.FileCodeGroup
System.Security.Policy.GacInstalled
System.Security.Policy.IIdentityPermissionFactory
System.Security.Policy.PolicyLevel.AddNamedPermissionSet(NamedPermissionSet)
System.Security.Policy.PolicyLevel.ChangeNamedPermissionSet(String,
PermissionSet)
System.Security.Policy.PolicyLevel.GetNamedPermissionSet(String)
System.Security.Policy.PolicyLevel.RemoveNamedPermissionSet
System.Security.Policy.PolicyStatement.PermissionSet
System.Security.Policy.PolicyStatement.PolicyStatement
System.Security.Policy.Publisher
System.Security.Policy.Site
System.Security.Policy.StrongName
System.Security.Policy.StrongNameMembershipCondition
System.Security.Policy.Url
System.Security.Policy.Zone
System.Security.SecurityManager
System.ServiceProcess.ServiceControllerPermission
System.ServiceProcess.ServiceControllerPermissionAttribute
System.Transactions.DistributedTransactionPermission
System.Transactions.DistributedTransactionPermissionAttribute
System.Web.AspNetHostingPermission
System.Web.AspNetHostingPermissionAttribute
System.Xaml.Permissions.XamlLoadPermission
CounterSet.CreateCounterSetInstance
now throws InvalidOperationException
if instance already exists
Article • 09/15/2021
Starting in .NET 5, 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 and later versions, when you call CreateCounterSetInstance and the counter
set exists, an InvalidOperationException exception is thrown.
Version introduced
5.0
Recommended action
If you catch ArgumentException exceptions in your app when calling
CreateCounterSetInstance, consider also catching InvalidOperationException exceptions.
7 Note
Catching ArgumentException exceptions is not recommended.
Affected APIs
System.Diagnostics.PerformanceData.CounterSet.CreateCounterSetInstance
Default ActivityIdFormat is W3C
Article • 11/08/2021
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. The default was changed in .NET 5 because the W3C format has
been ratified
and gained traction across multiple language implementations.
If your app targets a platform other than .NET 5 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 and later,
Activity.DefaultIdFormat is set to ActivityIdFormat.W3C.
Version introduced
5.0
Recommended action
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.
XML
<ItemGroup>
<RuntimeHostConfigurationOption
Include="System.Diagnostics.DefaultActivityIdFormatIsHierarchial"
Value="true" />
</ItemGroup>
In the runtimeconfig.json file.
JSON
{
"runtimeOptions": {
"configProperties": {
"System.Diagnostics.DefaultActivityIdFormatIsHierarchial":
true
}
}
}
Through an environment variable.
Set DOTNET_SYSTEM_DIAGNOSTICS_DEFAULTACTIVITYIDFORMATISHIERARCHIAL to true or
1.
Affected APIs
System.Diagnostics.Activity.DefaultIdFormat
Environment.OSVersion returns the
correct operating system version
Article • 09/15/2021
Environment.OSVersion returns the actual version of the operating system (OS) instead
of, for example, the OS that's selected for application compatibility.
Change description
In previous .NET versions, Environment.OSVersion returns an OS version that may be
incorrect when an application runs under Windows compatibility mode. For more
information, see GetVersionExA function remarks. On macOS, Environment.OSVersion
returns the underlying Darwin kernel version.
Starting in .NET 5, Environment.OSVersion returns the actual version of the operating
system for Windows and macOS.
The following table shows the difference in behavior.
Previous .NET versions
.NET 5+
Windows
6.2.9200.0
10.0.19042.0
macOS
19.6.0.0
10.15.7
Reason for change
Users of this property expect it to return the actual version of the operating system.
Most .NET apps don't specify their supported version in their application manifest, and
thus get the default supported version from the dotnet host. As a result, the
compatibility shim is rarely meaningful for the app that's running. When Windows
releases a new version and an older dotnet host is still in use, these apps may get an
incorrect OS version. Returning the actual version is more inline with developers'
expectations of this API.
With the introduction of OperatingSystem.IsWindowsVersionAtLeast,
OperatingSystem.IsMacOSVersionAtLeast, and
System.Runtime.Versioning.SupportedOSPlatformAttribute in .NET 5,
Environment.OSVersion was changed to be consistent for Windows and macOS.
Version introduced
5.0
Recommended action
Review and test any code that uses Environment.OSVersion to ensure it behaves as
desired.
Affected APIs
System.Environment.OSVersion
FrameworkDescription's value is .NET
instead of .NET Core
Article • 09/15/2021
RuntimeInformation.FrameworkDescription now returns ".NET" instead of ".NET Core".
Change description
In previous .NET versions, RuntimeInformation.FrameworkDescription returns ".NET
Core" as part of the description string, for example, .NET Core 3.1.1 .
Starting in .NET 5, RuntimeInformation.FrameworkDescription returns ".NET" as part of
the description string, for example, .NET 5.0.0 .
Reason for change
With .NET 5, netcoreapp is replaced by net as the short target-framework moniker. For
consistency, the framework's description has also been updated. The change is cosmetic,
as the FrameworkName isn't encoded anywhere else than in the
RuntimeInformation.FrameworkDescription property.
Version introduced
5.0
Recommended action
Update any code that searches for ".NET Core" in the string returned by
FrameworkDescription.
Affected APIs
System.Runtime.InteropServices.RuntimeInformation.FrameworkDescription
Global assembly cache APIs are obsolete
Article • 11/08/2021
.NET Core and .NET 5 and later versions eliminate the concept of the global assembly
cache (GAC) that was present in .NET Framework. As such, all .NET Core and .NET 5+
APIs that deal with the GAC either fail or perform no operation.
To help steer developers away from these APIs, some GAC-related APIs are marked as
obsolete, and generate a SYSLIB0005 warning at compile time. These APIs may be
removed in a future version of .NET.
Change description
The following APIs are marked obsolete.
API
Marked obsolete in...
Assembly.GlobalAssemblyCache
5.0 RC1
In .NET Framework 2.x - 4.x, the GlobalAssemblyCache property returns true if the
queried assembly was loaded from the GAC, and false if it was loaded from a different
location on disk. In .NET Core 2.x - 3.x, the GlobalAssemblyCache always returns false ,
reflecting that the GAC does not exist in .NET Core.
C#
Assembly asm = typeof(object).Assembly;
// Prints 'True' on .NET Framework, 'False' on .NET Core.
Console.WriteLine(asm.GlobalAssemblyCache);
In .NET 5 and later versions, the GlobalAssemblyCache property continues to always
return false . However, the property getter is also marked as obsolete to indicate to
callers that they should stop accessing the property. Libraries and apps should not use
the GlobalAssemblyCache API to make determinations about run-time behavior, as it
always returns false in .NET Core and .NET 5 and later versions.
C#
Assembly asm = typeof(object).Assembly;
// Prints 'False' on .NET 5+; also produces warning SYSLIB0005 at compile
time.
Console.WriteLine(asm.GlobalAssemblyCache);
This is a compile-time only change. There is no run-time change from previous versions
of .NET Core.
Reason for change
The global assembly cache (GAC) does not exist as a concept in .NET Core and .NET 5
and later versions.
Version introduced
.NET 5.0
Recommended action
If your application queries the GlobalAssemblyCache property, consider removing
the call. If you use the GlobalAssemblyCache value to choose between an
"assembly in the GAC"-flow vs. an "assembly not in the GAC"-flow at run time,
reconsider whether the flow still makes sense for a .NET Core or .NET 5+
application.
If you must continue to use the obsolete APIs, you can suppress the SYSLIB0005
warning in code.
C#
Assembly asm = typeof(object).Assembly;
#pragma warning disable SYSLIB0005 // Disable the warning.
// Prints 'False' on .NET 5+.
Console.WriteLine(asm.GlobalAssemblyCache);
#pragma warning restore SYSLIB0005 // Re-enable the warning.
You can also suppress the warning in your project file, which disables the warning
for all source files in the project.
XML
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net5.0</TargetFramework>
<!-- NoWarn below will suppress SYSLIB0005 project-wide -->
<NoWarn>$(NoWarn);SYSLIB0005</NoWarn>
</PropertyGroup>
</Project>
Suppressing SYSLIB0005 disables only the GlobalAssemblyCache obsoletion
warning. It does not disable any other warnings.
Affected APIs
System.Reflection.Assembly.GlobalAssemblyCache
Hardware intrinsic IsSupported checks
may differ for nested types
Article • 09/15/2021
Checking <Isa>.X64.IsSupported , where <Isa> refers to the classes in the
System.Runtime.Intrinsics.X86 namespace, may now produce a different result to
previous versions of .NET.
 Tip
ISA stands for industry standard architecture.
Version introduced
5.0
Change description
In previous versions of .NET, some of the System.Runtime.Intrinsics.X86 hardwareintrinsic types, for example, System.Runtime.Intrinsics.X86.Aes, didn't expose a nested
X64 class. For these types, calling <Isa>.X64.IsSupported resolved to an IsSupported
property on a nested X64 class of a parent class of <Isa> . This meant that the property
could return true even when <Isa>.IsSupported returns false .
In .NET 5 and later versions, all of the System.Runtime.Intrinsics.X86 types expose a
nested X64 class that appropriately reports support. This ensures that the general
hierarchy remains correct, and that if <Isa>.X64.IsSupported is true , then
<Isa>.IsSupported can also be assumed to be true .
Reason for change
It was intended that if <Isa>.X64.IsSupported is true , <Isa>.IsSupported is also implied
to be true . However, due to how member resolution works in C#, classes that didn't
have a nested X64 class exposed a situation where this wasn't always the case and led to
bugs in user code.
Recommended action
If necessary, adjust code that checks IsSupported to check for the appropriate ISA.
Affected APIs
System.Runtime.Intrinsics.X86.Aes.X64.IsSupported
System.Runtime.Intrinsics.X86.Avx.X64.IsSupported
System.Runtime.Intrinsics.X86.Avx2.X64.IsSupported
System.Runtime.Intrinsics.X86.Fma.X64.IsSupported
System.Runtime.Intrinsics.X86.Pclmulqdq.X64.IsSupported
System.Runtime.Intrinsics.X86.Sse3.X64.IsSupported
System.Runtime.Intrinsics.X86.Ssse3.X64.IsSupported
IntPtr and UIntPtr implement
IFormattable
Article • 09/15/2021
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 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
Recommended action
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() .
Affected APIs
Not detectable via API analysis.
LastIndexOf has improved handling of
empty search strings
Article • 09/15/2021
String.LastIndexOf and related APIs now return correct values when searching for a zerolength (or zero-length equivalent) substring within a larger string.
Change description
In .NET Framework and .NET Core 1.0 - 3.1, String.LastIndexOf and related APIs might
return an incorrect value when the caller searches for a zero-length substring.
C#
Console.WriteLine("Hello".LastIndexOf("")); // prints '4' (incorrect)
ReadOnlySpan<char> span = "Hello";
Console.WriteLine(span.LastIndexOf("")); // prints '0' (incorrect)
Starting with .NET 5, these APIs return the correct value for LastIndexOf .
C#
Console.WriteLine("Hello".LastIndexOf("")); // prints '5' (correct)
ReadOnlySpan<char> span = "Hello";
Console.WriteLine(span.LastIndexOf("")); // prints '5' (correct)
In these examples, 5 is the correct answer because "Hello".Substring(5) and
"Hello".AsSpan().Slice(5) both produce an empty string, which is trivially equal to the
empty substring that is sought.
Reason for change
This change was part of an overall bug fixing effort around string handling for .NET 5. It
also helps unify our behavior between Windows and non-Windows platforms. For more
information, see dotnet/runtime#13383
Version introduced
and dotnet/runtime##13382 .
5.0
Recommended action
You don't need to take any action. The .NET 5 runtime provides the new behaviors
automatically.
There is no compatibility switch to restore the old behavior.
Affected APIs
System.String.LastIndexOf
System.Globalization.CompareInfo.LastIndexOf
System.MemoryExtensions.LastIndexOf
URI paths with non-ASCII characters
parse correctly on Unix
Article • 11/08/2021
A bug was fixed in the System.Uri class such that absolute URI paths that contain nonASCII characters now parse correctly on Unix platforms.
Change description
In previous versions of .NET, absolute URI paths that contain non-ASCII characters are
parsed incorrectly on Unix platforms, and segments of the path are duplicated.
(Absolute paths are those that start with "/".) The parsing issue has been fixed for .NET 5.
If you move from a previous version of .NET to .NET 5 or later, you'll get different values
produced by Uri.AbsoluteUri, Uri.ToString(), and other Uri members.
Consider the output of the following code when run on Unix.
C#
var myUri = new Uri("/üri");
Console.WriteLine($"AbsoluteUri: {myUri.AbsoluteUri}");
Console.WriteLine($"ToString: {myUri.ToString()}");
Output on previous .NET version:
text
AbsoluteUri: /%C3%BCri/%C3%BCri
ToString: /üri/üri
Output on .NET 5 or later version:
text
AbsoluteUri: /%C3%BCri
ToString: /üri
Version introduced
5.0
Recommended action
If you have code that expects and accounts for the duplicated path segments, you can
remove that code.
Affected APIs
System.Uri
API obsoletions with non-default
diagnostic IDs
Article • 06/07/2022
Some APIs have been marked as obsolete, starting in .NET 5. This breaking change is
specific to APIs that have been marked as obsolete with a custom diagnostic ID.
Suppressing the default obsoletion diagnostic ID, which is CS0618 for the C# compiler,
does not suppress the warnings that the compiler generates when these APIs are used.
Change description
In previous .NET versions, these APIs can be used without any build warning. In .NET 5
and later versions, use of these APIs produces a compile-time warning or error with a
custom diagnostic ID. The use of custom diagnostic IDs allows you to suppress the
obsoletion warnings individually instead of blanket-suppressing all obsoletion warnings.
The following table lists the custom diagnostic IDs and their corresponding warning
messages for obsoleted APIs.
Diagnostic
ID
Description
Severity
SYSLIB0001
The UTF-7 encoding is insecure and should not be used. Consider using
UTF-8 instead.
Warning
SYSLIB0002
PrincipalPermissionAttribute is not honored by the runtime and must
not be used.
Error
SYSLIB0003
Code access security (CAS) is not supported or honored by the runtime.
Warning
SYSLIB0004
The constrained execution region (CER) feature is not supported.
Warning
SYSLIB0005
The global assembly cache (GAC) is not supported.
Warning
SYSLIB0006
Thread.Abort() is not supported and throws
PlatformNotSupportedException.
Warning
SYSLIB0007
The default implementation of this cryptography algorithm is not
supported.
Warning
SYSLIB0008
The CreatePdbGenerator() API is not supported and throws
PlatformNotSupportedException.
Warning
Diagnostic
ID
Description
Severity
SYSLIB0009
The AuthenticationManager.Authenticate and
AuthenticationManager.PreAuthenticate methods are not supported
Warning
and throw PlatformNotSupportedException.
SYSLIB0010
Some remoting APIs are not supported and throw
Warning
PlatformNotSupportedException.
SYSLIB0011
BinaryFormatter serialization is obsolete and should not be used.
Warning
SYSLIB0012
Assembly.CodeBase and Assembly.EscapedCodeBase are only included
for .NET Framework compatibility. Use Assembly.Location instead.
Warning
Version introduced
.NET 5.0
Recommended action
Follow the specific guidance provided for the each diagnostic ID using the URL link
provided on the warning.
Warnings or errors for these obsoletions can't be suppressed using the standard
diagnostic ID for obsolete types or members; use the custom SYSLIBxxxx
diagnostic ID value instead.
Affected APIs
SYSLIB0001
Encoding.UTF7
UTF7Encoding
SYSLIB0002
PrincipalPermissionAttribute(SecurityAction)
SYSLIB0003
Classes in the System.Security.Permissions namespace:
System.Security.Permissions.CodeAccessSecurityAttribute
System.Security.Permissions.DataProtectionPermission
System.Security.Permissions.DataProtectionPermissionAttribute
System.Security.Permissions.EnvironmentPermission
System.Security.Permissions.EnvironmentPermissionAttribute
System.Security.Permissions.FileDialogPermission
System.Security.Permissions.FileDialogPermissionAttribute
System.Security.Permissions.FileIOPermission
System.Security.Permissions.FileIOPermissionAttribute
System.Security.Permissions.GacIdentityPermission
System.Security.Permissions.GacIdentityPermissionAttribute
System.Security.Permissions.HostProtectionAttribute
System.Security.Permissions.IsolatedStorageFilePermission
System.Security.Permissions.IsolatedStorageFilePermissionAttribute
System.Security.Permissions.IsolatedStoragePermission
System.Security.Permissions.IsolatedStoragePermissionAttribute
System.Security.Permissions.KeyContainerPermission
System.Security.Permissions.KeyContainerPermissionAccessEntry
System.Security.Permissions.KeyContainerPermissionAccessEntryCollection
System.Security.Permissions.KeyContainerPermissionAccessEntryEnumerator
System.Security.Permissions.KeyContainerPermissionAttribute
System.Security.Permissions.MediaPermission
System.Security.Permissions.MediaPermissionAttribute
System.Security.Permissions.PermissionSetAttribute
System.Security.Permissions.PrincipalPermission
System.Security.Permissions.PrincipalPermissionAttribute
System.Security.Permissions.PublisherIdentityPermission
System.Security.Permissions.PublisherIdentityPermissionAttribute
System.Security.Permissions.ReflectionPermission
System.Security.Permissions.ReflectionPermissionAttribute
System.Security.Permissions.RegistryPermission
System.Security.Permissions.RegistryPermissionAttribute
System.Security.Permissions.ResourcePermissionBase
System.Security.Permissions.ResourcePermissionBaseEntry
System.Security.Permissions.SecurityAttribute
System.Security.Permissions.SecurityPermission
System.Security.Permissions.SecurityPermissionAttribute
System.Security.Permissions.SiteIdentityPermission
System.Security.Permissions.SiteIdentityPermissionAttribute
System.Security.Permissions.StorePermission
System.Security.Permissions.StorePermissionAttribute
System.Security.Permissions.StrongNameIdentityPermission
System.Security.Permissions.StrongNameIdentityPermissionAttribute
System.Security.Permissions.StrongNamePublicKeyBlob
System.Security.Permissions.TypeDescriptorPermission
System.Security.Permissions.TypeDescriptorPermissionAttribute
System.Security.Permissions.UIPermission
System.Security.Permissions.UIPermissionAttribute
System.Security.Permissions.UrlIdentityPermission
System.Security.Permissions.UrlIdentityPermissionAttribute
System.Security.Permissions.WebBrowserPermission
System.Security.Permissions.WebBrowserPermissionAttribute
System.Security.Permissions.ZoneIdentityPermission
System.Security.Permissions.ZoneIdentityPermissionAttribute
Classes that derive from CodeAccessSecurityAttribute :
System.Configuration.ConfigurationPermissionAttribute
System.Data.Common.DBDataPermissionAttribute
System.Data.Odbc.OdbcPermissionAttribute
System.Data.OleDb.OleDbPermissionAttribute
System.Data.OracleClient.OraclePermissionAttribute
System.Data.SqlClient.SqlClientPermissionAttribute
System.Diagnostics.EventLogPermissionAttribute
System.Diagnostics.PerformanceCounterPermissionAttribute
System.DirectoryServices.DirectoryServicesPermissionAttribute
System.Drawing.Printing.PrintingPermissionAttribute
System.Net.DnsPermissionAttribute
System.Net.SocketPermissionAttribute
System.Net.WebPermissionAttribute
System.Net.Mail.SmtpPermissionAttribute
System.Net.NetworkInformation.NetworkInformationPermissionAttribute
System.Net.PeerToPeer.PnrpPermissionAttribute
System.Net.PeerToPeer.Collaboration.PeerCollaborationPermissionAttribute
System.ServiceProcess.ServiceControllerPermissionAttribute
System.Transactions.DistributedTransactionPermissionAttribute
System.Web.AspNetHostingPermissionAttribute
Interfaces:
System.Security.Permissions.IUnrestrictedPermission
System.Security.IPermission
System.Security.IStackWalk
System.Security.Policy.IIdentityPermissionFactory
Classes that implement IStackWalk :
System.Security.NamedPermissionSet
System.Security.PermissionSet
Classes that implement IPermission :
System.Security.CodeAccessPermission
Classes that derive from CodeAccessPermission :
System.Configuration.ConfigurationPermission
System.Data.Common.DBDataPermission
System.Data.Odbc.OdbcPermission
System.Data.OleDb.OleDbPermission
System.Data.SqlClient.SqlClientPermission
System.Data.OracleClient.OraclePermission
System.Drawing.Printing.PrintingPermission
System.Net.DnsPermission
System.Net.SocketPermission
System.Net.WebPermission
System.Net.Mail.SmtpPermission
System.Net.NetworkInformation.NetworkInformationPermission
System.Net.PeerToPeer.PnrpPermission
System.Net.PeerToPeer.Collaboration.PeerCollaborationPermission
System.Transactions.DistributedTransactionPermission
System.Web.AspNetHostingPermission
System.Xaml.Permissions.XamlLoadPermission
Classes that derive from ResourcePermissionBase :
System.Diagnostics.EventLogPermission
System.Diagnostics.PerformanceCounterPermission
System.DirectoryServices.DirectoryServicesPermission
System.ServiceProcess.ServiceControllerPermission
Enums in the System.Security.Permissions namespace:
System.Security.Permissions.DataProtectionPermissionFlags
System.Security.Permissions.EnvironmentPermissionAccess
System.Security.Permissions.FileDialogPermissionAccess
System.Security.Permissions.FileIOPermissionAccess
System.Security.Permissions.HostProtectionResource
System.Security.Permissions.IsolatedStorageContainment
System.Security.Permissions.KeyContainerPermissionFlags
System.Security.Permissions.MediaPermissionAudio
System.Security.Permissions.MediaPermissionImage
System.Security.Permissions.MediaPermissionVideo
System.Security.Permissions.PermissionState
System.Security.Permissions.ReflectionPermissionFlag
System.Security.Permissions.RegistryPermissionAccess
System.Security.Permissions.SecurityAction
System.Security.Permissions.SecurityPermissionFlag
System.Security.Permissions.StorePermissionFlags
System.Security.Permissions.TypeDescriptorPermissionFlags
System.Security.Permissions.UIPermissionClipboard
System.Security.Permissions.UIPermissionWindow
System.Security.Permissions.WebBrowserPermissionLevel
Classes and members that depend on code access security types:
System.AppDomain.ExecuteAssembly(String, String[], Byte[],
AssemblyHashAlgorithm)
System.AppDomain.PermissionSet
System.Runtime.InteropServices.AllowReversePInvokeCallsAttribute
System.Security.HostProtectionException
System.Security.Policy.FileCodeGroup
System.Security.Policy.StrongName
System.Security.Policy.StrongNameMembershipCondition
System.Security.Policy.ApplicationTrust.ApplicationTrust(PermissionSet,
IEnumerable<StrongName>)
System.Security.Policy.ApplicationTrust.FullTrustAssemblies
System.Security.Policy.GacInstalled
System.Security.Policy.PolicyStatement.PolicyStatement
System.Security.Policy.PolicyLevel.AddNamedPermissionSet(NamedPermissionSet)
System.Security.Policy.PolicyLevel.ChangeNamedPermissionSet(String,
PermissionSet)
System.Security.Policy.PolicyLevel.GetNamedPermissionSet(String)
System.Security.Policy.PolicyLevel.RemoveNamedPermissionSet(String)
PolicyLevel.RemoveNamedPermissionSet(NamedPermissionSet)
System.Security.Policy.PolicyStatement.PermissionSet
System.Security.Policy.Publisher
System.Security.Policy.Site
System.Security.Policy.Url
System.Security.Policy.Zone
System.Security.SecurityManager
SYSLIB0004
RuntimeHelpers.ExecuteCodeWithGuaranteedCleanup(RuntimeHelpers+TryCode,
RuntimeHelpers+CleanupCode, Object)
RuntimeHelpers.PrepareConstrainedRegions()
RuntimeHelpers.PrepareConstrainedRegionsNoOP()
RuntimeHelpers.PrepareContractedDelegate(Delegate)
RuntimeHelpers.ProbeForSufficientStack()
System.Runtime.ConstrainedExecution.Cer
System.Runtime.ConstrainedExecution.Consistency
System.Runtime.ConstrainedExecution.PrePrepareMethodAttribute
System.Runtime.ConstrainedExecution.ReliabilityContractAttribute
SYSLIB0005
Assembly.GlobalAssemblyCache
SYSLIB0006
Thread.Abort()
Thread.Abort(Object)
SYSLIB0007
System.Security.Cryptography.AsymmetricAlgorithm.Create()
System.Security.Cryptography.HashAlgorithm.Create()
System.Security.Cryptography.HMAC.Create()
System.Security.Cryptography.KeyedHashAlgorithm.Create()
System.Security.Cryptography.SymmetricAlgorithm.Create()
SYSLIB0008
DebugInfoGenerator.CreatePdbGenerator()
SYSLIB0009
AuthenticationManager.Authenticate
AuthenticationManager.PreAuthenticate
SYSLIB0010
MarshalByRefObject.GetLifetimeService()
MarshalByRefObject.InitializeLifetimeService()
SYSLIB0011
System.Exception.SerializeObjectState
BinaryFormatter.Serialize
BinaryFormatter.Deserialize
Formatter.Serialize(Stream, Object)
Formatter.Deserialize(Stream)
IFormatter.Serialize(Stream, Object)
IFormatter.Deserialize(Stream)
SYSLIB0012
Assembly.CodeBase
Assembly.EscapedCodeBase
See also
API obsoletions with non-default diagnostic IDs (.NET 6)
API obsoletions with non-default diagnostic IDs (.NET 7)
Obsolete features in .NET 5+
Obsolete properties on
ConsoleLoggerOptions
Article • 09/15/2021
The Microsoft.Extensions.Logging.Console.ConsoleLoggerFormat type and some
properties on ConsoleLoggerOptions are now obsolete.
Change description
Starting in .NET 5, the Microsoft.Extensions.Logging.Console.ConsoleLoggerFormat type
and several properties on ConsoleLoggerOptions are obsolete. The obsolete properties
are:
ConsoleLoggerOptions.DisableColors
ConsoleLoggerOptions.IncludeScopes
ConsoleLoggerOptions.TimestampFormat
ConsoleLoggerOptions.UseUtcTimestamp
ConsoleLoggerOptions.Format
With the introduction of new formatters, these properties are now available on the
individual formatters.
Reason for change
The Format property is an enumeration type, which cannot represent a custom
formatter.
The remaining properties were set on ConsoleLoggerOptions and applied to both of the
built-in formats for console logs. However, with the introduction of a new formatter API,
it makes more sense for formatting to be represented on the formatter-specific options.
This change provides better separation between the logger and logger formatters.
Version introduced
5.0
Recommended action
Use the new ConsoleLoggerOptions.FormatterName property in place of the
ConsoleLoggerOptions.Format property. For example:
C#
loggingBuilder.AddConsole(options =>
{
options.FormatterName = ConsoleFormatterNames.Systemd;
});
There are several differences between FormatterName and Format:
Format has only two possible options: Default and Systemd .
FormatterName is case insensitive and can be any string. The reserved, built-in
names are Simple , Systemd , and Json (.NET 5 and later).
"Format": "Systemd" maps to "FormatterName": "Systemd" .
"Format": "Default" maps to "FormatterName": "Simple" .
For the DisableColors, IncludeScopes, TimestampFormat, and UseUtcTimestamp
properties, use the corresponding property on the new ConsoleFormatterOptions,
JsonConsoleFormatterOptions, or SimpleConsoleFormatterOptions types instead.
For example, the corresponding setting for ConsoleLoggerOptions.DisableColors is
SimpleConsoleFormatterOptions.ColorBehavior.
Previous code:
C#
loggingBuilder.AddConsole(options =>
{
options.DisableColors = true;
});
New code:
C#
loggingBuilder.AddSimpleConsole(options =>
{
options.ColorBehavior = LoggerColorBehavior.Disabled;
});
The following two JSON snippets show how the configuration file changes. Old
configuration file:
JSON
{
"Logging": {
"LogLevel": {
"Default": "None",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
},
"Console": {
"LogLevel": {
"Default": "Information"
},
"Format": "Systemd",
"IncludeScopes": true,
"TimestampFormat": "HH:mm:ss",
"UseUtcTimestamp": true
}
},
"AllowedHosts": "*"
}
New configuration file:
JSON
{
"Logging": {
"LogLevel": {
"Default": "None",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
},
"Console": {
"LogLevel": {
"Default": "Information"
},
"FormatterName": "Systemd",
"FormatterOptions": {
"IncludeScopes": true,
"TimestampFormat": "HH:mm:ss",
"UseUtcTimestamp": true
}
}
},
"AllowedHosts": "*"
}
Affected APIs
Microsoft.Extensions.Logging.Console.ConsoleLoggerOptions.DisableColors
Microsoft.Extensions.Logging.Console.ConsoleLoggerOptions.IncludeScopes
Microsoft.Extensions.Logging.Console.ConsoleLoggerOptions.TimestampFormat
Microsoft.Extensions.Logging.Console.ConsoleLoggerOptions.UseUtcTimestamp
Microsoft.Extensions.Logging.Console.ConsoleLoggerOptions.Format
Complexity of LINQ
OrderBy.First{OrDefault} increased
Article • 09/15/2021
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 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.
7 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
Recommended action
No action is required on the developer's part.
Affected APIs
System.Linq.Enumerable.OrderBy
System.Linq.Enumerable.OrderByDescending
System.Linq.Enumerable.First<TSource>(IEnumerable<TSource>,
Func<TSource,Boolean>)
System.Linq.Enumerable.FirstOrDefault<TSource>(IEnumerable<TSource>,
Func<TSource,Boolean>)
OSPlatform attributes renamed or
removed
Article • 09/15/2021
The following attributes that were introduced in .NET 5 Preview 8 have been removed or
renamed: MinimumOSPlatformAttribute , RemovedInOSPlatformAttribute , and
ObsoletedInOSPlatformAttribute .
Change description
.NET 5 Preview 8 introduced the following attributes in the System.Runtime.Versioning
namespace:
MinimumOSPlatformAttribute
RemovedInOSPlatformAttribute
ObsoletedInOSPlatformAttribute
In .NET 5 Preview 8, when a project targets an OS-specific flavor of .NET 5 by using a
target framework moniker such as net5.0-windows , the build adds an assembly-level
System.Runtime.Versioning.MinimumOSPlatformAttribute attribute.
In .NET 5 RC1, the ObsoletedInOSPlatformAttribute has been removed, and
MinimumOSPlatformAttribute and RemovedInOSPlatformAttribute have been renamed as
follows:
Preview 8 name
RC1 and later name
MinimumOSPlatformAttribute
SupportedOSPlatformAttribute
RemovedInOSPlatformAttribute
UnsupportedOSPlatformAttribute
In .NET 5 RC1 and later, when a project targets an OS-specific flavor of .NET 5 by using a
target framework moniker such as net5.0-windows , the build adds an assembly-level
SupportedOSPlatformAttribute attribute.
Reason for change
.NET 5 Preview 8 introduced attributes in System.Runtime.Versioning to specify
supported platforms for APIs. The attributes are consumed by the Platform compatibility
analyzer to produce build warnings when platform-specific APIs are consumed on
platforms that don't support those APIs.
For .NET 5 RC1, an additional feature was added to the platform compatibility analyzer
for platform exclusion. The feature allows APIs to be marked as entirely unsupported on
OS platforms. That feature prompted changes to the attributes, including using more
suitable names. The ObsoletedInOSPlatformAttribute was removed because it was no
longer needed.
Version introduced
5.0 RC1
Recommended action
When you retarget your project from .NET 5 Preview 8 to .NET 5 RC1, you might
encounter build or run-time errors due to these changes. For example, the renaming of
MinimumOSPlatformAttribute is likely to produce errors, because the attribute is applied
to platform-specific assemblies at build time, and old build artifacts will still reference
the old API name.
Example build-time errors:
error CS0246: The type or namespace name 'MinimumOSPlatformAttribute'
could not be found (are you missing a using directive or an assembly reference?)
error CS0246: The type or namespace name 'RemovedInOSPlatformAttribute'
could not be found (are you missing a using directive or an assembly reference?)
error CS0246: The type or namespace name 'ObsoletedInOSPlatformAttribute'
could not be found (are you missing a using directive or an assembly reference?)
Example run-time error:
Unhandled exception. System.TypeLoadException: Could not load type
'System.Runtime.Versioning.MinimumOSPlatformAttribute' from assembly
'System.Runtime, Version=5.0.0.0, Culture=neutral,
PublicKeyToken=b03f5f7f11d50a3a'.
To resolve these errors:
Update any references of MinimumOSPlatformAttribute to
SupportedOSPlatformAttribute.
Update any references of RemovedInOSPlatformAttribute to
UnsupportedOSPlatformAttribute.
Remove any references to ObsoletedInOSPlatformAttribute .
Rebuild your project (or perform clean + build) to delete old build artifacts.
Affected APIs
System.Runtime.Versioning.MinimumOSPlatformAttribute
System.Runtime.Versioning.ObsoletedInOSPlatformAttribute
System.Runtime.Versioning.RemovedInOSPlatformAttribute
Microsoft.DotNet.PlatformAbstractions
package removed
Article • 09/15/2021
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 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
Recommended action
You can continue to use older versions of the library if they meet your
requirements.
If the older versions don't meet your requirements, replace usages of the
PlatformAbstractions APIs with the recommended replacements.
PlatformAbstractions API
Recommended replacement
ApplicationEnvironment.ApplicationBasePath
AppContext.BaseDirectory
HashCodeCombiner
System.HashCode
RuntimeEnvironment.GetRuntimeIdentifier()
RuntimeInformation.RuntimeIdentifier
RuntimeEnvironment.OperatingSystemPlatform
RuntimeInformation.IsOSPlatform(OSPlatform)
RuntimeEnvironment.RuntimeArchitecture
RuntimeInformation.ProcessArchitecture
RuntimeEnvironment.OperatingSystem
RuntimeInformation.OSDescription
RuntimeEnvironment.OperatingSystemVersion
RuntimeInformation.OSDescription and
Environment.OSVersion
7 Note
Most use cases for RuntimeEnvironment.OperatingSystem and
RuntimeEnvironment.OperatingSystemVersion are for display purposes, for
example, displaying to a user, logging, and telemetry. It's not recommended
to make run-time decisions based on an operating system (OS) version.
Environment.OSVersion now returns the correct version for Windows and
macOS operating systems. However, for most Unix distributions, what is
considered to be the "OS version" is not as straightforward. For example, it
could be the Linux kernel version, or it could be the distro version. For most
Unix platforms, Environment.OSVersion and
RuntimeInformation.OSDescription return the version that's returned by
uname . To get the Linux distro name and version information, the
recommended approach is to read the /etc/os-release file.
Affected APIs
Microsoft.DotNet.PlatformAbstractions.ApplicationEnvironment.ApplicationBaseP
ath
Microsoft.DotNet.PlatformAbstractions.HashCodeCombiner
Microsoft.DotNet.PlatformAbstractions.RuntimeEnvironment.GetRuntimeIdentifier
()
Microsoft.DotNet.PlatformAbstractions.RuntimeEnvironment.OperatingSystem
Microsoft.DotNet.PlatformAbstractions.RuntimeEnvironment.OperatingSystemPlatf
orm
Microsoft.DotNet.PlatformAbstractions.RuntimeEnvironment.OperatingSystemVersi
on
Microsoft.DotNet.PlatformAbstractions.RuntimeEnvironment.RuntimeArchitecture
PrincipalPermissionAttribute is obsolete
as error
Article • 09/15/2021
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:
C#
[PrincipalPermission(SecurityAction.Demand, Role = "Administrators")]
public void MyMethod()
{
// Code that should only run when the current user is an administrator.
}
Starting in .NET 5, 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 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 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 or later.
Version introduced
5.0
Recommended action
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.
C#
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.
C#
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.
}
Affected APIs
System.Security.Permissions.PrincipalPermissionAttribute
Parameter names changed in RC2
Article • 09/15/2021
Some reference assembly parameter names have changed to match parameter names in
the implementation assemblies.
Change description
In previous .NET 5 preview and RC versions, some reference assembly parameter names
are different to their corresponding parameters in the implementation assembly. This
can cause problems while using named arguments and reflection.
In .NET 5 RC2, these mismatched parameter names were updated in the reference
assemblies to exactly match the corresponding parameter names in the implementation
assemblies.
The following table shows the APIs and parameter names that changed.
API
Old
parameter
name
New
parameter
name
ActivityContext(ActivityTraceId, ActivitySpanId,
ActivityTraceFlags, String, Boolean)
traceOptions
traceFlags
CompareInfo.IsPrefix(ReadOnlySpan<Char>,
ReadOnlySpan<Char>, CompareOptions, Int32)
suffix
prefix
Reason for change
The parameter names were changed for consistency and to avoid failures when using
named arguments and reflection.
Version introduced
5.0 RC2
Recommended action
If you encounter a compiler error due to a parameter name change, update the
parameter name accordingly.
Affected APIs
ActivityContext(ActivityTraceId, ActivitySpanId, ActivityTraceFlags, String, Boolean)
System.Globalization.CompareInfo.IsPrefix(ReadOnlySpan<Char>,
ReadOnlySpan<Char>, CompareOptions, Int32)
Parameter names changed in reference
assemblies
Article • 09/15/2021
Some reference assembly parameter names have changed to match parameter names in
the implementation assemblies.
Change description
In previous .NET versions, some reference assembly parameter names are different to
their corresponding parameters in the implementation assembly. This can cause
problems while using named arguments and reflection.
In .NET 5, these mismatched parameter names were updated in the reference assemblies
to exactly match the corresponding parameter names in the implementation assemblies.
The following table shows the APIs and parameter names that changed.
API
Old
parameter
name
New parameter
name
CodeGenerator.GenerateStatements(CodeStatementCollection)
stms
stmts
Icon.ISerializable.GetObjectData(SerializationInfo,
StreamingContext)
info
si
Image.ISerializable.GetObjectData(SerializationInfo,
info
si
IPAddress.Parse(ReadOnlySpan<Char>)
ipString
ipSpan
IPAddress.TryParse(ReadOnlySpan<Char>, IPAddress)
ipString
ipSpan
IsolatedStorageFileStream.BeginRead(Byte[], Int32, Int32,
AsyncCallback, Object)
buffer
array
IsolatedStorageFileStream.BeginWrite(Byte[], Int32, Int32,
AsyncCallback, Object)
buffer
array
NetworkCredential.GetCredential(String, Int32, String)
authType
authenticationType
ParenthesizePropertyNameAttribute.Equals(Object)
o
obj
RefreshPropertiesAttribute.Equals(Object)
value
obj
StreamingContext)
API
Old
parameter
New parameter
name
name
StackFrame(Boolean)
fNeedFileInfo
needFileInfo
StackFrame(Int32, Boolean)
fNeedFileInfo
needFileInfo
StringNormalizationExtensions.IsNormalized(String,
NormalizationForm)
value
strInput
StringNormalizationExtensions.IsNormalized(String)
value
strInput
StringNormalizationExtensions.Normalize(String,
NormalizationForm)
value
strInput
StringNormalizationExtensions.Normalize(String)
value
strInput
Reason for change
The parameter names were changed for consistency and to avoid failures when using
named arguments and reflection.
Version introduced
5.0
Recommended action
If you encounter a compiler error due to a parameter name change, update the
parameter name accordingly.
Affected APIs
System.CodeDom.Compiler.CodeGenerator.GenerateStatements(CodeStatementCo
llection)
System.ComponentModel.ParenthesizePropertyNameAttribute.Equals(Object)
System.ComponentModel.RefreshPropertiesAttribute.Equals(Object)
StackFrame(Boolean)
StackFrame(Int32, Boolean)
System.Drawing.Icon.System.Runtime.Serialization.ISerializable.GetObjectData(Seria
lizationInfo, StreamingContext)
System.Drawing.Image.System.Runtime.Serialization.ISerializable.GetObjectData(Se
rializationInfo, StreamingContext)
System.IO.IsolatedStorage.IsolatedStorageFileStream.BeginRead(Byte[], Int32, Int32,
AsyncCallback, Object)
System.IO.IsolatedStorage.IsolatedStorageFileStream.BeginWrite(Byte[], Int32,
Int32, AsyncCallback, Object)
System.Net.IPAddress.Parse(ReadOnlySpan<Char>)
System.Net.IPAddress.TryParse(ReadOnlySpan<Char>, IPAddress)
System.Net.NetworkCredential.GetCredential(String, Int32, String)
System.StringNormalizationExtensions.IsNormalized(String, NormalizationForm)
System.StringNormalizationExtensions.IsNormalized(String)
System.StringNormalizationExtensions.Normalize(String, NormalizationForm)
System.StringNormalizationExtensions.Normalize(String)
Remoting APIs are obsolete
Article • 09/15/2021
Some remoting-related APIs are marked as obsolete and generate a SYSLIB0010
warning at compile time. These APIs may be removed in a future version of .NET.
Change description
The following remoting APIs are marked obsolete.
API
Marked obsolete in...
MarshalByRefObject.GetLifetimeService()
5.0 RC1
MarshalByRefObject.InitializeLifetimeService()
5.0 RC1
In .NET Framework 2.x - 4.x, the GetLifetimeService() and InitializeLifetimeService()
methods control the lifetime of instances involved with .NET remoting. In .NET Core 2.x3.x, these methods always throw a PlatformNotSupportedException at run time.
In .NET 5 and later versions, the GetLifetimeService() and InitializeLifetimeService()
methods are marked obsolete as warning, but continue to throw a
PlatformNotSupportedException at run time.
C#
// MemoryStream, like all Stream instances, subclasses MarshalByRefObject.
MemoryStream stream = new MemoryStream();
// Throws PlatformNotSupportedException; also produces warning SYSLIB0010.
obj.InitializeLifetimeService();
This is a compile-time only change. There is no run-time change from previous versions
of .NET Core.
Reason for change
.NET remoting is a legacy technology. It allows instantiating an object in another process
(potentially even on a different machine) and interacting with that object as if it were an
ordinary, in-process .NET object instance. The .NET remoting infrastructure only exists in
.NET Framework 2.x - 4.x. .NET Core and .NET 5 and later versions don't have support for
.NET remoting, and the remoting APIs either don't exist or always throw exceptions on
these runtimes.
To help steer developers away from these APIs, we are obsoleting selected remotingrelated APIs. These APIs may be removed entirely in a future version of .NET.
Version introduced
.NET 5.0
Recommended action
Consider using WCF or HTTP-based REST services to communicate with objects in
other applications or across machines. For more information, see .NET Framework
technologies unavailable on .NET Core.
If you must continue to use the obsolete APIs, you can suppress the SYSLIB0010
warning in code.
C#
MarshalByRefObject obj = GetMarshalByRefObj();
#pragma warning disable SYSLIB0010 // Disable the warning.
obj.InitializeLifetimeService(); // Still throws PNSE.
obj.GetLifetimeService(); // Still throws PNSE.
#pragma warning restore SYSLIB0010 // Reenable the warning.
You can also suppress the warning in your project file, which disables the warning
for all source files in the project.
XML
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net5.0</TargetFramework>
<!-- NoWarn below will suppress SYSLIB0010 project-wide -->
<NoWarn>$(NoWarn);SYSLIB0010</NoWarn>
</PropertyGroup>
</Project>
Suppressing SYSLIB0010 disables only the remoting API obsoletion warnings. It
does not disable any other warnings. Additionally, it doesn't change the hardcoded
run-time behavior of always throwing PlatformNotSupportedException.
Affected APIs
System.MarshalByRefObject.GetLifetimeService()
System.MarshalByRefObject.InitializeLifetimeService()
Order of tags in Activity.Tags is reversed
Article • 04/16/2022
Activity.Tags now stores items in the list according to the order they're added. That is,
the first item that's added is first in the list. This change was made to match the
OpenTelemetry Attributes specification .
Change description
In previous .NET versions, Activity.Tags stores items in the reverse order from which
they're added. That is, the first item added is last in the list. Starting in .NET 5, the order
of the items is reversed, and the first item added is always first in the list.
Version introduced
5.0
Recommended action
If your app has a dependency on the Activity.Tags list order and you're upgrading to
.NET 5 or later, you'll need to change this part of your code.
Affected APIs
System.Diagnostics.Activity.Tags
SSE and SSE2 CompareGreaterThan
methods properly handle NaN inputs
Article • 09/15/2021
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, 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
Recommended action
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)
Affected APIs
System.Runtime.Intrinsics.X86.Sse.CompareGreaterThan(Vector128<Single>,
Vector128<Single>)
System.Runtime.Intrinsics.X86.Sse.CompareGreaterThanOrEqual(Vector128<Single
>, Vector128<Single>)
System.Runtime.Intrinsics.X86.Sse.CompareNotGreaterThan(Vector128<Single>,
Vector128<Single>)
System.Runtime.Intrinsics.X86.Sse.CompareNotGreaterThanOrEqual(Vector128<Sin
gle>, Vector128<Single>)
System.Runtime.Intrinsics.X86.Sse.CompareScalarGreaterThan(Vector128<Single>,
Vector128<Single>)
System.Runtime.Intrinsics.X86.Sse.CompareScalarGreaterThanOrEqual(Vector128<S
ingle>, Vector128<Single>)
System.Runtime.Intrinsics.X86.Sse.CompareScalarNotGreaterThan(Vector128<Singl
e>, Vector128<Single>)
System.Runtime.Intrinsics.X86.Sse.CompareScalarNotGreaterThanOrEqual(Vector12
8<Single>, Vector128<Single>)
System.Runtime.Intrinsics.X86.Sse2.CompareGreaterThan(Vector128<Double>,
Vector128<Double>)
System.Runtime.Intrinsics.X86.Sse2.CompareGreaterThanOrEqual(Vector128<Doubl
e>, Vector128<Double>)
System.Runtime.Intrinsics.X86.Sse2.CompareNotGreaterThan(Vector128<Double>,
Vector128<Double>)
System.Runtime.Intrinsics.X86.Sse2.CompareNotGreaterThanOrEqual(Vector128<D
ouble>, Vector128<Double>)
System.Runtime.Intrinsics.X86.Sse2.CompareScalarGreaterThan(Vector128<Double
>, Vector128<Double>)
System.Runtime.Intrinsics.X86.Sse2.CompareScalarGreaterThanOrEqual(Vector128<
Double>, Vector128<Double>)
System.Runtime.Intrinsics.X86.Sse2.CompareScalarNotGreaterThan(Vector128<Dou
ble>, Vector128<Double>)
System.Runtime.Intrinsics.X86.Sse2.CompareScalarNotGreaterThanOrEqual(Vector1
28<Double>, Vector128<Double>)
Thread.Abort is obsolete
Article • 06/01/2022
The Thread.Abort APIs are obsolete. Projects that target .NET 5 or a later version will
encounter compile-time warning SYSLIB0006 if these methods are called.
Change description
Previously, calls to Thread.Abort did not produce compile-time warnings, however, the
method did throw a PlatformNotSupportedException at run time.
Starting in .NET 5, Thread.Abort is marked obsolete as warning. Calling this method
produces compiler warning SYSLIB0006 . The implementation of the method is
unchanged, and it continues to throw a PlatformNotSupportedException.
Reason for change
Given that Thread.Abort always throws a PlatformNotSupportedException on all .NET
implementations except .NET Framework, ObsoleteAttribute was added to the method
to draw attention to places where it's called.
When you call Thread.Abort to abort a thread other than the current thread, you don't
know what code has executed or failed to execute when the ThreadAbortException is
thrown. You also cannot be certain of the state of your application or any application
and user state that it's responsible for preserving. For example, calling Thread.Abort may
prevent the execution of static constructors or the release of managed or unmanaged
resources. For this reason, Thread.Abort always throws a
PlatformNotSupportedException on .NET Core and .NET 5+.
Version introduced
5.0
Recommended action
Use a CancellationToken to abort processing of a unit of work instead of calling
Thread.Abort. The following example illustrates the use of CancellationToken.
C#
void ProcessPendingWorkItemsNew(CancellationToken cancellationToken)
{
if (QueryIsMoreWorkPending())
{
// If the CancellationToken is marked as "needs to cancel",
// this will throw the appropriate exception.
cancellationToken.ThrowIfCancellationRequested();
WorkItem work = DequeueWorkItem();
ProcessWorkItem(work);
}
}
For more information, see Cancellation in managed threads.
To suppress the compile-time warning, suppress warning code SYSLIB0006 . The
warning code is specific to Thread.Abort and suppressing it doesn't suppress other
obsoletion warnings in your code. However, we recommend that you remove calls
to Thread.Abort instead of suppressing the warning.
C#
void MyMethod()
{
#pragma warning disable SYSLIB0006
Thread.CurrentThread.Abort();
#pragma warning restore SYSLIB0006
}
You can also suppress the warning in the project file.
XML
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net5.0</TargetFramework>
<!-- Disable "Thread.Abort is obsolete" warnings for entire project.
-->
<NoWarn>$(NoWarn);SYSLIB0006</NoWarn>
</PropertyGroup>
Affected APIs
System.Threading.Thread.Abort
Uri recognition of UNC paths on Unix
Article • 09/15/2021
The Uri class now recognizes strings that start with two forward slashes ( // ) as universal
naming convention (UNC) paths on Unix operating systems. This change makes the
behavior for such strings consistent across all platforms.
Change description
In previous versions of .NET, the Uri class recognizes strings that start with two forward
slashes, for example, //contoso , as absolute file paths on Unix operating systems.
However, on Windows, such strings are recognized as UNC paths.
Starting in .NET 5, the Uri class recognizes strings that start with two forward slashes as
UNC paths on all platforms, including Unix. In addition, properties behave according to
UNC semantics:
Uri.IsUnc returns true .
Backslashes in the path are replaced with forward slashes. For example,
//first\second becomes //first/second .
Uri.LocalPath doesn't percent-encode characters. For example, //first/\uFFF0 is
not converted to //first/%EF%BF%B0 .
Version introduced
5.0
Recommended action
No action is required on the part of the developer.
Affected APIs
System.Uri
UTF-7 code paths are obsolete
Article • 01/27/2022
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:
C#
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, the Encoding.UTF7 property and UTF7Encoding constructors are
obsolete and produce warning SYSLIB0001 . However, to reduce the number of warnings
that callers receive when using the UTF7Encoding class, the UTF7Encoding type itself is
not marked obsolete.
C#
// 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.
C#
// 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.
C#
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
Recommended action
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.
C#
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.
C#
#pragma warning disable SYSLIB0001 // Disable the warning.
Encoding enc = Encoding.UTF7;
#pragma warning restore SYSLIB0001 // Re-enable the warning.
XML
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net5.0</TargetFramework>
<!-- NoWarn below suppresses SYSLIB0001 project-wide -->
<NoWarn>$(NoWarn);SYSLIB0001</NoWarn>
</PropertyGroup>
</Project>
7 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 runtime configuration
file, as shown in the following examples.
In the application's .csproj file:
XML
<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:
JSON
{
"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.
Affected APIs
System.Text.Encoding.UTF7
UTF7Encoding()
UTF7Encoding(Boolean)
System.Text.Encoding.GetEncoding(Int32)
System.Text.Encoding.GetEncoding(String)
System.Text.Encoding.GetEncoding(Int32, EncoderFallback, DecoderFallback)
System.Text.Encoding.GetEncoding(String, EncoderFallback, DecoderFallback)
System.Text.Encoding.GetEncodings()
Behavior change for Vector2.Lerp and
Vector4.Lerp
Article • 09/15/2021
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 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
Recommended action
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 .
Affected APIs
System.Numerics.Vector2.Lerp(Vector2, Vector2, Single)
System.Numerics.Vector4.Lerp(Vector4, Vector4, Single)
Vector<T> always throws
NotSupportedException for
unsupported types
Article • 06/29/2022
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, 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
Recommended action
Don't use an unsupported type for the type parameter of Vector<T>.
Affected APIs
System.Numerics.Vector<T> and all its members
System.Security.Cryptography APIs not
supported on Blazor WebAssembly
Article • 09/15/2021
System.Security.Cryptography APIs throw a PlatformNotSupportedException at run time
when run on a browser.
Change description
In previous .NET versions, most of the System.Security.Cryptography APIs aren't
available to Blazor WebAssembly apps. Starting in .NET 5, Blazor WebAssembly apps
target the full .NET 5 API surface area, however, not all .NET 5 APIs are supported due to
browser sandbox constraints. In .NET 5 and later versions, the unsupported
System.Security.Cryptography APIs throw a PlatformNotSupportedException when
running on WebAssembly.
 Tip
The Platform compatibility analyzer will flag any calls to the affected APIs when
you build a project that supports the browser platform. This analyzer runs by
default in .NET 5 and later apps.
Reason for change
Microsoft is unable to ship OpenSSL as a dependency in the Blazor WebAssembly
configuration. We attempted to work around this by trying to integrate with the
browser's SubtleCrypto API. Unfortunately, it required significant API changes that made
it too hard to integrate.
Version introduced
5.0
Recommended action
There are no good workarounds to suggest at this time.
Affected APIs
All System.Security.Cryptography APIs except the following:
System.Security.Cryptography.RandomNumberGenerator
System.Security.Cryptography.IncrementalHash
System.Security.Cryptography.SHA1
System.Security.Cryptography.SHA256
System.Security.Cryptography.SHA384
System.Security.Cryptography.SHA512
System.Security.Cryptography.SHA1Managed
System.Security.Cryptography.SHA256Managed
System.Security.Cryptography.SHA384Managed
System.Security.Cryptography.SHA512Managed
System.Security.Cryptography.Oid is
functionally init-only
Article • 09/15/2021
The System.Security.Cryptography.Oid class, which is used to represent ASN.1 Object
Identifier values and their "friendly" names, was previously fully mutable. This mutability
was often overlooked or came as a surprise. The property setters now throw a
PlatformNotSupportedException when you attempt to change the value after it's already
been assigned.
Change description
In previous versions, the property setters on Oid can be used to change the value of the
FriendlyName and Value properties.
In .NET 5 and later versions, the property setters can only be used to initialize the value.
Once the property has a value, either from a constructor or a previous call to the
property setter, the property setter always throws a PlatformNotSupportedException.
Reason for change
This change enables the reuse of Oid objects as part of return values in public APIs to
reduce object allocation profiles. It avoids the need to create temporary "defensive"
copies when Oid values are used as inputs.
Version introduced
5.0
Recommended action
Avoid using the Oid property setters other than for object initialization. To represent a
new value, use a new instance instead of changing the value on an existing object.
Affected APIs
System.Security.Cryptography.Oid.FriendlyName
System.Security.Cryptography.Oid.Value
Default TLS cipher suites for .NET on
Linux
Article • 12/04/2022
.NET, on Linux, now respects the OpenSSL configuration for default cipher suites when
doing TLS/SSL via the SslStream class or higher-level operations, such as HTTPS via the
HttpClient class. When default cipher suites aren't explicitly configured, .NET on Linux
uses a tightly restricted list of permitted cipher suites.
Change description
In previous .NET versions, .NET does not respect system configuration for default cipher
suites. The default cipher suite list for .NET on Linux is very permissive.
Starting in .NET 5, .NET on Linux respects the OpenSSL configuration for default cipher
suites when it's specified in openssl.cnf. When cipher suites aren't explicitly configured,
the only permitted cipher suites are as follows:
TLS 1.3 cipher suites
TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384
TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256
TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384
TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256
TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384
TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256
Since this fallback default doesn't include any cipher suites that are compatible with TLS
1.0 or TLS 1.1, these older protocol versions are effectively disabled by default.
Supplying a CipherSuitePolicy value to SslStream for a specific session will still replace
the configuration file content and/or .NET fallback default.
Reason for change
Users running .NET on Linux requested that the default configuration for SslStream be
changed to one that provided a high security rating from third-party assessment tools.
Version introduced
5.0
Recommended action
The new defaults are likely to work when communicating with modern clients or servers.
If you need to expand the default cipher suite list to accept legacy clients (or to contact
legacy servers), use one of the following workarounds:
Specify a cryptography policy by configuring the CipherSuitesPolicy type as it
pertains to SslServerAuthenticationOptions.CipherSuitesPolicy or
SslClientAuthenticationOptions.CipherSuitesPolicy.
C#
var clientOpts = new SslClientAuthenticationOptions
{
// ...
CipherSuitesPolicy = new CipherSuitesPolicy(
new[]
{
TlsCipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
TlsCipherSuite.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
}),
};
using (SslStream sslStream = new SslStream(networkStream))
{
sslStream.AuthenticateAsClient(clientOptions);
// ...
}
Or, for HttpClient:
C#
var handler = new SocketsHttpHandler
{
SslOptions =
{
CipherSuitesPolicy = new CipherSuitesPolicy(
new[]
{
TlsCipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
TlsCipherSuite.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
}),
},
};
using (var httpClient = new HttpClient(handler))
{
// ...
}
Change the OpenSSL configuration file. On many Linux distributions, the OpenSSL
configuration file is at /etc/ssl/openssl.cnf.
This sample openssl.cnf file is a minimal file that's equivalent to the default cipher
suites policy for .NET 5 and later on Linux. Instead of replacing the system file,
merge these concepts with the file that's present on your system.
ini
openssl_conf = default_conf
[default_conf]
ssl_conf = ssl_sect
[ssl_sect]
system_default = system_default_sect
[system_default_sect]
CipherString = ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-GCMSHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256:ECDHEECDSA-AES256-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES256SHA384:ECDHE-RSA-AES128-SHA256
On the Red Hat Enterprise Linux, CentOS, and Fedora distributions, .NET applications
default to the cipher suites permitted by the system-wide cryptographic policies. On
these distributions, use the crypto-policies configuration instead of changing the
OpenSSL configuration file.
Affected APIs
N/A
Instantiating default implementations of
cryptographic abstractions is not
supported
Article • 11/08/2021
The parameterless Create() overloads on cryptographic abstractions are obsolete as
warning as of .NET 5.0.
Change description
In .NET Framework 2.0 - 4.8, abstract cryptographic primitive factories such as
HashAlgorithm.Create() can be configured to return different algorithms. For example,
on a default install of .NET Framework 4.8, the parameterless, static method
HashAlgorithm.Create() returns an instance of the SHA1 algorithm, as shown in the
following snippet.
.NET Framework only
C#
// Return an instance of the default hash algorithm (SHA1).
HashAlgorithm alg = HashAlgorithm.Create();
// Prints 'System.Security.Cryptography.SHA1CryptoServiceProvider'.
Console.WriteLine(alg.GetType());
// Change the default algorithm to be SHA256, not SHA1.
CryptoConfig.AddAlgorithm(typeof(SHA256CryptoServiceProvider),
typeof(HashAlgorithm).FullName);
alg = HashAlgorithm.Create();
// Prints 'System.Security.Cryptography.SHA256CryptoServiceProvider'.
Console.WriteLine(alg.GetType());
You can also use machine-wide configuration to change the default algorithm without
needing to call into CryptoConfig programmatically.
In .NET Core 2.0 - 3.1, abstract cryptographic primitive factories such as
HashAlgorithm.Create() always throw a PlatformNotSupportedException.
C#
// Throws PlatformNotSupportedException on .NET Core.
HashAlgorithm alg = HashAlgorithm.Create();
In .NET 5 and later versions, abstract cryptographic primitive factories such as
HashAlgorithm.Create() are marked obsolete and produce a compile-time warning with
ID SYSLIB0007 . At run time, these methods continue to throw a
PlatformNotSupportedException.
C#
// Throws PlatformNotSupportedException.
// Also produces compile-time warning SYSLIB0007 on .NET 5+.
HashAlgorithm alg = HashAlgorithm.Create();
This is a compile-time only change. There is no run-time change from previous versions
of .NET Core.
7 Note
Only the parameterless overloads of the Create() methods are obsolete.
Parameterized overloads are not obsolete and still function as expected.
C#
// Call Create(string), providing an explicit algorithm family
name.
// Works in .NET Framework, .NET Core, and .NET 5+.
HashAlgorithm hashAlg = HashAlgorithm.Create("SHA256");
Parameterless overloads of specific algorithm families (not abstractions) are
not obsolete, and will continue to function as expected.
C#
// Call a specific algorithm family's parameterless Create() ctor.
// Works in .NET Framework, .NET Core, and .NET 5+.
Aes aesAlg = Aes.Create();
Reason for change
The cryptographic configuration system present in .NET Framework is no longer present
in .NET Core and .NET 5+, since that legacy system doesn't allow for proper
cryptographic agility. .NET's backward-compatibility requirements also prohibit the
framework from updating certain cryptographic APIs to keep up with advances in
cryptography. For example, the HashAlgorithm.Create() method was introduced in .NET
Framework 1.0, when the SHA-1 hash algorithm was state-of-the-art. Twenty years have
passed, and now SHA-1 is considered broken, but we cannot change
HashAlgorithm.Create() to return a different algorithm. Doing so would introduce an
unacceptable breaking change in consuming applications.
Best practice dictates that libraries that consume cryptographic primitives (such as AES,
SHA-*, and RSA) should be in full control over how they consume these primitives.
Applications that require future-proofing should utilize higher-level libraries that wrap
these primitives and add key management and cryptographic agility capabilities. These
libraries are often provided by the hosting environment. One example is ASP.NET's Data
Protection library, which handles these concerns on behalf of the calling application.
Version introduced
5.0
Recommended action
The recommended course of action is to replace calls to the now-obsolete APIs
with calls to factory methods for specific algorithms, for example, Aes.Create(). This
gives you full control over which algorithms are instantiated.
If you need to maintain compatibility with existing payloads generated by .NET
Framework apps that use the now-obsolete APIs, use the replacements suggested
in the following table. The table provides a mapping from .NET Framework default
algorithms to their .NET 5+ equivalents.
.NET Framework
.NET Core /
Remarks
.NET 5+
compatible
replacement
AsymmetricAlgorithm.Create()
RSA.Create()
HashAlgorithm.Create()
SHA1.Create()
The SHA-1 algorithm is considered
broken. Consider using a stronger
algorithm if possible. Consult your security
advisor for further guidance.
HMAC.Create()
HMACSHA1()
The HMACSHA1 algorithm is discouraged
for most modern applications. Consider
using a stronger algorithm if possible.
Consult your security advisor for further
guidance.
.NET Framework
.NET Core /
.NET 5+
compatible
Remarks
replacement
KeyedHashAlgorithm.Create()
HMACSHA1()
The HMACSHA1 algorithm is discouraged
for most modern applications. Consider
using a stronger algorithm if possible.
Consult your security advisor for further
guidance.
SymmetricAlgorithm.Create()
Aes.Create()
If you must continue to call the obsolete parameterless Create() overloads, you
can suppress the SYSLIB0007 warning in code.
C#
#pragma warning disable SYSLIB0007 // Disable the warning.
HashAlgorithm alg = HashAlgorithm.Create(); // Still throws PNSE.
#pragma warning restore SYSLIB0007 // Re-enable the warning.
You can also suppress the warning in your project file. Doing so disables the
warning for all source files within the project.
XML
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net5.0</TargetFramework>
<!-- NoWarn below suppresses SYSLIB0007 project-wide -->
<NoWarn>$(NoWarn);SYSLIB0007</NoWarn>
</PropertyGroup>
</Project>
7 Note
Suppressing SYSLIB0007 disables only the obsoletion warnings for the
cryptography APIs listed here. It does not disable any other warnings.
Additionally, even if you suppress the warning, these obsoleted APIs will still
throw a PlatformNotSupportedException at run time.
Affected APIs
System.Security.Cryptography.AsymmetricAlgorithm.Create()
System.Security.Cryptography.HashAlgorithm.Create()
System.Security.Cryptography.HMAC.Create()
System.Security.Cryptography.KeyedHashAlgorithm.Create()
System.Security.Cryptography.SymmetricAlgorithm.Create()
Default FeedbackSize value for instances
created by TripleDES.Create changed
Article • 10/12/2021
The default value for the SymmetricAlgorithm.FeedbackSize property on the TripleDES
instance returned from TripleDES.Create() has changed from 64 to 8 to make migration
from .NET Framework easier. This property, unless used directly in caller code, is used
only when the Mode property is CipherMode.CFB.
Support for the CFB mode was first added to .NET for the 5.0 RC1 release, so only .NET 5
RC1 and .NET 5 RC2 applications should be impacted by this change.
Change description
In .NET Core and previous pre-release versions of .NET 5,
TripleDES.Create().FeedbackSize has a default value of 64. Starting in the RTM version
of .NET 5, TripleDES.Create().FeedbackSize has a default value of 8.
Reason for change
In .NET Framework, the TripleDES base class defaults the value of FeedbackSize to 64,
but the TripleDESCryptoServiceProvider class overwrites the default to 8. When the
FeedbackSize property was introduced to .NET Core in version 2.0, this same behavior
was preserved. However, in .NET Framework, TripleDES.Create() returns an instance of
TripleDESCryptoServiceProvider, so the default value from the algorithm factory is 8. For
.NET Core and .NET 5+, the algorithm factory returns a non-public implementation,
which, until now, had a default value of 64.
Changing the TripleDES implementation class' FeedbackSize value to 8 allows for
applications written for .NET Framework that specified the cipher mode as CFB but
didn't explicitly assign the FeedbackSize property, to continue to function on .NET 5.
Version introduced
5.0
Recommended action
Applications that encrypt or decrypt data in the RC1 or RC2 versions of .NET 5 do so
with CFB64, when the following conditions are met:
With a TripleDES instance from TripleDES.Create().
Using the default value for FeedbackSize.
With the Mode property set to CipherMode.CFB.
To maintain this behavior, assign the FeedbackSize property to 64 .
Not all TripleDES implementations use the same default for FeedbackSize. We
recommend that if you use the CFB cipher mode on TripleDES instances, you should
always explicitly assign the FeedbackSize property value.
C#
TripleDES cipher = TripleDES.Create();
cipher.Mode = CipherMode.CFB;
// Explicitly set the FeedbackSize for CFB to control between CFB8 and
CFB64.
cipher.FeedbackSize = 8;
Affected APIs
System.Security.Cryptography.TripleDES.Create()
System.Security.Cryptography.SymmetricAlgorithm.FeedbackSize
Breaking changes in EF Core 5.0
Article • 02/18/2023
The following API and behavior changes have the potential to break existing
applications updating to EF Core 5.0.0.
Summary
Breaking change
Impact
EF Core 5.0 does not support .NET Framework
Medium
IProperty.GetColumnName() is now obsolete
Medium
Precision and scale are required for decimals
Medium
Required or non-nullable navigation from principal to dependent has different
semantics
Medium
Defining query is replaced with provider-specific methods
Medium
Non-null reference navigations are not overwritten by queries
Medium
ToView() is treated differently by migrations
Medium
ToTable(null) marks the entity type as not mapped to a table
Medium
Removed HasGeometricDimension method from SQLite NTS extension
Low
Azure Cosmos DB: Partition key is now added to the primary key
Low
Azure Cosmos DB: id property renamed to __id
Low
Azure Cosmos DB: byte[] is now stored as a base64 string instead of a number array
Low
Azure Cosmos DB: GetPropertyName and SetPropertyName were renamed
Low
Value generators are called when the entity state is changed from Detached to
Low
Unchanged, Updated, or Deleted
IMigrationsModelDiffer now uses IRelationalModel
Low
Discriminators are read-only
Low
Provider-specific EF.Functions methods throw for in-memory provider
Low
IndexBuilder.HasName is now obsolete
Low
A pluralizer is now included for scaffolding reverse engineered models
Low
Breaking change
Impact
INavigationBase replaces INavigation in some APIs to support skip navigations
Low
Some queries with correlated collection that also use Distinct or GroupBy are no longer
Low
supported
Using a collection of Queryable type in projection is not supported
Low
Medium-impact changes
EF Core 5.0 does not support .NET Framework
Tracking Issue #15498
Old behavior
EF Core 3.1 targets .NET Standard 2.0, which is supported by .NET Framework.
New behavior
EF Core 5.0 targets .NET Standard 2.1, which is not supported by .NET Framework. This
means EF Core 5.0 cannot be used with .NET Framework applications.
Why
This is part of the wider movement across .NET teams aimed at unification to a single
.NET target framework. For more information see the future of .NET Standard .
Mitigations
.NET Framework applications can continue to use EF Core 3.1, which is a long-term
support (LTS) release . Alternately, applications can be updated to use .NET Core 3.1 or
.NET 5, both of which support .NET Standard 2.1.
IProperty.GetColumnName() is now obsolete
Tracking Issue #2266
Old behavior
GetColumnName() returned the name of the column that a property is mapped to.
New behavior
GetColumnName() still returns the name of a column that a property is mapped to, but
this behavior is now ambiguous since EF Core 5 supports TPT and simultaneous
mapping to a view or a function where these mappings could use different column
names for the same property.
Why
We marked this method as obsolete to guide users to a more accurate overload GetColumnName(IProperty, StoreObjectIdentifier).
Mitigations
If the entity type is only ever mapped to a single table, and never to views, functions, or
multiple tables, the GetColumnBaseName(IReadOnlyProperty) can be used in EF Core
5.0 and 6.0 to obtain the table name. For example:
C#
var columnName = property.GetColumnBaseName();
In EF Core 7.0, this can again be replaced with the new GetColumnName , which behaves as
the original did for simple, single table only mappings.
If the entity type may be mapped to views, functions, or multiple tables, then a
StoreObjectIdentifier must be obtained to identity the table, view, or function. This can
be then be used to get the column name for that store object. For example:
C#
var columnName = property.GetColumnName(StoreObjectIdentifier.Table("Users",
null)));
Precision and scale are required for decimals
Tracking Issue #19293
Old behavior
EF Core did not normally set precision and scale on SqlParameter objects. This means
the full precision and scale was sent to SQL Server, at which point SQL Server would
round based on the precision and scale of the database column.
New behavior
EF Core now sets precision and scale on parameters using the values configured for
properties in the EF Core model. This means rounding now happens in SqlClient.
Consequentially, if the configured precision and scale do not match the database
precision and scale, then the rounding seen may change.
Why
Newer SQL Server features, including Always Encrypted, require that parameter facets
are fully specified. In addition, SqlClient made a change to round instead of truncate
decimal values, thereby matching the SQL Server behavior. This made it possible for EF
Core to set these facets without changing the behavior for correctly configured
decimals.
Mitigations
Map your decimal properties using a type name that includes precision and scale. For
example:
C#
public class Blog
{
public int Id { get; set; }
[Column(TypeName = "decimal(16, 5)")]
public decimal Score { get; set; }
}
Or use HasPrecision in the model building APIs. For example:
C#
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Blog>().Property(e => e.Score).HasPrecision(16,
5);
}
Required or non-nullable navigation from principal to
dependent has different semantics
Tracking Issue #17286
Old behavior
Only the navigations to principal could be configured as required. Therefore, using
RequiredAttribute on the navigation to the dependent (the entity containing the
foreign key) or marking it as non-nullable would instead create the foreign key on the
defining entity type.
New behavior
With the added support for required dependents, it is now possible to mark any
reference navigation as required, meaning that in the case shown above the foreign key
will be defined on the other side of the relationship and the properties won't be marked
as required.
Calling IsRequired before specifying the dependent end is now ambiguous:
C#
modelBuilder.Entity<Blog>()
.HasOne(b => b.BlogImage)
.WithOne(i => i.Blog)
.IsRequired()
.HasForeignKey<BlogImage>(b => b.BlogForeignKey);
Why
The new behavior is necessary to enable support for required dependents (see
#12100
).
Mitigations
Remove RequiredAttribute from the navigation to the dependent and place it instead
on the navigation to the principal or configure the relationship in OnModelCreating :
C#
modelBuilder.Entity<Blog>()
.HasOne(b => b.BlogImage)
.WithOne(i => i.Blog)
.HasForeignKey<BlogImage>(b => b.BlogForeignKey)
.IsRequired();
Defining query is replaced with provider-specific methods
Tracking Issue #18903
Old behavior
Entity types were mapped to defining queries at the Core level. Anytime the entity type
was used in the query root of the entity type was replaced by the defining query for any
provider.
New behavior
APIs for defining query are deprecated. New provider-specific APIs were introduced.
Why
While defining queries were implemented as replacement query whenever query root is
used in the query, it had a few issues:
If defining query is projecting entity type using new { ... } in Select method,
then identifying that as an entity required additional work and made it inconsistent
with how EF Core treats nominal types in the query.
For relational providers FromSql is still needed to pass the SQL string in LINQ
expression form.
Initially defining queries were introduced as client-side views to be used with InMemory provider for keyless entities (similar to database views in relational databases).
Such definition makes it easy to test application against in-memory database.
Afterwards they became broadly applicable, which was useful but brought inconsistent
and hard to understand behavior. So we decided to simplify the concept. We made
LINQ based defining query exclusive to In-Memory provider and treat them differently.
For more information, see this issue .
Mitigations
For relational providers, use ToSqlQuery method in OnModelCreating and pass in a SQL
string to use for the entity type. For the In-Memory provider, use ToInMemoryQuery
method in OnModelCreating and pass in a LINQ query to use for the entity type.
Non-null reference navigations are not overwritten by
queries
Tracking Issue #2693
Old behavior
In EF Core 3.1, reference navigations eagerly initialized to non-null values would
sometimes be overwritten by entity instances from the database, regardless of whether
or not key values matched. However, in other cases, EF Core 3.1 would do the opposite
and leave the existing non-null value.
New behavior
Starting with EF Core 5.0, non-null reference navigations are never overwritten by
instances returned from a query.
Note that eager initialization of a collection navigation to an empty collection is still
supported.
Why
Initialization of a reference navigation property to an "empty" entity instance results in
an ambiguous state. For example:
C#
public class Blog
{
public int Id { get; set; }
public Author Author { get; set; ) = new Author();
}
Normally a query for Blogs and Authors will first create Blog instances and then set the
appropriate Author instances based on the data returned from the database. However,
in this case every Blog.Author property is already initialized to an empty Author . Except
EF Core has no way to know that this instance is "empty". So overwriting this instance
could potentially silently throw away a valid Author . Therefore, EF Core 5.0 now
consistently does not overwrite a navigation that is already initialized.
This new behavior also aligns with the behavior of EF6 in most cases, although upon
investigation we also found some cases of inconsistency in EF6.
Mitigations
If this break is encountered, then the fix is to stop eagerly initializing reference
navigation properties.
ToView() is treated differently by migrations
Tracking Issue #2725
Old behavior
Calling ToView(string) made the migrations ignore the entity type in addition to
mapping it to a view.
New behavior
Now ToView(string) marks the entity type as not mapped to a table in addition to
mapping it to a view. This results in the first migration after upgrading to EF Core 5 to
try to drop the default table for this entity type as it's not longer ignored.
Why
EF Core now allows an entity type to be mapped to both a table and a view
simultaneously, so ToView is no longer a valid indicator that it should be ignored by
migrations.
Mitigations
Use the following code to mark the mapped table as excluded from migrations:
C#
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<User>().ToTable("UserView", t =>
t.ExcludeFromMigrations());
}
ToTable(null) marks the entity type as not mapped to a
table
Tracking Issue #21172
Old behavior
ToTable(null) would reset the table name to the default.
New behavior
ToTable(null) now marks the entity type as not mapped to any table.
Why
EF Core now allows an entity type to be mapped to both a table and a view
simultaneously, so ToTable(null) is used to indicate that it isn't mapped to any table.
Mitigations
Use the following code to reset the table name to the default if it's not mapped to a
view or a DbFunction:
C#
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<User>
().Metadata.RemoveAnnotation(RelationalAnnotationNames.TableName);
}
Low-impact changes
Removed HasGeometricDimension method from SQLite
NTS extension
Tracking Issue #14257
Old behavior
HasGeometricDimension was used to enable additional dimensions (Z and M) on
geometry columns. However, it only ever affected database creation. It was unnecessary
to specify it to query values with additional dimensions. It also didn't work correctly
when inserting or updating values with additional dimensions (see #14257
).
New behavior
To enable inserting and updating geometry values with additional dimensions (Z and M),
the dimension needs to be specified as part of the column type name. This API matches
more closely to the underlying behavior of SpatiaLite's AddGeometryColumn function.
Why
Using HasGeometricDimension after specifying the dimension in the column type is
unnecessary and redundant, so we removed HasGeometricDimension entirely.
Mitigations
Use HasColumnType to specify the dimension:
C#
modelBuilder.Entity<GeoEntity>(
x =>
{
// Allow any GEOMETRY value with optional Z and M values
x.Property(e => e.Geometry).HasColumnType("GEOMETRYZM");
// Allow only POINT values with an optional Z value
x.Property(e => e.Point).HasColumnType("POINTZ");
});
Azure Cosmos DB: Partition key is now added to the
primary key
Tracking Issue #15289
Old behavior
The partition key property was only added to the alternate key that includes id .
New behavior
The partition key property is now also added to the primary key by convention.
Why
This change makes the model better aligned with Azure Cosmos DB semantics and
improves the performance of Find and some queries.
Mitigations
To prevent the partition key property to be added to the primary key, configure it in
OnModelCreating .
C#
modelBuilder.Entity<Blog>()
.HasKey(b => b.Id);
Azure Cosmos DB: id property renamed to __id
Tracking Issue #17751
Old behavior
The shadow property mapped to the id JSON property was also named id .
New behavior
The shadow property created by convention is now named __id .
Why
This change makes it less likely that the id property clashes with an existing property
on the entity type.
Mitigations
To go back to the 3.x behavior, configure the id property in OnModelCreating .
C#
modelBuilder.Entity<Blog>()
.Property<string>("id")
.ToJsonProperty("id");
Azure Cosmos DB: byte[] is now stored as a base64 string
instead of a number array
Tracking Issue #17306
Old behavior
Properties of type byte[] were stored as a number array.
New behavior
Properties of type byte[] are now stored as a base64 string.
Why
This representation of byte[] aligns better with expectations and is the default behavior
of the major JSON serialization libraries.
Mitigations
Existing data stored as number arrays will still be queried correctly, but currently there
isn't a supported way to change back the insert behavior. If this limitation is blocking
your scenario, comment on this issue
Azure Cosmos DB: GetPropertyName and
SetPropertyName were renamed
Tracking Issue #17874
Old behavior
Previously the extension methods were called GetPropertyName and SetPropertyName
New behavior
The old API was removed and new methods added: GetJsonPropertyName ,
SetJsonPropertyName
Why
This change removes the ambiguity around what these methods are configuring.
Mitigations
Use the new API.
Value generators are called when the entity state is
changed from Detached to Unchanged, Updated, or
Deleted
Tracking Issue #15289
Old behavior
Value generators were only called when the entity state changed to Added.
New behavior
Value generators are now called when the entity state is changed from Detached to
Unchanged, Updated, or Deleted and the property contains the default values.
Why
This change was necessary to improve the experience with properties that are not
persisted to the data store and have their value generated always on the client.
Mitigations
To prevent the value generator from being called, assign a non-default value to the
property before the state is changed.
IMigrationsModelDiffer now uses IRelationalModel
Tracking Issue #20305
Old behavior
IMigrationsModelDiffer API was defined using IModel .
New behavior
IMigrationsModelDiffer API now uses IRelationalModel . However the model snapshot
still contains only IModel as this code is part of the application and Entity Framework
can't change it without making a bigger breaking change.
Why
IRelationalModel is a newly added representation of the database schema. Using it to
find differences is faster and more accurate.
Mitigations
Use the following code to compare the model from snapshot with the model from
context :
C#
var dependencies =
context.GetService<ProviderConventionSetBuilderDependencies>();
var relationalDependencies =
context.GetService<RelationalConventionSetBuilderDependencies>();
var typeMappingConvention = new TypeMappingConvention(dependencies);
typeMappingConvention.ProcessModelFinalizing(((IConventionModel)modelSnapsho
t.Model).Builder, null);
var relationalModelConvention = new RelationalModelConvention(dependencies,
relationalDependencies);
var sourceModel =
relationalModelConvention.ProcessModelFinalized(snapshot.Model);
var modelDiffer = context.GetService<IMigrationsModelDiffer>();
var hasDifferences = modelDiffer.HasDifferences(
((IMutableModel)sourceModel).FinalizeModel().GetRelationalModel(),
context.Model.GetRelationalModel());
We are planning to improve this experience in 6.0 (see #22031
)
Discriminators are read-only
Tracking Issue #21154
Old behavior
It was possible to change the discriminator value before calling SaveChanges
New behavior
An exception will be thrown in the above case.
Why
EF doesn't expect the entity type to change while it is still being tracked, so changing
the discriminator value leaves the context in an inconsistent state, which might result in
unexpected behavior.
Mitigations
If changing the discriminator value is necessary and the context will be disposed
immediately after calling SaveChanges , the discriminator can be made mutable:
C#
modelBuilder.Entity<BaseEntity>()
.Property<string>("Discriminator")
.Metadata.SetAfterSaveBehavior(PropertySaveBehavior.Save);
Provider-specific EF.Functions methods throw for inmemory provider
Tracking Issue #20294
Old behavior
Provider-specific EF.Functions methods contained implementation for client execution,
which allowed them to be executed on the in-memory provider. For example,
EF.Functions.DateDiffDay is a Sql Server specific method, which worked on InMemory
provider.
New behavior
Provider-specific methods have been updated to throw an exception in their method
body to block evaluating them on client side.
Why
Provider-specific methods map to a database function. The computation done by the
mapped database function can't always be replicated on the client side in LINQ. It may
cause the result from the server to differ when executing the same method on client.
Since these methods are used in LINQ to translate to specific database functions, they
don't need to be evaluated on client side. As the in-memory provider is a different
database, these methods aren't available for this provider. Trying to execute them for
InMemory provider, or any other provider that doesn't translate these methods, throws
an exception.
Mitigations
Since there's no way to mimic behavior of database functions accurately, you should test
the queries containing them against same kind of database as in production.
IndexBuilder.HasName is now obsolete
Tracking Issue #21089
Old behavior
Previously, only one index could be defined over a given set of properties. The database
name of an index was configured using IndexBuilder.HasName.
New behavior
Multiple indexes are now allowed on the same set or properties. These indexes are now
distinguished by a name in the model. By convention, the model name is used as the
database name; however it can also be configured independently using
HasDatabaseName.
Why
In the future, we'd like to enable both ascending and descending indexes or indexes
with different collations on the same set of properties. This change moves us another
step in that direction.
Mitigations
Any code that was previously calling IndexBuilder.HasName should be updated to call
HasDatabaseName instead.
If your project includes migrations generated prior to EF Core version 2.0.0, you can
safely ignore the warning in those files and suppress it by adding #pragma warning
disable 612, 618 .
A pluralizer is now included for scaffolding reverse
engineered models
Tracking Issue #11160
Old behavior
Previously, you had to install a separate pluralizer package in order to pluralize DbSet
and collection navigation names and singularize table names when scaffolding a
DbContext and entity types by reverse engineering a database schema.
New behavior
EF Core now includes a pluralizer that uses the Humanizer
library. This is the same
library Visual Studio uses to recommend variable names.
Why
Using plural forms of words for collection properties and singular forms for types and
reference properties is idiomatic in .NET.
Mitigations
To disable the pluralizer, use the --no-pluralize option on dotnet ef dbcontext
scaffold or the -NoPluralize switch on Scaffold-DbContext .
INavigationBase replaces INavigation in some APIs to
support skip navigations
Tracking Issue #2568
Old behavior
EF Core prior to 5.0 supported only one form of navigation property, represented by the
INavigation interface.
New behavior
EF Core 5.0 introduces many-to-many relationships which use "skip navigations". These
are represented by the ISkipNavigation interface, and most of the functionality of
INavigation has been pushed down to a common base interface: INavigationBase .
Why
Most of the functionality between normal and skip navigations is the same. However,
skip navigations have a different relationship to foreign keys than normal navigations,
since the FKs involved are not directly on either end of the relationship, but rather in the
join entity.
Mitigations
In many cases applications can switch to using the new base interface with no other
changes. However, in cases where the navigation is used to access foreign key
properties, application code should either be constrained to only normal navigations, or
updated to do the appropriate thing for both normal and skip navigations.
Some queries with correlated collection that also use
Distinct or GroupBy are no longer supported
Tracking Issue #15873
Old behavior
Previously, queries involving correlated collections followed by GroupBy , as well as some
queries using Distinct we allowed to execute.
GroupBy example:
C#
context.Parents
.Select(p => p.Children
.GroupBy(c => c.School)
.Select(g => g.Key))
Distinct example - specifically Distinct queries where inner collection projection
doesn't contain the primary key:
C#
context.Parents
.Select(p => p.Children
.Select(c => c.School)
.Distinct())
These queries could return incorrect results if the inner collection contained any
duplicates, but worked correctly if all the elements in the inner collection were unique.
New behavior
These queries are no longer supported. Exception is thrown indicating that we don't
have enough information to correctly build the results.
Why
For correlated collection scenarios we need to know entity's primary key in order to
assign collection entities to the correct parent. When inner collection doesn't use
GroupBy or Distinct , the missing primary key can simply be added to the projection.
However in case of GroupBy and Distinct it can't be done because it would change the
result of GroupBy or Distinct operation.
Mitigations
Rewrite the query to not use GroupBy or Distinct operations on the inner collection,
and perform these operations on the client instead.
C#
context.Parents
.Select(p => p.Children.Select(c => c.School))
.ToList()
.Select(x => x.GroupBy(c => c).Select(g => g.Key))
C#
context.Parents
.Select(p => p.Children.Select(c => c.School))
.ToList()
.Select(x => x.Distinct())
Using a collection of Queryable type in projection is not
supported
Tracking Issue #16314
Old behavior
Previously, it was possible to use collection of a Queryable type inside the projection in
some cases, for example as an argument to a List<T> constructor:
C#
context.Blogs
.Select(b => new List<Post>(context.Posts.Where(p => p.BlogId == b.Id)))
New behavior
These queries are no longer supported. Exception is thrown indicating that we can't
create an object of Queryable type and suggesting how this could be fixed.
Why
We can't materialize an object of a Queryable type, so they would automatically be
created using List<T> type instead. This would often cause an exception due to type
mismatch which was not very clear and could be surprising to some users. We decided
to recognize the pattern and throw a more meaningful exception.
Mitigations
Add ToList() call after the Queryable object in the projection:
C#
context.Blogs.Select(b => context.Posts.Where(p => p.BlogId ==
b.Id).ToList())
Globalization APIs use ICU libraries on
Windows 10
Article • 12/29/2022
.NET 5 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
In .NET Core 1.0 - 3.1 and .NET Framework 4 and later, .NET libraries use National
Language Support (NLS) APIs for globalization functionality on Windows. For example,
NLS functions were used to compare strings, get culture information, and perform string
casing in the appropriate culture.
Starting in .NET 5, if an app is running on Windows 10 May 2019 Update or later, .NET
libraries use ICU
globalization APIs, by default.
7 Note
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.
Behavioral differences
You might see changes in your app even if you don't realize you're using globalization
facilities. This section lists a couple of the behavioral changes you might see, but there
are others too.
String.IndexOf
Consider the following code that calls String.IndexOf(String) to find the index of the
newline character in a string.
C#
string s = "Hello\r\nworld!";
int idx = s.IndexOf("\n");
Console.WriteLine(idx);
In .NET Core 3.1 and earlier versions on Windows, the snippet prints 6 .
In .NET 5 and on Windows 10 May 2019 Update and later versions, the snippet
prints -1 .
In .NET 6 and later versions, the snippet prints 6 , however, ICU libraries are still
used.
To fix this code by conducting an ordinal search instead of a culture-sensitive search, call
the IndexOf(String, StringComparison) overload and pass in StringComparison.Ordinal as
an argument.
You can run code analysis rules CA1307: Specify StringComparison for clarity and
CA1309: Use ordinal StringComparison to find these call sites in your code.
For more information, see Behavior changes when comparing strings on .NET 5+.
Currency symbol
Consider the following code that formats a string using the currency format specifier C .
The current thread's culture is set to a culture that includes only the language and not
the country or region.
C#
System.Threading.Thread.CurrentThread.CurrentCulture = new
System.Globalization.CultureInfo("de");
string text = string.Format("{0:C}", 100);
In .NET Core 3.1 and earlier versions on Windows, the value of text is "100,00 €" .
In .NET 5 and later versions on Windows 19H1 and later versions, the value of text
is "100,00 ¤" , which uses the international currency symbol instead of the euro. In
ICU, the design is that a currency is a property of a country or region, not a
language.
Reason for change
This change was introduced to unify .NET's globalization behavior across all supported
operating systems. It also provides the ability for applications to bundle their own
globalization libraries rather than depend on the operating system's built-in libraries.
Version introduced
.NET 5.0
Recommended action
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.
Affected APIs
System.Span<T>
System.String
Most types in the System.Globalization namespace
System.Array.Sort (when sorting an array of strings)
System.Collections.Generic.List<T>.Sort() (when the list elements are strings)
System.Collections.Generic.SortedDictionary<TKey,TValue> (when the keys are
strings)
System.Collections.Generic.SortedList<TKey,TValue> (when the keys are strings)
System.Collections.Generic.SortedSet<T> (when the set contains strings)
See also
Globalization APIs use ICU libraries on Windows Server
StringInfo and TextElementEnumerator
are now UAX29-compliant
Article • 09/15/2021
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:
C#
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:
txt
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:
txt
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
Recommended action
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.
Affected APIs
System.Globalization.StringInfo
System.Globalization.TextElementEnumerator
Microsoft.VisualBasic.Strings.StrReverse
Unicode category changed for some
Latin-1 characters
Article • 11/08/2021
Char methods now return the correct Unicode category for characters in the Latin-1
range. The category matches that of the Unicode standard.
Change description
In previous .NET versions, Char methods used a fixed list of Unicode categories for
characters in the Latin-1 range. However, the Unicode standard has changed the
categories of some of these characters since those APIs were implemented, creating a
discrepancy. In addition, there was also a discrepancy between Char and
CharUnicodeInfo APIs, which follow the Unicode standard. In .NET 5 and later versions,
Char methods use and return the Unicode category that matches the Unicode standard
for all characters.
The following table shows the characters whose Unicode categories have changed in
.NET 5:
Character
Unicode category
in previous .NET versions
Unicode category
in .NET 5 and later versions
§ (\u00a7)
OtherSymbol
OtherPunctuation
ª (\u00aa)
LowercaseLetter
OtherLetter
SHY (\u00ad)
DashPunctuation
Format
¶ (\u00b6)
OtherSymbol
OtherPunctuation
º (\u00ba)
LowercaseLetter
OtherLetter
Version introduced
.NET 5.0
Recommended action
If you have any code that gets the Unicode character category by using the Char class
and assumes the category will never change, you may need to update it.
Reason for change
This change was made so that the categories returned by the Char type are consistent
with both the Unicode standard and the CharUnicodeInfo type.
Affected APIs
System.Char.GetUnicodeCategory
System.Char.IsLetter
System.Char.IsPunctuation
System.Char.IsSymbol
System.Char.IsLower
Additionally, any class that depends on Char to obtain the Unicode character category,
for example, Regex, is affected by this change.
TextInfo.ListSeparator values changed
Article • 09/15/2021
The default TextInfo.ListSeparator values for different cultures have changed on all
operating systems.
Change description
In .NET 5.0.0, as part of the switch from NLS to ICU libraries, the default
TextInfo.ListSeparator values for different cultures changed on Windows. Decimal
separator values, obtained from International Components for Unicode (ICU), were used
as the ListSeparator values. On Linux and macOS, there was no change in
TextInfo.ListSeparator values; that is, they continued to use decimal separator values.
For all operating systems in .NET 5.0.1 and later versions, the values for
TextInfo.ListSeparator are equivalent to the values that would be obtained from NLS. For
Windows, this means the values are equivalent to what they were in .NET Framework
and .NET Core 1.0 - 3.1. For Linux and macOS, the TextInfo.ListSeparator values now
match the TextInfo.ListSeparator values for Windows.
The following table summarizes the changes for TextInfo.ListSeparator values.
.NET Framework
.NET Core 1.0 - 3.1
.NET 5
.NET 5.0.1
Windows
Obtain from NLS
Decimal separator from
ICU.
Can switch back to NLS.
Equivalent to
NLS
Linux and
macOS
Decimal separator from
ICU
Decimal separator from ICU
Equivalent to
NLS
Version introduced
5.0.1
Reason for change
Developers reported that they use the TextInfo.ListSeparator property when parsing
comma-separated value (CSV) files, and the new TextInfo.ListSeparator values broke that
parsing.
Recommended action
If your code relies on the previous decimal separator values, you can hardcode the
desired TextInfo.ListSeparator values.
Affected APIs
System.Globalization.TextInfo.ListSeparator
Built-in support for WinRT is removed
from .NET
Article • 04/02/2022
Built-in support for consumption of Windows runtime (WinRT) APIs in .NET is removed.
Version introduced
5.0
Change description
Previously, CoreCLR could consume Windows metadata (WinMD) files to active and
consume WinRT types. Starting in .NET 5, 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) trimming, and ahead-of-time (AOT) compilation.
To simplify the .NET runtime codebase.
Recommended action
Remove references to the Microsoft.Windows.SDK.Contracts package
specify the version of the Windows APIs that you want to access via the
TargetFramework property of the project. For example:
XML
<TargetFramework>net5.0-windows10.0.19041.0</TargetFramework>
. Instead,
If you're consuming a third-party runtime component that's defined in a .winmd
file, add a reference to the Microsoft.Windows.CsWinRT NuGet package . For
information on how to generate the C# projection, see the C#/WinRT
documentation.
For more information, see Call Windows Runtime APIs in desktop apps.
Affected APIs
System.IO.WindowsRuntimeStorageExtensions
System.IO.WindowsRuntimeStreamExtensions
System.Runtime.InteropServices.WindowsRuntime
System.WindowsRuntimeSystemExtensions
Windows.Foundation.Point
Windows.Foundation.Size
Windows.UI.Color
Casting RCW to an
InterfaceIsIInspectable interface
throws PlatformNotSupportedException
Article • 12/04/2021
Casting a runtime callable wrapper (RCW) to an interface marked as
InterfaceIsIInspectable now throws a PlatformNotSupportedException. This change is a
follow up to the removal of WinRT support from .NET.
Version introduced
.NET 5
Change description
In previous .NET versions, casting an RCW to an interface marked as
InterfaceIsIInspectable worked as expected. Starting in .NET 5, casting an RCW to an
interface marked as InterfaceIsIInspectable throws a PlatformNotSupportedException at
cast time.
Reason for change
The support for InterfaceIsIInspectable was removed. Since the underlying support in
the runtime no longer exists, throwing a PlatformNotSupportedException enables a
graceful failure path. Throwing an exception also makes it more discoverable that this
feature is no longer supported.
Recommended action
If you can define the interface in a Windows runtime metadata (WinMD) file, use
the C#/WinRT tool instead.
Otherwise, mark the interface as InterfaceIsIUnknown instead of
InterfaceIsIInspectable, and add three dummy entries to the start of the interface
for the InterfaceIsIInspectable methods. The following code snippet shows an
example.
C#
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
interface IMine
{
// Do not call these three methods.
// They're exclusively to fill in the slots in the vtable.
void GetIIdsSlot();
void GetRuntimeClassNameSlot();
void GetTrustLevelSlot();
// The original members of the IMine interface go here.
...
}
Affected APIs
System.Runtime.InteropServices.ComInterfaceType.InterfaceIsIInspectable
No A/W suffix probing on nonWindows platforms
Article • 09/15/2021
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
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 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.
Recommended action
To mitigate the change, you can manually add the desired suffix on non-Windows
platforms. For example:
C#
[DllImport(...)]
extern static void SetWindowTextW();
Affected APIs
System.Runtime.InteropServices.DllImportAttribute
Cookie path handling now conforms to
RFC 6265
Article • 09/15/2021
Path-handling algorithms defined in RFC 6265
were implemented for the Cookie and
CookieContainer classes.
Version introduced
5.0
Change description
The Path property is no longer required to be a prefix of the request path.
The calculation of the default value of Path and path-matching algorithms were
implemented as defined in section 5.1.4
of RFC 6265.
Recommended action
In most cases, you won't need to take any action. However, if your code was dependent
on Path validation, you'll need to implement required validation in your code. If your
code was dependent on default value calculation for Path, consider supplying the Path
value manually instead of using the default value.
Affected APIs
System.Net.Cookie
System.Net.CookieContainer
Socket.LocalEndPoint is updated after
calling SendToAsync
Article • 09/15/2021
Socket.SendToAsync(SocketAsyncEventArgs) now updates the value of the
Socket.LocalEndPoint property to the socket's local address.
Version introduced
5.0
Change description
In previous .NET versions, Socket.SendToAsync(SocketAsyncEventArgs) does not alter
the value of the Socket.LocalEndPoint property on the socket instance. Starting in .NET
5, when SendToAsync(SocketAsyncEventArgs) successfully completes, the value of
Socket.LocalEndPoint is the implicitly bound socket's local address. This new behavior is
consistent with the behavior of SendTo(Byte[], EndPoint) and BeginSendTo(Byte[], Int32,
Int32, SocketFlags, EndPoint, AsyncCallback, Object)/EndSendTo(IAsyncResult).
Reason for change
This change fixes a bug
and makes the behavior consistent across SendTo variants.
Recommended action
Alter any code that assumes that SendToAsync(SocketAsyncEventArgs) won't alter the
value of Socket.LocalEndPoint.
Affected APIs
System.Net.Sockets.Socket.SendToAsync(SocketAsyncEventArgs)
MulticastOption.Group doesn't accept a
null value
Article • 09/15/2021
MulticastOption.Group no longer accepts a value of null . If you set the property to
null , an ArgumentNullException is thrown.
Version introduced
5.0
Change description
In previous versions of .NET, you can set the MulticastOption.Group property to null . If
the MulticastOption is later passed to Socket.SetSocketOption, the runtime throws a
NullReferenceException.
In .NET 5 and later, an ArgumentNullException is thrown if you set the property to null .
Reason for change
To be consistent with the Framework Design Guidelines, the property has been updated
to throw an ArgumentNullException if the value is null .
Recommended action
Make sure that you don't set MulticastOption.Group to null .
Affected APIs
System.Net.Sockets.MulticastOption.Group
NegotiateStream and SslStream allow
successive Begin operations
Article • 09/15/2021
Error cases on security streams are handled differently, and successive calls to
BeginAuthenticateAsServer or BeginAuthenticateAsClient may no longer fail.
Version introduced
5.0
Change description
In previous .NET versions, calling BeginAuthenticateAsServer or
BeginAuthenticateAsClient successively without first calling EndAuthenticateAsServer or
EndAuthenticateAsClient results in a NotSupportedException. Starting in .NET 5,
successive calls to BeginAuthenticateAsServer or BeginAuthenticateAsClient no longer
result in a NotSupportedException, because these APIs are backed by a Task-based
implementation.
Reason for change
Switching the internal implementation from asynchronous programming model (APM)
to Task-based improves performance and decreases code complexity.
Recommended action
No action is required on the part of the developer.
Affected APIs
System.Net.Security.SslStream.BeginAuthenticateAsServer
System.Net.Security.SslStream.BeginAuthenticateAsClient
System.Net.Security.NegotiateStream.BeginAuthenticateAsServer
System.Net.Security.NegotiateStream.BeginAuthenticateAsClient
WinHttpHandler removed from .NET
runtime
Article • 09/15/2021
The WinHttpHandler class was removed from the System.Net.Http.dll assembly. It's now
available only as an out-of-band (OOB) NuGet package .
Version introduced
5.0
Change description
In previous .NET versions, the WinHttpHandler class is available as part of the core .NET
libraries. Starting in .NET 5, the WinHttpHandler class is only available as a separately
installed NuGet package .
Recommended action
Install the System.Net.Http.WinHttpHandler NuGet package . Or, if you don't require
WinHTTP-specific features, use SocketsHttpHandler instead.
Affected APIs
System.Net.Http.WinHttpHandler
Error generated when executable
project references mismatched
executable
Article • 09/15/2021
Generally, an executable project references library projects, not other executable
projects. An executable project can also reference another executable project to use
APIs that are defined in it. Some developers want to reference an executable project
from another executable project so that both apps are placed in and are runnable from
the same output folder. However, this scenario does not work if a self-contained
executable references a non-self-contained executable, or vice versa. Because of how
the application host works, neither app can be launched. To prevent situations where
apps aren't runnable, .NET SDK 5+ produces compile-time errors NETSDK1150 and
NETSDK1151 when it detects mismatched executable references.
Change description
In previous .NET SDK versions, you could reference a self-contained executable project
from a non-self-contained executable project without a build error. However, both apps
would not be runnable. Starting in .NET SDK 5, an error is generated if an executable
project references another executable project and the SelfContained values don't
match.
Version introduced
.NET SDK 5.0.300
Reason for change
The errors were introduced to prevent situations where you expect to be able to launch
both applications but cannot.
Recommended action
If the referenced project doesn't need to be runnable from the output folder, you can
set a property to avoid this error check:
XML
<ValidateExecutableReferencesMatchSelfContained>false</ValidateExecutableRef
erencesMatchSelfContained>
For more information, see ValidateExecutableReferencesMatchSelfContained.
Affected APIs
None.
OutputType set to WinExe for WPF and
WinForms apps
Article • 09/15/2021
OutputType is automatically set to WinExe for Windows Presentation Foundation (WPF)
and Windows Forms apps. When OutputType is set to WinExe , a console window doesn't
open when the app is executed.
Change description
In previous versions of the .NET SDK, the value that's specified for OutputType in the
project file is used. For example:
XML
<PropertyGroup>
<OutputType>Exe</OutputType>
</PropertyGroup>
Starting in the 5.0.100 version of the .NET SDK, when OutputType is set to Exe , it is
automatically changed to WinExe for WPF and Windows Forms apps that target any
framework version, including .NET Framework.
XML
<PropertyGroup>
<OutputType>WinExe</OutputType>
</PropertyGroup>
If OutputType is not specified in the project file, it defaults to Library and that value
doesn't change.
Reason for change
It's assumed that most users don't want a console window to open when a WPF or
Windows Forms app is executed. In addition, now that these application types use the
.NET SDK instead of the Windows Desktop SDK, the correct default will be set. Further,
when support for targeting iOS and Android is added, it will be easier to multi-target
between multiple platforms if they all use the same output type.
Version introduced
.NET SDK 5.0.100
Recommended action
No action is required in your part. However, if you want to revert to the old behavior, set
the DisableWinExeOutputInference property to true in your project file.
XML
<DisableWinExeOutputInference>true</DisableWinExeOutputInference>
Affected APIs
Not detectable via API analysis.
WinForms and WPF apps use
Microsoft.NET.Sdk
Article • 09/15/2021
Windows Forms and Windows Presentation Framework (WPF) apps now use the .NET
SDK ( Microsoft.NET.Sdk ) instead of the .NET Core WinForms and WPF SDK
( Microsoft.NET.Sdk.WindowsDesktop ).
Change description
In previous .NET Core versions, WinForms and WPF apps used a separate project SDK
( Microsoft.NET.Sdk.WindowsDesktop ). Starting in .NET 5, the WinForms and WPF SDK has
been unified with the .NET SDK ( Microsoft.NET.Sdk ). In addition, new target framework
monikers (TFM) replace netcoreapp and netstandard in .NET 5. The following example
shows the changes you'd need to make for a WPF project file when retargeting to .NET
5 or later.
In previous .NET Core versions:
XML
<Project Sdk="Microsoft.NET.Sdk.WindowsDesktop">
<PropertyGroup>
<OutputType>WinExe</OutputType>
<TargetFramework>netcoreapp3.1</TargetFramework>
<UseWPF>true</UseWPF>
</PropertyGroup>
</Project>
In .NET 5 and later versions:
XML
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>WinExe</OutputType>
<TargetFramework>net5.0-windows</TargetFramework>
<UseWPF>true</UseWPF>
</PropertyGroup>
</Project>
Version introduced
.NET SDK 5.0.100
Recommended action
In your WPF or Windows Forms project file:
Update the Sdk attribute to Microsoft.NET.Sdk .
Update the TargetFramework property to net5.0-windows .
Affected APIs
None.
Directory.Packages.props files is
imported by default
Article • 09/15/2021
NuGet's .props files automatically import a file named Directory.Packages.props if it's
found in the project folder or any of its ancestors.
Version introduced
5.0
Change description
In previous .NET versions, you could have a file named Directory.Packages.props in your
project file and it wouldn't be automatically imported by NuGet's .props file at build
time.
Starting in .NET 5, such a file is automatically imported if it exists in the project folder or
any of its ancestors. If you have a file with this name in your project folder, this
automatic import could change behavior of the build. For example, the file will be
imported but it wasn't before, or the order of when it's imported could change if you
specifically import it.
Reason for change
This change was made in order to support central package versioning
for NuGet.
Recommended action
Rename the existing Directory.Packages.props file if it should not be imported
automatically.
Affected APIs
N/A
NETCOREAPP3_1 preprocessor symbol is
not defined when targeting .NET 5
Article • 09/15/2021
In .NET 5 RC2 and later versions, projects no longer define preprocessor symbols for
earlier versions, but only for the version that they target. This is the same behavior as
.NET Core 1.0 - 3.1.
Version introduced
5.0 RC2
Change description
In .NET 5 preview 7 through RC1, projects that target net5.0 define both NETCOREAPP3_1
and NET5_0 preprocessor symbols. The intent behind this behavior change was that
starting with .NET 5, conditional compilation symbols would be cumulative .
In .NET 5 RC2 and later, projects only define symbols for the target framework monikers
(TFM) that it targets and not for any earlier versions.
Reason for change
The change from preview 7 was reverted due to customer feedback. Defining symbols
for earlier versions surprised and confused customers, and some assumed it was a bug
in the C# compiler.
Recommended action
Make sure that your #if logic doesn't assume that NETCOREAPP3_1 is defined when the
project targets net5.0 or higher. Instead, assume that NETCOREAPP3_1 is only defined
when the project explicitly targets netcoreapp3.1 .
For example, if your project multitargets for .NET Core 2.1 and .NET Core 3.1 and you
call APIs that were introduced in .NET Core 3.1, your #if logic should look as follows:
C#
#if NETCOREAPP2_1 || NETCOREAPP3_0
// Fallback behavior for old versions.
#elif NETCOREAPP
// Behavior for .NET Core 3.1 and later.
#endif
Affected APIs
N/A
PublishDepsFilePath behavior change
Article • 09/15/2021
The PublishDepsFilePath MSBuild property is empty for single-file applications.
Additionally, for non single-file applications, the deps.json file may not be copied to the
output directory until later in the build.
Version introduced
5.0
Change description
In previous .NET versions, the PublishDepsFilePath MSBuild property is the path to the
app's deps.json file in the output directory for non single-file applications, and a path in
the intermediate directory for single-file apps.
Starting in .NET 5, PublishDepsFilePath is empty for single-file applications and a new
IntermediateDepsFilePath property specifies the deps.json location in the intermediate
directory. Additionally, for non single-file applications, the deps.json file may not be
copied to the output directory (that is, the path specified by PublishDepsFilePath ) until
later in the build.
Reason for change
This change was made for a couple of reasons:
Due to a refactoring of the publish logic in order to support improved single-file
apps
in .NET 5.
In single-file apps, to help guard against targets that try to rewrite the deps.json file
after deps.json has already been bundled, thus silently not affecting the app. For
this reason, PublishDepsFilePath is empty for single-file applications.
Recommended action
Targets that rewrite the deps.json file should generally do so using the
IntermediateDepsFilePath property.
Affected APIs
N/A
TargetFramework change from
netcoreapp to net
Article • 09/15/2021
The value for the MSBuild TargetFramework property changed from netcoreapp3.1 to
net5.0 . This can break code that relies on parsing the value of TargetFramework .
Version introduced
5.0
Change description
In .NET Core 1.0 - 3.1, the value for the MSBuild TargetFramework property starts with
netcoreapp , for example, netcoreapp3.1 for apps that target .NET Core 3.1. Starting in
.NET 5, this value is simplified to just start with net , for example, net5.0 for .NET 5.0.
For more information, see The future of .NET Standard
and Target framework names
in .NET 5 .
Reason for change
Simplifies the TargetFramework value.
Enables projects to include a TargetPlatform in the TargetFramework property.
Recommended action
If you have logic that parses the value of TargetFramework , you'll need to update it. For
example, the following MSBuild condition relies on the value of TargetFramework .
XML
<PropertyGroup Condition="$(TargetFramework.StartsWith('netcoreapp'))">
For this requirement, you can update the code to compare the target framework
identifier instead.
XML
<PropertyGroup
Condition="'$([MSBuild]::GetTargetFrameworkIdentifier('$(TargetFramework)'))
' == '.NETCoreApp'">
Affected APIs
N/A
FrameworkReference replaced with
WindowsSdkPackageVersion for
Windows SDK
Article • 09/15/2021
Starting in .NET 5.0.8 (which includes .NET SDK 5.0.302 and .NET SDK 5.0.205),
developers targeting Windows can't use the FrameworkReference item to override their
version of the Windows SDK targeting package. The WindowsSdkPackageVersion property
replaces this functionality.
7 Note
We don't recommend overriding the Windows SDK version, because the Windows
SDK targeting packages are included with the .NET 5+ SDK. Instead, to reference
the latest Windows SDK package, update your version of the .NET SDK.
Version introduced
.NET SDK 5.0.302, .NET SDK 5.0.205
Previous behavior
Developers could use the FrameworkReference item to override the Windows SDK
package version in .NET 5 applications. For example:
XML
<ItemGroup>
<FrameworkReference Update="Microsoft.Windows.SDK.NET.Ref"
RuntimeFrameworkVersion="10.0.19041.18" />
<FrameworkReference Update="Microsoft.Windows.SDK.NET.Ref"
TargetingPackVersion="10.0.19041.18" />
</ItemGroup>
New behavior
The WindowsSdkPackageVersion property replaces the behavior of the
FrameworkReference override. For example:
XML
<PropertyGroup>
<WindowsSdkPackageVersion>10.0.19041.18</WindowsSdkPackageVersion>
</PropertyGroup>
Category of change
This change might affect source compatibility.
Reason for change
This change was introduced to simplify the package override behavior for targeting the
Windows SDK packages produced by C#/WinRT.
Recommended action
Remove any use of FrameworkReference in your .NET 5+ app's project file when targeting
the Windows SDK.
Affected APIs
Windows APIs in .NET 5 and later versions that are provided by the Windows SDK
targeting package
.
Most code access security APIs are
obsolete
Article • 11/08/2021
Most code access security (CAS)-related types in .NET are now obsolete as warning. This
includes CAS attributes, such as SecurityPermissionAttribute, CAS permission objects,
such as SocketPermission, EvidenceBase-derived types, and other supporting APIs.
Change description
In .NET Framework 2.x - 4.x, CAS attributes and APIs can influence the course of code
execution, including ensuring that CAS-demand stack walks succeed or fail.
C#
// In .NET Framework, the attribute causes CAS stack walks
// to terminate successfully when this permission is demanded.
[SocketPermission(SecurityAction.Assert, Host = "contoso.com", Port =
"443")]
public void DoSomething()
{
// open a socket to contoso.com:443
}
In .NET Core 2.x - 3.x, the runtime does not honor CAS attributes or CAS APIs. The
runtime ignores attributes on method entry, and most programmatic APIs have no
effect.
C#
// The .NET Core runtime ignores the following attribute.
[SocketPermission(SecurityAction.Assert, Host = "contoso.com", Port =
"443")]
public void DoSomething()
{
// open a socket to contoso.com:443
}
Additionally, programmatic calls to expansive APIs ( Assert ) always succeed, while
programmatic calls to restrictive APIs ( Deny , PermitOnly ) always throw an exception at
run time. (PrincipalPermission is an exception to this rule. See the Recommended action
section below.)
C#
public void DoAssert()
{
// The line below has no effect at run time.
new SocketPermission(PermissionState.Unrestricted).Assert();
}
public void DoDeny()
{
// The line below throws PlatformNotSupportedException at run time.
new SocketPermission(PermissionState.Unrestricted).Deny();
}
In .NET 5 and later versions, most CAS-related APIs are obsolete and produce compiletime warning SYSLIB0003 .
C#
[SocketPermission(SecurityAction.Assert, Host = "contoso.com", Port =
"443")] // warning SYSLIB0003
public void DoSomething()
{
new SocketPermission(PermissionState.Unrestricted).Assert(); // warning
SYSLIB0003
new SocketPermission(PermissionState.Unrestricted).Deny(); // warning
SYSLIB0003
}
This is a compile-time only change. There is no run-time change from previous versions
of .NET Core. Methods that perform no operation in .NET Core 2.x - 3.x will continue to
perform no operation at run time in .NET 5 and later. Methods that throw
PlatformNotSupportedException in .NET Core 2.x - 3.x will continue to throw a
PlatformNotSupportedException at run time in .NET 5 and later.
Reason for change
Code access security (CAS) is an unsupported legacy technology. The infrastructure to
enable CAS exists only in .NET Framework 2.x - 4.x, but is deprecated and not receiving
servicing or security fixes.
Due to CAS's deprecation, the supporting infrastructure was not brought forward to
.NET Core or .NET 5+. However, the APIs were brought forward so that apps could
cross-compile against .NET Framework and .NET Core. This led to "fail open" scenarios,
where some CAS-related APIs exist and are callable but perform no action at run time.
This can lead to security issues for components that expect the runtime to honor CAS-
related attributes or programmatic API calls. To better communicate that the runtime
doesn't respect these attributes or APIs, we have obsoleted the majority of them in .NET
5.0.
Version introduced
5.0
Recommended action
If you're asserting any security permission, remove the attribute or call that asserts
the permission.
C#
// REMOVE the attribute below.
[SecurityPermission(SecurityAction.Assert, ControlThread = true)]
public void DoSomething()
{
}
public void DoAssert()
{
// REMOVE the line below.
new
SecurityPermission(SecurityPermissionFlag.ControlThread).Assert();
}
If you're denying or restricting (via PermitOnly ) any permission, contact your
security advisor. Because CAS attributes are not honored by the .NET 5+ runtime,
your application could have a security hole if it incorrectly relies on the CAS
infrastructure to restrict access to these methods.
C#
// REVIEW the attribute below; could indicate security vulnerability.
[SecurityPermission(SecurityAction.Deny, ControlThread = true)]
public void DoSomething()
{
}
public void DoPermitOnly()
{
// REVIEW the line below; could indicate security vulnerability.
new
SecurityPermission(SecurityPermissionFlag.ControlThread).PermitOnly();
}
If you're demanding any permission (except PrincipalPermission), remove the
demand. All demands will succeed at run time.
C#
// REMOVE the attribute below; it will always succeed.
[SecurityPermission(SecurityAction.Demand, ControlThread = true)]
public void DoSomething()
{
}
public void DoDemand()
{
// REMOVE the line below; it will always succeed.
new
SecurityPermission(SecurityPermissionFlag.ControlThread).Demand();
}
If you're demanding PrincipalPermission, consult the guidance for
PrincipalPermissionAttribute is obsolete as error. That guidance applies for both
PrincipalPermission and PrincipalPermissionAttribute.
If you absolutely must disable these warnings (which is not recommended), you
can suppress the SYSLIB0003 warning in code.
C#
#pragma warning disable SYSLIB0003 // disable the warning
[SecurityPermission(SecurityAction.Demand, ControlThread = true)]
#pragma warning restore SYSLIB0003 // re-enable the warning
public void DoSomething()
{
}
public void DoDemand()
{
#pragma warning disable SYSLIB0003 // disable the warning
new
SecurityPermission(SecurityPermissionFlag.ControlThread).Demand();
#pragma warning restore SYSLIB0003 // re-enable the warning
}
You can also suppress the warning in your project file. Doing so disables the
warning for all source files within the project.
XML
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net5.0</TargetFramework>
<!-- NoWarn below suppresses SYSLIB0003 project-wide -->
<NoWarn>$(NoWarn);SYSLIB0003</NoWarn>
</PropertyGroup>
</Project>
7 Note
Suppressing SYSLIB0003 disables only the CAS-related obsoletion warnings. It
does not disable any other warnings or change the behavior of the .NET 5+
runtime.
Security
Affected APIs
System.AppDomain.PermissionSet
System.Configuration.ConfigurationPermission
System.Configuration.ConfigurationPermissionAttribute
System.Data.Common.DBDataPermission
System.Data.Common.DBDataPermissionAttribute
System.Data.Odbc.OdbcPermission
System.Data.Odbc.OdbcPermissionAttribute
System.Data.OleDb.OleDbPermission
System.Data.OleDb.OleDbPermissionAttribute
System.Data.OracleClient.OraclePermission
System.Data.OracleClient.OraclePermissionAttribute
System.Data.SqlClient.SqlClientPermission
System.Data.SqlClient.SqlClientPermissionAttribute
System.Diagnostics.EventLogPermission
System.Diagnostics.EventLogPermissionAttribute
System.Diagnostics.PerformanceCounterPermission
System.Diagnostics.PerformanceCounterPermissionAttribute
System.DirectoryServices.DirectoryServicesPermission
System.DirectoryServices.DirectoryServicesPermissionAttribute
System.Drawing.Printing.PrintingPermission
System.Drawing.Printing.PrintingPermissionAttribute
System.Net.DnsPermission
System.Net.DnsPermissionAttribute
System.Net.Mail.SmtpPermission
System.Net.Mail.SmtpPermissionAttribute
System.Net.NetworkInformation.NetworkInformationPermission
System.Net.NetworkInformation.NetworkInformationPermissionAttribute
System.Net.PeerToPeer.Collaboration.PeerCollaborationPermission
System.Net.PeerToPeer.Collaboration.PeerCollaborationPermissionAttribute
System.Net.PeerToPeer.PnrpPermission
System.Net.PeerToPeer.PnrpPermissionAttribute
System.Net.SocketPermission
System.Net.SocketPermissionAttribute
System.Net.WebPermission
System.Net.WebPermissionAttribute
System.Runtime.InteropServices.AllowReversePInvokeCallsAttribute
System.Security.CodeAccessPermission
System.Security.HostProtectionException
System.Security.IPermission
System.Security.IStackWalk
System.Security.NamedPermissionSet
System.Security.PermissionSet
System.Security.Permissions.CodeAccessSecurityAttribute
System.Security.Permissions.DataProtectionPermission
System.Security.Permissions.DataProtectionPermissionAttribute
System.Security.Permissions.DataProtectionPermissionFlags
System.Security.Permissions.EnvironmentPermission
System.Security.Permissions.EnvironmentPermissionAccess
System.Security.Permissions.EnvironmentPermissionAttribute
System.Security.Permissions.FileDialogPermission
System.Security.Permissions.FileDialogPermissionAccess
System.Security.Permissions.FileDialogPermissionAttribute
System.Security.Permissions.FileIOPermission
System.Security.Permissions.FileIOPermissionAccess
System.Security.Permissions.FileIOPermissionAttribute
System.Security.Permissions.GacIdentityPermission
System.Security.Permissions.GacIdentityPermissionAttribute
System.Security.Permissions.HostProtectionAttribute
System.Security.Permissions.HostProtectionResource
System.Security.Permissions.IUnrestrictedPermission
System.Security.Permissions.IsolatedStorageContainment
System.Security.Permissions.IsolatedStorageFilePermission
System.Security.Permissions.IsolatedStorageFilePermissionAttribute
System.Security.Permissions.IsolatedStoragePermission
System.Security.Permissions.IsolatedStoragePermissionAttribute
System.Security.Permissions.KeyContainerPermission
System.Security.Permissions.KeyContainerPermissionAccessEntry
System.Security.Permissions.KeyContainerPermissionAccessEntryCollection
System.Security.Permissions.KeyContainerPermissionAccessEntryEnumerator
System.Security.Permissions.KeyContainerPermissionAttribute
System.Security.Permissions.KeyContainerPermissionFlags
System.Security.Permissions.MediaPermission
System.Security.Permissions.MediaPermissionAttribute
System.Security.Permissions.MediaPermissionAudio
System.Security.Permissions.MediaPermissionImage
System.Security.Permissions.MediaPermissionVideo
System.Security.Permissions.PermissionSetAttribute
System.Security.Permissions.PermissionState
System.Security.Permissions.PrincipalPermission
System.Security.Permissions.PrincipalPermissionAttribute
System.Security.Permissions.PublisherIdentityPermission
System.Security.Permissions.PublisherIdentityPermissionAttribute
System.Security.Permissions.ReflectionPermission
System.Security.Permissions.ReflectionPermissionAttribute
System.Security.Permissions.ReflectionPermissionFlag
System.Security.Permissions.RegistryPermission
System.Security.Permissions.RegistryPermissionAccess
System.Security.Permissions.RegistryPermissionAttribute
System.Security.Permissions.ResourcePermissionBase
System.Security.Permissions.ResourcePermissionBaseEntry
System.Security.Permissions.SecurityAction
System.Security.Permissions.SecurityAttribute
System.Security.Permissions.SecurityPermission
System.Security.Permissions.SecurityPermissionAttribute
System.Security.Permissions.SecurityPermissionFlag
System.Security.Permissions.SiteIdentityPermission
System.Security.Permissions.SiteIdentityPermissionAttribute
System.Security.Permissions.StorePermission
System.Security.Permissions.StorePermissionAttribute
System.Security.Permissions.StorePermissionFlags
System.Security.Permissions.StrongNameIdentityPermission
System.Security.Permissions.StrongNameIdentityPermissionAttribute
System.Security.Permissions.StrongNamePublicKeyBlob
System.Security.Permissions.TypeDescriptorPermission
System.Security.Permissions.TypeDescriptorPermissionAttribute
System.Security.Permissions.TypeDescriptorPermissionFlags
System.Security.Permissions.UIPermission
System.Security.Permissions.UIPermissionAttribute
System.Security.Permissions.UIPermissionClipboard
System.Security.Permissions.UIPermissionWindow
System.Security.Permissions.UrlIdentityPermission
System.Security.Permissions.UrlIdentityPermissionAttribute
System.Security.Permissions.WebBrowserPermission
System.Security.Permissions.WebBrowserPermissionAttribute
System.Security.Permissions.WebBrowserPermissionLevel
System.Security.Permissions.ZoneIdentityPermission
System.Security.Permissions.ZoneIdentityPermissionAttribute
System.Security.Policy.ApplicationTrust.ApplicationTrust(PermissionSet,
IEnumerable<StrongName>)
System.Security.Policy.ApplicationTrust.FullTrustAssemblies
System.Security.Policy.FileCodeGroup
System.Security.Policy.GacInstalled
System.Security.Policy.IIdentityPermissionFactory
System.Security.Policy.PolicyLevel.AddNamedPermissionSet(NamedPermissionSet)
System.Security.Policy.PolicyLevel.ChangeNamedPermissionSet(String,
PermissionSet)
System.Security.Policy.PolicyLevel.GetNamedPermissionSet(String)
System.Security.Policy.PolicyLevel.RemoveNamedPermissionSet
System.Security.Policy.PolicyStatement.PermissionSet
System.Security.Policy.PolicyStatement.PolicyStatement
System.Security.Policy.Publisher
System.Security.Policy.Site
System.Security.Policy.StrongName
System.Security.Policy.StrongNameMembershipCondition
System.Security.Policy.Url
System.Security.Policy.Zone
System.Security.SecurityManager
System.ServiceProcess.ServiceControllerPermission
System.ServiceProcess.ServiceControllerPermissionAttribute
System.Transactions.DistributedTransactionPermission
System.Transactions.DistributedTransactionPermissionAttribute
System.Web.AspNetHostingPermission
System.Web.AspNetHostingPermissionAttribute
System.Xaml.Permissions.XamlLoadPermission
PrincipalPermissionAttribute is obsolete
as error
Article • 09/15/2021
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:
C#
[PrincipalPermission(SecurityAction.Demand, Role = "Administrators")]
public void MyMethod()
{
// Code that should only run when the current user is an administrator.
}
Starting in .NET 5, 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 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 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 or later.
Version introduced
5.0
Recommended action
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.
C#
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.
C#
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.
}
Affected APIs
System.Security.Permissions.PrincipalPermissionAttribute
UTF-7 code paths are obsolete
Article • 01/27/2022
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:
C#
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, the Encoding.UTF7 property and UTF7Encoding constructors are
obsolete and produce warning SYSLIB0001 . However, to reduce the number of warnings
that callers receive when using the UTF7Encoding class, the UTF7Encoding type itself is
not marked obsolete.
C#
// 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.
C#
// 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.
C#
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
Recommended action
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.
C#
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.
C#
#pragma warning disable SYSLIB0001 // Disable the warning.
Encoding enc = Encoding.UTF7;
#pragma warning restore SYSLIB0001 // Re-enable the warning.
XML
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net5.0</TargetFramework>
<!-- NoWarn below suppresses SYSLIB0001 project-wide -->
<NoWarn>$(NoWarn);SYSLIB0001</NoWarn>
</PropertyGroup>
</Project>
7 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 runtime configuration
file, as shown in the following examples.
In the application's .csproj file:
XML
<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:
JSON
{
"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.
Affected APIs
System.Text.Encoding.UTF7
UTF7Encoding()
UTF7Encoding(Boolean)
System.Text.Encoding.GetEncoding(Int32)
System.Text.Encoding.GetEncoding(String)
System.Text.Encoding.GetEncoding(Int32, EncoderFallback, DecoderFallback)
System.Text.Encoding.GetEncoding(String, EncoderFallback, DecoderFallback)
System.Text.Encoding.GetEncodings()
BinaryFormatter serialization methods
are obsolete and prohibited in ASP.NET
apps
Article • 05/17/2023
Serialize and Deserialize methods on BinaryFormatter, Formatter, and IFormatter are
now obsolete as warning. Additionally, BinaryFormatter serialization is prohibited by
default for ASP.NET apps.
7 Note
In .NET 7, the affected APIs are obsolete as error. For more information, see
BinaryFormatter serialization APIs produce compiler errors.
Change description
Due to security vulnerabilities in BinaryFormatter, the following methods are now
obsolete and produce a compile-time warning with ID SYSLIB0011 . Additionally, in
ASP.NET Core 5.0 and later apps, they will throw a NotSupportedException, unless the
web app has re-enabled BinaryFormatter functionality.
BinaryFormatter.Serialize
BinaryFormatter.Deserialize
The following serialization methods are also obsolete and produce warning SYSLIB0011 ,
but have no behavioral changes:
Formatter.Serialize(Stream, Object)
Formatter.Deserialize(Stream)
IFormatter.Serialize(Stream, Object)
IFormatter.Deserialize(Stream)
Version introduced
5.0
Reason for change
These methods are marked obsolete as part of an effort to wind down usage of
BinaryFormatter within the .NET ecosystem.
Recommended action
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.
C#
// 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.
XML
<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.
XML
<PropertyGroup>
<TargetFramework>net5.0</TargetFramework>
<!-- Warning: Setting the following switch is *NOT* recommended in
web apps. -->
<EnableUnsafeBinaryFormatterSerialization>true</EnableUnsafeBinaryForma
tterSerialization>
</PropertyGroup>
For more information about recommended actions, see Resolving BinaryFormatter
obsoletion and disablement errors.
Affected APIs
System.Runtime.Serialization.Formatters.Binary.BinaryFormatter.Serialize
System.Runtime.Serialization.Formatters.Binary.BinaryFormatter.Deserialize
System.Runtime.Serialization.Formatter.Serialize(Stream, Object)
System.Runtime.Serialization.Formatter.Deserialize(Stream)
System.Runtime.Serialization.IFormatter.Serialize(Stream, Object)
System.Runtime.Serialization.IFormatter.Deserialize(Stream)
See also
SerializationFormat.Binary is obsolete (.NET 7)
BinaryFormatter serialization APIs produce compiler errors (.NET 7)
BinaryFormatter disabled across most project types (.NET 8)
BinaryFormatter.Deserialize rewraps
some exceptions in
SerializationException
Article • 09/15/2021
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 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
Recommended action
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.
C#
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.
}
Affected APIs
System.Runtime.Serialization.Formatters.Binary.BinaryFormatter.Deserialize
System.Text.Json requires single-char
string to deserialize a char
Article • 09/15/2021
To successfully deserialize a Char using System.Text.Json, the JSON string must contain a
single character.
Change description
In previous .NET versions, a multi- char string in the JSON is successfully deserialized to
a char property or field. Only the first char of the string is used, as in the following
example:
C#
// .NET Core 3.0 and 3.1: Returns the first char 'a'.
// .NET 5 and later: Throws JsonException because payload has more than one
char.
char deserializedChar = JsonSerializer.Deserialize<char>("\"abc\"");
In .NET 5 and later, anything other than a single- char string causes a JsonException to
be thrown when the deserialization target is a char . The following example string is
successfully deserialized in all .NET versions:
C#
// Correct usage.
char deserializedChar = JsonSerializer.Deserialize<char>("\"a\"");
Version introduced
5.0
Reason for change
Parsing for deserialization should only succeed when the provided payload is valid for
the target type. For a char type, the only valid payload is a single- char string.
Recommended action
When you deserialize JSON into a char target, make sure the string consists of a single
char .
Affected APIs
System.Text.Json.JsonSerializer.Deserialize
ASP.NET Core apps allow deserializing
quoted numbers
Article • 09/15/2021
Starting in .NET 5, ASP.NET Core apps use the default deserialization options as specified
by JsonSerializerDefaults.Web. The Web set of options includes setting NumberHandling
to JsonNumberHandling.AllowReadingFromString. This change means that ASP.NET
Core apps will successfully deserialize numbers that are represented as JSON strings
instead of throwing an exception.
Change description
In .NET Core 3.0 - 3.1, JsonSerializer throws a JsonException during deserialization if it
encounters a quoted number in a JSON payload. The quoted numbers are used to map
with number properties in object graphs. In .NET Core 3.0 - 3.1, numbers are only read
from JsonTokenType.Number tokens.
Starting in .NET 5, quoted numbers in JSON payloads are considered valid, by default,
for ASP.NET Core apps. No exception is thrown during deserialization of quoted
numbers.
 Tip
There is no behavior change for the default, standalone JsonSerializer or
JsonSerializerOptions.
This is technically not a breaking change, since it makes a scenario more
permissive instead of more restrictive (that is, it succeeds in coercing a
number from a JSON string instead of throwing an exception). However, since
this is a significant behavioral change that affects many ASP.NET Core apps, it
is documented here.
The HttpClientJsonExtensions.GetFromJsonAsync and
HttpContentJsonExtensions.ReadFromJsonAsync extension methods also use
the Web set of serialization options.
Version introduced
5.0
Reason for change
Multiple users have requested an option for more permissive number handling in
JsonSerializer. This feedback indicates that many JSON producers (for example, services
across the web) emit quoted numbers. By allowing quoted numbers to be read
(deserialized), .NET apps can successfully parse these payloads, by default, in web
contexts. The configuration is exposed via JsonSerializerDefaults.Web so that you can
specify the same options across different application layers, for example, client, server,
and shared.
Recommended action
If this change is disruptive, for example, if you depend on the strict number handling for
validation, you can re-enable the previous behavior. Set the
JsonSerializerOptions.NumberHandling option to JsonNumberHandling.Strict.
For ASP.NET Core MVC and web API apps, you can configure the option in Startup by
using the following code:
C#
services.AddControllers()
.AddJsonOptions(options => options.JsonSerializerOptions.NumberHandling =
JsonNumberHandling.Strict);
Affected APIs
System.Text.Json.JsonSerializer.Deserialize
System.Text.Json.JsonSerializer.DeserializeAsync
JsonSerializer.Serialize throws
ArgumentNullException when type
parameter is null
Article • 09/15/2021
JsonSerializer.Serialize, JsonSerializer.SerializeAsync, and
JsonSerializer.SerializeToUtf8Bytes overloads that have a parameter of type Type now
throw an ArgumentNullException whenever null is passed for that parameter.
Change description
In .NET Core 3.1, the JsonSerializer.Serialize, JsonSerializer.SerializeAsync(Stream, Object,
Type, JsonSerializerOptions, CancellationToken), and
JsonSerializer.SerializeToUtf8Bytes(Object, Type, JsonSerializerOptions) overloads that
have a Type parameter throw an ArgumentNullException when null is passed for the
Type inputType parameter, but not if the Object value parameter is also null . Starting
in .NET 5, these methods always throw an ArgumentNullException when null is passed
for the Type parameter.
Behavior in .NET Core 3.1:
C#
// Returns a string with value "null".
JsonSerializer.Serialize(null, null);
// Returns a byte array with value "null".
JsonSerializer.SerializeToUtf8Bytes(null, null);
Behavior in .NET 5 and later:
C#
// Throws ArgumentNullException: "Value cannot be null. (Parameter
'inputType')".
JsonSerializer.Serialize(null, null);
// Throws ArgumentNullException: "Value cannot be null. (Parameter
'inputType')".
JsonSerializer.SerializeToUtf8Bytes(null, null);
Version introduced
5.0
Reason for change
Passing in null for the Type inputType parameter is unacceptable and should always
throw an ArgumentNullException.
Recommended action
Make sure that you are not passing null for the Type inputType parameter of these
methods.
Affected APIs
System.Text.Json.JsonSerializer.Serialize(Object, Type, JsonSerializerOptions)
System.Text.Json.JsonSerializer.Serialize(Utf8JsonWriter, Object, Type,
JsonSerializerOptions)
System.Text.Json.JsonSerializer.SerializeAsync(Stream, Object, Type,
JsonSerializerOptions, CancellationToken)
System.Text.Json.JsonSerializer.SerializeToUtf8Bytes(Object, Type,
JsonSerializerOptions)
Non-public, parameterless constructors
not used for deserialization
Article • 09/15/2021
For consistency across all supported target framework monikers (TFMs), non-public,
parameterless constructors are no longer used for deserialization with JsonSerializer, by
default.
Change description
The standalone System.Text.Json NuGet packages
that support .NET Standard 2.0 and
higher, that is, versions 4.6.0-4.7.2, behave inconsistently with the built-in behavior on
.NET Core 3.0 and 3.1. On .NET Core 3.x, internal and private constructors can be used
for deserialization. In the standalone packages, non-public constructors are not allowed,
and a MissingMethodException is thrown if no public, parameterless constructor is
defined.
Starting with .NET 5 and System.Text.Json NuGet package 5.0.0, the behavior is
consistent between the NuGet package and the built-in APIs. Non-public constructors,
including parameterless constructors, are ignored by the serializer by default. The
serializer uses one of the following constructors for deserialization:
Public constructor annotated with JsonConstructorAttribute.
Public parameterless constructor.
Public parameterized constructor (if it's the only public constructor present).
If none of these constructors are available, a NotSupportedException is thrown if you
attempt to deserialize the type.
Version introduced
5.0
Reason for change
To enforce consistent behavior between all target framework monikers (TFMs) that
System.Text.Json builds for (.NET Core 3.0 and later versions and .NET Standard 2.0)
Because JsonSerializer shouldn't call the non-public surface area of a type, whether
that's a constructor, a property, or a field.
Recommended action
If you own the type and it's feasible, make the parameterless constructor public.
Otherwise, implement a JsonConverter<T> for the type and control the
deserialization behavior. You can call a non-public constructor from a
JsonConverter<T> implementation if C# accessibility rules for that scenario allow
it.
Affected APIs
System.Text.Json.JsonSerializer.Deserialize
System.Text.Json.JsonSerializer.DeserializeAsync
PropertyNamingPolicy,
PropertyNameCaseInsensitive, and
Encoder options are honored when
serializing and deserializing key-value
pairs
Article • 10/04/2022
JsonSerializer now honors the PropertyNamingPolicy and Encoder options when
serializing the Key and Value property names of a KeyValuePair<TKey,TValue> instance.
Additionally, JsonSerializer honors the PropertyNamingPolicy and
PropertyNameCaseInsensitive options when deserializing KeyValuePair<TKey,TValue>
instances.
Change description
Serialization
In .NET Core 3.x versions and in the 4.6.0-4.7.2 versions of the System.Text.Json NuGet
package , the properties of KeyValuePair<TKey,TValue> instances are always serialized
as "Key" and "Value" exactly, regardless of any
JsonSerializerOptions.PropertyNamingPolicy and JsonSerializerOptions.Encoder options.
The following code example shows how the Key and Value properties are not camelcased after serialization, even though the specified property-naming policy dictates so.
C#
var options = new JsonSerializerOptions { PropertyNamingPolicy =
JsonNamingPolicy.CamelCase };
KeyValuePair<int, int> kvp = KeyValuePair.Create(1, 1);
Console.WriteLine(JsonSerializer.Serialize(kvp, options));
// Expected: {"key":1,"value":1}
// Actual: {"Key":1,"Value":1}
Starting in .NET 5, the PropertyNamingPolicy and Encoder options are honored when
serializing KeyValuePair<TKey,TValue> instances. The following code example shows
how the Key and Value properties are camel-cased after serialization, in accordance with
the specified property-naming policy.
C#
var options = new JsonSerializerOptions { PropertyNamingPolicy =
JsonNamingPolicy.CamelCase };
KeyValuePair<int, int> kvp = KeyValuePair.Create(1, 1);
Console.WriteLine(JsonSerializer.Serialize(kvp, options));
// {"key":1,"value":1}
Deserialization
In .NET Core 3.x versions and in the 4.7.x versions of the System.Text.Json NuGet
package , a JsonException is thrown when the JSON property names are not precisely
Key and Value , for example, if they don't start with an uppercase letter. The exception is
thrown even if a specified property-naming policy expressly permits it.
C#
var options = new JsonSerializerOptions { PropertyNamingPolicy =
JsonNamingPolicy.CamelCase };
string json = @"{""key"":1,""value"":1}";
// Throws JsonException.
JsonSerializer.Deserialize<KeyValuePair<int, int>>(json, options);
Starting in .NET 5, the PropertyNamingPolicy and PropertyNameCaseInsensitive options
are honored when deserializing using JsonSerializer. For example, the following code
snippet shows successful deserialization of lowercased Key and Value property names
because the specified property-naming policy permits it.
C#
var options = new JsonSerializerOptions { PropertyNamingPolicy =
JsonNamingPolicy.CamelCase };
string json = @"{""key"":1,""value"":1}";
KeyValuePair<int, int> kvp = JsonSerializer.Deserialize<KeyValuePair<int,
int>>(json);
Console.WriteLine(kvp.Key); // 1
Console.WriteLine(kvp.Value); // 1
To accommodate payloads that were serialized with previous versions, "Key" and "Value"
are special-cased to match when deserializing. Even though the Key and Value property
names aren't camel-cased according to the PropertyNamingPolicy option in the
following code example, they deserialize successfully.
C#
var options = new JsonSerializerOptions { PropertyNamingPolicy =
JsonNamingPolicy.CamelCase };
string json = @"{""Key"":1,""Value"":1}";
KeyValuePair<int, int> kvp = JsonSerializer.Deserialize<KeyValuePair<int,
int>>(json);
Console.WriteLine(kvp.Key); // 1
Console.WriteLine(kvp.Value); // 1
Version introduced
5.0
Reason for change
Substantial customer feedback indicated that the PropertyNamingPolicy should be
honored. For completeness, the PropertyNameCaseInsensitive and Encoder options are
also honored, so that KeyValuePair<TKey,TValue> instances are treated the same as any
other plain old CLR object (POCO).
Recommended action
If this change is disruptive to you, you can use a custom converter that implements the
desired semantics.
Affected APIs
System.Text.Json.JsonSerializer.Serialize
System.Text.Json.JsonSerializer.SerializeToUtf8Bytes
System.Text.Json.JsonSerializer.SerializeAsync
System.Text.Json.JsonSerializer.Deserialize
System.Text.Json.JsonSerializer.DeserializeAsync
Native code can't access Windows
Forms objects
Article • 03/11/2022
Starting in .NET 5, you can no longer access Windows Forms objects from native code.
Change description
In previous .NET versions, some Windows Forms types were decorated as visible to COM
interop, and thus were accessible to native code. Starting in .NET 5, no Windows Forms
API are visible to COM interop or accessible to native code. The .NET runtime no longer
supports creating custom type libraries out of the box. In addition, the .NET runtime
can't depend on the type library for .NET Framework (which would require maintaining
the shape of classes as they were in .NET Framework).
Reason for change
Removal of ComVisible(true) from enumerations that were used for type library
(TLB file) generation and lookup: Since there is no WinForms TLB provided by .NET
Core, there's no value in keeping this attribute.
Removal of ComVisible(true) from AccessibleObject classes: The classes are not
CoCreateable (they have no parameterless constructor), and exposing an already
existing instance to COM does not require that attribute.
Removal of ComVisible(true) from Control and Component classes: This was used
to allow hosting of WinForms controls via OLE/ActiveX, for example in VB6 or MFC.
However, this requires a TLB for WinForms, which is no longer provided, as well as
registry-based activation, which also would not work out of the box. Generally,
there was no maintenance of COM-based hosting of WinForms controls, so
support was removed instead of leaving it in an unsupported state.
Removal of ClassInterface attributes from controls: If hosting via OLE/ActiveX is
not supported, these attributes aren't needed anymore. They are kept in other
places where objects are still exposed to COM and the attribute may be relevant.
Removal of ComVisible(true) from EventArgs : They were most likely used with
OLE/ActiveX hosting, which is no longer supported. They are not CoCreateable
either, so the attribute has no purpose. Also, exposing existing instances without
providing a TLB makes no sense.
Removal of ComVisible(true) from delegates: Purpose is unknown, but since
ActiveX hosting of WinForms Controls is no longer supported, it's unlikely to have
any usefulness.
Removal of ComVisible(true) from some non-public code: The only potential
consumer would be the new Visual Studio designer, but without a GUID specified,
it's unlikely that it's still needed.
Removal of ComVisible(true) from some arbitrary public designer classes: The old
Visual Studio designer may have been using COM interop to talk to these classes.
However, the old designer doesn't support .NET Core, so few people would need
these as ComVisible .
IWin32Window defined the same GUID that was defined in .NET Framework, which
has dangerous consequences. If you require interop with .NET Framework, use
ComImport .
The WinForms managed IDataObject was made ComVisible . This is not required,
there is a separate ComImport interface declaration for IDataObject COM interop.
It's counterproductive to have the managed IDataObject be ComVisible , since no
TLB is provided and marshalling will always fail. Also, the GUID was not specified
and differed from .NET Framework, so its unlikely that removing an undocumented
IID will affect customers negatively.
Removal of ComVisible(false) : Those are placed in seemingly arbitrary places and
are redundant when the default is to not expose classes to COM interop.
Version introduced
.NET 5.0
Recommended action
The following example works on .NET Framework and .NET Core 3.1. This example relies
on the .NET Framework type library, which allows the JavaScript to call back into the
form subclass via reflection.
C#
[PermissionSet(SecurityAction.Demand, Name="FullTrust")]
[System.Runtime.InteropServices.ComVisibleAttribute(true)]
public class Form1 : Form
{
private WebBrowser webBrowser1 = new WebBrowser();
protected override void OnLoad(EventArgs e)
{
webBrowser1.AllowWebBrowserDrop = false;
webBrowser1.IsWebBrowserContextMenuEnabled = false;
webBrowser1.WebBrowserShortcutsEnabled = false;
webBrowser1.ObjectForScripting = this;
webBrowser1.DocumentText =
"<html><body><button " +
"onclick=\"window.external.Test('called from script code')\">" +
"call client code from script code</button>" +
"</body></html>";
}
public void Test(String message)
{
MessageBox.Show(message, "client code");
}
}
There are two possible ways to make the example work on .NET 5 and later versions:
Introduce a user-declared ObjectForScripting object that supports IDispatch
(which is applied by default, unless changed explicitly at the project level).
C#
public class MyScriptObject
{
private Form1 _form;
public MyScriptObject(Form1 form)
{
_form = form;
}
public void Test(string message)
{
MessageBox.Show(message, "client code");
}
}
public partial class Form1 : Form
{
protected override void OnLoad(EventArgs e)
{
...
// Works correctly.
webBrowser1.ObjectForScripting = new MyScriptObject(this);
...
}
}
Declare an interface with the methods to expose.
C#
public interface IForm1
{
void Test(string message);
}
[ComDefaultInterface(typeof(IForm1))]
public partial class Form1 : Form, IForm1
{
protected override void OnLoad(EventArgs e)
{
...
// Works correctly.
webBrowser1.ObjectForScripting = this;
...
}
}
Affected APIs
All Windows Forms APIs.
OutputType set to WinExe for WPF and
WinForms apps
Article • 09/15/2021
OutputType is automatically set to WinExe for Windows Presentation Foundation (WPF)
and Windows Forms apps. When OutputType is set to WinExe , a console window doesn't
open when the app is executed.
Change description
In previous versions of the .NET SDK, the value that's specified for OutputType in the
project file is used. For example:
XML
<PropertyGroup>
<OutputType>Exe</OutputType>
</PropertyGroup>
Starting in the 5.0.100 version of the .NET SDK, when OutputType is set to Exe , it is
automatically changed to WinExe for WPF and Windows Forms apps that target any
framework version, including .NET Framework.
XML
<PropertyGroup>
<OutputType>WinExe</OutputType>
</PropertyGroup>
If OutputType is not specified in the project file, it defaults to Library and that value
doesn't change.
Reason for change
It's assumed that most users don't want a console window to open when a WPF or
Windows Forms app is executed. In addition, now that these application types use the
.NET SDK instead of the Windows Desktop SDK, the correct default will be set. Further,
when support for targeting iOS and Android is added, it will be easier to multi-target
between multiple platforms if they all use the same output type.
Version introduced
.NET SDK 5.0.100
Recommended action
No action is required in your part. However, if you want to revert to the old behavior, set
the DisableWinExeOutputInference property to true in your project file.
XML
<DisableWinExeOutputInference>true</DisableWinExeOutputInference>
Affected APIs
Not detectable via API analysis.
DataGridView no longer resets fonts for
customized cell styles
Article • 09/15/2021
When the ambient font changes, DataGridView no longer resets default cell style fonts
to match the ambient font if the cell style font has been customized.
Change description
In previous .NET versions, if the ambient font changes, DataGridView resets and
overrides user-defined fonts in the DefaultCellStyle, ColumnHeadersDefaultCellStyle,
and RowHeadersDefaultCellStyle properties.
Starting in .NET 5, if you configure font settings in the DefaultCellStyle,
ColumnHeadersDefaultCellStyle, or RowHeadersDefaultCellStyle properties, those
settings are retained even when the ambient font changes. For any of these properties
that you don't customize the font, the font will change to match the ambient font
settings.
Reason for change
With the change of the default font in .NET Core 3.0, the default font settings for the
various cell styles also changed. This behavior is undesirable for apps that rely on
custom styling in their DataGridView controls, and impeded the migration of these apps
from .NET Framework to .NET 5.0.
Version introduced
.NET 5.0
Recommended action
No action is required on your part. However, if you've customized the font in the
DefaultCellStyle, ColumnHeadersDefaultCellStyle, or RowHeadersDefaultCellStyle
properties and want the font to match the ambient font, set DataGridViewCellStyle.Font
to null for each property.
Affected APIs
System.Windows.Forms.DataGridView.DefaultCellStyle
System.Windows.Forms.DataGridView.ColumnHeadersDefaultCellStyle
System.Windows.Forms.DataGridView.RowHeadersDefaultCellStyle
WinForms methods now throw
ArgumentException
Article • 09/15/2021
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, 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
Recommended action
Update the code to prevent passing invalid arguments.
If necessary, handle an ArgumentException when calling the method.
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
Preview
1
TabPage.
Method
Parameter
name
Condition
Version
added
System.Windows.Forms.DataFormats.GetFormat(String)
format
Argument is
null ,
Preview
5
String.Empty,
or white
space.
InputLanguageChangedEventArgs(CultureInfo, Byte)
culture
Unable to
Preview
retrieve an
7
InputLanguage
for the
specified
culture.
WinForms methods now throw
ArgumentNullException
Article • 09/15/2021
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, 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
Recommended action
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.
Affected APIs
The following table lists the affected methods and parameters:
Method
Parameter
Version
name
added
Control.ControlCollection(Control)
owner
Preview 1
TabControl.GetToolTipText(Object)
item
Preview 1
Method
Parameter
name
Version
added
TableLayoutControlCollection(TableLayoutPanel)
container
Preview 1
ToolStripRenderer.OnRenderArrow(ToolStripArrowRenderEventArg
e
Preview 1
e
Preview 1
e
Preview 1
ToolStripRenderer.OnRenderItemText(ToolStripItemTextRenderEven
tArgs)
e
Preview 1
ToolStripRenderer.OnRenderStatusStripSizingGrip(ToolStripRender
EventArgs) >
e
Preview 1
DataGridViewComboBoxEditingControl.ApplyCellStyleToEditingCo
ntrol(DataGridViewCellStyle)
dataGridViewCe
Preview 2
llStyle
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, Strea
mingContext)
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
Preview 8
s)
ToolStripRenderer.OnRenderItemCheck(ToolStripItemImageRender
EventArgs)
ToolStripRenderer.OnRenderItemImage(ToolStripItemImageRender
EventArgs)
empty
Method
ListView.ListViewItemCollection.Find(String, Boolean)
Parameter
Version
name
added
key is null or
RC1
empty
ScrollableControl.OnPaintBackground(PaintEventArgs)
e
RC1
WinForms properties now throw
ArgumentOutOfRangeException
Article • 09/15/2021
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, 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
Recommended action
Update the code to prevent passing invalid arguments.
If necessary, handle an ArgumentOutOfRangeException when setting the property.
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
TextFormatFlags.ModifyString is
obsolete
Article • 09/15/2021
The TextFormatFlags.ModifyString field is obsolete, as a warning, and may be removed
in a future .NET version.
Change description
In previous .NET versions, the TextFormatFlags.ModifyString enumeration field is not
marked obsolete. In .NET 5 and later versions, it is marked obsolete as a warning. This
field may be removed in a future .NET version.
Reason for change
Passing a string to TextRenderer.MeasureText with TextFormatFlags.ModifyString alters
the string in some situations. This behavior breaks the string immutability promise and
may lead to a fatal .NET runtime state corruption.
Version introduced
.NET 5.0
Recommended action
Update any code that relies on TextFormatFlags.ModifyString.
Affected APIs
System.Windows.Forms.TextFormatFlags.ModifyString
DataGridView-related APIs throw
InvalidOperationException
Article • 09/15/2021
Some APIs related to DataGridView now throw an InvalidOperationException if the
object's DataGridViewCell.DataGridViewCellAccessibleObject.Owner value is null .
Change description
In previous .NET versions, the affected APIs throw a NullReferenceException when they
are invoked and the Owner property value is null . Starting in .NET 5, these APIs throw
an InvalidOperationException instead of a NullReferenceException if the Owner property
value is null when they're invoked.
Reason for change
Throwing an InvalidOperationException conforms to the behavior of the .NET runtime. It
also improves the debugging experience by clearly communicating the invalid property.
Version introduced
.NET 5.0
Recommended action
Review your code and, if necessary, update it to prevent constructing the affected types
with the Owner property as null .
Affected APIs
The following table lists the affected APIs:
Affected method or property
Validated
property
Version
added
DataGridViewButtonCell.DataGridViewButtonCellAccessibleObject.DoDe
Owner
5.0
faultAction()
Affected method or property
Validated
property
Version
added
DataGridViewCheckBoxCell.DataGridViewCheckBoxCellAccessibleObject.
DefaultAction
Owner
5.0
DataGridViewCheckBoxCell.DataGridViewCheckBoxCellAccessibleObject.
State
Owner
5.0
DataGridViewCheckBoxCell.DataGridViewCheckBoxCellAccessibleObject.
DoDefaultAction()
Owner
5.0
DataGridViewColumnHeaderCell.DataGridViewColumnHeaderCellAccess
ibleObject.Bounds
Owner
5.0
DataGridViewColumnHeaderCell.DataGridViewColumnHeaderCellAccess
Owner
5.0
Owner
5.0
Owner
5.0
Owner
5.0
Owner
5.0
Owner
5.0
Owner
5.0
ibleObject.DefaultAction
DataGridViewColumnHeaderCell.DataGridViewColumnHeaderCellAccess
ibleObject.Name
DataGridViewColumnHeaderCell.DataGridViewColumnHeaderCellAccess
ibleObject.Parent
DataGridViewColumnHeaderCell.DataGridViewColumnHeaderCellAccess
ibleObject.DoDefaultAction()
DataGridViewColumnHeaderCell.DataGridViewColumnHeaderCellAccess
ibleObject.Navigate(AccessibleNavigation)
DataGridViewImageCell.DataGridViewImageCellAccessibleObject.DoDef
aultAction()
DataGridViewLinkCell.DataGridViewLinkCellAccessibleObject.DoDefault
Action()
See also
DataGridView-related APIs throw InvalidOperationException (.NET 6)
WinForms and WPF apps use
Microsoft.NET.Sdk
Article • 09/15/2021
Windows Forms and Windows Presentation Framework (WPF) apps now use the .NET
SDK ( Microsoft.NET.Sdk ) instead of the .NET Core WinForms and WPF SDK
( Microsoft.NET.Sdk.WindowsDesktop ).
Change description
In previous .NET Core versions, WinForms and WPF apps used a separate project SDK
( Microsoft.NET.Sdk.WindowsDesktop ). Starting in .NET 5, the WinForms and WPF SDK has
been unified with the .NET SDK ( Microsoft.NET.Sdk ). In addition, new target framework
monikers (TFM) replace netcoreapp and netstandard in .NET 5. The following example
shows the changes you'd need to make for a WPF project file when retargeting to .NET
5 or later.
In previous .NET Core versions:
XML
<Project Sdk="Microsoft.NET.Sdk.WindowsDesktop">
<PropertyGroup>
<OutputType>WinExe</OutputType>
<TargetFramework>netcoreapp3.1</TargetFramework>
<UseWPF>true</UseWPF>
</PropertyGroup>
</Project>
In .NET 5 and later versions:
XML
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>WinExe</OutputType>
<TargetFramework>net5.0-windows</TargetFramework>
<UseWPF>true</UseWPF>
</PropertyGroup>
</Project>
Version introduced
.NET SDK 5.0.100
Recommended action
In your WPF or Windows Forms project file:
Update the Sdk attribute to Microsoft.NET.Sdk .
Update the TargetFramework property to net5.0-windows .
Affected APIs
None.
Removed status bar controls
Article • 09/15/2021
Starting in .NET 5, some Windows Forms controls are no longer available.
Change description
Starting with .NET 5, 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
Recommended action
Move to the replacement APIs for these controls and their scenarios:
Old Control (API)
Recommended Replacement
StatusBar
StatusStrip
StatusBarPanel
ToolStripStatusLabel
Affected APIs
System.Windows.Forms.StatusBar
System.Windows.Forms.StatusBarDrawItemEventArgs
System.Windows.Forms.StatusBarDrawItemEventHandler
System.Windows.Forms.StatusBarPanel
System.Windows.Forms.StatusBarPanelAutoSize
System.Windows.Forms.StatusBarPanelBorderStyle
System.Windows.Forms.StatusBarPanelClickEventArgs
System.Windows.Forms.StatusBarPanelClickEventHandler
System.Windows.Forms.StatusBarPanelStyle
OutputType set to WinExe for WPF and
WinForms apps
Article • 09/15/2021
OutputType is automatically set to WinExe for Windows Presentation Foundation (WPF)
and Windows Forms apps. When OutputType is set to WinExe , a console window doesn't
open when the app is executed.
Change description
In previous versions of the .NET SDK, the value that's specified for OutputType in the
project file is used. For example:
XML
<PropertyGroup>
<OutputType>Exe</OutputType>
</PropertyGroup>
Starting in the 5.0.100 version of the .NET SDK, when OutputType is set to Exe , it is
automatically changed to WinExe for WPF and Windows Forms apps that target any
framework version, including .NET Framework.
XML
<PropertyGroup>
<OutputType>WinExe</OutputType>
</PropertyGroup>
If OutputType is not specified in the project file, it defaults to Library and that value
doesn't change.
Reason for change
It's assumed that most users don't want a console window to open when a WPF or
Windows Forms app is executed. In addition, now that these application types use the
.NET SDK instead of the Windows Desktop SDK, the correct default will be set. Further,
when support for targeting iOS and Android is added, it will be easier to multi-target
between multiple platforms if they all use the same output type.
Version introduced
.NET SDK 5.0.100
Recommended action
No action is required in your part. However, if you want to revert to the old behavior, set
the DisableWinExeOutputInference property to true in your project file.
XML
<DisableWinExeOutputInference>true</DisableWinExeOutputInference>
Affected APIs
Not detectable via API analysis.
WinForms and WPF apps use
Microsoft.NET.Sdk
Article • 09/15/2021
Windows Forms and Windows Presentation Framework (WPF) apps now use the .NET
SDK ( Microsoft.NET.Sdk ) instead of the .NET Core WinForms and WPF SDK
( Microsoft.NET.Sdk.WindowsDesktop ).
Change description
In previous .NET Core versions, WinForms and WPF apps used a separate project SDK
( Microsoft.NET.Sdk.WindowsDesktop ). Starting in .NET 5, the WinForms and WPF SDK has
been unified with the .NET SDK ( Microsoft.NET.Sdk ). In addition, new target framework
monikers (TFM) replace netcoreapp and netstandard in .NET 5. The following example
shows the changes you'd need to make for a WPF project file when retargeting to .NET
5 or later.
In previous .NET Core versions:
XML
<Project Sdk="Microsoft.NET.Sdk.WindowsDesktop">
<PropertyGroup>
<OutputType>WinExe</OutputType>
<TargetFramework>netcoreapp3.1</TargetFramework>
<UseWPF>true</UseWPF>
</PropertyGroup>
</Project>
In .NET 5 and later versions:
XML
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>WinExe</OutputType>
<TargetFramework>net5.0-windows</TargetFramework>
<UseWPF>true</UseWPF>
</PropertyGroup>
</Project>
Version introduced
.NET SDK 5.0.100
Recommended action
In your WPF or Windows Forms project file:
Update the Sdk attribute to Microsoft.NET.Sdk .
Update the TargetFramework property to net5.0-windows .
Affected APIs
None.
Breaking changes in .NET Core 3.1
Article • 07/28/2023
If you're migrating to version 3.1 of .NET Core or ASP.NET Core, the breaking changes
listed in this article may affect your app.
ASP.NET Core
HTTP: Browser SameSite changes impact authentication
Some browsers, such as Chrome and Firefox, made breaking changes to their
implementations of SameSite for cookies. The changes impact remote authentication
scenarios, such as OpenID Connect and WS-Federation, which must opt out by sending
SameSite=None . However, SameSite=None breaks on iOS 12 and some older versions of
other browsers. The app needs to sniff these versions and omit SameSite .
For discussion on this issue, see dotnet/aspnetcore#14996
.
Version introduced
3.1 Preview 1
Old behavior
SameSite is a 2016 draft standard extension to HTTP cookies. It's intended to mitigate
Cross-Site Request Forgery (CSRF). This was originally designed as a feature the servers
would opt into by adding the new parameters. ASP.NET Core 2.0 added initial support
for SameSite .
New behavior
Google proposed a new draft standard that isn't backwards compatible. The standard
changes the default mode to Lax and adds a new entry None to opt out. Lax suffices for
most app cookies; however, it breaks cross-site scenarios like OpenID Connect and WSFederation login. Most OAuth logins aren't affected because of differences in how the
request flows. The new None parameter causes compatibility problems with clients that
implemented the prior draft standard (for example, iOS 12). Chrome 80 will include the
changes. See SameSite Updates
for the Chrome product launch timeline.
ASP.NET Core 3.1 has been updated to implement the new SameSite behavior. The
update redefines the behavior of SameSiteMode.None to emit SameSite=None and adds a
new value SameSiteMode.Unspecified to omit the SameSite attribute. All cookie APIs now
default to Unspecified , though some components that use cookies set values more
specific to their scenarios such as the OpenID Connect correlation and nonce cookies.
For other recent changes in this area, see HTTP: Some cookie SameSite defaults
changed to None. In ASP.NET Core 3.0, most defaults were changed from
SameSiteMode.Lax to SameSiteMode.None (but still using the prior standard).
Reason for change
Browser and specification changes as outlined in the preceding text.
Recommended action
Apps that interact with remote sites, such as through third-party login, need to:
Test those scenarios on multiple browsers.
Apply the cookie policy browser sniffing mitigation discussed in Support older
browsers.
For testing and browser sniffing instructions, see the following section.
Determine if you're affected
Test your web app using a client version that can opt into the new behavior. Chrome,
Firefox, and Microsoft Edge Chromium all have new opt-in feature flags that can be
used for testing. Verify that your app is compatible with older client versions after you've
applied the patches, especially Safari. For more information, see Support older browsers.
Chrome
Chrome 78 and later yield misleading test results. Those versions have a temporary
mitigation in place and allow cookies less than two minutes old. With the appropriate
test flags enabled, Chrome 76 and 77 yield more accurate results. To test the new
behavior, toggle chrome://flags/#same-site-by-default-cookies to enabled. Chrome 75
and earlier are reported to fail with the new None setting. For more information, see
Support older browsers.
Google doesn't make older Chrome versions available. You can, however, download
older versions of Chromium, which will suffice for testing. Follow the instructions at
Download Chromium
.
Chromium 76 Win64
Chromium 74 Win64
Safari
Safari 12 strictly implemented the prior draft and fails if it sees the new None value in
cookies. This must be avoided via the browser sniffing code shown in Support older
browsers. Ensure you test Safari 12 and 13 as well as WebKit-based, OS-style logins
using Microsoft Authentication Library (MSAL), Active Directory Authentication Library
(ADAL), or whichever library you're using. The problem is dependent on the underlying
OS version. OSX Mojave 10.14 and iOS 12 are known to have compatibility problems
with the new behavior. Upgrading to OSX Catalina 10.15 or iOS 13 fixes the problem.
Safari doesn't currently have an opt-in flag for testing the new specification behavior.
Firefox
Firefox support for the new standard can be tested on version 68 and later by opting in
on the about:config page with the feature flag network.cookie.sameSite.laxByDefault .
No compatibility issues have been reported on older versions of Firefox.
Microsoft Edge
While Microsoft Edge supports the old SameSite standard, as of version 44 it didn't have
any compatibility problems with the new standard.
Microsoft Edge Chromium
The feature flag is edge://flags/#same-site-by-default-cookies . No compatibility issues
were observed when testing with Microsoft Edge Chromium 78.
Electron
Versions of Electron include older versions of Chromium. For example, the version of
Electron used by Microsoft Teams is Chromium 66, which exhibits the older behavior.
Perform your own compatibility testing with the version of Electron your product uses.
For more information, see Support older browsers.
Support older browsers
The 2016 SameSite standard mandated that unknown values be treated as
SameSite=Strict values. Consequently, any older browsers that support the original
standard may break when they see a SameSite property with a value of None . Web apps
must implement browser sniffing if they intend to support these old browsers. ASP.NET
Core doesn't implement browser sniffing for you because User-Agent request header
values are highly unstable and change on a weekly basis. Instead, an extension point in
the cookie policy allows you to add User-Agent -specific logic.
In Startup.cs, add the following code:
C#
private void CheckSameSite(HttpContext httpContext, CookieOptions options)
{
if (options.SameSite == SameSiteMode.None)
{
var userAgent = httpContext.Request.Headers["UserAgent"].ToString();
// TODO: Use your User Agent library of choice here.
if (/* UserAgent doesn't support new behavior */)
{
options.SameSite = SameSiteMode.Unspecified;
}
}
}
public void ConfigureServices(IServiceCollection services)
{
services.Configure<CookiePolicyOptions>(options =>
{
options.MinimumSameSitePolicy = SameSiteMode.Unspecified;
options.OnAppendCookie = cookieContext =>
CheckSameSite(cookieContext.Context,
cookieContext.CookieOptions);
options.OnDeleteCookie = cookieContext =>
CheckSameSite(cookieContext.Context,
cookieContext.CookieOptions);
});
}
public void Configure(IApplicationBuilder app)
{
// Before UseAuthentication or anything else that writes cookies.
app.UseCookiePolicy();
app.UseAuthentication();
// code omitted for brevity
}
Opt-out switches
The Microsoft.AspNetCore.SuppressSameSiteNone compatibility switch enables you to
temporarily opt out of the new ASP.NET Core cookie behavior. Add the following JSON
to a runtimeconfig.template.json file in your project:
JSON
{
"configProperties": {
"Microsoft.AspNetCore.SuppressSameSiteNone": "true"
}
}
Other Versions
Related SameSite patches are forthcoming for:
ASP.NET Core 2.1, 2.2, and 3.0
Microsoft.Owin 4.1
System.Web (for .NET Framework 4.7.2 and later)
Category
ASP.NET
Affected APIs
Microsoft.AspNetCore.Builder.CookiePolicyOptions.MinimumSameSitePolicy
Microsoft.AspNetCore.Http.CookieBuilder.SameSite
Microsoft.AspNetCore.Http.CookieOptions.SameSite
Microsoft.AspNetCore.Http.SameSiteMode
Microsoft.Net.Http.Headers.SameSiteMode
Microsoft.Net.Http.Headers.SetCookieHeaderValue.SameSite
Deployment
x86 host path on 64-bit Windows
MSBuild
Design-time builds only return top-level package
references
Starting in .NET Core SDK 3.1.400, only top-level package references are returned by the
RunResolvePackageDependencies target.
Version introduced
.NET Core SDK 3.1.400
Change description
In previous versions of the .NET Core SDK, the RunResolvePackageDependencies target
created the following MSBuild items that contained information from the NuGet assets
file:
PackageDefinitions
PackageDependencies
TargetDefinitions
FileDefinitions
FileDependencies
This data is used by Visual Studio to populate the Dependencies node in Solution
Explorer. However, it can be a large amount of data, and the data isn't needed unless
the Dependencies node is expanded.
Starting in the .NET Core SDK version 3.1.400, most of these items aren't generated by
default. Only items of type Package are returned. If Visual Studio needs the items to
populate the Dependencies node, it reads the information directly from the assets file.
Reason for change
This changed was introduced to improve solution-load performance inside of Visual
Studio. Previously, all package references would be loaded, which involved loading
many references that most users would never view.
Recommended action
If you have MSBuild logic that depends on these items being created, set the
EmitLegacyAssetsFileItems property to true in your project file. This setting enables the
previous behavior where all the items are created.
Category
MSBuild
Affected APIs
N/A
SDK
Tool manifests in root folder
Windows Forms
Removed controls
Starting in .NET Core 3.1, some Windows Forms controls are no longer available.
Change description
Starting with .NET Core 3.1, various 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.
The following types are no longer available:
ContextMenu
DataGrid
DataGrid.HitTestType
DataGridBoolColumn
DataGridCell
DataGridColumnStyle
DataGridLineStyle
DataGridParentRowsLabelStyle
DataGridPreferredColumnWidthTypeConverter
DataGridTableStyle
DataGridTextBox
DataGridTextBoxColumn
GridColumnStylesCollection
GridTablesFactory
GridTableStylesCollection
IDataGridEditingService
IMenuEditorService
MainMenu
Menu
Menu.MenuItemCollection
MenuItem
ToolBar
ToolBarAppearance
ToolBarButton
ToolBar.ToolBarButtonCollection
ToolBarButtonClickEventArgs
ToolBarButtonStyle
ToolBarTextAlign
Version introduced
3.1
Recommended action
Each removed control has a recommended replacement control. Refer to the following
table:
Removed
control (API)
Recommended
replacement
ContextMenu
ContextMenuStrip
DataGrid
DataGridView
Associated APIs that are removed
DataGridCell, DataGridRow,
DataGridTableCollection,
DataGridColumnCollection, DataGridTableStyle,
DataGridColumnStyle, DataGridLineStyle,
DataGridParentRowsLabel,
DataGridParentRowsLabelStyle,
DataGridBoolColumn, DataGridTextBox,
GridColumnStylesCollection,
GridTableStylesCollection, HitTestType
MainMenu
MenuStrip
Menu
ToolStripDropDown,
ToolStripDropDownMenu
MenuItemCollection
Removed
control (API)
Recommended
replacement
Associated APIs that are removed
MenuItem
ToolStripMenuItem
ToolBar
ToolStrip
ToolBarAppearance
ToolBarButton
ToolStripButton
ToolBarButtonClickEventArgs,
ToolBarButtonClickEventHandler,
ToolBarButtonStyle, ToolBarTextAlign
Category
Windows Forms
Affected APIs
System.Windows.Forms.ContextMenu
System.Windows.Forms.GridColumnStylesCollection
System.Windows.Forms.GridTablesFactory
System.Windows.Forms.GridTableStylesCollection
System.Windows.Forms.IDataGridEditingService
System.Windows.Forms.MainMenu
System.Windows.Forms.Menu
System.Windows.Forms.Menu.MenuItemCollection
System.Windows.Forms.MenuItem
System.Windows.Forms.ToolBar
System.Windows.Forms.ToolBar.ToolBarButtonCollection
System.Windows.Forms.ToolBarAppearance
System.Windows.Forms.ToolBarButton
System.Windows.Forms.ToolBarButtonClickEventArgs
System.Windows.Forms.ToolBarButtonStyle
System.Windows.Forms.ToolBarTextAlign
System.Windows.Forms.DataGrid
System.Windows.Forms.DataGrid.HitTestType
System.Windows.Forms.DataGridBoolColumn
System.Windows.Forms.DataGridCell
System.Windows.Forms.DataGridColumnStyle
System.Windows.Forms.DataGridLineStyle
System.Windows.Forms.DataGridParentRowsLabelStyle
System.Windows.Forms.DataGridPreferredColumnWidthTypeConverter
System.Windows.Forms.DataGridTableStyle
System.Windows.Forms.DataGridTextBox
System.Windows.Forms.DataGridTextBoxColumn
System.Windows.Forms.Design.IMenuEditorService
CellFormatting event not raised if tooltip is shown
A DataGridView now shows a cell's text and error tooltips when hovered by a mouse
and when selected via the keyboard. If a tooltip is shown, the
DataGridView.CellFormatting event is not raised.
Change description
Prior to .NET Core 3.1, a DataGridView that had the ShowCellToolTips property set to
true showed a tooltip for a cell's text and errors when the cell was hovered by a mouse.
Tooltips were not shown when a cell was selected via the keyboard (for example, by
using the Tab key, shortcut keys, or arrow navigation). If the user edited a cell, and then,
while the DataGridView was still in edit mode, hovered over a cell that did not have the
ToolTipText property set, a CellFormatting event was raised to format the cell's text for
display in the cell.
To meet accessibility standards, starting in .NET Core 3.1, a DataGridView that has the
ShowCellToolTips property set to true shows tooltips for a cell's text and errors not only
when the cell is hovered, but also when it's selected via the keyboard. As a consequence
of this change, the CellFormatting event is not raised when cells that don't have the
ToolTipText property set are hovered while the DataGridView is in edit mode. The event
is not raised because the content of the hovered cell is shown as a tooltip instead of
being displayed in the cell.
Version introduced
3.1
Recommended action
Refactor any code that depends on the CellFormatting event while the DataGridView is
in edit mode.
Category
Windows Forms
Affected APIs
None
See also
What's new in .NET Core 3.1
Breaking changes in .NET Core 3.0
Article • 03/18/2023
If you're migrating to version 3.0 of .NET Core, ASP.NET Core, or EF Core, the breaking
changes listed in this article may affect your app.
ASP.NET Core
Obsolete Antiforgery, CORS, Diagnostics, MVC, and Routing APIs removed
"Pubternal" APIs removed
Authentication: Google+ deprecation
Authentication: HttpContext.Authentication property removed
Authentication: Newtonsoft.Json types replaced
Authentication: OAuthHandler ExchangeCodeAsync signature changed
Authorization: AddAuthorization overload moved to different assembly
Authorization: IAllowAnonymous removed from AuthorizationFilterContext.Filters
Authorization: IAuthorizationPolicyProvider implementations require new method
Caching: CompactOnMemoryPressure property removed
Caching: Microsoft.Extensions.Caching.SqlServer uses new SqlClient package
Caching: ResponseCaching "pubternal" types changed to internal
Data Protection: DataProtection.Blobs uses new Azure Storage APIs
Hosting: AspNetCoreModule V1 removed from Windows Hosting Bundle
Hosting: Generic host restricts Startup constructor injection
Hosting: HTTPS redirection enabled for IIS out-of-process apps
Hosting: IHostingEnvironment and IApplicationLifetime types replaced
Hosting: ObjectPoolProvider removed from WebHostBuilder dependencies
HTTP: DefaultHttpContext extensibility removed
HTTP: HeaderNames fields changed to static readonly
HTTP: Response body infrastructure changes
HTTP: Some cookie SameSite default values changed
HTTP: Synchronous IO disabled by default
Identity: AddDefaultUI method overload removed
Identity: UI Bootstrap version change
Identity: SignInAsync throws exception for unauthenticated identity
Identity: SignInManager constructor accepts new parameter
Identity: UI uses static web assets feature
Kestrel: Connection adapters removed
Kestrel: Empty HTTPS assembly removed
Kestrel: Request trailer headers moved to new collection
Kestrel: Transport abstraction layer changes
Localization: APIs marked obsolete
Logging: DebugLogger class made internal
MVC: Controller action Async suffix removed
MVC: JsonResult moved to Microsoft.AspNetCore.Mvc.Core
MVC: Precompilation tool deprecated
MVC: Types changed to internal
MVC: Web API compatibility shim removed
Razor: RazorTemplateEngine API removed
Razor: Runtime compilation moved to a package
Session state: Obsolete APIs removed
Shared framework: Assembly removal from Microsoft.AspNetCore.App
Shared framework: Microsoft.AspNetCore.All removed
SignalR: HandshakeProtocol.SuccessHandshakeData replaced
SignalR: HubConnection methods removed
SignalR: HubConnectionContext constructors changed
SignalR: JavaScript client package name change
SignalR: Obsolete APIs
SPAs: SpaServices and NodeServices marked obsolete
SPAs: SpaServices and NodeServices console logger fallback default change
Target framework: .NET Framework not supported
Obsolete Antiforgery, CORS, Diagnostics, MVC, and
Routing APIs removed
Obsolete members and compatibility switches in ASP.NET Core 2.2 were removed.
Version introduced
3.0
Reason for change
Improvement of API surface over time.
Recommended action
While targeting .NET Core 2.2, follow the guidance in the obsolete build messages to
adopt new APIs instead.
Category
ASP.NET Core
Affected APIs
The following types and members were marked as obsolete for ASP.NET Core 2.1 and
2.2:
Types
Microsoft.AspNetCore.Diagnostics.Views.WelcomePage
Microsoft.AspNetCore.DiagnosticsViewPage.Views.AttributeValue
Microsoft.AspNetCore.DiagnosticsViewPage.Views.BaseView
Microsoft.AspNetCore.DiagnosticsViewPage.Views.HelperResult
Microsoft.AspNetCore.Mvc.Formatters.Xml.ProblemDetails21Wrapper
Microsoft.AspNetCore.Mvc.Formatters.Xml.ValidationProblemDetails21Wrapper
Microsoft.AspNetCore.Mvc.Razor.Compilation.ViewsFeatureProvider
Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure.PageArgumentBinder
Microsoft.AspNetCore.Routing.IRouteValuesAddressMetadata
Microsoft.AspNetCore.Routing.RouteValuesAddressMetadata
Constructors
Microsoft.AspNetCore.Cors.Infrastructure.CorsService(IOptions{CorsOptions})
Microsoft.AspNetCore.Routing.Tree.TreeRouteBuilder(ILoggerFactory,UrlEncoder,O
bjectPool{UriBuildingContext},IInlineConstraintResolver)
Microsoft.AspNetCore.Mvc.Formatters.OutputFormatterCanWriteContext
Microsoft.AspNetCore.Mvc.ApiExplorer.DefaultApiDescriptionProvider(IOptions{Mv
cOptions},IInlineConstraintResolver,IModelMetadataProvider)
Microsoft.AspNetCore.Mvc.ApiExplorer.DefaultApiDescriptionProvider(IOptions{Mv
cOptions},IInlineConstraintResolver,IModelMetadataProvider,IActionResultTypeMa
pper)
Microsoft.AspNetCore.Mvc.Formatters.FormatFilter(IOptions{MvcOptions})
Microsoft.AspNetCore.Mvc.ModelBinding.Binders.ArrayModelBinder`1(IModelBinder
)
Microsoft.AspNetCore.Mvc.ModelBinding.Binders.ByteArrayModelBinder
Microsoft.AspNetCore.Mvc.ModelBinding.Binders.CollectionModelBinder`1(IModel
Binder)
Microsoft.AspNetCore.Mvc.ModelBinding.Binders.ComplexTypeModelBinder(IDicti
onary`2)
Microsoft.AspNetCore.Mvc.ModelBinding.Binders.DictionaryModelBinder`2(IModelBi
nder,IModelBinder)
Microsoft.AspNetCore.Mvc.ModelBinding.Binders.DoubleModelBinder(System.Globali
zation.NumberStyles)
Microsoft.AspNetCore.Mvc.ModelBinding.Binders.FloatModelBinder(System.Globaliz
ation.NumberStyles)
Microsoft.AspNetCore.Mvc.ModelBinding.Binders.FormCollectionModelBinder
Microsoft.AspNetCore.Mvc.ModelBinding.Binders.FormFileModelBinder
Microsoft.AspNetCore.Mvc.ModelBinding.Binders.HeaderModelBinder
Microsoft.AspNetCore.Mvc.ModelBinding.Binders.KeyValuePairModelBinder`2(IModel
Binder,IModelBinder)
Microsoft.AspNetCore.Mvc.ModelBinding.Binders.SimpleTypeModelBinder(System.Ty
pe)
Microsoft.AspNetCore.Mvc.ModelBinding.ModelAttributes(IEnumerable{System.Obje
ct})
Microsoft.AspNetCore.Mvc.ModelBinding.ModelAttributes(IEnumerable{System.Objec
t},IEnumerable{System.Object})
Microsoft.AspNetCore.Mvc.ModelBinding.ModelBinderFactory(IModelMetadataProvide
r,IOptions{MvcOptions})
Microsoft.AspNetCore.Mvc.ModelBinding.ParameterBinder(IModelMetadataProvider,I
ModelBinderFactory,IObjectModelValidator)
Microsoft.AspNetCore.Mvc.Routing.KnownRouteValueConstraint()
Microsoft.AspNetCore.Mvc.Formatters.XmlDataContractSerializerInputFormatter
Microsoft.AspNetCore.Mvc.Formatters.XmlDataContractSerializerInputFormatter(Sy
stem.Boolean)
Microsoft.AspNetCore.Mvc.Formatters.XmlDataContractSerializerInputFormatter(Mv
cOptions)
Microsoft.AspNetCore.Mvc.Formatters.XmlSerializerInputFormatter
Microsoft.AspNetCore.Mvc.Formatters.XmlSerializerInputFormatter(System.Boolea
n)
Microsoft.AspNetCore.Mvc.Formatters.XmlSerializerInputFormatter(MvcOptions)
Microsoft.AspNetCore.Mvc.TagHelpers.ImageTagHelper(IHostingEnvironment,IMe
moryCache,HtmlEncoder,IUrlHelperFactory)
Microsoft.AspNetCore.Mvc.TagHelpers.LinkTagHelper(IHostingEnvironment,IMemoryC
ache,HtmlEncoder,JavaScriptEncoder,IUrlHelperFactory)
Microsoft.AspNetCore.Mvc.TagHelpers.ScriptTagHelper(IHostingEnvironment,IMemor
yCache,HtmlEncoder,JavaScriptEncoder,IUrlHelperFactory)
Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure.RazorPageAdapter(RazorPage
Base)
Properties
Microsoft.AspNetCore.Antiforgery.AntiforgeryOptions.CookieDomain
Microsoft.AspNetCore.Antiforgery.AntiforgeryOptions.CookieName
Microsoft.AspNetCore.Antiforgery.AntiforgeryOptions.CookiePath
Microsoft.AspNetCore.Antiforgery.AntiforgeryOptions.RequireSsl
Microsoft.AspNetCore.Mvc.ApiBehaviorOptions.AllowInferringBindingSourceForColl
ectionTypesAsFromQuery
Microsoft.AspNetCore.Mvc.ApiBehaviorOptions.SuppressUseValidationProblemDetail
sForInvalidModelStateResponses
Microsoft.AspNetCore.Mvc.CookieTempDataProviderOptions.CookieName
Microsoft.AspNetCore.Mvc.CookieTempDataProviderOptions.Domain
Microsoft.AspNetCore.Mvc.CookieTempDataProviderOptions.Path
Microsoft.AspNetCore.Mvc.DataAnnotations.MvcDataAnnotationsLocalizationOptions
.AllowDataAnnotationsLocalizationForEnumDisplayAttributes
Microsoft.AspNetCore.Mvc.Formatters.Xml.MvcXmlOptions.AllowRfc7807CompliantPro
blemDetailsFormat
Microsoft.AspNetCore.Mvc.MvcOptions.AllowBindingHeaderValuesToNonStringModelT
ypes
Microsoft.AspNetCore.Mvc.MvcOptions.AllowCombiningAuthorizeFilters
Microsoft.AspNetCore.Mvc.MvcOptions.AllowShortCircuitingValidationWhenNoValida
torsArePresent
Microsoft.AspNetCore.Mvc.MvcOptions.AllowValidatingTopLevelNodes
Microsoft.AspNetCore.Mvc.MvcOptions.InputFormatterExceptionPolicy
Microsoft.AspNetCore.Mvc.MvcOptions.SuppressBindingUndefinedValueToEnumType
Microsoft.AspNetCore.Mvc.MvcViewOptions.AllowRenderingMaxLengthAttribute
Microsoft.AspNetCore.Mvc.MvcViewOptions.SuppressTempDataAttributePrefix
Microsoft.AspNetCore.Mvc.RazorPages.RazorPagesOptions.AllowAreas
Microsoft.AspNetCore.Mvc.RazorPages.RazorPagesOptions.AllowDefaultHandlingForO
ptionsRequests
Microsoft.AspNetCore.Mvc.RazorPages.RazorPagesOptions.AllowMappingHeadRequests
ToGetHandler
Methods
Microsoft.AspNetCore.Mvc.LocalRedirectResult.ExecuteResult(ActionContext)
Microsoft.AspNetCore.Mvc.RedirectResult.ExecuteResult(ActionContext)
Microsoft.AspNetCore.Mvc.RedirectToActionResult.ExecuteResult(ActionContext)
Microsoft.AspNetCore.Mvc.RedirectToPageResult.ExecuteResult(ActionContext)
Microsoft.AspNetCore.Mvc.RedirectToRouteResult.ExecuteResult(ActionContext)
Microsoft.AspNetCore.Mvc.ModelBinding.ParameterBinder.BindModelAsync(ActionCon
text,IValueProvider,ParameterDescriptor)
Microsoft.AspNetCore.Mvc.ModelBinding.ParameterBinder.BindModelAsync(Action
Context,IValueProvider,ParameterDescriptor,Object)
"Pubternal" APIs removed
To better maintain the public API surface of ASP.NET Core, most of the types in
*.Internal namespaces (referred to as "pubternal" APIs) have become truly internal.
Members in these namespaces were never meant to be supported as public-facing APIs.
The APIs could break in minor releases and often did. Code that depends on these APIs
breaks when updating to ASP.NET Core 3.0.
For more information, see dotnet/aspnetcore#4932
and dotnet/aspnetcore#11312 .
Version introduced
3.0
Old behavior
The affected APIs are marked with the public access modifier and exist in *.Internal
namespaces.
New behavior
The affected APIs are marked with the internal access modifier and can no longer be
used by code outside that assembly.
Reason for change
The guidance for these "pubternal" APIs was that they:
Could change without notice.
Weren't subject to .NET policies to prevent breaking changes.
Leaving the APIs public (even in the *.Internal namespaces) was confusing to
customers.
Recommended action
Stop using these "pubternal" APIs. If you have questions about alternate APIs, open an
issue in the dotnet/aspnetcore
repository.
For example, consider the following HTTP request buffering code in an ASP.NET Core 2.2
project. The EnableRewind extension method exists in the
Microsoft.AspNetCore.Http.Internal namespace.
C#
HttpContext.Request.EnableRewind();
In an ASP.NET Core 3.0 project, replace the EnableRewind call with a call to the
EnableBuffering extension method. The request buffering feature works as it did in the
past. EnableBuffering calls the now internal API.
C#
HttpContext.Request.EnableBuffering();
Category
ASP.NET Core
Affected APIs
All APIs in the Microsoft.AspNetCore.* and Microsoft.Extensions.* namespaces that
have an Internal segment in the namespace name. For example:
Microsoft.AspNetCore.Authentication.Internal
Microsoft.AspNetCore.Builder.Internal
Microsoft.AspNetCore.DataProtection.Cng.Internal
Microsoft.AspNetCore.DataProtection.Internal
Microsoft.AspNetCore.Hosting.Internal
Microsoft.AspNetCore.Http.Internal
Microsoft.AspNetCore.Mvc.Core.Infrastructure
Microsoft.AspNetCore.Mvc.Core.Internal
Microsoft.AspNetCore.Mvc.Cors.Internal
Microsoft.AspNetCore.Mvc.DataAnnotations.Internal
Microsoft.AspNetCore.Mvc.Formatters.Internal
Microsoft.AspNetCore.Mvc.Formatters.Json.Internal
Microsoft.AspNetCore.Mvc.Formatters.Xml.Internal
Microsoft.AspNetCore.Mvc.Internal
Microsoft.AspNetCore.Mvc.ModelBinding.Internal
Microsoft.AspNetCore.Mvc.Razor.Internal
Microsoft.AspNetCore.Mvc.RazorPages.Internal
Microsoft.AspNetCore.Mvc.TagHelpers.Internal
Microsoft.AspNetCore.Mvc.ViewFeatures.Internal
Microsoft.AspNetCore.Rewrite.Internal
Microsoft.AspNetCore.Routing.Internal
Microsoft.AspNetCore.Server.Kestrel.Core.Adapter.Internal
Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure
Microsoft.AspNetCore.Server.Kestrel.Https.Internal
Authentication: Google+ deprecated and replaced
Google is starting to shut down
Google+ Sign-in for apps as early as January 28, 2019.
Change description
ASP.NET 4.x and ASP.NET Core have been using the Google+ Sign-in APIs to
authenticate Google account users in web apps. The affected NuGet packages are
Microsoft.AspNetCore.Authentication.Google
Microsoft.Owin.Security.Google
for ASP.NET Core and
for Microsoft.Owin with ASP.NET Web Forms and
MVC.
Google's replacement APIs use a different data source and format. The mitigations and
solutions provided below account for the structural changes. Apps should verify the
data itself still satisfies their requirements. For example, names, email addresses, profile
links, and profile photos may provide subtly different values than before.
Version introduced
All versions. This change is external to ASP.NET Core.
Recommended action
Owin with ASP.NET Web Forms and MVC
For Microsoft.Owin 3.1.0 and later, a temporary mitigation is outlined here . Apps
should complete testing with the mitigation to check for changes in the data format.
There are plans to release Microsoft.Owin 4.0.1 with a fix. Apps using any prior version
should update to version 4.0.1.
ASP.NET Core 1.x
The mitigation in Owin with ASP.NET Web Forms and MVC can be adapted to ASP.NET
Core 1.x. NuGet package patches aren't planned because 1.x has reached end of life
status.
ASP.NET Core 2.x
For Microsoft.AspNetCore.Authentication.Google version 2.x, replace your existing call
to AddGoogle in Startup.ConfigureServices with the following code:
C#
.AddGoogle(o =>
{
o.ClientId = Configuration["Authentication:Google:ClientId"];
o.ClientSecret = Configuration["Authentication:Google:ClientSecret"];
o.UserInformationEndpoint =
"https://www.googleapis.com/oauth2/v2/userinfo";
o.ClaimActions.Clear();
o.ClaimActions.MapJsonKey(ClaimTypes.NameIdentifier, "id");
o.ClaimActions.MapJsonKey(ClaimTypes.Name, "name");
o.ClaimActions.MapJsonKey(ClaimTypes.GivenName, "given_name");
o.ClaimActions.MapJsonKey(ClaimTypes.Surname, "family_name");
o.ClaimActions.MapJsonKey("urn:google:profile", "link");
o.ClaimActions.MapJsonKey(ClaimTypes.Email, "email");
});
The February 2.1 and 2.2 patches incorporated the preceding reconfiguration as the new
default. No patch is planned for ASP.NET Core 2.0 since it has reached end of life .
ASP.NET Core 3.0
The mitigation given for ASP.NET Core 2.x can also be used for ASP.NET Core 3.0. In
future 3.0 previews, the Microsoft.AspNetCore.Authentication.Google package may be
removed. Users would be directed to
Microsoft.AspNetCore.Authentication.OpenIdConnect instead. The following code shows
how to replace AddGoogle with AddOpenIdConnect in Startup.ConfigureServices . This
replacement can be used with ASP.NET Core 2.0 and later and can be adapted for
ASP.NET Core 1.x as needed.
C#
.AddOpenIdConnect("Google", o =>
{
o.ClientId = Configuration["Authentication:Google:ClientId"];
o.ClientSecret = Configuration["Authentication:Google:ClientSecret"];
o.Authority = "https://accounts.google.com";
o.ResponseType = OpenIdConnectResponseType.Code;
o.CallbackPath = "/signin-google"; // Or register the default "/signinoidc"
o.Scope.Add("email");
});
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();
Category
ASP.NET Core
Affected APIs
Microsoft.AspNetCore.Authentication.Google
Authentication: HttpContext.Authentication property
removed
The deprecated Authentication property on HttpContext has been removed.
Change description
As part of dotnet/aspnetcore#6504
, the deprecated Authentication property on
HttpContext has been removed. The Authentication property has been deprecated
since 2.0. A migration guide was published to migrate code using this deprecated
property to the new replacement APIs. The remaining unused classes / APIs related to
the old ASP.NET Core 1.x authentication stack were removed in commit
dotnet/aspnetcore@d7a7c65 .
For discussion, see dotnet/aspnetcore#6533
.
Version introduced
3.0
Reason for change
ASP.NET Core 1.0 APIs have been replaced by extension methods in
Microsoft.AspNetCore.Authentication.AuthenticationHttpContextExtensions.
Recommended action
See the migration guide.
Category
ASP.NET Core
Affected APIs
Microsoft.AspNetCore.Http.Authentication.AuthenticateInfo
Microsoft.AspNetCore.Http.Authentication.AuthenticationManager
Microsoft.AspNetCore.Http.Authentication.AuthenticationProperties
Microsoft.AspNetCore.Http.Features.Authentication.AuthenticateContext
Microsoft.AspNetCore.Http.Features.Authentication.ChallengeBehavior
Microsoft.AspNetCore.Http.Features.Authentication.ChallengeContext
Microsoft.AspNetCore.Http.Features.Authentication.DescribeSchemesContext
Microsoft.AspNetCore.Http.Features.Authentication.IAuthenticationHandler
Microsoft.AspNetCore.Http.Features.Authentication.IHttpAuthenticationFeature.Ha
ndler
Microsoft.AspNetCore.Http.Features.Authentication.SignInContext
Microsoft.AspNetCore.Http.Features.Authentication.SignOutContext
Microsoft.AspNetCore.Http.HttpContext.Authentication
Authentication: Newtonsoft.Json types replaced
In ASP.NET Core 3.0, Newtonsoft.Json types used in Authentication APIs have been
replaced with System.Text.Json types. Except for the following cases, basic usage of the
Authentication packages remains unaffected:
Classes derived from the OAuth providers, such as those from aspnet-contrib .
Advanced claim manipulation implementations.
For more information, see dotnet/aspnetcore#7105
dotnet/aspnetcore#7289
. For discussion, see
.
Version introduced
3.0
Recommended action
For derived OAuth implementations, the most common change is to replace
JObject.Parse with JsonDocument.Parse in the CreateTicketAsync override as shown
here . JsonDocument implements IDisposable .
The following list outlines known changes:
ClaimAction.Run(JObject, ClaimsIdentity, String) becomes
ClaimAction.Run(JsonElement userData, ClaimsIdentity identity, string issuer) .
All derived implementations of ClaimAction are similarly affected.
ClaimActionCollectionMapExtensions.MapCustomJson(ClaimActionCollection,
String, Func<JObject,String>) becomes MapCustomJson(this ClaimActionCollection
collection, string claimType, Func<JsonElement, string> resolver)
ClaimActionCollectionMapExtensions.MapCustomJson(ClaimActionCollection,
String, String, Func<JObject,String>) becomes MapCustomJson(this
ClaimActionCollection collection, string claimType, string valueType,
Func<JsonElement, string> resolver)
OAuthCreatingTicketContext has had one old constructor removed and the other
replaced JObject with JsonElement . The User property and RunClaimActions
method have been updated to match.
Success(JObject) now accepts a parameter of type JsonDocument instead of
JObject . The Response property has been updated to match. OAuthTokenResponse
is now disposable and will be disposed by OAuthHandler . Derived OAuth
implementations overriding ExchangeCodeAsync don't need to dispose the
JsonDocument or OAuthTokenResponse .
UserInformationReceivedContext.User changed from JObject to JsonDocument .
TwitterCreatingTicketContext.User changed from JObject to JsonElement .
The last parameter of
TwitterHandler.CreateTicketAsync(ClaimsIdentity,AuthenticationProperties,AccessTo
ken,JObject) changed from JObject to JsonElement . The replacement method is
TwitterHandler.CreateTicketAsync(ClaimsIdentity, AuthenticationProperties,
AccessToken, JsonElement).
Category
ASP.NET Core
Affected APIs
Microsoft.AspNetCore.Authentication.Facebook
Microsoft.AspNetCore.Authentication.Google
Microsoft.AspNetCore.Authentication.MicrosoftAccount
Microsoft.AspNetCore.Authentication.OAuth
Microsoft.AspNetCore.Authentication.OpenIdConnect
Microsoft.AspNetCore.Authentication.Twitter
Authentication: OAuthHandler ExchangeCodeAsync
signature changed
In ASP.NET Core 3.0, the signature of OAuthHandler.ExchangeCodeAsync was changed
from:
C#
protected virtual
System.Threading.Tasks.Task<Microsoft.AspNetCore.Authentication.OAuth.OAuthT
okenResponse> ExchangeCodeAsync(string code, string redirectUri) { throw
null; }
To:
C#
protected virtual
System.Threading.Tasks.Task<Microsoft.AspNetCore.Authentication.OAuth.OAuthT
okenResponse>
ExchangeCodeAsync(Microsoft.AspNetCore.Authentication.OAuth.OAuthCodeExchang
eContext context) { throw null; }
Version introduced
3.0
Old behavior
The code and redirectUri strings were passed as separate arguments.
New behavior
Code and RedirectUri are properties on OAuthCodeExchangeContext that can be set via
the OAuthCodeExchangeContext constructor. The new OAuthCodeExchangeContext type is
the only argument passed to OAuthHandler.ExchangeCodeAsync .
Reason for change
This change allows additional parameters to be provided in a non-breaking manner.
There's no need to create new ExchangeCodeAsync overloads.
Recommended action
Construct an OAuthCodeExchangeContext with the appropriate code and redirectUri
values. An AuthenticationProperties instance must be provided. This single
OAuthCodeExchangeContext instance can be passed to OAuthHandler.ExchangeCodeAsync
instead of multiple arguments.
Category
ASP.NET Core
Affected APIs
OAuthHandler<TOptions>.ExchangeCodeAsync(String, String)
Authorization: AddAuthorization overload moved to
different assembly
The core AddAuthorization methods that used to reside in
Microsoft.AspNetCore.Authorization were renamed to AddAuthorizationCore . The old
AddAuthorization methods still exist, but are in the
Microsoft.AspNetCore.Authorization.Policy assembly instead. Apps using both
methods should see no impact. Note that Microsoft.AspNetCore.Authorization.Policy
now ships in the shared framework rather than a standalone package as discussed in
Shared framework: Assemblies removed from Microsoft.AspNetCore.App.
Version introduced
3.0
Old behavior
AddAuthorization methods existed in Microsoft.AspNetCore.Authorization .
New behavior
AddAuthorization methods exist in Microsoft.AspNetCore.Authorization.Policy .
AddAuthorizationCore is the new name for the old methods.
Reason for change
AddAuthorization is a better method name for adding all common services needed for
authorization.
Recommended action
Either add a reference to Microsoft.AspNetCore.Authorization.Policy or use
AddAuthorizationCore instead.
Category
ASP.NET Core
Affected APIs
Microsoft.Extensions.DependencyInjection.AuthorizationServiceCollectionExtensions.Add
Authorization(IServiceCollection, Action<AuthorizationOptions>)
Authorization: IAllowAnonymous removed from
AuthorizationFilterContext.Filters
As of ASP.NET Core 3.0, MVC doesn't add AllowAnonymousFilters for [AllowAnonymous]
attributes that were discovered on controllers and action methods. This change is
addressed locally for derivatives of AuthorizeAttribute, but it's a breaking change for
IAsyncAuthorizationFilter and IAuthorizationFilter implementations. Such
implementations wrapped in a [TypeFilter] attribute are a popular
and supported way
to achieve strongly-typed, attribute-based authorization when both configuration and
dependency injection are required.
Version introduced
3.0
Old behavior
IAllowAnonymous appeared in the AuthorizationFilterContext.Filters collection. Testing
for the interface's presence was a valid approach to override or disable the filter on
individual controller methods.
New behavior
IAllowAnonymous no longer appears in the AuthorizationFilterContext.Filters
collection. IAsyncAuthorizationFilter implementations that are dependent on the old
behavior typically cause intermittent HTTP 401 Unauthorized or HTTP 403 Forbidden
responses.
Reason for change
A new endpoint routing strategy was introduced in ASP.NET Core 3.0.
Recommended action
Search the endpoint metadata for IAllowAnonymous . For example:
C#
var endpoint = context.HttpContext.GetEndpoint();
if (endpoint?.Metadata?.GetMetadata<IAllowAnonymous>() != null)
{
}
An example of this technique is seen in this HasAllowAnonymous method .
Category
ASP.NET Core
Affected APIs
None
Authorization: IAuthorizationPolicyProvider
implementations require new method
In ASP.NET Core 3.0, a new GetFallbackPolicyAsync method was added to
IAuthorizationPolicyProvider . This fallback policy is used by the authorization
middleware when no policy is specified.
For more information, see dotnet/aspnetcore#9759
.
Version introduced
3.0
Old behavior
Implementations of IAuthorizationPolicyProvider didn't require a
GetFallbackPolicyAsync method.
New behavior
Implementations of IAuthorizationPolicyProvider require a GetFallbackPolicyAsync
method.
Reason for change
A new method was needed for the new AuthorizationMiddleware to use when no policy
is specified.
Recommended action
Add the GetFallbackPolicyAsync method to your implementations of
IAuthorizationPolicyProvider .
Category
ASP.NET Core
Affected APIs
Microsoft.AspNetCore.Authorization.IAuthorizationPolicyProvider
Caching: CompactOnMemoryPressure property removed
The ASP.NET Core 3.0 release removed the obsolete MemoryCacheOptions APIs .
Change description
This change is a follow-up to aspnet/Caching#221 . For discussion, see
dotnet/extensions#1062 .
Version introduced
3.0
Old behavior
MemoryCacheOptions.CompactOnMemoryPressure property was available.
New behavior
The MemoryCacheOptions.CompactOnMemoryPressure property has been removed.
Reason for change
Automatically compacting the cache caused problems. To avoid unexpected behavior,
the cache should only be compacted when needed.
Recommended action
To compact the cache, downcast to MemoryCache and call Compact when needed.
Category
ASP.NET Core
Affected APIs
MemoryCacheOptions.CompactOnMemoryPressure
Caching: Microsoft.Extensions.Caching.SqlServer uses
new SqlClient package
The Microsoft.Extensions.Caching.SqlServer package will use the new
Microsoft.Data.SqlClient package instead of System.Data.SqlClient package. This
change could cause slight behavioral breaking changes. For more information, see
Introducing the new Microsoft.Data.SqlClient .
Version introduced
3.0
Old behavior
The Microsoft.Extensions.Caching.SqlServer package used the System.Data.SqlClient
package.
New behavior
Microsoft.Extensions.Caching.SqlServer is now using the Microsoft.Data.SqlClient
package.
Reason for change
Microsoft.Data.SqlClient is a new package that is built off of System.Data.SqlClient .
It's where all new feature work will be done from now on.
Recommended action
Customers shouldn't need to worry about this breaking change unless they were using
types returned by the Microsoft.Extensions.Caching.SqlServer package and casting
them to System.Data.SqlClient types. For example, if someone was casting a
DbConnection to the old SqlConnection type, they would need to change the cast to the
new Microsoft.Data.SqlClient.SqlConnection type.
Category
ASP.NET Core
Affected APIs
None
Caching: ResponseCaching "pubternal" types changed to
internal
In ASP.NET Core 3.0, "pubternal" types in ResponseCaching have been changed to
internal .
In addition, default implementations of IResponseCachingPolicyProvider and
IResponseCachingKeyProvider are no longer added to services as part of the
AddResponseCaching method.
Change description
In ASP.NET Core, "pubternal" types are declared as public but reside in a namespace
suffixed with .Internal . While these types are public, they have no support policy and
are subject to breaking changes. Unfortunately, accidental use of these types has been
common, resulting in breaking changes to these projects and limiting the ability to
maintain the framework.
Version introduced
3.0
Old behavior
These types were publicly visible, but unsupported.
New behavior
These types are now internal .
Reason for change
The internal scope better reflects the unsupported policy.
Recommended action
Copy types that are used by your app or library.
Category
ASP.NET Core
Affected APIs
Microsoft.AspNetCore.ResponseCaching.Internal.CachedResponse
Microsoft.AspNetCore.ResponseCaching.Internal.CachedVaryByRules
Microsoft.AspNetCore.ResponseCaching.Internal.IResponseCache
Microsoft.AspNetCore.ResponseCaching.Internal.IResponseCacheEntry
Microsoft.AspNetCore.ResponseCaching.Internal.IResponseCachingKeyProvider
Microsoft.AspNetCore.ResponseCaching.Internal.IResponseCachingPolicyProvider
Microsoft.AspNetCore.ResponseCaching.Internal.MemoryResponseCache
Microsoft.AspNetCore.ResponseCaching.Internal.ResponseCachingContext
Microsoft.AspNetCore.ResponseCaching.Internal.ResponseCachingKeyProvider
Microsoft.AspNetCore.ResponseCaching.Internal.ResponseCachingPolicyProvider
Microsoft.AspNetCore.ResponseCaching.ResponseCachingMiddleware.ResponseCa
chingMiddleware(RequestDelegate, IOptions<ResponseCachingOptions>,
ILoggerFactory, IResponseCachingPolicyProvider, IResponseCache,
IResponseCachingKeyProvider)
Data Protection: DataProtection.Blobs uses new Azure
Storage APIs
Azure.Extensions.AspNetCore.DataProtection.Blobs depends on the Azure Storage
libraries
. These libraries renamed their assemblies, packages, and namespaces.
Starting in ASP.NET Core 3.0, Azure.Extensions.AspNetCore.DataProtection.Blobs uses
the new Azure.Storage. -prefixed APIs and packages.
For questions about the Azure Storage APIs, use https://github.com/Azure/azurestorage-net . For discussion on this issue, see dotnet/aspnetcore#19570
.
Version introduced
3.0
Old behavior
The package referenced the WindowsAzure.Storage NuGet package. The package
references the Microsoft.Azure.Storage.Blob NuGet package.
New behavior
The package references the Azure.Storage.Blob NuGet package.
Reason for change
This change allows Azure.Extensions.AspNetCore.DataProtection.Blobs to migrate to
the recommended Azure Storage packages.
Recommended action
If you still need to use the older Azure Storage APIs with ASP.NET Core 3.0, add a direct
dependency to the package WindowsAzure.Storage
or Microsoft.Azure.Storage .
This package can be installed alongside the new Azure.Storage APIs.
In many cases, the upgrade only involves changing the using statements to use the new
namespaces:
diff
+
+
using
using
using
using
using
using
Microsoft.WindowsAzure.Storage;
Microsoft.WindowsAzure.Storage.Blob;
Microsoft.Azure.Storage;
Microsoft.Azure.Storage.Blob;
Azure.Storage;
Azure.Storage.Blobs;
Category
ASP.NET Core
Affected APIs
None
Hosting: AspNetCoreModule V1 removed from Windows
Hosting Bundle
Starting with ASP.NET Core 3.0, the Windows Hosting Bundle won't contain
AspNetCoreModule (ANCM) V1.
ANCM V2 is backwards compatible with ANCM OutOfProcess and is recommended for
use with ASP.NET Core 3.0 apps.
For discussion, see dotnet/aspnetcore#7095
.
Version introduced
3.0
Old behavior
ANCM V1 is included in the Windows Hosting Bundle.
New behavior
ANCM V1 isn't included in the Windows Hosting Bundle.
Reason for change
ANCM V2 is backwards compatible with ANCM OutOfProcess and is recommended for
use with ASP.NET Core 3.0 apps.
Recommended action
Use ANCM V2 with ASP.NET Core 3.0 apps.
If ANCM V1 is required, it can be installed using the ASP.NET Core 2.1 or 2.2 Windows
Hosting Bundle.
This change will break ASP.NET Core 3.0 apps that:
Explicitly opted into using ANCM V1 with
<AspNetCoreModuleName>AspNetCoreModule</AspNetCoreModuleName> .
Have a custom web.config file with <add name="aspNetCore" path="*" verb="*"
modules="AspNetCoreModule" resourceType="Unspecified" /> .
Category
ASP.NET Core
Affected APIs
None
Hosting: Generic host restricts Startup constructor
injection
The only types the generic host supports for Startup class constructor injection are
IHostEnvironment , IWebHostEnvironment , and IConfiguration . Apps using WebHost are
unaffected.
Change description
Prior to ASP.NET Core 3.0, constructor injection could be used for arbitrary types in the
Startup class's constructor. In ASP.NET Core 3.0, the web stack was replatformed onto
the generic host library. You can see the change in the Program.cs file of the templates:
ASP.NET Core 2.x:
https://github.com/dotnet/aspnetcore/blob/5cb615fcbe8559e49042e93394008077e304
54c0/src/Templating/src/Microsoft.DotNet.Web.ProjectTemplates/content/EmptyWebCSharp/Program.cs#L20-L22
ASP.NET Core 3.0:
https://github.com/dotnet/aspnetcore/blob/b1ca2c1155da3920f0df5108b9fedbe82efaa
11c/src/ProjectTemplates/Web.ProjectTemplates/content/EmptyWebCSharp/Program.cs#L19-L24
Host uses one dependency injection (DI) container to build the app. WebHost uses two
containers: one for the host and one for the app. As a result, the Startup constructor no
longer supports custom service injection. Only IHostEnvironment , IWebHostEnvironment ,
and IConfiguration can be injected. This change prevents DI issues such as the
duplicate creation of a singleton service.
Version introduced
3.0
Reason for change
This change is a consequence of replatforming the web stack onto the generic host
library.
Recommended action
Inject services into the Startup.Configure method signature. For example:
C#
public void Configure(IApplicationBuilder app, IOptions<MyOptions> options)
Category
ASP.NET Core
Affected APIs
None
Hosting: HTTPS redirection enabled for IIS out-of-process
apps
Version 13.0.19218.0 of the ASP.NET Core Module (ANCM) for hosting via IIS out-ofprocess enables an existing HTTPS redirection feature for ASP.NET Core 3.0 and 2.2
apps.
For discussion, see dotnet/AspNetCore#15243 .
Version introduced
3.0
Old behavior
The ASP.NET Core 2.1 project template first introduced support for HTTPS middleware
methods like UseHttpsRedirection and UseHsts. Enabling HTTPS redirection required the
addition of configuration, since apps in development don't use the default port of 443.
HTTP Strict Transport Security (HSTS)
is active only if the request is already using
HTTPS. Localhost is skipped by default.
New behavior
In ASP.NET Core 3.0, the IIS HTTPS scenario was enhanced . With the enhancement, an
app could discover the server's HTTPS ports and make UseHttpsRedirection work by
default. The in-process component accomplished port discovery with the
IServerAddresses feature, which only affects ASP.NET Core 3.0 apps because the in-
process library is versioned with the framework. The out-of-process component
changed to automatically add the ASPNETCORE_HTTPS_PORT environment variable. This
change affected both ASP.NET Core 2.2 and 3.0 apps because the out-of-process
component is shared globally. ASP.NET Core 2.1 apps aren't affected because they use a
prior version of ANCM by default.
The preceding behavior was modified in ASP.NET Core 3.0.1 and 3.1.0 Preview 3 to
reverse the behavior changes in ASP.NET Core 2.x. These changes only affect IIS out-ofprocess apps.
As detailed above, installing ASP.NET Core 3.0.0 had the side effect of also activating the
UseHttpsRedirection middleware in ASP.NET Core 2.x apps. A change was made to
ANCM in ASP.NET Core 3.0.1 and 3.1.0 Preview 3 such that installing them no longer has
this effect on ASP.NET Core 2.x apps. The ASPNETCORE_HTTPS_PORT environment variable
that ANCM populated in ASP.NET Core 3.0.0 was changed to
ASPNETCORE_ANCM_HTTPS_PORT in ASP.NET Core 3.0.1 and 3.1.0 Preview 3.
UseHttpsRedirection was also updated in these releases to understand both the new
and old variables. ASP.NET Core 2.x won't be updated. As a result, it reverts to the
previous behavior of being disabled by default.
Reason for change
Improved ASP.NET Core 3.0 functionality.
Recommended action
No action is required if you want all clients to use HTTPS. To allow some clients to use
HTTP, take one of the following steps:
Remove the calls to UseHttpsRedirection and UseHsts from your project's
Startup.Configure method, and redeploy the app.
In your web.config file, set the ASPNETCORE_HTTPS_PORT environment variable to an
empty string. This change can occur directly on the server without redeploying the
app. For example:
XML
<aspNetCore processPath="dotnet" arguments=".\WebApplication3.dll"
stdoutLogEnabled="false" stdoutLogFile="\\?\%home%\LogFiles\stdout" >
<environmentVariables>
<environmentVariable name="ASPNETCORE_HTTPS_PORT" value="" />
</environmentVariables>
</aspNetCore>
UseHttpsRedirection can still be:
Activated manually in ASP.NET Core 2.x by setting the ASPNETCORE_HTTPS_PORT
environment variable to the appropriate port number (443 in most production
scenarios).
Deactivated in ASP.NET Core 3.x by defining ASPNETCORE_ANCM_HTTPS_PORT with an
empty string value. This value is set in the same fashion as the preceding
ASPNETCORE_HTTPS_PORT example.
Machines running ASP.NET Core 3.0.0 apps should install the ASP.NET Core 3.0.1
runtime before installing the ASP.NET Core 3.1.0 Preview 3 ANCM. Doing so ensures
that UseHttpsRedirection continues to operate as expected for the ASP.NET Core 3.0
apps.
In Azure App Service, ANCM deploys on a separate schedule from the runtime because
of its global nature. ANCM was deployed to Azure with these changes after ASP.NET
Core 3.0.1 and 3.1.0 were deployed.
Category
ASP.NET Core
Affected APIs
HttpsPolicyBuilderExtensions.UseHttpsRedirection(IApplicationBuilder)
Hosting: IHostingEnvironment and IApplicationLifetime
types marked obsolete and replaced
New types have been introduced to replace existing IHostingEnvironment and
IApplicationLifetime types.
Version introduced
3.0
Old behavior
There were two different IHostingEnvironment and IApplicationLifetime types from
Microsoft.Extensions.Hosting and Microsoft.AspNetCore.Hosting .
New behavior
The old types have been marked as obsolete and replaced with new types.
Reason for change
When Microsoft.Extensions.Hosting was introduced in ASP.NET Core 2.1, some types
like IHostingEnvironment and IApplicationLifetime were copied from
Microsoft.AspNetCore.Hosting . Some ASP.NET Core 3.0 changes cause apps to include
both the Microsoft.Extensions.Hosting and Microsoft.AspNetCore.Hosting namespaces.
Any use of those duplicate types causes an "ambiguous reference" compiler error when
both namespaces are referenced.
Recommended action
Replaced any usages of the old types with the newly introduced types as below:
Obsolete types (warning):
Microsoft.Extensions.Hosting.IHostingEnvironment
Microsoft.AspNetCore.Hosting.IHostingEnvironment
Microsoft.Extensions.Hosting.IApplicationLifetime
Microsoft.AspNetCore.Hosting.IApplicationLifetime
Microsoft.Extensions.Hosting.EnvironmentName
Microsoft.AspNetCore.Hosting.EnvironmentName
New types:
Microsoft.Extensions.Hosting.IHostEnvironment
Microsoft.AspNetCore.Hosting.IWebHostEnvironment : IHostEnvironment
Microsoft.Extensions.Hosting.IHostApplicationLifetime
Microsoft.Extensions.Hosting.Environments
The new IHostEnvironment IsDevelopment and IsProduction extension methods are in
the Microsoft.Extensions.Hosting namespace. That namespace may need to be added
to your project.
Category
ASP.NET Core
Affected APIs
Microsoft.AspNetCore.Hosting.EnvironmentName
Microsoft.AspNetCore.Hosting.IApplicationLifetime
Microsoft.AspNetCore.Hosting.IHostingEnvironment
Microsoft.Extensions.Hosting.EnvironmentName
Microsoft.Extensions.Hosting.IApplicationLifetime
Microsoft.Extensions.Hosting.IHostingEnvironment
Hosting: ObjectPoolProvider removed from
WebHostBuilder dependencies
As part of making ASP.NET Core more pay for play, the ObjectPoolProvider was
removed from the main set of dependencies. Specific components relying on
ObjectPoolProvider now add it themselves.
For discussion, see dotnet/aspnetcore#5944
Version introduced
.
3.0
Old behavior
WebHostBuilder provides ObjectPoolProvider by default in the DI container.
New behavior
WebHostBuilder no longer provides ObjectPoolProvider by default in the DI container.
Reason for change
This change was made to make ASP.NET Core more pay for play.
Recommended action
If your component requires ObjectPoolProvider , it needs to be added to your
dependencies via the IServiceCollection .
Category
ASP.NET Core
Affected APIs
None
HTTP: DefaultHttpContext extensibility removed
As part of ASP.NET Core 3.0 performance improvements, the extensibility of
DefaultHttpContext was removed. The class is now sealed . For more information, see
dotnet/aspnetcore#6504
.
If your unit tests use Mock<DefaultHttpContext> , use Mock<HttpContext> or new
DefaultHttpContext() instead.
For discussion, see dotnet/aspnetcore#6534
Version introduced
.
3.0
Old behavior
Classes can derive from DefaultHttpContext .
New behavior
Classes can't derive from DefaultHttpContext .
Reason for change
The extensibility was provided initially to allow pooling of the HttpContext , but it
introduced unnecessary complexity and impeded other optimizations.
Recommended action
If you're using Mock<DefaultHttpContext> in your unit tests, begin using
Mock<HttpContext> instead.
Category
ASP.NET Core
Affected APIs
Microsoft.AspNetCore.Http.DefaultHttpContext
HTTP: HeaderNames constants changed to static
readonly
Starting in ASP.NET Core 3.0 Preview 5, the fields in
Microsoft.Net.Http.Headers.HeaderNames changed from const to static readonly .
For discussion, see dotnet/aspnetcore#9514
Version introduced
3.0
.
Old behavior
These fields used to be const .
New behavior
These fields are now static readonly .
Reason for change
The change:
Prevents the values from being embedded across assembly boundaries, allowing
for value corrections as needed.
Enables faster reference equality checks.
Recommended action
Recompile against 3.0. Source code using these fields in the following ways can no
longer do so:
As an attribute argument
As a case in a switch statement
When defining another const
To work around the breaking change, switch to using self-defined header name
constants or string literals.
Category
ASP.NET Core
Affected APIs
Microsoft.Net.Http.Headers.HeaderNames
HTTP: Response body infrastructure changes
The infrastructure backing an HTTP response body has changed. If you're using
HttpResponse directly, you shouldn't need to make any code changes. Read further if
you're wrapping or replacing HttpResponse.Body or accessing HttpContext.Features .
Version introduced
3.0
Old behavior
There were three APIs associated with the HTTP response body:
IHttpResponseFeature.Body
IHttpSendFileFeature.SendFileAsync
IHttpBufferingFeature.DisableResponseBuffering
New behavior
If you replace HttpResponse.Body , it replaces the entire IHttpResponseBodyFeature with a
wrapper around your given stream using StreamResponseBodyFeature to provide default
implementations for all of the expected APIs. Setting back the original stream reverts
this change.
Reason for change
The motivation is to combine the response body APIs into a single new feature interface.
Recommended action
Use IHttpResponseBodyFeature where you previously were using
IHttpResponseFeature.Body , IHttpSendFileFeature , or IHttpBufferingFeature .
Category
ASP.NET Core
Affected APIs
Microsoft.AspNetCore.Http.Features.IHttpBufferingFeature
Microsoft.AspNetCore.Http.Features.IHttpResponseFeature.Body
Microsoft.AspNetCore.Http.Features.IHttpSendFileFeature
HTTP: Some cookie SameSite defaults changed to None
SameSite is an option for cookies that can help mitigate some Cross-Site Request
Forgery (CSRF) attacks. When this option was initially introduced, inconsistent defaults
were used across various ASP.NET Core APIs. The inconsistency has led to confusing
results. As of ASP.NET Core 3.0, these defaults are better aligned. You must opt in to this
feature on a per-component basis.
Version introduced
3.0
Old behavior
Similar ASP.NET Core APIs used different default SameSiteMode values. An example of
the inconsistency is seen in HttpResponse.Cookies.Append(String, String) and
HttpResponse.Cookies.Append(String, String, CookieOptions) , which defaulted to
SameSiteMode.None and SameSiteMode.Lax , respectively.
New behavior
All the affected APIs default to SameSiteMode.None .
Reason for change
The default value was changed to make SameSite an opt-in feature.
Recommended action
Each component that emits cookies needs to decide if SameSite is appropriate for its
scenarios. Review your usage of the affected APIs and reconfigure SameSite as needed.
Category
ASP.NET Core
Affected APIs
IResponseCookies.Append(String, String, CookieOptions)
CookiePolicyOptions.MinimumSameSitePolicy
HTTP: Synchronous IO disabled in all servers
Starting with ASP.NET Core 3.0, synchronous server operations are disabled by default.
Change description
AllowSynchronousIO is an option in each server that enables or disables synchronous IO
APIs like HttpRequest.Body.Read , HttpResponse.Body.Write , and Stream.Flush . These
APIs have long been a source of thread starvation and app hangs. Starting in ASP.NET
Core 3.0 Preview 3, these synchronous operations are disabled by default.
Affected servers:
Kestrel
HttpSys
IIS in-process
TestServer
Expect errors similar to:
Synchronous operations are disallowed. Call ReadAsync or set
AllowSynchronousIO to true instead.
Synchronous operations are disallowed. Call WriteAsync or set
AllowSynchronousIO to true instead.
Synchronous operations are disallowed. Call FlushAsync or set
AllowSynchronousIO to true instead.
Each server has an AllowSynchronousIO option that controls this behavior and the
default for all of them is now false .
The behavior can also be overridden on a per-request basis as a temporary mitigation.
For example:
C#
var syncIOFeature = HttpContext.Features.Get<IHttpBodyControlFeature>();
if (syncIOFeature != null)
{
syncIOFeature.AllowSynchronousIO = true;
}
If you have trouble with a TextWriter or another stream calling a synchronous API in
Dispose , call the new DisposeAsync API instead.
For discussion, see dotnet/aspnetcore#7644
.
Version introduced
3.0
Old behavior
HttpRequest.Body.Read , HttpResponse.Body.Write , and Stream.Flush were allowed by
default.
New behavior
These synchronous APIs are disallowed by default:
Expect errors similar to:
Synchronous operations are disallowed. Call ReadAsync or set
AllowSynchronousIO to true instead.
Synchronous operations are disallowed. Call WriteAsync or set
AllowSynchronousIO to true instead.
Synchronous operations are disallowed. Call FlushAsync or set
AllowSynchronousIO to true instead.
Reason for change
These synchronous APIs have long been a source of thread starvation and app hangs.
Starting in ASP.NET Core 3.0 Preview 3, the synchronous operations are disabled by
default.
Recommended action
Use the asynchronous versions of the methods. The behavior can also be overridden on
a per-request basis as a temporary mitigation.
C#
var syncIOFeature = HttpContext.Features.Get<IHttpBodyControlFeature>();
if (syncIOFeature != null)
{
syncIOFeature.AllowSynchronousIO = true;
}
Category
ASP.NET Core
Affected APIs
Stream.Flush
Stream.Read
Stream.Write
Identity: AddDefaultUI method overload removed
Starting with ASP.NET Core 3.0, the
IdentityBuilderUIExtensions.AddDefaultUI(IdentityBuilder,UIFramework) method
overload no longer exists.
Version introduced
3.0
Reason for change
This change was a result of adoption of the static web assets feature.
Recommended action
Call IdentityBuilderUIExtensions.AddDefaultUI(IdentityBuilder) instead of the overload
that takes two arguments. If you're using Bootstrap 3, also add the following line to a
<PropertyGroup> element in your project file:
XML
<IdentityUIFrameworkVersion>Bootstrap3</IdentityUIFrameworkVersion>
Category
ASP.NET Core
Affected APIs
IdentityBuilderUIExtensions.AddDefaultUI(IdentityBuilder,UIFramework)
Identity: Default Bootstrap version of UI changed
Starting in ASP.NET Core 3.0, Identity UI defaults to using version 4 of Bootstrap.
Version introduced
3.0
Old behavior
The services.AddDefaultIdentity<IdentityUser>().AddDefaultUI(); method call was the
same as services.AddDefaultIdentity<IdentityUser>
().AddDefaultUI(UIFramework.Bootstrap3);
New behavior
The services.AddDefaultIdentity<IdentityUser>().AddDefaultUI(); method call is the
same as services.AddDefaultIdentity<IdentityUser>
().AddDefaultUI(UIFramework.Bootstrap4);
Reason for change
Bootstrap 4 was released during ASP.NET Core 3.0 timeframe.
Recommended action
You're impacted by this change if you use the default Identity UI and have added it in
Startup.ConfigureServices as shown in the following example:
C#
services.AddDefaultIdentity<IdentityUser>().AddDefaultUI();
Take one of the following actions:
Migrate your app to use Bootstrap 4 using their migration guide .
Update Startup.ConfigureServices to enforce usage of Bootstrap 3. For example:
C#
services.AddDefaultIdentity<IdentityUser>
().AddDefaultUI(UIFramework.Bootstrap3);
Category
ASP.NET Core
Affected APIs
None
Identity: SignInAsync throws exception for
unauthenticated identity
By default, SignInAsync throws an exception for principals / identities in which
IsAuthenticated is false .
Version introduced
3.0
Old behavior
SignInAsync accepts any principals / identities, including identities in which
IsAuthenticated is false .
New behavior
By default, SignInAsync throws an exception for principals / identities in which
IsAuthenticated is false . There's a new flag to suppress this behavior, but the default
behavior has changed.
Reason for change
The old behavior was problematic because, by default, these principals were rejected by
[Authorize] / RequireAuthenticatedUser() .
Recommended action
In ASP.NET Core 3.0 Preview 6, there's a RequireAuthenticatedSignIn flag on
AuthenticationOptions that is true by default. Set this flag to false to restore the old
behavior.
Category
ASP.NET Core
Affected APIs
None
Identity: SignInManager constructor accepts new
parameter
Starting with ASP.NET Core 3.0, a new IUserConfirmation<TUser> parameter was added
to the SignInManager constructor. For more information, see dotnet/aspnetcore#8356 .
Version introduced
3.0
Reason for change
The motivation for the change was to add support for new email / confirmation flows in
Identity.
Recommended action
If manually constructing a SignInManager , provide an implementation of
IUserConfirmation or grab one from dependency injection to provide.
Category
ASP.NET Core
Affected APIs
SignInManager<TUser>
Identity: UI uses static web assets feature
ASP.NET Core 3.0 introduced a static web assets feature, and Identity UI has adopted it.
Change description
As a result of Identity UI adopting the static web assets feature:
Framework selection is accomplished by using the IdentityUIFrameworkVersion
property in your project file.
Bootstrap 4 is the default UI framework for Identity UI. Bootstrap 3 has reached
end of life, and you should consider migrating to a supported version.
Version introduced
3.0
Old behavior
The default UI framework for Identity UI was Bootstrap 3. The UI framework could be
configured using a parameter to the AddDefaultUI method call in
Startup.ConfigureServices .
New behavior
The default UI framework for Identity UI is Bootstrap 4. The UI framework must be
configured in your project file, instead of in the AddDefaultUI method call.
Reason for change
Adoption of the static web assets feature required that the UI framework configuration
move to MSBuild. The decision on which framework to embed is a build-time decision,
not a runtime decision.
Recommended action
Review your site UI to ensure the new Bootstrap 4 components are compatible. If
necessary, use the IdentityUIFrameworkVersion MSBuild property to revert to Bootstrap
3. Add the property to a <PropertyGroup> element in your project file:
XML
<IdentityUIFrameworkVersion>Bootstrap3</IdentityUIFrameworkVersion>
Category
ASP.NET Core
Affected APIs
IdentityBuilderUIExtensions.AddDefaultUI(IdentityBuilder, UIFramework)
Kestrel: Connection adapters removed
As part of the move to move "pubternal" APIs to public , the concept of an
IConnectionAdapter was removed from Kestrel. Connection adapters are being replaced
with connection middleware (similar to HTTP middleware in the ASP.NET Core pipeline,
but for lower-level connections). HTTPS and connection logging have moved from
connection adapters to connection middleware. Those extension methods should
continue to work seamlessly, but the implementation details have changed.
For more information, see dotnet/aspnetcore#11412
dotnet/aspnetcore#11475
. For discussion, see
.
Version introduced
3.0
Old behavior
Kestrel extensibility components were created using IConnectionAdapter .
New behavior
Kestrel extensibility components are created as middleware
.
Reason for change
This change is intended to provide a more flexible extensibility architecture.
Recommended action
Convert any implementations of IConnectionAdapter to use the new middleware pattern
as shown here .
Category
ASP.NET Core
Affected APIs
Microsoft.AspNetCore.Server.Kestrel.Core.Adapter.Internal.IConnectionAdapter
Kestrel: Empty HTTPS assembly removed
The assembly Microsoft.AspNetCore.Server.Kestrel.Https has been removed.
Version introduced
3.0
Reason for change
In ASP.NET Core 2.1, the contents of Microsoft.AspNetCore.Server.Kestrel.Https were
moved to Microsoft.AspNetCore.Server.Kestrel.Core. This change was done in a nonbreaking way using [TypeForwardedTo] attributes.
Recommended action
Libraries referencing Microsoft.AspNetCore.Server.Kestrel.Https 2.0 should
update all ASP.NET Core dependencies to 2.1 or later. Otherwise, they may break
when loaded into an ASP.NET Core 3.0 app.
Apps and libraries targeting ASP.NET Core 2.1 and later should remove any direct
references to the Microsoft.AspNetCore.Server.Kestrel.Https NuGet package.
Category
ASP.NET Core
Affected APIs
None
Kestrel: Request trailer headers moved to new collection
In prior versions, Kestrel added HTTP/1.1 chunked trailer headers into the request
headers collection when the request body was read to the end. This behavior caused
concerns about ambiguity between headers and trailers. The decision was made to
move the trailers to a new collection.
HTTP/2 request trailers were unavailable in ASP.NET Core 2.2 but are now also available
in this new collection in ASP.NET Core 3.0.
New request extension methods have been added to access these trailers.
HTTP/1.1 trailers are available once the entire request body has been read.
HTTP/2 trailers are available once they're received from the client. The client won't send
the trailers until the entire request body has been at least buffered by the server. You
may need to read the request body to free up buffer space. Trailers are always available
if you read the request body to the end. The trailers mark the end of the body.
Version introduced
3.0
Old behavior
Request trailer headers would be added to the HttpRequest.Headers collection.
New behavior
Request trailer headers aren't present in the HttpRequest.Headers collection. Use the
following extension methods on HttpRequest to access them:
GetDeclaredTrailers() - Gets the request "Trailer" header that lists which trailers
to expect after the body.
SupportsTrailers() - Indicates if the request supports receiving trailer headers.
CheckTrailersAvailable() - Determines if the request supports trailers and if
they're available for reading.
GetTrailer(string trailerName) - Gets the requested trailing header from the
response.
Reason for change
Trailers are a key feature in scenarios like gRPC. Merging the trailers in to request
headers was confusing to users.
Recommended action
Use the trailer-related extension methods on HttpRequest to access trailers.
Category
ASP.NET Core
Affected APIs
HttpRequest.Headers
Kestrel: Transport abstractions removed and made public
As part of moving away from "pubternal" APIs, the Kestrel transport layer APIs are
exposed as a public interface in the Microsoft.AspNetCore.Connections.Abstractions
library.
Version introduced
3.0
Old behavior
Transport-related abstractions were available in the
Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions library.
The ListenOptions.NoDelay property was available.
New behavior
The IConnectionListener interface was introduced in the
Microsoft.AspNetCore.Connections.Abstractions library to expose the most used
functionality from the ...Transport.Abstractions library.
The NoDelay is now available in transport options ( LibuvTransportOptions and
SocketTransportOptions ).
SchedulingMode is no longer available.
Reason for change
ASP.NET Core 3.0 has moved away from "pubternal" APIs.
Recommended action
Category
ASP.NET Core
Affected APIs
None
Localization: ResourceManagerWithCultureStringLocalizer
and WithCulture marked obsolete
The ResourceManagerWithCultureStringLocalizer
class and WithCulture
interface
member are often sources of confusion for users of localization, especially when
creating their own IStringLocalizer implementation. These items give the user the
impression that an IStringLocalizer instance is "per-language, per-resource". In reality,
the instances should only be "per-resource". The language searched for is determined
by the CultureInfo.CurrentUICulture at execution time. To eliminate the source of
confusion, the APIs were marked as obsolete in ASP.NET Core 3.0 Preview 3. The APIs
will be removed in a future release.
For context, see dotnet/aspnetcore#3324
dotnet/aspnetcore#7756
. For discussion, see
.
Version introduced
3.0
Old behavior
Methods weren't marked as Obsolete .
New behavior
Methods are marked Obsolete .
Reason for change
The APIs represented a use case that isn't recommended. There was confusion about the
design of localization.
Recommended action
The recommendation is to use ResourceManagerStringLocalizer instead. Let the culture
be set by the CurrentCulture . If that isn't an option, create and use a copy of
ResourceManagerWithCultureStringLocalizer .
Category
ASP.NET Core
Affected APIs
ResourceManagerWithCultureStringLocalizer
ResourceManagerStringLocalizer.WithCulture
Logging: DebugLogger class made internal
Prior to ASP.NET Core 3.0, DebugLogger 's access modifier was public . In ASP.NET Core
3.0, the access modifier changed to internal .
Version introduced
3.0
Reason for change
The change is being made to:
Enforce consistency with other logger implementations such as ConsoleLogger .
Reduce the API surface.
Recommended action
Use the AddDebug ILoggingBuilder extension method to enable debug logging.
DebugLoggerProvider is also still public in the event the service needs to be registered
manually.
Category
ASP.NET Core
Affected APIs
Microsoft.Extensions.Logging.Debug.DebugLogger
MVC: Async suffix trimmed from controller action names
As part of addressing dotnet/aspnetcore#4849
, ASP.NET Core MVC trims the suffix
Async from action names by default. Starting with ASP.NET Core 3.0, this change affects
both routing and link generation.
Version introduced
3.0
Old behavior
Consider the following ASP.NET Core MVC controller:
C#
public class ProductController : Controller
{
public async IActionResult ListAsync()
{
var model = await DbContext.Products.ToListAsync();
return View(model);
}
}
The action is routable via Product/ListAsync . Link generation requires specifying the
Async suffix. For example:
CSHTML
<a asp-controller="Product" asp-action="ListAsync">List</a>
New behavior
In ASP.NET Core 3.0, the action is routable via Product/List . Link generation code
should omit the Async suffix. For example:
CSHTML
<a asp-controller="Product" asp-action="List">List</a>
This change doesn't affect names specified using the [ActionName] attribute. The new
behavior can be disabled by setting MvcOptions.SuppressAsyncSuffixInActionNames to
false in Startup.ConfigureServices :
C#
services.AddMvc(options =>
{
options.SuppressAsyncSuffixInActionNames = false;
});
Reason for change
By convention, asynchronous .NET methods are suffixed with Async . However, when a
method defines an MVC action, it's undesirable to use the Async suffix.
Recommended action
If your app depends on MVC actions preserving the name's Async suffix, choose one of
the following mitigations:
Use the [ActionName] attribute to preserve the original name.
Disable the renaming entirely by setting
MvcOptions.SuppressAsyncSuffixInActionNames to false in
Startup.ConfigureServices :
C#
services.AddMvc(options =>
{
options.SuppressAsyncSuffixInActionNames = false;
});
Category
ASP.NET Core
Affected APIs
None
MVC: JsonResult moved to
Microsoft.AspNetCore.Mvc.Core
JsonResult has moved to the Microsoft.AspNetCore.Mvc.Core assembly. This type used
to be defined in Microsoft.AspNetCore.Mvc.Formatters.Json
. An assembly-level
[TypeForwardedTo] attribute was added to Microsoft.AspNetCore.Mvc.Formatters.Json
to address this issue for the majority of users. Apps that use third-party libraries may
encounter issues.
Version introduced
3.0 Preview 6
Old behavior
An app using a 2.2-based library builds successfully.
New behavior
An app using a 2.2-based library fails compilation. An error containing a variation of the
following text is provided:
Output
The type 'JsonResult' exists in both 'Microsoft.AspNetCore.Mvc.Core,
Version=3.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60' and
'Microsoft.AspNetCore.Mvc.Formatters.Json, Version=2.0.0.0, Culture=neutral,
PublicKeyToken=adb9793829ddae60'
For an example of such an issue, see dotnet/aspnetcore#7220
.
Reason for change
Platform-level changes to the composition of ASP.NET Core as described at
aspnet/Announcements#325
.
Recommended action
Libraries compiled against the 2.2 version of Microsoft.AspNetCore.Mvc.Formatters.Json
may need to recompile to address the problem for all consumers. If affected, contact the
library author. Request recompilation of the library to target ASP.NET Core 3.0.
Category
ASP.NET Core
Affected APIs
Microsoft.AspNetCore.Mvc.JsonResult
MVC: Precompilation tool deprecated
In ASP.NET Core 1.1, the Microsoft.AspNetCore.Mvc.Razor.ViewCompilation (MVC
precompilation tool) package was introduced to add support for publish-time
compilation of Razor files (.cshtml files). In ASP.NET Core 2.1, the Razor SDK was
introduced to expand upon features of the precompilation tool. The Razor SDK added
support for build- and publish-time compilation of Razor files. The SDK verifies the
correctness of .cshtml files at build time while improving on app startup time. The Razor
SDK is on by default, and no gesture is required to start using it.
In ASP.NET Core 3.0, the ASP.NET Core 1.1-era MVC precompilation tool was removed.
Earlier package versions will continue receiving important bug and security fixes in the
patch release.
Version introduced
3.0
Old behavior
The Microsoft.AspNetCore.Mvc.Razor.ViewCompilation package was used to pre-compile
MVC Razor views.
New behavior
The Razor SDK natively supports this functionality. The
Microsoft.AspNetCore.Mvc.Razor.ViewCompilation package is no longer updated.
Reason for change
The Razor SDK provides more functionality and verifies the correctness of .cshtml files at
build time. The SDK also improves app startup time.
Recommended action
For users of ASP.NET Core 2.1 or later, update to use the native support for
precompilation in the Razor SDK. If bugs or missing features prevent migration to the
Razor SDK, open an issue at dotnet/aspnetcore .
Category
ASP.NET Core
Affected APIs
None
MVC: "Pubternal" types changed to internal
In ASP.NET Core 3.0, all "pubternal" types in MVC were updated to either be public in a
supported namespace or internal as appropriate.
Change description
In ASP.NET Core, "pubternal" types are declared as public but reside in a .Internal suffixed namespace. While these types are public , they have no support policy and are
subject to breaking changes. Unfortunately, accidental use of these types has been
common, resulting in breaking changes to these projects and limiting the ability to
maintain the framework.
Version introduced
3.0
Old behavior
Some types in MVC were public but in a .Internal namespace. These types had no
support policy and were subject to breaking changes.
New behavior
All such types are updated either to be public in a supported namespace or marked as
internal .
Reason for change
Accidental use of the "pubternal" types has been common, resulting in breaking
changes to these projects and limiting the ability to maintain the framework.
Recommended action
If you're using types that have become truly public and have been moved into a new,
supported namespace, update your references to match the new namespaces.
If you're using types that have become marked as internal , you'll need to find an
alternative. The previously "pubternal" types were never supported for public use. If
there are specific types in these namespaces that are critical to your apps, file an issue at
dotnet/aspnetcore
. Considerations may be made for making the requested types
public .
Category
ASP.NET Core
Affected APIs
This change includes types in the following namespaces:
Microsoft.AspNetCore.Mvc.Cors.Internal
Microsoft.AspNetCore.Mvc.DataAnnotations.Internal
Microsoft.AspNetCore.Mvc.Formatters.Internal
Microsoft.AspNetCore.Mvc.Formatters.Json.Internal
Microsoft.AspNetCore.Mvc.Formatters.Xml.Internal
Microsoft.AspNetCore.Mvc.Internal
Microsoft.AspNetCore.Mvc.ModelBinding.Internal
Microsoft.AspNetCore.Mvc.Razor.Internal
Microsoft.AspNetCore.Mvc.RazorPages.Internal
Microsoft.AspNetCore.Mvc.TagHelpers.Internal
Microsoft.AspNetCore.Mvc.ViewFeatures.Internal
MVC: Web API compatibility shim removed
Starting with ASP.NET Core 3.0, the Microsoft.AspNetCore.Mvc.WebApiCompatShim
package is no longer available.
Change description
The Microsoft.AspNetCore.Mvc.WebApiCompatShim (WebApiCompatShim) package
provides partial compatibility in ASP.NET Core with ASP.NET 4.x Web API 2 to simplify
migrating existing Web API implementations to ASP.NET Core. However, apps using the
WebApiCompatShim don't benefit from the API-related features shipping in recent
ASP.NET Core releases. Such features include improved Open API specification
generation, standardized error handling, and client code generation. To better focus the
API efforts in 3.0, WebApiCompatShim was removed. Existing apps using the
WebApiCompatShim should migrate to the newer [ApiController] model.
Version introduced
3.0
Reason for change
The Web API compatibility shim was a migration tool. It restricts user access to new
functionality added in ASP.NET Core.
Recommended action
Remove usage of this shim and migrate directly to the similar functionality in ASP.NET
Core itself.
Category
ASP.NET Core
Affected APIs
Microsoft.AspNetCore.Mvc.WebApiCompatShim
Razor: RazorTemplateEngine API removed
The RazorTemplateEngine API was removed and replaced with
Microsoft.AspNetCore.Razor.Language.RazorProjectEngine .
For discussion, see GitHub issue dotnet/aspnetcore#25215
.
Version introduced
3.0
Old behavior
A template engine can be created and used to parse and generate code for Razor files.
New behavior
RazorProjectEngine can be created and provided the same type of information as
RazorTemplateEngine to parse and generate code for Razor files. RazorProjectEngine
also provides extra levels of configuration.
Reason for change
RazorTemplateEngine was too tightly coupled to the existing implementations. This tight
coupling led to more questions when trying to properly configure a Razor
parsing/generation pipeline.
Recommended action
Use RazorProjectEngine instead of RazorTemplateEngine . Consider the following
examples.
Create and configure the RazorProjectEngine
C#
RazorProjectEngine projectEngine =
RazorProjectEngine.Create(RazorConfiguration.Default,
RazorProjectFileSystem.Create(@"C:\source\repos\ConsoleApp4\ConsoleApp4"),
builder =>
{
builder.ConfigureClass((document, classNode) =>
{
classNode.ClassName = "MyClassName";
// Can also configure other aspects of the class here.
});
// More configuration can go here
});
Generate code for a Razor file
C#
RazorProjectItem item = projectEngine.FileSystem.GetItem(
@"C:\source\repos\ConsoleApp4\ConsoleApp4\Example.cshtml",
FileKinds.Legacy);
RazorCodeDocument output = projectEngine.Process(item);
// Things available
RazorSyntaxTree syntaxTree = output.GetSyntaxTree();
DocumentIntermediateNode intermediateDocument =
output.GetDocumentIntermediateNode();
RazorCSharpDocument csharpDocument = output.GetCSharpDocument();
Category
ASP.NET Core
Affected APIs
RazorTemplateEngine
RazorTemplateEngineOptions
Razor: Runtime compilation moved to a package
Support for runtime compilation of Razor views and Razor Pages has moved to a
separate package.
Version introduced
3.0
Old behavior
Runtime compilation is available without needing additional packages.
New behavior
The functionality has been moved to the
Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation
package.
The following APIs were previously available in
Microsoft.AspNetCore.Mvc.Razor.RazorViewEngineOptions to support runtime
compilation. The APIs are now available via
Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation.MvcRazorRuntimeCompilationOption
s.
RazorViewEngineOptions.FileProviders is now
MvcRazorRuntimeCompilationOptions.FileProviders
RazorViewEngineOptions.AdditionalCompilationReferences is now
MvcRazorRuntimeCompilationOptions.AdditionalReferencePaths
In addition,
Microsoft.AspNetCore.Mvc.Razor.RazorViewEngineOptions.AllowRecompilingViewsOnFileC
hange has been removed. Recompilation on file changes is enabled by default by
referencing the Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation package.
Reason for change
This change was necessary to remove the ASP.NET Core shared framework dependency
on Roslyn.
Recommended action
Apps that require runtime compilation or recompilation of Razor files should take the
following steps:
1. Add a reference to the Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation
package.
2. Update the project's Startup.ConfigureServices method to include a call to
AddRazorRuntimeCompilation . For example:
C#
services.AddMvc()
.AddRazorRuntimeCompilation();
Category
ASP.NET Core
Affected APIs
Microsoft.AspNetCore.Mvc.Razor.RazorViewEngineOptions
Session state: Obsolete APIs removed
Obsolete APIs for configuring session cookies were removed. For more information, see
aspnet/Announcements#257
.
Version introduced
3.0
Reason for change
This change enforces consistency across APIs for configuring features that use cookies.
Recommended action
Migrate usage of the removed APIs to their newer replacements. Consider the following
example in Startup.ConfigureServices :
C#
public void ConfigureServices(ServiceCollection services)
{
services.AddSession(options =>
{
// Removed obsolete APIs
options.CookieName = "SessionCookie";
options.CookieDomain = "contoso.com";
options.CookiePath = "/";
options.CookieHttpOnly = true;
options.CookieSecure = CookieSecurePolicy.Always;
// new API
options.Cookie.Name = "SessionCookie";
options.Cookie.Domain = "contoso.com";
options.Cookie.Path = "/";
options.Cookie.HttpOnly = true;
options.Cookie.SecurePolicy = CookieSecurePolicy.Always;
});
}
Category
ASP.NET Core
Affected APIs
Microsoft.AspNetCore.Builder.SessionOptions.CookieDomain
Microsoft.AspNetCore.Builder.SessionOptions.CookieHttpOnly
Microsoft.AspNetCore.Builder.SessionOptions.CookieName
Microsoft.AspNetCore.Builder.SessionOptions.CookiePath
Microsoft.AspNetCore.Builder.SessionOptions.CookieSecure
Shared framework: Assemblies removed from
Microsoft.AspNetCore.App
Starting in ASP.NET Core 3.0, the ASP.NET Core shared framework
( Microsoft.AspNetCore.App ) only contains first-party assemblies that are fully developed,
supported, and serviceable by Microsoft.
Change description
Think of the change as the redefining of boundaries for the ASP.NET Core "platform."
The shared framework will be source-buildable by anybody via GitHub
and will
continue to offer the existing benefits of .NET Core shared frameworks to your apps.
Some benefits include smaller deployment size, centralized patching, and faster startup
time.
As part of the change, some notable breaking changes are introduced in
Microsoft.AspNetCore.App .
Version introduced
3.0
Old behavior
Projects referenced Microsoft.AspNetCore.App via a <PackageReference> element in the
project file.
Additionally, Microsoft.AspNetCore.App contained the following subcomponents:
Json.NET ( Newtonsoft.Json )
Entity Framework Core (assemblies prefixed with Microsoft.EntityFrameworkCore. )
Roslyn ( Microsoft.CodeAnalysis )
New behavior
A reference to Microsoft.AspNetCore.App no longer requires a <PackageReference>
element in the project file. The .NET Core SDK supports a new element called
<FrameworkReference> , which replaces the use of <PackageReference> .
For more information, see dotnet/aspnetcore#3612
.
Entity Framework Core ships as NuGet packages. This change aligns the shipping model
with all other data access libraries on .NET. It provides Entity Framework Core the
simplest path to continue innovating while supporting the various .NET platforms. The
move of Entity Framework Core out of the shared framework has no impact on its status
as a Microsoft-developed, supported, and serviceable library. The .NET Core support
policy
continues to cover it.
Json.NET and Entity Framework Core continue to work with ASP.NET Core. They won't,
however, be included in the shared framework.
For more information, see The future of JSON in .NET Core 3.0 . Also see the complete
list of binaries
removed from the shared framework.
Reason for change
This change simplifies the consumption of Microsoft.AspNetCore.App and reduces the
duplication between NuGet packages and shared frameworks.
For more information on the motivation for this change, see this blog post .
Recommended action
Starting with ASP.NET Core 3.0, it is no longer necessary for projects to consume
assemblies in Microsoft.AspNetCore.App as NuGet packages. To simplify the targeting
and usage of the ASP.NET Core shared framework, many NuGet packages shipped since
ASP.NET Core 1.0 are no longer produced. The APIs those packages provide are still
available to apps by using a <FrameworkReference> to Microsoft.AspNetCore.App .
Common API examples include Kestrel, MVC, and Razor.
This change doesn't apply to all binaries referenced via Microsoft.AspNetCore.App in
ASP.NET Core 2.x. Notable exceptions include:
Microsoft.Extensions libraries that continue to target .NET Standard are available
as NuGet packages (see https://github.com/dotnet/extensions ).
APIs produced by the ASP.NET Core team that aren't part of
Microsoft.AspNetCore.App . For example, the following components are available as
NuGet packages:
Entity Framework Core
APIs that provide third-party integration
Experimental features
APIs with dependencies that couldn't fulfill the requirements to be in the shared
framework
Extensions to MVC that maintain support for Json.NET. An API is provided as a
NuGet package
to support using Json.NET and MVC. See the ASP.NET Core
migration guide for more details.
The SignalR .NET client continues to support .NET Standard and ships as a NuGet
package . It's intended for use on many .NET runtimes, such as Xamarin and
UWP.
For more information, see Stop producing packages for shared framework assemblies in
3.0 . For discussion, see dotnet/aspnetcore#3757
.
Category
ASP.NET Core
Affected APIs
Microsoft.CodeAnalysis
Microsoft.EntityFrameworkCore
Shared framework: Removed Microsoft.AspNetCore.All
Starting in ASP.NET Core 3.0, the Microsoft.AspNetCore.All metapackage and the
matching Microsoft.AspNetCore.All shared framework are no longer produced. This
package is available in ASP.NET Core 2.2 and will continue to receive servicing updates
in ASP.NET Core 2.1.
Version introduced
3.0
Old behavior
Apps could use the Microsoft.AspNetCore.All metapackage to target the
Microsoft.AspNetCore.All shared framework on .NET Core.
New behavior
.NET Core 3.0 doesn't include a Microsoft.AspNetCore.All shared framework.
Reason for change
The Microsoft.AspNetCore.All metapackage included a large number of external
dependencies.
Recommended action
Migrate your project to use the Microsoft.AspNetCore.App framework. Components that
were previously available in Microsoft.AspNetCore.All are still available on NuGet.
Those components are now deployed with your app instead of being included in the
shared framework.
Category
ASP.NET Core
Affected APIs
None
SignalR: HandshakeProtocol.SuccessHandshakeData
replaced
The HandshakeProtocol.SuccessHandshakeData
field was removed and replaced with
a helper method that generates a successful handshake response given a specific
IHubProtocol .
Version introduced
3.0
Old behavior
HandshakeProtocol.SuccessHandshakeData was a public static ReadOnlyMemory<byte>
field.
New behavior
HandshakeProtocol.SuccessHandshakeData has been replaced by a static
GetSuccessfulHandshake(IHubProtocol protocol) method that returns a
ReadOnlyMemory<byte> based on the specified protocol.
Reason for change
Additional fields were added to the handshake response that are non-constant and
change depending on the selected protocol.
Recommended action
None. This type isn't designed for use from user code. It's public , so it can be shared
between the SignalR server and client. It may also be used by customer SignalR clients
written in .NET. Users of SignalR shouldn't be affected by this change.
Category
ASP.NET Core
Affected APIs
HandshakeProtocol.SuccessHandshakeData
SignalR: HubConnection ResetSendPing and
ResetTimeout methods removed
The ResetSendPing and ResetTimeout methods were removed from the SignalR
HubConnection API. These methods were originally intended only for internal use but
were made public in ASP.NET Core 2.2. These methods won't be available starting in the
ASP.NET Core 3.0 Preview 4 release. For discussion, see dotnet/aspnetcore#8543 .
Version introduced
3.0
Old behavior
APIs were available.
New behavior
APIs are removed.
Reason for change
These methods were originally intended only for internal use but were made public in
ASP.NET Core 2.2.
Recommended action
Don't use these methods.
Category
ASP.NET Core
Affected APIs
HubConnection.ResetSendPing()
HubConnection.ResetTimeout()
SignalR: HubConnectionContext constructors changed
SignalR's HubConnectionContext constructors changed to accept an options type, rather
than multiple parameters, to future-proof adding options. This change replaces two
constructors with a single constructor that accepts an options type.
Version introduced
3.0
Old behavior
HubConnectionContext has two constructors:
C#
public HubConnectionContext(ConnectionContext connectionContext, TimeSpan
keepAliveInterval, ILoggerFactory loggerFactory);
public HubConnectionContext(ConnectionContext connectionContext, TimeSpan
keepAliveInterval, ILoggerFactory loggerFactory, TimeSpan
clientTimeoutInterval);
New behavior
The two constructors were removed and replaced with one constructor:
C#
public HubConnectionContext(ConnectionContext connectionContext,
HubConnectionContextOptions contextOptions, ILoggerFactory loggerFactory)
Reason for change
The new constructor uses a new options object. Consequently, the features of
HubConnectionContext can be expanded in the future without making more constructors
and breaking changes.
Recommended action
Instead of using the following constructor:
C#
HubConnectionContext connectionContext = new HubConnectionContext(
connectionContext,
keepAliveInterval: TimeSpan.FromSeconds(15),
loggerFactory,
clientTimeoutInterval: TimeSpan.FromSeconds(15));
Use the following constructor:
C#
HubConnectionContextOptions contextOptions = new
HubConnectionContextOptions()
{
KeepAliveInterval = TimeSpan.FromSeconds(15),
ClientTimeoutInterval = TimeSpan.FromSeconds(15)
};
HubConnectionContext connectionContext = new
HubConnectionContext(connectionContext, contextOptions, loggerFactory);
Category
ASP.NET Core
Affected APIs
HubConnectionContext(ConnectionContext, TimeSpan, ILoggerFactory)
HubConnectionContext(ConnectionContext, TimeSpan, ILoggerFactory, TimeSpan)
SignalR: JavaScript client package name changed
In ASP.NET Core 3.0 Preview 7, the SignalR JavaScript client package name changed
from @aspnet/signalr to @microsoft/signalr . The name change reflects the fact that
SignalR is useful in more than just ASP.NET Core apps, thanks to the Azure SignalR
Service.
To react to this change, change references in your package.json files, require
statements, and ECMAScript import statements. No API will change as part of this
rename.
For discussion, see dotnet/aspnetcore#11637
.
Version introduced
3.0
Old behavior
The client package was named @aspnet/signalr .
New behavior
The client package is named @microsoft/signalr .
Reason for change
The name change clarifies that SignalR is useful beyond ASP.NET Core apps, thanks to
the Azure SignalR Service.
Recommended action
Switch to the new package @microsoft/signalr .
Category
ASP.NET Core
Affected APIs
None
SignalR: UseSignalR and UseConnections methods
marked obsolete
The methods UseConnections and UseSignalR and the classes ConnectionsRouteBuilder
and HubRouteBuilder are marked as obsolete in ASP.NET Core 3.0.
Version introduced
3.0
Old behavior
SignalR hub routing was configured using UseSignalR or UseConnections .
New behavior
The old way of configuring routing has been obsoleted and replaced with endpoint
routing.
Reason for change
Middleware is being moved to the new endpoint routing system. The old way of adding
middleware is being obsoleted.
Recommended action
Replace UseSignalR with UseEndpoints :
Old code:
C#
app.UseSignalR(routes =>
{
routes.MapHub<SomeHub>("/path");
});
New code:
C#
app.UseEndpoints(endpoints =>
{
endpoints.MapHub<SomeHub>("/path");
});
Category
ASP.NET Core
Affected APIs
Microsoft.AspNetCore.Builder.ConnectionsAppBuilderExtensions.UseConnections(I
ApplicationBuilder, Action<ConnectionsRouteBuilder>)
Microsoft.AspNetCore.Builder.SignalRAppBuilderExtensions.UseSignalR(IApplicatio
nBuilder, Action<HubRouteBuilder>)
Microsoft.AspNetCore.Http.Connections.ConnectionsRouteBuilder
Microsoft.AspNetCore.SignalR.HubRouteBuilder
SPAs: SpaServices and NodeServices marked obsolete
The contents of the following NuGet packages have all been unnecessary since ASP.NET
Core 2.1. Consequently, the following packages are being marked as obsolete:
Microsoft.AspNetCore.SpaServices
Microsoft.AspNetCore.NodeServices
For the same reason, the following npm modules are being marked as deprecated:
aspnet-angular
aspnet-prerendering
aspnet-webpack
aspnet-webpack-react
domain-task
The preceding packages and npm modules will later be removed in .NET 5.
Version introduced
3.0
Old behavior
The deprecated packages and npm modules were intended to integrate ASP.NET Core
with various Single-Page App (SPA) frameworks. Such frameworks include Angular,
React, and React with Redux.
New behavior
A new integration mechanism exists in the
Microsoft.AspNetCore.SpaServices.Extensions
NuGet package. The package remains
the basis of the Angular and React project templates since ASP.NET Core 2.1.
Reason for change
ASP.NET Core supports integration with various Single-Page App (SPA) frameworks,
including Angular, React, and React with Redux. Initially, integration with these
frameworks was accomplished with ASP.NET Core-specific components that handled
scenarios like server-side prerendering and integration with Webpack. As time went on,
industry standards changed. Each of the SPA frameworks released their own standard
command-line interfaces. For example, Angular CLI and create-react-app.
When ASP.NET Core 2.1 was released in May 2018, the team responded to the change in
standards. A newer and simpler way to integrate with the SPA frameworks' own
toolchains was provided. The new integration mechanism exists in the package
Microsoft.AspNetCore.SpaServices.Extensions and remains the basis of the Angular and
React project templates since ASP.NET Core 2.1.
To clarify that the older ASP.NET Core-specific components are irrelevant and not
recommended:
The pre-2.1 integration mechanism is marked as obsolete.
The supporting npm packages are marked as deprecated.
Recommended action
If you're using these packages, update your apps to use the functionality:
In the Microsoft.AspNetCore.SpaServices.Extensions package.
Provided by the SPA frameworks you're using
To enable features like server-side prerendering and hot module reload, see the
documentation for the corresponding SPA framework. The functionality in
Microsoft.AspNetCore.SpaServices.Extensions is not obsolete and will continue to be
supported.
Category
ASP.NET Core
Affected APIs
Microsoft.AspNetCore.Builder.SpaRouteExtensions
Microsoft.AspNetCore.Builder.WebpackDevMiddleware
Microsoft.AspNetCore.NodeServices.EmbeddedResourceReader
Microsoft.AspNetCore.NodeServices.INodeServices
Microsoft.AspNetCore.NodeServices.NodeServicesFactory
Microsoft.AspNetCore.NodeServices.NodeServicesOptions
Microsoft.AspNetCore.NodeServices.StringAsTempFile
Microsoft.AspNetCore.NodeServices.HostingModels.INodeInstance
Microsoft.AspNetCore.NodeServices.HostingModels.NodeInvocationException
Microsoft.AspNetCore.NodeServices.HostingModels.NodeInvocationInfo
Microsoft.AspNetCore.NodeServices.HostingModels.NodeServicesOptionsExtensio
ns
Microsoft.AspNetCore.NodeServices.HostingModels.OutOfProcessNodeInstance
Microsoft.AspNetCore.SpaServices.Prerendering.ISpaPrerenderer
Microsoft.AspNetCore.SpaServices.Prerendering.ISpaPrerendererBuilder
Microsoft.AspNetCore.SpaServices.Prerendering.JavaScriptModuleExport
Microsoft.AspNetCore.SpaServices.Prerendering.Prerenderer
Microsoft.AspNetCore.SpaServices.Prerendering.PrerenderTagHelper
Microsoft.AspNetCore.SpaServices.Prerendering.RenderToStringResult
Microsoft.AspNetCore.SpaServices.Webpack.WebpackDevMiddlewareOptions
Microsoft.Extensions.DependencyInjection.NodeServicesServiceCollectionExtension
s
Microsoft.Extensions.DependencyInjection.PrerenderingServiceCollectionExtension
s
SPAs: SpaServices and NodeServices no longer fall back
to console logger
Microsoft.AspNetCore.SpaServices and Microsoft.AspNetCore.NodeServices won't
display console logs unless logging is configured.
Version introduced
3.0
Old behavior
Microsoft.AspNetCore.SpaServices and Microsoft.AspNetCore.NodeServices used to
automatically create a console logger when logging isn't configured.
New behavior
Microsoft.AspNetCore.SpaServices and Microsoft.AspNetCore.NodeServices won't
display console logs unless logging is configured.
Reason for change
There's a need to align with how other ASP.NET Core packages implement logging.
Recommended action
If the old behavior is required, to configure console logging, add
services.AddLogging(builder => builder.AddConsole()) to your
Setup.ConfigureServices method.
Category
ASP.NET Core
Affected APIs
None
Target framework: .NET Framework support dropped
Starting with ASP.NET Core 3.0, .NET Framework is an unsupported target framework.
Change description
.NET Framework 4.8 is the last major version of .NET Framework. New ASP.NET Core
apps should be built on .NET Core. Starting with the .NET Core 3.0 release, you can think
of ASP.NET Core 3.0 as being part of .NET Core.
Customers using ASP.NET Core with .NET Framework can continue in a fully supported
fashion using the 2.1 LTS release . Support and servicing for 2.1 continues until at least
August 21, 2021. This date is three years after declaration of the LTS release per the .NET
Support Policy . Support for ASP.NET Core 2.1 packages on .NET Framework will
extend indefinitely, similar to the servicing policy for other package-based ASP.NET
frameworks .
For more information about porting from .NET Framework to .NET Core, see Porting to
.NET Core.
Microsoft.Extensions packages (such as logging, dependency injection, and
configuration) and Entity Framework Core aren't affected. They'll continue to support
.NET Standard.
For more information on the motivation for this change, see the original blog post .
Version introduced
3.0
Old behavior
ASP.NET Core apps could run on either .NET Core or .NET Framework.
New behavior
ASP.NET Core apps can only be run on .NET Core.
Recommended action
Take one of the following actions:
Keep your app on ASP.NET Core 2.1.
Migrate your app and dependencies to .NET Core.
Category
ASP.NET Core
Affected APIs
None
Core .NET libraries
APIs that report version now report product and not file version
Custom EncoderFallbackBuffer instances cannot fall back recursively
Floating point formatting and parsing behavior changes
Floating-point parsing operations no longer fail or throw an OverflowException
InvalidAsynchronousStateException moved to another assembly
Replacing ill-formed UTF-8 byte sequences follows Unicode guidelines
TypeDescriptionProviderAttribute moved to another assembly
ZipArchiveEntry no longer handles archives with inconsistent entry sizes
FieldInfo.SetValue throws exception for static, init-only fields
Passing GroupCollection to extension methods taking IEnumerable<T> requires
disambiguation
APIs that report version now report product and not file
version
Many of the APIs that return versions in .NET Core now return the product version rather
than the file version.
Change description
In .NET Core 2.2 and previous versions, methods such as Environment.Version,
RuntimeInformation.FrameworkDescription, and the file properties dialog for .NET Core
assemblies reflect the file version. Starting with .NET Core 3.0, they reflect the product
version.
The following figure illustrates the difference in version information for the
System.Runtime.dll assembly for .NET Core 2.2 (on the left) and .NET Core 3.0 (on the
right) as displayed by the Windows Explorer file properties dialog.
Version introduced
3.0
Recommended action
None. This change should make version detection intuitive rather than obtuse.
Category
Core .NET libraries
Affected APIs
Environment.Version
RuntimeInformation.FrameworkDescription
Custom EncoderFallbackBuffer instances cannot fall back
recursively
Custom EncoderFallbackBuffer instances cannot fall back recursively. The
implementation of EncoderFallbackBuffer.GetNextChar() must result in a character
sequence that is convertible to the destination encoding. Otherwise, an exception
occurs.
Change description
During a character-to-byte transcoding operation, the runtime detects ill-formed or
nonconvertible UTF-16 sequences and provides those characters to the
EncoderFallbackBuffer.Fallback method. The Fallback method determines which
characters should be substituted for the original nonconvertible data, and these
characters are drained by calling EncoderFallbackBuffer.GetNextChar in a loop.
The runtime then attempts to transcode these substitution characters to the target
encoding. If this operation succeeds, the runtime continues transcoding from where it
left off in the original input string.
Previously, custom implementations of EncoderFallbackBuffer.GetNextChar() can return
character sequences that are not convertible to the destination encoding. If the
substituted characters cannot be transcoded to the target encoding, the runtime
invokes the EncoderFallbackBuffer.Fallback method once again with the substitution
characters, expecting the EncoderFallbackBuffer.GetNextChar() method to return a new
substitution sequence. This process continues until the runtime eventually sees a wellformed, convertible substitution, or until a maximum recursion count is reached.
Starting with .NET Core 3.0, custom implementations of
EncoderFallbackBuffer.GetNextChar() must return character sequences that are
convertible to the destination encoding. If the substituted characters cannot be
transcoded to the target encoding, an ArgumentException is thrown. The runtime will
no longer make recursive calls into the EncoderFallbackBuffer instance.
This behavior only applies when all three of the following conditions are met:
The runtime detects an ill-formed UTF-16 sequence or a UTF-16 sequence that
cannot be converted to the target encoding.
A custom EncoderFallback has been specified.
The custom EncoderFallback attempts to substitute a new ill-formed or
nonconvertible UTF-16 sequence.
Version introduced
3.0
Recommended action
Most developers needn't take any action.
If an application uses a custom EncoderFallback and EncoderFallbackBuffer class, ensure
the implementation of EncoderFallbackBuffer.Fallback populates the fallback buffer with
well-formed UTF-16 data that is directly convertible to the target encoding when the
Fallback method is first invoked by the runtime.
Category
Core .NET libraries
Affected APIs
EncoderFallbackBuffer.Fallback
EncoderFallbackBuffer.GetNextChar()
Floating-point formatting and parsing behavior changed
Floating-point parsing and formatting behavior (by the Double and Single types) are
now IEEE-compliant . This ensures that the behavior of floating-point types in .NET
matches that of other IEEE-compliant languages. For example,
double.Parse("SomeLiteral") should always match what C# produces for double x =
SomeLiteral .
Change description
In .NET Core 2.2 and earlier versions, formatting with Double.ToString and
Single.ToString, and parsing with Double.Parse, Double.TryParse, Single.Parse, and
Single.TryParse are not IEEE-compliant. As a result, it's impossible to guarantee that a
value will roundtrip with any supported standard or custom format string. For some
inputs, the attempt to parse a formatted value can fail, and for others, the parsed value
doesn't equal the original value.
Starting with .NET Core 3.0, floating-point parsing and formatting operations are IEEE
754-compliant.
The following table shows two code snippets and how the output changes between
.NET Core 2.2 and .NET Core 3.1.
Code snippet
Output on .NET
Core 2.2
Output on .NET
Core 3.1
Console.WriteLine((-0.0).ToString());
0
-0
var value = -3.123456789123456789;
False
True
Console.WriteLine(value ==
double.Parse(value.ToString()));
For more information, see the Floating-point parsing and formatting improvements in
.NET Core 3.0
blog post.
Version introduced
3.0
Recommended action
The Potential impact to existing code
section of the Floating-point parsing and
formatting improvements in .NET Core 3.0
blog post suggests some changes you can
make to your code if you want to maintain the previous behavior.
For some differences in formatting, you can get behavior equivalent to the
previous behavior by specifying a different format string.
For differences in parsing, there's no mechanism to fall back to the previous
behavior.
Category
Core .NET libraries
Affected APIs
Double.ToString
Single.ToString
Double.Parse
Double.TryParse
Single.Parse
Single.TryParse
Floating-point parsing operations no longer fail or throw
an OverflowException
The floating-point parsing methods no longer throw an OverflowException or return
false when they parse a string whose numeric value is outside the range of the Single
or Double floating-point type.
Change description
In .NET Core 2.2 and earlier versions, the Double.Parse and Single.Parse methods throw
an OverflowException for values that outside the range of their respective type. The
Double.TryParse and Single.TryParse methods return false for the string representations
of out-of-range numeric values.
Starting with .NET Core 3.0, the Double.Parse, Double.TryParse, Single.Parse, and
Single.TryParse methods no longer fail when parsing out-of-range numeric strings.
Instead, the Double parsing methods return Double.PositiveInfinity for values that
exceed Double.MaxValue, and they return Double.NegativeInfinity for values that are
less than Double.MinValue. Similarly, the Single parsing methods return
Single.PositiveInfinity for values that exceed Single.MaxValue, and they return
Single.NegativeInfinity for values that are less than Single.MinValue.
This change was made for improved IEEE 754:2008 compliance.
Version introduced
3.0
Recommended action
This change can affect your code in either of two ways:
Your code depends on the handler for the OverflowException to execute when an
overflow occurs. In this case, you should remove the catch statement and place
any necessary code in an If statement that tests whether Double.IsInfinity or
Single.IsInfinity is true .
Your code assumes that floating-point values are not Infinity . In this case, you
should add the necessary code to check for floating-point values of
PositiveInfinity and NegativeInfinity .
Category
Core .NET libraries
Affected APIs
Double.Parse
Double.TryParse
Single.Parse
Single.TryParse
InvalidAsynchronousStateException moved to another
assembly
The InvalidAsynchronousStateException class has been moved.
Change description
In .NET Core 2.2 and earlier versions, the InvalidAsynchronousStateException class is
found in the System.ComponentModel.TypeConverter assembly.
Starting with .NET Core 3.0, it is found in the System.ComponentModel.Primitives
assembly.
Version introduced
3.0
Recommended action
This change only affects applications that use reflection to load the
InvalidAsynchronousStateException by calling a method such as Assembly.GetType or an
overload of Activator.CreateInstance that assumes the type is in a particular assembly. If
that is the case, update the assembly referenced in the method call to reflect the type's
new assembly location.
Category
Core .NET libraries
Affected APIs
None.
Replacing ill-formed UTF-8 byte sequences follows
Unicode guidelines
When the UTF8Encoding class encounters an ill-formed UTF-8 byte sequence during a
byte-to-character transcoding operation, it replaces that sequence with a '�' (U+FFFD
REPLACEMENT CHARACTER) character in the output string. .NET Core 3.0 differs from
previous versions of .NET Core and the .NET Framework by following the Unicode best
practice for performing this replacement during the transcoding operation.
This is part of a larger effort to improve UTF-8 handling throughout .NET, including by
the new System.Text.Unicode.Utf8 and System.Text.Rune types. The UTF8Encoding type
was given improved error handling mechanics so that it produces output consistent with
the newly introduced types.
Change description
Starting with .NET Core 3.0, when transcoding bytes to characters, the UTF8Encoding
class performs character substitution based on Unicode best practices. The substitution
mechanism used is described by The Unicode Standard, Version 12.0, Sec. 3.9 (PDF)
in
the heading titled U+FFFD Substitution of Maximal Subparts.
This behavior only applies when the input byte sequence contains ill-formed UTF-8 data.
Additionally, if the UTF8Encoding instance has been constructed with
throwOnInvalidBytes: true , the UTF8Encoding instance will continue to throw on invalid
input rather than perform U+FFFD replacement. For more information about the
UTF8Encoding constructor, see UTF8Encoding(Boolean, Boolean).
The following table illustrates the impact of this change with an invalid 3-byte input:
Ill-formed 3-byte
input
Output before .NET Core 3.0
Output starting with .NET Core 3.0
[ ED A0 90 ]
[ FFFD FFFD ] (2-character
[ FFFD FFFD FFFD ] (3-character
output)
output)
The 3-char output is the preferred output, according to Table 3-9 of the previously
linked Unicode Standard PDF.
Version introduced
3.0
Recommended action
No action is required on the part of the developer.
Category
Core .NET libraries
Affected APIs
UTF8Encoding.GetCharCount
UTF8Encoding.GetChars
UTF8Encoding.GetString(Byte[], Int32, Int32)
TypeDescriptionProviderAttribute moved to another
assembly
The TypeDescriptionProviderAttribute class has been moved.
Change description
In .NET Core 2.2 and earlier versions, The TypeDescriptionProviderAttribute class is
found in the System.ComponentModel.TypeConverter assembly.
Starting with .NET Core 3.0, it is found in the System.ObjectModel assembly.
Version introduced
3.0
Recommended action
This change only affects applications that use reflection to load the
TypeDescriptionProviderAttribute type by calling a method such as Assembly.GetType or
an overload of Activator.CreateInstance that assumes the type is in a particular
assembly. If that is the case, the assembly referenced in the method call should be
updated to reflect the type's new assembly location.
Category
Windows Forms
Affected APIs
None.
ZipArchiveEntry no longer handles archives with
inconsistent entry sizes
Zip archives list both compressed size and uncompressed size in the central directory
and local header. The entry data itself also indicates its size. In .NET Core 2.2 and earlier
versions, these values were never checked for consistency. Starting with .NET Core 3.0,
they now are.
Change description
In .NET Core 2.2 and earlier versions, ZipArchiveEntry.Open() succeeds even if the local
header disagrees with the central header of the zip file. Data is decompressed until the
end of the compressed stream is reached, even if its length exceeds the uncompressed
file size listed in the central directory/local header.
Starting with .NET Core 3.0, the ZipArchiveEntry.Open() method checks that local header
and central header agree on compressed and uncompressed sizes of an entry. If they do
not, the method throws an InvalidDataException if the archive's local header and/or data
descriptor list sizes that disagree with the central directory of the zip file. When reading
an entry, decompressed data is truncated to the uncompressed file size listed in the
header.
This change was made to ensure that a ZipArchiveEntry correctly represents the size of
its data and that only that amount of data is read.
Version introduced
3.0
Recommended action
Repackage any zip archive that exhibits these problems.
Category
Core .NET libraries
Affected APIs
ZipArchiveEntry.Open()
ZipFileExtensions.ExtractToDirectory
ZipFileExtensions.ExtractToFile
ZipFile.ExtractToDirectory
FieldInfo.SetValue throws exception for static, init-only
fields
Starting in .NET Core 3.0, an exception is thrown when you attempt to set a value on a
static, InitOnly field by calling System.Reflection.FieldInfo.SetValue.
Change description
In .NET Framework and versions of .NET Core prior to 3.0, you could set the value of a
static field that's constant after it is initialized (readonly in C#) by calling
System.Reflection.FieldInfo.SetValue. However, setting such a field in this way resulted in
unpredictable behavior based on the target framework and optimization settings.
In .NET Core 3.0 and later versions, when you call SetValue on a static, InitOnly field, a
System.FieldAccessException exception is thrown.
 Tip
An InitOnly field is one that can only be set at the time it's declared or in the
constructor for the containing class. In other words, it's constant after it is
initialized.
Version introduced
3.0
Recommended action
Initialize static, InitOnly fields in a static constructor. This applies to both dynamic and
non-dynamic types.
Alternatively, you can remove the FieldAttributes.InitOnly attribute from the field, and
then call FieldInfo.SetValue.
Category
Core .NET libraries
Affected APIs
FieldInfo.SetValue(Object, Object)
FieldInfo.SetValue(Object, Object, BindingFlags, Binder, CultureInfo)
Passing GroupCollection to extension methods taking
IEnumerable<T> requires disambiguation
When calling an extension method that takes an IEnumerable<T> on a GroupCollection,
you must disambiguate the type using a cast.
Change description
Starting in .NET Core 3.0, System.Text.RegularExpressions.GroupCollection implements
IEnumerable<KeyValuePair<String,Group>> in addition to the other types it implements,
including IEnumerable<Group> . This results in ambiguity when calling an extension
method that takes an IEnumerable<T>. If you call such an extension method on a
GroupCollection instance, for example, Enumerable.Count, you'll see the following
compiler error:
CS1061: 'GroupCollection' does not contain a definition for 'Count' and no accessible
extension method 'Count' accepting a first argument of type 'GroupCollection' could
be found (are you missing a using directive or an assembly reference?)
In previous versions of .NET, there was no ambiguity and no compiler error.
Version introduced
3.0
Reason for change
This was an unintentional breaking change . Because it has been like this for some
time, we don't plan to revert it. In addition, such a change would itself be breaking.
Recommended action
For GroupCollection instances, disambiguate calls to extension methods that accept an
IEnumerable<T> with a cast.
C#
// Without a cast - causes CS1061.
match.Groups.Count(_ => true)
// With a disambiguating cast.
((IEnumerable<Group>)m.Groups).Count(_ => true);
Category
Core .NET libraries
Affected APIs
Any extension method that accepts an IEnumerable<T> is affected. For example:
System.Collections.Immutable.ImmutableArray.ToImmutableArray<TSource>
(IEnumerable<TSource>)
System.Collections.Immutable.ImmutableDictionary.ToImmutableDictionary
System.Collections.Immutable.ImmutableHashSet.ToImmutableHashSet
System.Collections.Immutable.ImmutableList.ToImmutableList<TSource>
(IEnumerable<TSource>)
System.Collections.Immutable.ImmutableSortedDictionary.ToImmutableSortedDicti
onary
System.Collections.Immutable.ImmutableSortedSet.ToImmutableSortedSet
System.Data.DataTableExtensions.CopyToDataTable
Most of the System.Linq.Enumerable methods, for example,
System.Linq.Enumerable.Count
System.Linq.ParallelEnumerable.AsParallel
System.Linq.Queryable.AsQueryable
Cryptography
BEGIN TRUSTED CERTIFICATE syntax no longer supported on Linux
EnvelopedCms defaults to AES-256 encryption
Minimum size for RSAOpenSsl key generation has increased
.NET Core 3.0 prefers OpenSSL 1.1.x to OpenSSL 1.0.x
CryptoStream.Dispose transforms final block only when writing
"BEGIN TRUSTED CERTIFICATE" syntax no longer
supported for root certificates on Linux
Root certificates on Linux and other Unix-like systems (but not macOS) can be presented
in two forms: the standard BEGIN CERTIFICATE PEM header, and the OpenSSL-specific
BEGIN TRUSTED CERTIFICATE PEM header. The latter syntax allows for additional
configuration that has caused compatibility issues with .NET Core's
System.Security.Cryptography.X509Certificates.X509Chain class. BEGIN TRUSTED
CERTIFICATE root certificate contents are no longer loaded by the chain engine starting
in .NET Core 3.0.
Change description
Previously, both the BEGIN CERTIFICATE and BEGIN TRUSTED CERTIFICATE syntaxes were
used to populate the root trust list. If the BEGIN TRUSTED CERTIFICATE syntax was used
and additional options were specified in the file, X509Chain may have reported that the
chain trust was explicitly disallowed (X509ChainStatusFlags.ExplicitDistrust). However, if
the certificate was also specified with the BEGIN CERTIFICATE syntax in a previously
loaded file, the chain trust was allowed.
Starting in .NET Core 3.0, BEGIN TRUSTED CERTIFICATE contents are no longer read. If the
certificate is not also specified via a standard BEGIN CERTIFICATE syntax, the X509Chain
reports that the root is not trusted (X509ChainStatusFlags.UntrustedRoot).
Version introduced
3.0
Recommended action
Most applications are unaffected by this change, but applications that cannot see both
root certificate sources because of permissions problems may experience unexpected
UntrustedRoot errors after upgrading.
Many Linux distributions (or distros) write root certificates into two locations: a onecertificate-per-file directory, and a one-file concatenation. On some distros, the onecertificate-per-file directory uses the BEGIN TRUSTED CERTIFICATE syntax while the file
concatenation uses the standard BEGIN CERTIFICATE syntax. Ensure that any custom root
certificates are added as BEGIN CERTIFICATE in at least one of these locations, and that
both locations can be read by your application.
The typical directory is /etc/ssl/certs/ and the typical concatenated file is
/etc/ssl/cert.pem. Use the command openssl version -d to determine the platformspecific root, which may differ from /etc/ssl/. For example, on Ubuntu 18.04, the
directory is /usr/lib/ssl/certs/ and 
Download