Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Extensions breaking changes #33714

Merged
merged 7 commits into from
Mar 7, 2023
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions docs/core/compatibility/8.0.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,14 @@ If you're migrating an app to .NET 8, the breaking changes listed here might aff
| [AesGcm authentication tag size on macOS](cryptography/8.0/aesgcm-auth-tag-size.md) | Behavioral change | Preview 1 |
| [RSA.EncryptValue and RSA.DecryptValue obsolete](cryptography/8.0/rsa-encrypt-decrypt-value-obsolete.md) | Behavioral change | Preview 1 |

## Extensions

| Title | Type of change | Introduced |
| ---------------------------------------------------------------------------------------------------------------------- | ----------------- | ---------- |
| [ActivatorUtilities.CreateInstance behaves consistently](extensions/8.0/activatorutilities-createinstance-behavior.md) | Behavioral change | Preview 1 |
| [ActivatorUtilities.CreateInstance requires non-null provider](extensions/8.0/activatorutilities-createinstance-null-provider.md) | Behavioral change | Preview 1 |
| [ConfigurationBinder throws for mismatched value](extensions/8.0/configurationbinder-exceptions.md) | Behavioral change | Preview 1 |

## Globalization

| Title | Type of change | Introduced |
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
---
title: "Breaking change: ActivatorUtilities.CreateInstance behaves consistently"
description: Learn about the .NET 8 breaking change in .NET extensions where ActivatorUtilities.CreateInstance behaves consistently regardless of the order of constructor overloads.
ms.date: 02/28/2023
---
# ActivatorUtilities.CreateInstance behaves consistently

The behavior of <xref:Microsoft.Extensions.DependencyInjection.ActivatorUtilities.CreateInstance%2A?displayProperty=nameWithType> is now more consistent with <xref:Microsoft.Extensions.DependencyInjection.ActivatorUtilities.CreateFactory(System.Type,System.Type[])>. When <xref:Microsoft.Extensions.DependencyInjection.IServiceProviderIsService> isn't present in the dependency injection (DI) container, <xref:Microsoft.Extensions.DependencyInjection.ActivatorUtilities.CreateInstance%2A> falls back to the <xref:Microsoft.Extensions.DependencyInjection.ActivatorUtilities.CreateFactory(System.Type,System.Type[])> logic. In that logic, only one constructor is allowed to match with all the provided input parameters.

In the more general case when <xref:Microsoft.Extensions.DependencyInjection.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:

```csharp
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

<xref:Microsoft.Extensions.DependencyInjection.ActivatorUtilities.CreateInstance%2A?displayProperty=nameWithType> 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

<xref:Microsoft.Extensions.DependencyInjection.ActivatorUtilities.CreateInstance%2A> tries to find the longest constructor that matches all parameters based on the behavior of <xref:Microsoft.Extensions.DependencyInjection.IServiceProviderIsService>.

- If no constructors are found or if <xref:Microsoft.Extensions.DependencyInjection.IServiceProviderIsService> isn't present, it falls back to <xref:Microsoft.Extensions.DependencyInjection.ActivatorUtilities.CreateFactory(System.Type,System.Type[])> logic.
- If it finds more than one constructor, it throws an <xref:System.InvalidOperationException>.

> [!NOTE]
> If <xref:Microsoft.Extensions.DependencyInjection.IServiceProviderIsService> is configured incorrectly or doesn't exist, `CreateInstance` may function incorrectly or ambiguously.

## Type of breaking change

This change is a [behavioral change](../../categories.md#behavioral-change).
gewarren marked this conversation as resolved.
Show resolved Hide resolved

## 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](#new-behavior) section.

## Affected APIs

- <xref:Microsoft.Extensions.DependencyInjection.ActivatorUtilities.CreateInstance%60%601(System.IServiceProvider,System.Object[])?displayProperty=fullName>
- <xref:Microsoft.Extensions.DependencyInjection.ActivatorUtilities.CreateInstance(System.IServiceProvider,System.Type,System.Object[])?displayProperty=fullName>

## See also

- [ActivatorUtilities.CreateInstance requires non-null provider](activatorutilities-createinstance-null-provider.md)
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
---
title: "Breaking change: ActivatorUtilities.CreateInstance requires non-null provider"
description: Learn about the .NET 8 breaking change in .NET extensions where ActivatorUtilities.CreateInstance throws an ArgumentNullException if the provider is null.
ms.date: 02/28/2023
---
# ActivatorUtilities.CreateInstance requires non-null provider

The two <xref:Microsoft.Extensions.DependencyInjection.ActivatorUtilities.CreateInstance%2A?displayProperty=nameWithType> methods now throw an <xref:System.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 <xref:System.ArgumentNullException> exception is thrown.

## Type of breaking change

This change is a [behavioral change](../../categories.md#behavioral-change).

## Reason for change

We fixed various the parameter validation along with [constructor-matching issues](activatorutilities-createinstance-behavior.md) to align with the intended purpose of <xref:Microsoft.Extensions.DependencyInjection.ActivatorUtilities.CreateInstance%2A>. 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 <xref:System.IServiceProvider> for the `provider` argument. If the provider also implements <xref:Microsoft.Extensions.DependencyInjection.IServiceProviderIsService>, constructor arguments can be obtained through that.

Alternatively, if your scenario doesn't require dependency injection, since <xref:System.IServiceProvider> is `null`, use <xref:System.Activator.CreateInstance%2A?displayProperty=nameWithType> instead.

## Affected APIs

- <xref:Microsoft.Extensions.DependencyInjection.ActivatorUtilities.CreateInstance%60%601(System.IServiceProvider,System.Object[])?displayProperty=fullName>
- <xref:Microsoft.Extensions.DependencyInjection.ActivatorUtilities.CreateInstance(System.IServiceProvider,System.Type,System.Object[])?displayProperty=fullName>

## See also

- [ActivatorUtilities.CreateInstance behaves consistently](activatorutilities-createinstance-behavior.md)
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
---
title: "Breaking change: ConfigurationBinder throws for mismatched value"
description: Learn about the .NET 8 breaking change in .NET extensions where ConfigurationBinder throws an exception if ErrorOnUnknownConfiguration is true and a config value doesn't match the model value.
ms.date: 01/27/2023
---
# ConfigurationBinder throws for mismatched value

Previously, <xref:Microsoft.Extensions.Configuration.BinderOptions.ErrorOnUnknownConfiguration?displayProperty=nameWithType> 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:

```csharp
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"},
};
gewarren marked this conversation as resolved.
Show resolved Hide resolved

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 <xref:System.InvalidOperationException> is thrown.

## Type of breaking change

This change is a [behavioral change](../../categories.md#behavioral-change).

## Reason for change

The previous behavior was confusing for some developers. They set <xref:Microsoft.Extensions.Configuration.BinderOptions.ErrorOnUnknownConfiguration?displayProperty=nameWithType> 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 <xref:Microsoft.Extensions.Configuration.BinderOptions.ErrorOnUnknownConfiguration?displayProperty=nameWithType> to `false`.

## Affected APIs

- <xref:Microsoft.Extensions.Configuration.ConfigurationBinder.Bind(Microsoft.Extensions.Configuration.IConfiguration,System.Object,System.Action{Microsoft.Extensions.Configuration.BinderOptions})?displayProperty=fullName>
- <xref:Microsoft.Extensions.Configuration.ConfigurationBinder.Get%60%601(Microsoft.Extensions.Configuration.IConfiguration,System.Action{Microsoft.Extensions.Configuration.BinderOptions})?displayProperty=fullName>
- <xref:Microsoft.Extensions.Configuration.ConfigurationBinder.Get(Microsoft.Extensions.Configuration.IConfiguration,System.Type,System.Action{Microsoft.Extensions.Configuration.BinderOptions})?displayProperty=fullName>
16 changes: 16 additions & 0 deletions docs/core/compatibility/toc.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,14 @@ items:
href: cryptography/8.0/aesgcm-auth-tag-size.md
- name: RSA.EncryptValue and RSA.DecryptValue are obsolete
href: cryptography/8.0/rsa-encrypt-decrypt-value-obsolete.md
- name: Extensions
items:
- name: ActivatorUtilities.CreateInstance behaves consistently
href: extensions/8.0/activatorutilities-createinstance-behavior.md
- name: ActivatorUtilities.CreateInstance requires non-null provider
href: extensions/8.0/activatorutilities-createinstance-null-provider.md
- name: ConfigurationBinder throws for mismatched value
href: extensions/8.0/configurationbinder-exceptions.md
- name: Globalization
items:
- name: TwoDigitYearMax default is 2049
Expand Down Expand Up @@ -1100,6 +1108,14 @@ items:
href: /ef/core/what-is-new/ef-core-3.x/breaking-changes?toc=/dotnet/core/compatibility/toc.json&bc=/dotnet/breadcrumb/toc.json
- name: Extensions
items:
- name: .NET 8
items:
- name: ActivatorUtilities.CreateInstance behaves consistently
href: extensions/8.0/activatorutilities-createinstance-behavior.md
- name: ActivatorUtilities.CreateInstance requires non-null provider
href: extensions/8.0/activatorutilities-createinstance-null-provider.md
- name: ConfigurationBinder throws for mismatched value
href: extensions/8.0/configurationbinder-exceptions.md
- name: .NET 7
items:
- name: ContentRootPath for apps launched by Windows Shell
Expand Down