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

IConfiguration not resolving inside plugins #111022

Open
codeaphex opened this issue Jan 1, 2025 · 7 comments
Open

IConfiguration not resolving inside plugins #111022

codeaphex opened this issue Jan 1, 2025 · 7 comments
Labels
area-Extensions-Configuration needs-further-triage Issue has been initially triaged, but needs deeper consideration or reconsideration
Milestone

Comments

@codeaphex
Copy link

Description

If I try to resolve the IConfiguration inside a loaded plugin, the serviceProvider fails to resolve the instance, even if it is listed with its resolve-function inside the service descriptors.

I basically followed this tutorial: https://learn.microsoft.com/en-us/dotnet/core/tutorials/creating-app-with-plugin-support
Changed the console app to use GenericHost to enable DI and Configuration usage.
And tried to access the IConfiguration instance inside the plugin.

Relevant output from StackTrace: Method System.Reflection.MemberInfo.get_CustomAttributes cannot be called in this context

AdditionalInfo:
IConfiguration is listed in the ServiceDescriptors with

  • Lifetime = Singleton
  • ServiceType = "Microsoft.Extensions.Configuration.IConfiguration"
  • ImplementationFactory = Microsoft.Extensions.Configuration.IConfiguration b__0(System.IServiceProvider)

I also can not create a Scope from the injected ServiceProvider.

Reproduction Steps

  1. Clone the repro: https://github.com/codeaphex/pluginIConfigRepro
  2. Enable either the direct constructor injection of IConfiguration, or retrieval of IConfiguration from the serviceProvider in https://github.com/codeaphex/pluginIConfigRepro/blob/main/HelloPlugin/HelloCommand.cs

Expected behavior

IConfiguration should be resolved inside the plugin just like the service provider.

Actual behavior

  1. Get an error indicating that no type has been registered for IConfiguration
  2. Stack Trace shows some error saying: Method System.Reflection.MemberInfo.get_CustomAttributes cannot be called in this context

Regression?

No response

Known Workarounds

No response

Configuration

.NET 9
Windows 11
x64

Other information

No response

@dotnet-policy-service dotnet-policy-service bot added the untriaged New issue has not been triaged by the area owner label Jan 1, 2025
@codeaphex codeaphex changed the title Some services not resolving inside plugins IConfiguration not resolving inside plugins Jan 1, 2025
@KalleOlaviNiemitalo
Copy link

I think the problem is that the plugin has its own copy of the Microsoft.Extensions.Configuration.Abstractions assembly, and PluginLoadContext resolves to that, so there are two copies of the IConfiguration type.

Does it work if you change the package references in HelloPlugin to these (i.e. use the *.Abstractions packages and exclude runtime assets):

  <ItemGroup>
    <PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="9.0.0"> 
      <ExcludeAssets>runtime</ExcludeAssets>
    </PackageReference>
    <PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="9.0.0">
      <ExcludeAssets>runtime</ExcludeAssets>
    </PackageReference>
  </ItemGroup>

Alternatively, I guess you could change PluginLoadContext to force the plugin to use the application's copies of those assemblies; but it seems better to exclude the assets so that they are not unnecessarily deployed with the plugin.

@tarekgh tarekgh added this to the Future milestone Jan 1, 2025
@tarekgh tarekgh added needs-author-action An issue or pull request that requires more info or actions from the author. and removed untriaged New issue has not been triaged by the area owner labels Jan 1, 2025
@KalleOlaviNiemitalo
Copy link

Relevant output from StackTrace: Method System.Reflection.MemberInfo.get_CustomAttributes cannot be called in this context

I didn't find the "cannot be called in this context" message anywhere in the .NET Runtime source code.

@codeaphex
Copy link
Author

Does it work if you change the package references in HelloPlugin to these (i.e. use the *.Abstractions packages and exclude runtime assets):

Awesome, this fixed it. This seems to be explained in the docs somehow, but was confused as the serviceType still showed as registered with the serviceProvider. Didnt think about having it twice and then not receiving any.

@dotnet-policy-service dotnet-policy-service bot added needs-further-triage Issue has been initially triaged, but needs deeper consideration or reconsideration and removed needs-author-action An issue or pull request that requires more info or actions from the author. labels Jan 1, 2025
@KalleOlaviNiemitalo
Copy link

KalleOlaviNiemitalo commented Jan 2, 2025

Perhaps the DI container implementation could be changed so that, if it is going to throw an exception because a service is required but has not been registered, then it would check if a service has been registered for an identically-named but distinct type, and change Exception.Message to mention that as a probable reason of the error.

@codeaphex
Copy link
Author

Perhaps the DI container implementation could be changed so that, if it is going to throw an exception because a service is required but has not been registered, then it would check if a service has been registered for an identically-named but distinct type, and change Exception.Message to mention that as a probable reason of the error.

If I understand you correctly this would be a reasonable solution. So the current problem was that there were two IConfiguration types which are of different types because they were loaded from different assemblies (the plugin and the project one).
The plugin tries to get the implementation from its own interface which has no registrations of IConfiguration obviously.
The service provider showed the IConfiguration type that is registered with the main application, and the dev has currently no way to see this difference other than not getting the implementation back.

So with your solution the plugin developer, would get a warning that there is an identical type registered and probably point to the suggested solution in the docs to exclude the assets at runtime?

@KalleOlaviNiemitalo
Copy link

KalleOlaviNiemitalo commented Jan 2, 2025

It could be part of ServiceProviderOptions.ValidateOnBuild too. If validation fails, then BuildServiceProvider compare the type names in order to detect whether an assembly loading problem is the cause. So the developer would get a better error message in the "development" environment where this validation is enabled by default. If implemented that way, I hope the extra checks would not hurt performance in production environments.

@KalleOlaviNiemitalo
Copy link

In your plugin scenario, it might be best to make the PluginBase project reference the Microsoft.Extensions.Configuration.Abstractions package and let HelloPlugin get the reference from there. If plugins require IConfiguration to be available for injection into their constructors, then that is part of the plugin contract even if PluginBase.ICommand does not reference IConfiguration. If the set of assemblies that the host provides to plugins changes over time, then it is nicer to define it centrally in PluginBase than separately in each plugin.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area-Extensions-Configuration needs-further-triage Issue has been initially triaged, but needs deeper consideration or reconsideration
Projects
None yet
Development

No branches or pull requests

3 participants