You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Remove RequiresDynamicCode from Microsoft.Extensions.DependencyInjection #79286
In .NET 7, with #73829, we annotated Microsoft.Extensions.DependencyInjection as RequiresDynamicCode because it supports enumerable and generic servcies with ValueTypes. When using DI with ValuesTypes, the array and generic code might not be available.
In .NET 8 we need a better approach in order to support applications that use DependencyInjection and publish for NativeAOT. DependencyInjection needs to have reliable behavior before and after publishing for NativeAOT. The application can't work successfully at development-time, but then fail after publishing with PublishAot=true.
Problem
The good thing, there are only limited scenarios where DependencyInjection needs to use dynamic code. And these scenarios are not used very often in real-world applications.
Using "Enumerable" services with ValueTypes. The code currently creates an Array for the enumerable services. The issue is with ValueTypes, there is no guarantee that the runtime has code for an Array of the ValueType.
Using "Open Generic" services with ValuesTypes. The code currently calls MakeGenericType, potentially passing in a ValueType. There is a similar issue here that there is no guarantee that the NativeAOT compiler generated code for the generic type for the ValueType.
Proposal
The proposed approach is similar to how we solved trim-compatibility with DependencyInjection in #55102.
We will resolve the 2 NativeAOT warnings above by adding a runtime check that is behind a new AppContext switch Microsoft.Extensions.DependencyInjection.VerifyAotCompatibility. The runtime check ensures the Types being used with Enumerable and Open Generic services are only Reference Types. If an application tries to create an Enumerable or Closed Generic service of a ValueType, DependencyInjection will throw an exception. The check is enabled by default when PublishAot=true.
The recommendation for applications that want to publish for NativeAOT is to put <PublishAot>true</PublishAot> in their .csproj. That way they get the appropriate analyzers and runtime behaviors during development-time, i.e. F5 in VS or dotnet run from the command line. Applications using the incompatible features above will be broken at development time if the app is intended to be published for AOT. This will give the app developer indication their application won't work after publishing for NativeAOT as well.
I definitely don't intend to break existing AOT scenarios. For iOS and Android, I don't believe they are currently using the <PublishAot>true</PublishAot> MSBuild property. @jonathanpeppers@rolfbjarne - can you confirm?
But if they are, and need this functionality turned off, they will be able to disable this property by default, even when PublishAot=true.
We don't set PublishAot outside of Native AOT. It has other SDK behaviors attached to it that would not be applicable outside Native AOT (e.g. it gates the RequiresDynamicCode analyzer). There was a discussion around using this elsewhere in dotnet/sdk#25392, but it didn't come through.
If we ever want to have this property mean something else than Native AOT, adjusting this feature flag would be just one of the many other things in the SDK that need adjusting.
Right now PublishAot means "publish with the limitations (and optimization opportunities) that apply to Native AOT" and it doesn't have a mapping to a mode of the Mono runtime. It could in the future.
Right now PublishAot means "publish with the limitations (and optimization opportunities) that apply to Native AOT" and it doesn't have a mapping to a mode of the Mono runtime. It could in the future.
For iOS, tvOS and partially Mac Catalyst, AOT is on by default, and tweaked/turned off (kind of) with UseInterpreter instead.
We will resolve the 2 NativeAOT warnings above by adding a runtime check that is behind a new AppContext switch Microsoft.Extensions.DependencyInjection.VerifyAotCompatibility.
Might I suggest Microsoft.Extensions.DependencyInjection.VerifyNativeAotCompatibility instead? Since this seems limited to NativeAOT in particular.
@eerhardt as a datapoint I've used dependency injection as a simple extensibility mechanism where I'm injecting an IEnumerable into a service where there has been multiple T's also injected into the DI infra:
I'd want to make sure IEnumerable injection for reference types would still work. From your issue description it wasn't entirely clear whether you saw this as a valid scenario.
Correct - reference types are still 100% supported. From the proposal above:
The runtime check ensures the Types being used with Enumerable and Open Generic services are only Reference Types. If an application tries to create an Enumerable or Closed Generic service of a ValueType, DependencyInjection will throw an exception.
I've been considering contributing to the DI library. It seems to me a like a code generator would be a good solution for this. It may require people modify their code in how they define their DI. But I'm curious, is anyone currently considering/working on this?
It seems to me a like a code generator would be a good solution for this. It may require people modify their code in how they define their DI. But I'm curious, is anyone currently considering/working on this?
I've been considering contributing to the DI library. It seems to me a like a code generator would be a good solution for this. It may require people modify their code in how they define their DI. But I'm curious, is anyone currently considering/working on this?
With the current pattern, a code generator doesn't work well because you can't generate "efficient" code unless you have the full closure of dependencies at compile time. That isn't possible today because the service collection is a runtime list of dependencies which makes this sort of code generation hard.
We're don't have plans to re-design how dependencies are registered (like jab does), at least not as part of the core container implementation.
ghost locked as resolved and limited conversation to collaborators on Mar 1, 2023
Activity
marek-safar commentedon Dec 7, 2022
Please don't break other AOT scenarios
davidfowl commentedon Dec 7, 2022
We're trying to make AOT scenarios work, not break them 😄
eerhardt commentedon Dec 7, 2022
I definitely don't intend to break existing AOT scenarios. For iOS and Android, I don't believe they are currently using the
<PublishAot>true</PublishAot>MSBuild property. @jonathanpeppers @rolfbjarne - can you confirm?But if they are, and need this functionality turned off, they will be able to disable this property by default, even when
PublishAot=true.MichalStrehovsky commentedon Dec 7, 2022
We don't set
PublishAotoutside of Native AOT. It has other SDK behaviors attached to it that would not be applicable outside Native AOT (e.g. it gates the RequiresDynamicCode analyzer). There was a discussion around using this elsewhere in dotnet/sdk#25392, but it didn't come through.If we ever want to have this property mean something else than Native AOT, adjusting this feature flag would be just one of the many other things in the SDK that need adjusting.
Right now PublishAot means "publish with the limitations (and optimization opportunities) that apply to Native AOT" and it doesn't have a mapping to a mode of the Mono runtime. It could in the future.
jonathanpeppers commentedon Dec 8, 2022
For platforms running Mono,
$(RunAOTCompilation)is used instead:runtime/src/mono/nuget/Microsoft.NET.Workload.Mono.Toolchain.Manifest/WorkloadManifest.targets.in
Line 57 in 41ae1ae
I don't know of anything Mono-related that uses
$(PublishAot)yet. @radical might know for sure.radical commentedon Dec 8, 2022
Yep, that's my understanding too.
rolfbjarne commentedon Dec 12, 2022
For iOS, tvOS and partially Mac Catalyst, AOT is on by default, and tweaked/turned off (kind of) with
UseInterpreterinstead.Might I suggest
Microsoft.Extensions.DependencyInjection.VerifyNativeAotCompatibilityinstead? Since this seems limited to NativeAOT in particular.jkotas commentedon Dec 12, 2022
The impact is not limited to native AOT. The patterns flagged by these checks are going to be slow on platforms without a JIT, like iOS, etc.
mitchdenny commentedon Dec 13, 2022
@eerhardt as a datapoint I've used dependency injection as a simple extensibility mechanism where I'm injecting an IEnumerable into a service where there has been multiple T's also injected into the DI infra:
Example:
Registration: https://github.com/Azure/azure-sdk-tools/blob/28c8db9782063964505d875bbc9d161baf930ab1/tools/pipeline-witness/Azure.Sdk.Tools.PipelineWitness/Startup.cs#L71
Injection Site: https://github.com/Azure/azure-sdk-tools/blob/28c8db9782063964505d875bbc9d161baf930ab1/tools/pipeline-witness/Azure.Sdk.Tools.PipelineWitness/Services/FailureAnalysis/FailureAnalyzer.cs#L13
I'd want to make sure IEnumerable injection for reference types would still work. From your issue description it wasn't entirely clear whether you saw this as a valid scenario.
mitchdenny commentedon Dec 13, 2022
@davidfowl has informed me that IEnumerable where T is a reference type would be supported.
eerhardt commentedon Dec 14, 2022
Correct - reference types are still 100% supported. From the proposal above:
Remove RequiresDynamicCode from Microsoft.Extensions.DependencyInjection
eerhardt commentedon Jan 9, 2023
I have changed the proposal to be based off the DynamicCodeSupport feature switch instead of adding one specific for DependencyInjection.
Remove RequiresDynamicCode from Microsoft.Extensions.DependencyInject…
YAJeff commentedon Jan 30, 2023
I've been considering contributing to the DI library. It seems to me a like a code generator would be a good solution for this. It may require people modify their code in how they define their DI. But I'm curious, is anyone currently considering/working on this?
eerhardt commentedon Jan 30, 2023
See https://github.com/pakrym/jab
davidfowl commentedon Jan 30, 2023
With the current pattern, a code generator doesn't work well because you can't generate "efficient" code unless you have the full closure of dependencies at compile time. That isn't possible today because the service collection is a runtime list of dependencies which makes this sort of code generation hard.
We're don't have plans to re-design how dependencies are registered (like jab does), at least not as part of the core container implementation.