In HttpclientAdd, set same urlbase for all Interface from a assembly
I'm migrating a Web API client from .NET to .NET Core 5.
I have more 100 entities to migrate. I try it with a console project type.
In IHostBuilder.AddServices
I have:
services.AddHttpClient("App", client => { client.BaseAddress = new Uri(@"https://localhost:60447/"); }
services.AddTransient(IEntity001HttpClient, Entity001HttpClient);
services.AddTransient(IEntity002HttpClient, Entity002HttpClient);
.....
services.AddTransient(IEntity100HttpClient, Entity100HttpClient);
This is an example class to call server
public class Entiy001HttpClient : IEntity001HttpClient
{
private readonly System.Net.Http.HttpClient _httpClient;
public Entity001HttpClient(System.Net.Http.HttpClient _hc)
{
_httpClient = _hc;
}
public async Task<IResult<List<Entity001>>>GetAllAsync()
{
var response = await _httpClient.GetAsync(Entity001HttpRoutes.GetAll);
return await response.ToResult<List<Entity001>>();
}
}
At this point
var response = await _httpClient.GetAsync(Entity001Routes.GetAll);
_httpClient.BaseUrl
is null.
If I change IHostBuilder.AddServices
to:
services.AddHttpClient<IEntity001HttpClient>("App", client => { client.BaseAddress = new Uri(@"https://localhost:60447/"); }
services.AddHttpClient<IEntity002HttpClient>("App", client => {client.BaseAddress = new Uri(@"https://localhost:60447/"); }
....
services.AddHttpClient<IEntity100HttpClient>("App", client => {client.BaseAddress = new Uri(@"https://localhost:60447/"); }
services.AddTransient(IEntity001HttpClient, Entity001HttpClient);
services.AddTransient(IEntity002HttpClient, Entity002HttpClient);
.....
services.AddTransient(IEntity100HttpClient, Entity100HttpClient);
At this point
var response = await _httpClient.GetAsync(Entity001Routes.GetAll);
now _httpClient.BaseUrl
is https://localhost:60447
and the API call is running Ok.
Is there a way to assign the urlbase without having to add the interfaces one by one 100 times?
Another idea, is assign it by reflection, I can assign AddTransient
, but I don't know how to assign AddHttpclient
using reflection
public static IServiceCollection AddManagers(this IServiceCollection services)
{
var managers = typeof(IHttpClient);
var types = managers
.Assembly
.GetExportedTypes()
.Where(t => t.IsClass && !t.IsAbstract)
.Select(t => new
{
Service = t.GetInterface($"I{t.Name}"),
Implementation = t
})
.Where(t => t.Service != null);
foreach (var type in types)
{
if (managers.IsAssignableFrom(type.Service))
{
services.AddTransient(type.Service, type.Implementation);
services.AddHttpClient<type.Service>("App", client =>
{
client.BaseAddress = new Uri(@"https://localhost:60447/");
});
}
}
return services;
}
At this point in my code:
services.AddHttpClient<type.Service>("App", client =>
I get a compile error type.Service
Thanks
UPDATE
Thanks Richard Deeming.
I had to change some code.
First Add the cliente Extension:
static class EntityHttpClientExtensions
{
private static readonly MethodInfo AddMethodBase = typeof(EntityHttpClientExtensions).GetMethod(nameof(AddEntityHttpClient));
public static IServiceCollection AddEntityHttpClient<TClientImplementation>(this IServiceCollection services)
where TClientImplementation : class
//TClientImplementation : TClientInterface
{
string mibaseurl = @"https://localhost:57608/";
services.AddHttpClient<TClientImplementation>("App", client =>
{
client.BaseAddress = new Uri(mibaseurl);
});
return services;
}
public static IServiceCollection AddEntitiesHttpClientsAndTransientFrom(this IServiceCollection services, Assembly assembly)
{
var types = assembly.GetExportedTypes()
.Where(t => t.IsClass && !t.IsAbstract)
.Select(t => new
{
Implementation = t,
Service = t.GetInterface($"I{t.Name}"),
})
.Where(t => t.Service != null);
var typeParameters = new Type[1];
var methodParameters = new object[] { services };
foreach (var type in types)
{
services.AddTransient(type.Service, type.Implementation);
typeParameters[0] = type.Implementation;
var method = AddMethodBase.MakeGenericMethod(typeParameters);
method.Invoke(null, methodParameters);
}
return services;
}
}
}
And configure
public static IHostBuilder AddServices(this IHostBuilder host)
{
string ClientName = "IU.Consola";
host.ConfigureServices((hostingContext, services) =>
{
var configurationRoot = hostingContext.Configuration;
services.AddEntitiesHttpClientsAndTransientFrom(typeof(IHttpClient).Assembly);
services.AddHostedService<RunConsole>();
});
return host;
}
And use
var mihttpclient001 = _serviceProvider.GetRequiredService<HttpClient001>();
var midata001 = await mihttpclient001.GetAllAsync();
I have a structure that my entities inherit from class base
IHttpClient
- IHttpClient001
- IHttpClient002
- IHttpClient003
- IHttpClient003 ..... IHttpClient100
Now I append the assembly base class and the extension automatically I have httpclientfactory configurated for all my entities.
Thank you very much
1 answer
-
answered 2022-01-24 14:54
Richard Deeming
You could create your own generic extension method to configure the client and set the base address in one go:
static class EntityHttpClientExtensions { public static IServiceCollection AddEntityHttpClient<TInterface, TImplementation>( this IServiceCollection services) where TImplementation : TInterface { services.AddHttpClient<TInterface>("App", client => { client.BaseAddress = new Uri(@"https://localhost:60447/"); }); services.AddTransient<TInterface, TImplementation>(); } }
Usage:
services.AddEntityHttpClient<IEntity002HttpClient, Entity002HttpClient>(); .... services.AddEntityHttpClient<IEntity100HttpClient, Entity100HttpClient>();
If you want to register all applicable types from an assembly automatically, you'll need to use reflection:
static class EntityHttpClientExtensions { private static readonly MethodInfo AddMethodBase = typeof(EntityHttpClientExtensions) .GetMethod(nameof(AddEntityHttpClient)); public static IServiceCollection AddEntityHttpClient<TInterface, TImplementation>( this IServiceCollection services) where TImplementation : TInterface { services.AddHttpClient<TInterface>("App", client => { client.BaseAddress = new Uri(@"https://localhost:60447/"); }); services.AddTransient<TInterface, TImplementation>(); } public static IServiceCollection AddEntityHttpClientsFrom( this IServiceCollection services, Assembly assembly) { var types = assembly.GetExportedTypes() .Where(t => t.IsClass && !t.IsAbstract) .Select(t => new { Implementation = t, Service = t.GetInterface($"I{t.Name}"), }) .Where(t => t.Service != null); var typeParameters = new Type[2]; var methodParameters = new object[] { services }; foreach (var type in types) { typeParameters[0] = type.Service; typeParameters[1] = type.Implementation; var method = AddMethodBase.MakeGenericMethod(typeParameters); method.Invoke(null, methodParameters); } return services; } }
Usage:
services.AddEntityHttpClientsFrom(typeof(IEntity001HttpClient).Assembly);
do you know?
how many words do you know