simpleinjector / SimpleInjector

An easy, flexible, and fast Dependency Injection library that promotes best practice to steer developers towards the pit of success.
https://simpleinjector.org
MIT License
1.22k stars 152 forks source link

AutoCrossWireFrameworkComponents not working as expected #943

Closed CasperWSchmidt closed 2 years ago

CasperWSchmidt commented 2 years ago

Describe the bug

According to the documentation (and code) AutoCrossWireFrameworkComponents should be true by default. However we experience the need to manually cross-wire stuff from MS.DI to SimpleInjector. As mentioned in the code in #936 IHttpContextAccessor does not seem to be cross wired automatically. Recently we made some changes to our internal Http package (moving away from RestSharp and into using IHttpClientFactory). It now looks like this needs manual cross-wiring as well.

Expected behavior

Unless setting AutoCrossWireFrameworkComponents to false I would think that cross-wiring is handled automatically.

Actual behavior

In our Azure Functions Integration package we have the following code:

services.AddSimpleInjector(Container, options =>
{
    // Prevent the use of hosted services (not supported by Azure Functions).
    options.EnableHostedServiceResolution = false;

    // Allow injecting ILogger into application components
    options.AddLogging();

    // Somehow the Asp.NetCore IHttpContextAccessor is not available internally in our 
    // SimpleInjector container unless we explicitly cross-wire it. 
    options.CrossWire<IHttpContextAccessor>();
});

trying to get any instance from the container using the updated internal Http package (called HttpClient) we get the following error:

SimpleInjector.ActivationException The configuration is invalid. Creating the instance for type IRequestHandler<GetReconciliationResultByReleaseGroupRequest, ReconciliationResult> failed. The constructor of type HttpClient contains the parameter with name 'httpClientFactory' and type IHttpClientFactory, but IHttpClientFactory is not registered. For IHttpClientFactory to be resolved, it must be registered in the container. Verification was triggered because Container.Options.EnableAutoVerification was enabled. To prevent the container from being verified on first resolve, set Container.Options.EnableAutoVerification to false.

The mentioned class HttpClient in the exception is our internal adapter/helper that handles the connection to IHttpClientFactory and contains some helper methods. All code that wants to do Http calls uses this class (through an interface) instead of IHttpClientFactory directly.

Additional context

Add any other context about the problem here. e.g.:

dotnetjunkie commented 2 years ago

If Simple Injector can't resolve IHttpClientFactory, it means that it hasn't mean registered in MS.DI's ServiceCollection. If it isn't physically part of the ServiceCollection, Simple Injector will be unable to find it and resolve it through its auto-cross-wiring feature.

CasperWSchmidt commented 2 years ago

Through debugging the startup test, we concluded that IHttpClientFactory should indeed be part of the service collection when SI is asked to resolve the service in question (and so is IHttpContextAccessor IIRC but that is secondary for now). I will ask my colleague to debug the test again and present documentation for this :)

CasperWSchmidt commented 2 years ago

So this is what the service collection looks like after our call to container.AddHttpClient - our own extension method for SI to add our http client implementation to the container as well as adding the necessary framework stuff to the service collection: image It is worth noting that the registrations for IHttpClientFactory and IHttpMessageHandlerFactory both use a lambda to resolve the DefaultHttpClientFactory from the service collection, which must be the reason that ImplementationType is null in the picture.

CasperWSchmidt commented 2 years ago

After some more debugging, my colleague decided to follow the service collection integration guide closely and add his own service collection, calling AddSimpleInjector, building the service provider and calling UseSimpleInjector before passing the servicecollection on to our Startup method. Seeing this code (and reading the integration guide REALLY CLOSELY) I had an apiphany - The call to UseSimpleInjector is triggered when the Azure Functions Runtime needs to resolve IMediator when an Azure Function is triggered. This is what the unit tests are missing - we do not rely on the Azure Functions Runtime to serve an instance of IMediator and so the call to UseSimpleInjector to finish the setup is never done!

So the issue is with our test, not the library - sorry for disturbing you :)

dotnetjunkie commented 2 years ago

Hi Casper,

I'm happy you figured this out. I was starting to expect some weird behavior in the framework. I'm the first to admit, though, that the Azure Functions integration for Simple Injector is a bit clunky and confusing. It tries to work around the limitations in Azure Functions to get the integration done. Hopefully this is something that Microsoft will improve in the future so the integration with Simple Injector can be simplified in the future.

CasperWSchmidt commented 2 years ago

Yeah I know, and I agree :) I remember we worked on the integration "together"