Azure / Azure/azure-functions-host
IServiceCollection cannot resolve type when loaded from an outside assembly
- Dominant language
- C#
- Stars
- 2k
- Forks
- 482
- Avg merge
- 2d 11h
- Merged PRs (30d)
- 37
Description
Full Repro: [BugRepro.zip](https://github.com/Azure/Azure-Functions/files/5923767/BugRepro.zip)
I'm running into a strange issue using dependency injection, adding in a Singleton for a type that comes from an outside assembly. This is using the Azure Function framework, but I'm not sure if that has anything to do with it or if this would repro with ASP.NET Core as well. My actual "real world" implementation is far, far too complicated to outline here, but I've managed to come up with a minimal repro.
## BugRepro.dll (This is a Azure Function project)
This has two files.
Test.cs:
```
public class Test
{
private readonly AppConfig config;
public Test(AppConfig config)
{
this.config = config;
}
[FunctionName("Test")]
public async Task RunAsync([HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)] HttpRequest req)
{
return new OkObjectResult(config.SampleSetting);
}
}
```
Startup.cs:
```
namespace BugRepro
{
public class AppConfig
{
public string SampleSetting { get; set; } = "Test";
}
public static class Startup
{
public static void Configure(IServiceCollection services)
{
services.AddSingleton(new AppConfig());
}
}
}
```
This assembly has a reference to `TestDll.dll`.
## TestDll.dll (This is a normal .NET Core library)
Startup.cs:
```
[assembly: FunctionsStartup(typeof(TestDll.Startup))]
namespace TestDll
{
public class Startup : FunctionsStartup
{
public override void Configure(IFunctionsHostBuilder builder)
{
var asm = Assembly.LoadFile(Path.Combine(builder.GetContext().ApplicationRootPath, @"bin\BugRepro.dll"));
var type = asm.GetType("BugRepro.Startup");
var method = type.GetMethod("Configure");
method.Invoke(null, new object[] {builder.Services});
}
}
}
```
When the Azure Function runs, the `TestDll.Startup.Configure` method is automatically called by the framework. This method loads the other assembly, `BugRepro.dll` using reflection and calls the `BugRepro.Startup.Configure` static method, passing the `IServiceCollection`.
The `BugRepro.Startup.Configure` static method adds a single instance of `AppConfig` to the service collection. I can verify the instance has successfully been added to the service collection, and stepping all the way into the code, the right ServiceDescriptor and everything has been created. Everything appears perfect.
However, when I call the `/Test` endpoint, I get the error:
```
[2021-02-04T06:39:10.502Z] Executed 'Test' (Failed, Id=22cfb587-3ba9-401f-b0a5-8688aed7bc9d, Duration=386ms)
[2021-02-04T06:39:10.506Z] Microsoft.Extensions.DependencyInjection.Abstractions: Unable to resolve service for type 'BugRepro.AppConfig' while attempting to activate 'BugRepro.Test'.
```
Basically, it acts like the Singleton has never been registered and it cannot resolve that type.
## Ways to fix:
So, if I move the `AppConfig` class from the BugRepro.dll to TestDll.dll (basically the AppConfig type is in the same DLL as my `FunctionsStartup` class), the code works as expected.
Another way to fix it is to use an interface, which is defined in TestDll:
```
public interface IConfig
{
}
```
Then make `AppConfig` implement that interface, then register the Singleton using its interface:
```
services.AddSingleton(new AppConfig());
```
However, then I have to inject `IConfig` into the `Test` constructor rather than `AppConfig` which I do not want to do.
I'm curious if this is a bug with Azure Functions, or ASP.NET Core. I'm also wondering if there's a workaround for this issue.
Contributor guide
Research direction
Start with the supplied BugRepro.zip and inspect Test.cs and Startup.cs in BugRepro.dll, then TestDll.dll's Startup.cs and its reflective assembly loading. Run the /Test endpoint and trace the IServiceCollection registration through FunctionsStartup.Configure. Done means identifying whether the resolution failure is in Azure Functions or dependency injection and documenting a verified fix or workaround.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- azure, csharp
- Domain
- backend, cloud
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 25/100