-
Notifications
You must be signed in to change notification settings - Fork 4.9k
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
Loss of scoped lifetime service instances when resolving HttpClient services configured through IServiceCollection.AddHttpClient(...) #42142
Comments
@pranavkm @mkArtakMSFT can one of you transfer this if appropriate? If no, please respond. |
After further experimentation on this I believe I have narrowed down the scenario where this issue surfaces. Anytime the HttpClientFactory internally determines that it needs to generate a new HttpClientMessageHandler it seemingly wipes out whatever scoped lifetime service instances were already instantiated at the time. Otherwise if the HttpClientFactory creates a new instance of HttpClient but without creating new HttpClientMessageHandlers then the scoped lifetime services remain intact. This would explain why on the first resolution of HttpClients in my sample code above it forces re-instantiation of class Scoped2 (since on the first time it must create message handlers) and on the 2nd+ resolutions it doesn't... that is until some time later when the HttpClientFactory determines it must create new MessageHandlers again. At that point the scoped services are wiped out again. |
Thanks for the details, @BoricuaEnLaLuna. |
Ping @rynowak |
@rynowak have you had a chance to confirm this behavior? Thanks! |
I believe I provided all the needed details. If the behavior is indeed as I described it blocks me from using HttpClient services since I will not have control over this behavior and it causes random failures for my solution. I would appreciate if there could be some updates here since it has been a while now. Thanks! |
Hi folks, any chance there are any updates on this? Has someone at least been able to confirm the behavior I described above 8 months ago? Thanks! |
Hi again, I had put this project on hold ever since I found & opened this issue but decided to revisit it this past weekend. I can confirm again this is still very much reproable. I also tried updating to the latest versions of all the relevant libraries (including upgrade to Azure Functions v3) and the same issue also easily reproes there. For your convenience I have attached my own version of the sample projects (one with my original Azure Functions V2 setup and a second one with latest Azure Functions V3). All you need to do is:
@Pilchie could you please tag whoever is needed for investigation on this? If my outside-view assessment of the issue is correct I feel this merits some attention. Thanks! |
@karelz any ideas who could take a look here? @BoricuaEnLaLuna sorry for the slowness here. Unfortunately, the primary engineer who worked on HttpClientFactory is no longer on the team, so it might take a bit to dig in here. |
I can assign someone next week-ish. Unfortunately we have to ramp up on the code base and API usage patterns from scratch first :(, so it will take some time. |
I understand if it takes some time for ramp-up. |
Tagging subscribers to this area: @dotnet/ncl |
Tagging subscribers to this area: @maryamariyan |
Ping @dotnet/ncl @karelz |
Maybe another perspective would help, if you consider this service registration: services.AddHttpClient<MyClient>(ConfigureClient).AddHttpMessageHandler(CreateHandler);
DelegatingHandler CreateHandler(IServiceProvider sp)
{
// Something where scope of sp is important
}
void ConfigureClient(HttpClient client) { ... } Since But this doesn't work because runtime/src/libraries/Microsoft.Extensions.Http/src/DefaultHttpClientFactory.cs Lines 153 to 166 in f9004fe
So instead you're forced to workaround by basically replacing services.AddHttpClient();
services.AddTransient<MyClient>(sp =>
{
var messageHandlerFactory = sp.GetRequiredService<IHttpMessageHandlerFactory>();
var clientFactory = sp.GetRequiredService<ITypedHttpClientFactory<MyClient>>();
var factoryHandler = messageHandlerFactory.CreateHandler();
var fullHandler = CreateHandler(sp);
fullHandler.InnerHandler = factoryHandler;
var httpClient = new HttpClient(fullHandler, disposeHandler: false);
ConfigureClient(httpClient);
return clientFactory.CreateClient(httpClient);
} Which is not ideal for many reasons. I see a couple possible solutions for this, but unfortunately nothing non-breaking.. One way though could be to add an overload to But then you get into a more fundamental problem. Since the main point of Edit: there's some other great discussion on this in #43660 |
Triage: Reproduces on Azure Functions environment, doesn't reproduce on .NET Core Generic Host. Next step: try ASP.NET Core environment |
The issue was also not reproducible on ASP.NET Core which made me search for the root of the problem inside Azure Functions environment. I discovered that by default Azure Functions work with DI scopes in a non-"conventional" way: creating a new scope "hides" an existing scope until that new scope is disposed. That is why the behavior described in this issue is only reproducible in Azure Functions environment. There is actually a tracking issue for that Azure/azure-functions-host#5098 Please let me know if adding this feature flag solved the problem @BoricuaEnLaLuna |
Closing as answered above |
Issue description
Hi, it seems there is an issue that causes scoped lifetime service instances to be lost (and potentially re-instantiated within the same request/scope). This is completely the opposite of what one would intend when setting up a service with scoped lifetime and can cause bugs that are really cryptic to figure out (especially since the behavior can vary depending on the order in which services are defined in the constructors of the services they are injected into).
This seems to happen only when resolving services that are setup as "http clients" following the instructions in the following documentation: https://docs.microsoft.com/en-us/aspnet/core/fundamentals/http-requests?view=aspnetcore-2.2
This is a bit tedious to explain, but Ill try my best here. I am using Azure Functions V2 (.Net Core 2.2 & Microsoft.Extensions.Http 2.2.0). For the purposes of the following scenarios assume I have the following:
Scenario 1: If NOT using AddHttpClient the scoped lifetimes work as expected.
In this case I setup my http client services as simple Transients:
With this setup when I call the "Function1" httptrigger through POSTMAN I see the expected sequence of service instantiations:
Scenario 2: If using AddHttpClient then scoped lifetime services can be instantiated more than once per request.
In this case I setup my http client services with AddHttpClient following the documentation I referenced above:
With this setup when I call the "Function1" httptrigger through POSTMAN I see the unexpected re-instantiations of class Scoped2:
Additional to this example if I switch around the order of the parameters in the constructor of class "Scoped1" I also see different unexpected instantiation sequences. After playing around with this for a while I believe what is happening is that each time an "http client" service is resolved for the very first time in the lifetime of a request, it seems to clear any services that may have already been resolved in the scoped lifetime up to that point. However if that same service is resolved a 2nd+ time (within the same request lifetime) then it no longer seems to clear the scoped lifetime services.
Is this a known issue or am I misunderstanding something?
Thanks
Software versions
Check the .NET target framework(s) being used, and include the version number(s).
If using the .NET Core SDK, include
dotnet --info
output. If using .NET Framework without the .NET Core SDK, include info from Visual Studio's Help > About Microsoft Visual Studio dialog.dotnet --info output or About VS info
<replace>
The text was updated successfully, but these errors were encountered: