question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

`AddHttpClient<MyService>()` fails within Modules.

See original GitHub issue

new version of #6677 as it was asked that a new issue be created.

Typed calls to AddHttpClient are failing within a module in .Net 5.0 and .Net 6.0. (I tried rolling back to .Net 5.0 in case OC 1.1 was incompatibile with 6.0).

// MainProject/Startup.cs
class MainStartup
{        
  public void ConfigureServices(IServiceCollection services)
  {
    services.AddHttpClient<MyService>(); // this is cool
  }
}

// Module/Startup.cs
class ModuleStartup : OrchardCore.Modules.StartupBase
{ 
  public void ConfigureServices(IServiceCollection services)
  {
    services.AddHttpClient<MyService>(); // NullReferenceException
  }
}

The issue is as follows. In AspNetCore, AddHttpClient registers a singleton of HttpClientMappingRegistry.

https://github.com/dotnet/runtime/blob/57bfe474518ab5b7cfe6bf7424a79ce3af9d6657/src/libraries/Microsoft.Extensions.Http/src/DependencyInjection/HttpClientFactoryServiceCollectionExtensions.cs#L52-L54

When initializing a typed HTTP Client, HttpClientBuilderExtensions.AddTypedClient calls HttpClientBuilderExtensions.ReserveClient, which iterates through the Builder’s Services and attempts to find an instantiated instance of HttpClientMappingRegistry.

https://github.com/dotnet/runtime/blob/57bfe474518ab5b7cfe6bf7424a79ce3af9d6657/src/libraries/Microsoft.Extensions.Http/src/DependencyInjection/HttpClientBuilderExtensions.cs#L634-L636

Orchard Core’s FeatureAwareServiceCollection doesn’t automatically instantiate Singletons, for the reason that @marlon-tucker linked in the initial issue – it doesn’t implement IDisposable, so it isn’t instantiated until ServiceProvider.GetService<HttpClientMappingRegistry>() is called:

https://github.com/OrchardCMS/OrchardCore/blob/ec2154ae099913ee81751f30c4f7660952fe63c9/src/OrchardCore/OrchardCore/Shell/Builders/Extensions/ServiceProviderExtensions.cs#L42-L59

As you can see in the ReserveClient method above, however, GetService is never called on the HttpClientMappingRegistry – it is instead selected through LINQ, meaning it is never instantiated. Single() returns a registered service, but Single().ImplementedInstance is null.

image

I’ll try and provide a repro when I have the chance.

Issue Analytics

  • State:closed
  • Created 2 years ago
  • Reactions:3
  • Comments:39 (33 by maintainers)

github_iconTop GitHub Comments

2reactions
ShaneCourtrillecommented, Mar 14, 2023

We have a main TenantStartup.cs that we run before doing any other modules and were able to work around this problem by adding the following code earlier in that

    var registry = services.SingleOrDefault(sd => sd.ServiceType.Name == "HttpClientMappingRegistry");
                if (registry?.ImplementationInstance == null)
                    services.Remove(registry);
1reaction
jtkechcommented, Jun 1, 2023

In the meantime here some thoughts based on what I saw.

  • First I think that connections sharing is done at a lower IO level.

  • What I saw is that anyway a new HttpClient is always created and that handlers are resolved or activated, for a typed client an object factory is cached but not sure it takes a lot of memory.

    Yes, this is the Handlers Pipeline that is cached when built by the DefaultHttpClientFactory, but here too not sure that it takes a lot of memory, this is not the activated handler instances that are cached.

    Note: Tenants are isolated “Sites” with their Filters/Handlers, Pipeline, APIs and so on, so make sense that they also have their own http client Handlers Pipeline, but I understand the memory concern.

  • One annoying thing is that the client factory creates a scope that will be used to activate handlers, which is not a ShellScope which e.g. prevents a shell to be disposed while a scope is still active.

          if (!options.SuppressHandlerScope)
          {
              scope = _scopeFactory.CreateScope();
              services = scope.ServiceProvider;
          }
    

    Anyway the scope is created from the service provider resolved by the factory, so what happens if a DelegatingHandler resolve services that may depend on the enabled features of a given tenant.

    This is the case of our Twitter handler that injects tenant specific services including a security service, more over this handler is only registered by the tenants having the Twitter feature enabled.

    Note: Here we may need a custom service provider as we use for the RequestServices that we made aware of the current ShellScope so that it returns the ShellScope.Services if a current one exists.

  • So, in case there is not so much memory consumption by duplicating some caches, maybe a good compromise would be to support the point 2. from my previous list.

    This to allow http client configurations at both host and tenant levels but without sharing them, and let it working as before at the tenant level (no sharing across tenants).

Read more comments on GitHub >

github_iconTop Results From Across the Web

c# - GetRequiredService and AddHttpClient freeze .NET ...
1 Answer 1 ... What you're experiencing is a stack overflow exception, caused by the AddHttpClient<IHttpClientFactory> registration. This causes a ...
Read more >
Using IHttpClientFactory with Asp.Net MVC 5 - Hamid Mosalla
This post is about how we can use IHttpClientFactory with old Asp.Net MVC applications. We're going to use ninject as our DI container....
Read more >
problem with orchard core Add module programmatically
in my project i have to build a core and modules for project i found OrchardCore but the problem is : i want...
Read more >
Implement HTTP call retries with exponential backoff ...
Learn how to handle HTTP failures with Polly and IHttpClientFactory.
Read more >
Angular - How to use HttpClientModule? - Le Blog | Ninja Squad
The first obvious one is your app.module.ts file, which contains your main NgModule. Replace HttpModule with HttpClientModule in your module's ...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found