Reputation: 19
i read about everything i could find about HttpClientFactory
and after a few days struggling with this, i am about to give up, but taking a chance here in case anyone could help. I am just trying to implement HttpClientFactory
in my .net 4.7.2 Winforms app for a rest-api client.
I tried implementing both Typed and Named client, but i am getting a null reference each time i am trying to instantiate it in my code. SO here what i did so far:
For the Typed Client, i created a class:
Imports System.Net.Http
Imports Microsoft.Extensions.Http
Imports Microsoft.Extensions.DependencyInjection
Public Class TypedCustomHTTPClient
Public Property _Client() As HttpClient
Public Sub New(ByVal httpClient As HttpClient)
'httpClient.BaseAddress = New Uri("https://api.google.com/")
httpClient.DefaultRequestHeaders.Add("Accept", "application/json")
httpClient.DefaultRequestHeaders.Add("User-Agent", "HttpClientFactory-Sample")
_Client = httpClient
End Sub
End Class
then my Project Main Sub i am registering my Typed Client an also a Named client (i am not sure that i am doing it the correct way)
Imports Microsoft.Extensions.DependencyInjection
Imports Microsoft.Extensions.Hosting
Imports Microsoft.Extensions.Http
Imports Polly
Module MainModule
Public Sub Main()
Dim seviceColl As New ServiceCollection
'
'--------Registering and injecting HttpClients (by Name)
seviceColl.AddHttpClient("s2sfHTTPClient", Sub(c)
'c.BaseAddress = New Uri("https://api.google.com/")
c.DefaultRequestHeaders.Add("Accept", "application/json")
End Sub).AddPolicyHandler(PolHolder.httpRetryWithReauthorizationPolicy())
seviceColl.AddHttpClient("GitHub", Sub(httpClient)
httpClient.BaseAddress = New Uri("https://api.github.com/")
' using Microsoft.Net.Http.Headers;
' The GitHub API requires two headers.
''httpClient.DefaultRequestHeaders.Add(HeaderNames.Accept, "application/vnd.github.v3+json")
''httpClient.DefaultRequestHeaders.Add(HeaderNames.UserAgent, "HttpRequestsSample")
End Sub)
'Registering and injecting HttpClients (by Type)
seviceColl.AddHttpClient(Of TypedCustomHTTPClient)().SetHandlerLifetime(TimeSpan.FromMinutes(5)).AddPolicyHandler(PolHolder.httpRetryWithReauthorizationPolicy()) 'Set lifetime to five minutes
'Building Service Provider
Dim serviceprovider = servicecoll.BuildServiceProvider
'
'
'
Application.Run(New Form1()) ''//Use your main form here
End Sub
End Module
when i try to either use the typed client or the named client (with the .CreateClient
method as it should) i am getting a Null reference error on the CreateClient
Line.
Private Property _httpClientFactory As IHttpClientFactory
Public Function TestQuery2(ByVal soqlQuery As String) As String
Dim customclient = _httpClientFactory.CreateClient("s2sfHTTPClient")
'Using customclient._Client '= New HttpClient()
'
Dim restRequest As String = InstanceUrl + API_ENDPOINT & "query/?q=" & soqlQuery
Dim request = New HttpRequestMessage(HttpMethod.[Get], restRequest)
request.Headers.Add("Authorization", "Bearer " & AuthToken)
request.Headers.Accept.Add(New MediaTypeWithQualityHeaderValue("application/json"))
request.Headers.Add("X-PrettyPrint", "1")
Dim response = customclient.SendAsync(request).Result
Return response.Content.ReadAsStringAsync().Result
'End Using
End Function
Any idea? what am i doing wrong?
Upvotes: 0
Views: 3278
Reputation: 22829
In order to be able to use IHttpClientFactory
in a .NET Framework console application you need the following things:
(I haven't used Visual Basic for long time that's why I share the sample code in C#.)
using System;
using System.Net.Http;
using Microsoft.Extensions.DependencyInjection;
class Program
{
private static readonly ServiceProvider serviceProvider;
static Program()
{
serviceProvider = new ServiceCollection().AddHttpClient().BuildServiceProvider();
}
public static void Main(string[] args)
{
var factory = serviceProvider.GetService<IHttpClientFactory>();
Console.WriteLine(factory == null); //False
}
}
ServiceCollection
and we call the AddHttpClient
extension method on it
DefaultHttpClientFactory
as singleton for IHttpClientFactory
serviceProvider
from the serviceCollection
IHttpClientFactory
from the serviceProvider
If you prefer to use SimpleInjector
then the code should look like this:
using System;
using System.Net.Http;
using Microsoft.Extensions.DependencyInjection;
using SimpleInjector;
class Program
{
private static readonly Container container = new Container();
static Program()
{
var serviceProvider = new ServiceCollection().AddHttpClient().BuildServiceProvider();
container.RegisterInstance(serviceProvider.GetService<IHttpClientFactory>());
//These are not mandatory calls but highly recommended
//container.ContainerScope.RegisterForDisposal(serviceProvider);
//container.Verify();
}
public static void Main(string[] args)
{
var factory = container.GetInstance<IHttpClientFactory>();
Console.WriteLine(factory == null); //False
}
}
IHttpClientFactory
implementation from the MS DISimpleInjector
's containerUPDATE #1 Using named client
class Program
{
private static readonly Container container = new Container();
private const string ClientName = "namedClient";
static Program()
{
var serviceCollection = new ServiceCollection().AddHttpClient();
serviceCollection.AddHttpClient(ClientName, c => c.BaseAddress = new Uri("https://httpstat.us/"));
var serviceProvider = serviceCollection.BuildServiceProvider();
container.RegisterInstance(serviceProvider.GetService<IHttpClientFactory>());
}
public static void Main()
{
var factory = container.GetInstance<IHttpClientFactory>();
var client = factory.CreateClient(ClientName);
Console.WriteLine( client.BaseAddress); // https://httpstat.us/
}
}
UPDATE #2 Using typed client
class Program
{
private static readonly Container container = new Container();
private const string ClientName = "namedClient";
static Program()
{
var serviceCollection = new ServiceCollection().AddHttpClient();
serviceCollection.AddHttpClient(ClientName, c => c.BaseAddress = new Uri("https://httpstat.us/"));
serviceCollection.AddHttpClient<ITestClient, TestClient>();
var serviceProvider = serviceCollection.BuildServiceProvider();
container.RegisterInstance(serviceProvider.GetService<IHttpClientFactory>());
container.RegisterInstance(serviceProvider.GetService<ITypedHttpClientFactory<TestClient>>());
}
public static void Main(string[] args)
{
var namedFactory = container.GetInstance<IHttpClientFactory>();
var typedFactory = container.GetInstance<ITypedHttpClientFactory<TestClient>>();
var client = typedFactory.CreateClient(namedFactory.CreateClient(ClientName));
Console.WriteLine(client == null); // False
}
}
Please note that the type parameter of ITypedHttpClientFactory
is the concrete type (TestClient
), not the interface (ITestClient
)
UPDATE #3
I just need to keep a reference to the container to retrieve the factory later from anywhere in the code and call the
CreateClient
method which work fine.
This Dependency Injection package was designed for (and as a part of) ASP.NET Core. A .NET Framework Console/WinForms application can't really harvest its true power.
Both GetService
and GetRequiredService
calls are acting as service locator rather than as dependency injector. ASP.NET Core can use this library to inject dependencies either via constructor or via property.
The only thing I don't understand is why in your code, for the Typed client example, you also make use of the NamedClient and its CreateClient method?
That's a good question. The typedFactory.CreateClient
call anticipates a HttpClient
instance. If you would pass a brand new instance
var client = typedFactory.CreateClient(new HttpClient());
//OR
var client = namedFactory.CreateClient(); //without specifying a name
then the pre-configured BaseAddress
(, c => c.BaseAddress = new Uri("https://httpstat.us/"));
) won't be available there.
So, if you have a pre-configured named client then you can retrieve it and used it to create a typed client with that. (The end result will a named typed client.)
Upvotes: 3