Breaking changes for migration from Version 2.2 to 3.0

Important

This article is under construction. This is not a complete list of .NET Core breaking changes. For more information on .NET Core breaking changes, you can examine individual breaking changes issues in the dotnet/docs repository on GitHub.

If you are migrating from version 2.2 to version 3.0 of .NET Core, ASP.NET Core, or EF Core, review the following topics for breaking changes that may affect your app:

CoreFx

APIs that report version now report product and not file version

Many of the APIs that return versions in .NET Core now returned 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.

Difference in product version information

Version introduced

3.0

None. This change should make version detection intuitive rather than obtuse.

Category

CoreFx

Affected APIs


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.

In .NET Core Preview 7 and earlier versions, 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 well-formed, 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

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

CoreFx

Affected APIs


Floating-point formatting and parsing behavior changed

Floating point parsing and formatting behavior (by the Double and Single types) are now IEEE-compliant.

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 is 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, parsing and formatting operations are IEEE 754-compliant. This ensures that the behavior of floating-point types in .NET matches that of IEEE-compliant languages such as C#. For more information, see the Floating-point parsing and formatting improvements in .NET Core 3.0 blog post.

Version introduced

3.0

The "Potential impact to existing code" section of the Floating-point parsing and formatting improvements in .NET Core 3.0 blog post suggests changes to your code if you observe a change of behavior when compared to .NET Core 2.2 applications Generally, this involves using a different standard or custom format string to enforce the desired behavior. Some results may not have a workaround if they were previously incorrect.

Category

CoreFx

Affected APIs


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

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, the assembly the assembly referenced in the method call should be updated to reflect the type's new assembly location.

Category

CoreFx

Affected APIs

  • None

.NET Core 3.0 follows Unicode best practices when replacing ill-formed UTF-8 byte sequences

When the UTF8Encoding class encounters an ill-formed UTF-8 byte sequence during a byte-to-character transcoding operation, it will replace 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 (see the [UTF8Encoding constructor documentation](UTF8Encoding(Boolean, Boolean), the UTF8Encoding instance will continue to throw on invalid input rather than perform U+FFFD replacement.

The following 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 output) [ FFFD FFFD FFFD ] (3-character output)

This 3-char output is the preferred output, according to Table 3-9 of the previously linked Unicode Standard PDF.

Version introduced

3.0

No action is required on the part of the developer.

Category

CoreFx

Affected APIs


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

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

Repackage any zip archive that exhibits these problems.

Category

CoreFx

Affected APIs

Cryptography

EnvelopedCms defaults to AES-256 encryption

The default symmetric encryption algorithm used by EnvelopedCms has changed from TripleDES to AES-256.

Change description

In .NET Core Preview 7 and earlier versions, when EnvelopedCms is used to encrypt data without specifying a symmetric encryption algorithm via a constructor overload, the data was encrypted with the TripleDES/3DES/3DEA/DES3-EDE algorithm.

Starting with .NET Core 3.0 Preview 8 (via version 4.6.0 of the System.Security.Cryptography.Pkcs NuGet package), the default algorithm has been changed to AES-256 for algorithm modernization and to improve the security of default options. If a message recipient certificate has a (non-EC) Diffie-Hellman public key, the encryption operation may fail with a CryptographicException due to limitations in the underlying platform.

In the following sample code, the data is encrypted with TripleDES if running on .NET Core 3.0 Preview 7 or earlier. If running on .NET Core 3.0 Preview 8 or later, it is encrypted with AES-256.

EnvelopedCms cms = new EnvelopedCms(content);
cms.Encrypt(recipient);
return cms.Encode();

Version introduced

3.0 Preview 8

If you are negatively impacted by the change, you can restore TripleDES encryption by explicitly specifying the encryption algorithm identifier in an EnvelopedCms constructor that includes a parameter of type AlgorithmIdentifier, such as:

Oid tripleDesOid = new Oid("1.2.840.113549.3.7", null);
AlgorithmIdentifier tripleDesIdentifier = new AlgorithmIdentifier(tripleDesOid);
EnvelopedCms cms = new EnvelopedCms(content, tripleDesIdentifier);

cms.Encrypt(recipient);
return cms.Encode()l

Category

Cryptography

Affected APIs


Minimum size for RSAOpenSsl key generation has increased

The minimum size for generating new RSA keys on Linux has increased from 384-bit to 512-bit.

Change description

Starting with .NET Core 3.0, the minimum legal key size reported by the LegalKeySizes property on RSA instances from RSA.Create, RSAOpenSsl.RSAOpenSsl, and RSACryptoServiceProvider.RSACryptoServiceProvider on Linux has increased from 384 to 512.

As a result, in .NET Core 2.2 and earlier versions, a method call such as RSA.Create(384) succeeds. In .NET Core 3.0 and later versions, the method call RSA.Create(384) throws an exception indicating the size is too small.

This change was made because OpenSSL, which performs the cryptographic operations on Linux, raised its minimum between versions 1.0.2 and 1.1.0. .NET Core 3.0 prefers OpenSSL 1.1.x to 1.0.x, and the minimum reported version was raised to reflect this new higher dependency limitation.

Version introduced

3.0

If you call any of the affected APIs, ensure that the size of any generated keys is not less than the provider minimum.

Note

384-bit RSA is already considered insecure (as is 512-bit RSA). Modern recommendations, such as NIST Special Publication 800-57 Part 1 Revision 4, suggest 2048-bit as the minimum size for newly generated keys.

Category

Cryptography

Affected APIs


.NET Core 3.0 prefers OpenSSL 1.1.x to OpenSSL 1.0.x

.NET Core for Linux, which works across multiple Linux distributions, can support both OpenSSL 1.0.x and OpenSSL 1.1.x. .NET Core 2.1 and .NET Core 2.2 look for 1.0.x first, then fall back to 1.1.x; .NET Core 3.0 looks for 1.1.x first. This change was made to add support for new cryptographic standards.

This change may impact libraries or applications that do platform interop with the OpenSSL-specific interop types in .NET Core.

Change description

In .NET Core 2.2 and earlier versions, the runtime prefers loading OpenSSL 1.0.x over 1.1.x. This means that the IntPtr and SafeHandle types for interop with OpenSSL are used with libcrypto.so.1.0.0 / libcrypto.so.1.0 / libcrypto.so.10 by preference.

Starting with .NET Core 3.0, the runtime prefers loading OpenSSL 1.1.x over OpenSSL 1.0.x, so the IntPtr and SafeHandle types for interop with OpenSSL are used with libcrypto.so.1.1 / libcrypto.so.11 / libcrypto.so.1.1.0 / libcrypto.so.1.1.1 by preference. As a result, libraries and applications that interoperate with OpenSSL directly may have incompatible pointers with the .NET Core-exposed values when upgrading from .NET Core 2.1 or .NET Core 2.2.

Version introduced

3.0

Libraries and applications that do direct operations with OpenSSL need to be careful to ensure they are using the same version of OpenSSL as the .NET Core runtime.

All libraries or applications that use IntPtr or SafeHandle values from the .NET Core cryptographic types directly with OpenSSL should compare the version of the library they use with the new SafeEvpPKeyHandle.OpenSslVersion property to ensure the pointers are compatible.

Category

Cryptography

Affected APIs

Globalization

"C" locale maps to the invariant locale

.NET Core 2.2 and earlier versions depend on the default ICU behavior, which maps the "C" locale to the en_US_POSIX locale. The en_US_POSIX locale has an undesirable collation behavior, because it doesn't support case-insensitive string comparisons. Because some Linux distributions set the "C" locale as the default locale, users were experiencing unexpected behavior.

Change description

Starting with .NET Core 3.0, the "C" locale mapping has changed to use the Invariant locale instead of en_US_POSIX. The "C" locale to Invariant mapping is also applied to Windows for consistency.

Mapping "C" to en_US_POSIX culture caused customer confusion, because en_US_POSIX doesn't support case insensitive sorting/searching string operations. Because the "C" locale is used as a default locale in some of the Linux distros, customers experienced this undesired behavior on these operating systems.

Version introduced

3.0

Nothing specific more than the awareness of this change. This change affects only applications that use the "C" locale mapping.

Category

Globalization

Affected APIs

All collation and culture APIs are affected by this change.

Visual Basic

Microsoft.VisualBasic.Constants.vbNewLine is obsolete

The Microsoft.VisualBasic.Constants.vbNewLine constant is marked Obsolete starting with .NET Core 3.0 Preview 8.

Version introduced

3.0 Preview 8

Change description

Starting with .NET Core 3.0 Preview 8, the Obsolete attribute has been applied to the Microsoft.VisualBasic.Constants.vbNewLine constant. Use of the constant produces a compiler warning. In previous releases of both .NET Core and .NET Framework, it was not marked as obsolete.

This change was made to support Visual Basic as a language for multi-platform development. The vbNewLine constant is equivalent to \r\n, the newline character sequence on Windows. On Unix-based systems, the newline character is \n.

The Obsolete attribute message for vbNewLine includes the following recommendation:

For a carriage return and line feed, use vbCrLf. For the current platform's newline, use Environment.NewLine.

Category

Visual Basic

Affected APIs

Entity Framework Core

Entity Framework Core breaking changes