Skip to content
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

Support for AddHttpClient #180

Open
jasontaylordev opened this issue Aug 16, 2022 · 4 comments
Open

Support for AddHttpClient #180

jasontaylordev opened this issue Aug 16, 2022 · 4 comments

Comments

@jasontaylordev
Copy link

Just wondering if there is currently any way to support registration of Typed clients?
For example:

services.AddHttpClient<ICatalogService, CatalogService>();

Use IHttpClientFactory to implement resilient HTTP requests | Microsoft Docs

The implementation might look something like this:

builder.Services.Scan(scan => scan
    .FromAssemblyOf<ICatalogService>()
    .AddClasses()
    .AsImplementedInterfaces()
    .WithHttpClient());

Thanks! 🙂

@khellang
Copy link
Owner

Hi @jasontaylordev! 👋🏻

It could technically be accomplished using an existing extensibility point, RegistrationStrategy:

using Microsoft.Extensions.DependencyInjection;

namespace Scrutor;

public static class HttpClientExtensions
{
    public static IServiceTypeSelector WithHttpClient(this IServiceTypeSelector selector)
    {
        return selector.UsingRegistrationStrategy(HttpClientRegistrationStrategy.Instance);
    }

    private class HttpClientRegistrationStrategy : RegistrationStrategy
    {
        public static readonly RegistrationStrategy Instance = new HttpClientRegistrationStrategy();

        public override void Apply(IServiceCollection services, ServiceDescriptor descriptor)
        {
            // TODO: Implement call to AddHttpClient, based on descriptor, using reflection...
            // services.AddHttpClient<TClient, TImplementation>();
        }
    }
}

...but there's a couple of reasons why shipping something like this out-of-the-box is problematic;

  1. I'd have to add a new dependency; Microsoft.Extensions.Http
  2. There's no non-generic overload of AddHttpClient, so I'd have to use some ugly reflection (MakeGenericMethod) for each type combination you'd want to register. This will be both slow and lead to some code I don't want to maintain 😅

Also, quite often, you'd want to configure the HttpClient instance, using the Action<HttpClient> parameter. I guess you could pass it to the registration strategy, but that would lead all the configured instances to getting the same configuration. At that point, I don't see much value in scanning and registering based on convention.

@khellang
Copy link
Owner

khellang commented Aug 17, 2022

2. There's no non-generic overload of AddHttpClient

If you could convince Microsoft to add this, it would be pretty easy to write, but looking at the code, the generic seems to go pretty deep, so it might be a hard ask 😅

@mmajcica
Copy link

No matter the implementation, this would be a nice thing to have as we too have several registrations done via AddHttpClient and all the rest is managed by Scrutor.

@ChrisonSimtian
Copy link

Hey, I stumpled across this and thought I'd share an example of how to implement a simplistic version for AddHttpClient without any additional parameters being passed through:

        public override void Apply(IServiceCollection services, ServiceDescriptor descriptor)
        {
            Type TInterface = descriptor.ServiceType;
            Type TClass = descriptor.ImplementationType!;
            Type TImplementingClass = typeof(HttpClientFactoryServiceCollectionExtensions);


            /* Get all methods named "AddHttpClient" */
            var methods = TImplementingClass.GetMethods()
                .Where(m => m.Name == "AddHttpClient" && m.IsGenericMethodDefinition)
                .ToArray();

            /* Then get the one that matches the amount of generic arguments we want to provide, 
             * in our case 2: Interface + Implementation AddHttpClient<TInterface,TImplementation>
             * Can be modified to add more parameters, but minimum is one AddHttpClient(services)
             */
            var method = methods.FirstOrDefault(m =>
            {
                var parameters = m.GetParameters();
                var genericArguments = m.GetGenericArguments();
                return parameters.Length == 1 && genericArguments.Length == 2 && parameters[0].ParameterType == typeof(IServiceCollection);
            });

            var genericMethod = method.MakeGenericMethod(TInterface, TClass);
            genericMethod.Invoke(services, [services]);
        }

This is mostly ChatGPT generated code so take it with a grain of salt. But it seems to be working so far and a good starting point for others looking into adding HttpClients via Scrutor :-)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants