-
Notifications
You must be signed in to change notification settings - Fork 4.6k
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
Microsoft.Extensions.Http.Polly 5.0.0 broke AddHttpClient - configureClient not called #45035
Comments
Polly seems like a re-herring here. It's likely updating from 3.1.x of HttpClientFactory to 5.0.x is the real change here. |
Tagging subscribers to this area: @dotnet/ncl Issue DetailsAfter updating from 3.1.10 to 5.0.0, the The relevant code (F#): type Startup(config: IConfiguration) =
member __.ConfigureServices(services: IServiceCollection) : unit =
services
.AddHttpClient(HttpClientNames.chkbox, fun client -> ... ) Hopefully this is trivial to reproduce. Let me know if not. Additional contextOutput of
|
@cmeeren Unfortunately I wasn't able to reproduce the issue. Could you please provide a minimal repro? |
Here's a repro: @BrennanConroy was right in that Polly was a red herring. If you target The relevant code: open System
open System.Net.Http
open Microsoft.AspNetCore.Builder
open Microsoft.AspNetCore.Hosting
open Microsoft.Extensions.DependencyInjection
open Microsoft.Extensions.Hosting
type Startup() =
member _.ConfigureServices(services: IServiceCollection) : unit =
services.AddHttpClient("test", fun client -> client.BaseAddress <- Uri("https://example.com"))
|> ignore
member _.Configure(app: IApplicationBuilder) : unit =
let factory = app.ApplicationServices.GetService<IHttpClientFactory>()
let client = factory.CreateClient("test")
Console.WriteLine(
"\nCLIENT BASE ADDRESS = "
+ if isNull client.BaseAddress then "NULL" else client.BaseAddress.ToString()
+ "\n"
)
[<EntryPoint>]
let main args =
Host
.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(fun builder ->
builder.UseStartup<Startup>()|> ignore)
.Build()
.Run()
0 Expected output (and actual output with
Actual output (with
|
Thank you @cmeeren! It turns out the issue happens only with F#. The problem here is that the wrong overload of AddHttpClient(this IServiceCollection services, string name, Action<HttpClient> configureClient) (link) but the one that gets actually called from F# is AddHttpClient<TClient,TImplementation>(this IServiceCollection services, string name, Func<HttpClient,TImplementation> factory) (link) where both The latter It seems that F# treats Meanwhile, you may use the following workaround services.AddHttpClient("test")
.ConfigureHttpClient(fun client -> client.BaseAddress <- Uri("https://example.com")) Please let me know if the workaround worked for you. |
Thank you for looking into this! I can confirm that the workaround works. |
Hey @eiriktsarpalis my team suggested that you could take a look at this, because you know F# well 😃 Could you please help us to understand what is happening? |
Another workaround is to a named argument for the AddHttpClient configuration callback member _.ConfigureServices(services: IServiceCollection) : unit =
services.AddHttpClient("test", configureClient = fun client -> client.BaseAddress <- Uri("https://example.com"))
|> ignore This works because the incorrect overload's callback is named |
This is a known limitation of F# delegate interop. In order to support currying, F# native lambdas use their own dedicated type but the compiler is generally capable of implicitly converting lambda literals into delegate types, sometimes requiring a bit of help. There are a few ways you can provide hints to the compiler so that the right overload is resolved (other than the one provided by @halter73): In this case, constraining the type parameter arity should suffice: member _.ConfigureServices(services: IServiceCollection) : unit =
services.AddHttpClient<_>("test", fun client -> client.BaseAddress <- Uri("https://example.com"))
|> ignore My personal preference however is to just explicitly define the delegate type, which should make your code more resilient against potential new overloads breaking your code in the future: member _.ConfigureServices(services: IServiceCollection) : unit =
services.AddHttpClient("test", Action<HttpClient>(fun client -> client.BaseAddress <- Uri("https://example.com")))
|> ignore That being said, it feels to me that the compiler should have produced an error here. @cartermp thoughts? |
I should note that |
Yep, I think @eiriktsarpalis's suggestion about being explicit w.r.t Note: I still consider myself a novice when it comes to the intricacies of overload resolution. As far as I can tell, F# will prefer the There's a general problem of F# type inference combined with overloads that is unavoidable - an added overload can be a breaking change for F# callers that rely on type inference - and this is what hit. This is an example of that. Using explicit types or named parameters can help. |
Perhaps the |
@eiriktsarpalis @cartermp thanks a lot for the explanations! @cartermp can you please link the F# docs issue once you file? |
After updating from 3.1.10 to 5.0.0, the
configureClient
parameter ofAddHttpClient
is no longer called. In particular, it is the overload that acceptsstring
andAction<HttpClient>
I have tried. I have not tested others. Reverting to 3.1.10 fixes the issue.The relevant code (F#):
Hopefully this is trivial to reproduce. Let me know if not.
Additional context
Output of
dotnet --info
:The text was updated successfully, but these errors were encountered: