Ninject Dependency Resolver - c#

The client which I am calling looks like this
public class CmsClient : ICmsClient
{
private readonly HttpClient _client;
private readonly ICmsSettings _cmsSettings;
public CmsClient(HttpClient client, ICmsSettings cmsSettings)
{
_client = client;
_cmsSettings = cmsSettings;
}
}
In NInjectWebCommon.cs file I am resolving like this.
kernel.Bind<ICmsClient>().To<CmsClient>()
This is not working as the constructor is expecting httpClient and cmsSetting class.
How can I resolve this?

You need to tell your kernel how to resolve those types -
kernel.Bind<ICmsClient>().To<CmsClient>();
kernel.Bind<ICmsSettings>().To<CmsSettings>();
kernel.Bind<HttpClient>().ToSelf();
var client = kernel.Get<ICmsClient>();

Related

Injecting IHttpClientFactory in structuremap

We are using an older version of Structuremap (3.1.9.463). It's been a while since I used structuremap and HttpClients alongside and I wonter how to properly inject the IHttpClientFactory in structuremap.
Simply using bootStrapper.For<IHttpClientFactory>().Use<HttpClient>(); won't work
A usage example is
public class DialogClient : IDialogClient
{
private readonly HttpClient _client;
public DialogClient(IHttpClientFactory httpClientFactory)
{
_client = httpClientFactory.CreateClient();
_client.BaseAddress = new Uri(ConfigurationManager.AppSettings["Dialog:url"]);
}
}
The project also use .NET Framework, not Core.
implement the interface
class MyHttpClientFactory: IHttpClientFactory
{
public HttpClient CreateClient(string name)
{
// logic for creating client here
}
}
and then register it
For<IHttpClientFactory>().Singleton().Use<MyHttpClientFactory>();

Using injected HttpClient from HttpClientFactory out of scope

Consider this code
public class ScopedService {
public readonly HttpClient client;
public readonly MyHostedService hostedService;
public ScopedService(HttpClient client, MyHostedService hostedService) {
this.client = client;
this.hostedService = hostedService;
}
public void LongAsyncOperationWeDontWantToWaitFor()
{
var httpTask = client.GetAsync("...");
hostedService.SaveTaskForProcessSometimeInTheFuture(httpTask);
}
}
I have a scoped service which makes an http call. I want the scope to close before the http call completes. If I created and disposed httpClient manually, then I'd do it out of scope myself. But since in this case the client is Dependency Injected, I have no control over when it's going to be disposed. Can there be a situation, that the context won't be able to close until the injected httpClient is disposed?
You could inject IHttpClientFactory and use it to create the HttpClient when you need it.

how to inject a httprequestmessage / endpoint

I have a controller in my REST API, where I do a HttpRequestMessage. The way I am doing it right now is by using the IConfiguration interface to fetch the endpoint as a variable:
public class MyController : Controller
{
private readonly IConfiguration _configuration;
private readonly HttpClient _httpClient;
public MyController(IConfiguration configuration, HttpClient httpClient){
_configuration = configuration;
_httpClient = httpClient;
}
...
...
[HttpGet]
public async Task<IActionResult> Get(){
...
...
var httpRequest = new HttpRequestMessage(HttpMethod.Get, _configuration["MY_ENDPOINT"]);
await _httpClient.SendAsync(httpRequest);
...
...
return Ok();
}
The thing is, that it is apparently better to have the api endpoint injected via an interface and I honestly don't know what or how that is done.
I do inject the HttpClient and the IConfiguration, but that's something I've done several times and seen others do. But just injecting an endpoint (without IConfiguration), seems unfamiliar for me. The ... is just because I've taken out code that doesn't have influence on the question.
Is there any simple way to just inject the endpoint - and is it just me that don't understand the reason for it?
I guess I have to create an interface and in that some logic that just returns the endpoint? But isn't that just double work?
MY SOLUTION:
the only workaround I can think of at the moment is just by injecting a string:
private readonly string _myEndpoint;
and then inject it:
_myEndpoint = Environment.GetEnvironmentVariable("MY_ENDPOINT");
and finally use that in my httpRequestMessage:
var httpRequest = new HttpRequestMessage(HttpMethod.Get, _myEndpoint);
That's not an interface, but again I don't use the IConfiguration-interface and don't write a lot of unneeded code.
If any better / smarter suggestion, then please shout out.
There is a way that you can load "options" into the service collection via:
services.Configure<EndpointConfig>(Configuration.GetSection("EndPointConfig"));
The EndpointConfig here is a class that you would have to define:
public class EndpointConfig
{
public string EndpointUrl {get;set;}
}
In this particular example the appsettings.json "EndPointConfig" would need a EndpointUrl, here's a rough example:
{
"EndPointConfig" : {
"EndpointUrl" : "https://localhost"
}
}
then when you get to your controller you pass in the config like so:
private readonly EndpointConfig _configuration;
private readonly HttpClient _httpClient;
public MyController(IOptions<EndpointConfig> configuration, HttpClient httpClient){
_configuration = configuration.Value;
_httpClient = httpClient;
}
There is some good documentation around this if you would like to try it: https://learn.microsoft.com/en-us/aspnet/core/fundamentals/configuration/options?view=aspnetcore-3.1
If you don't want to define a EndpointConfig section in your appsettings.json as your describing in the comments then you would simply configue i with the Configuration object:
services.Configure<EndpointConfig>(Configuration);
Now it will search for the property name (in this case EndpointUrl) in the base object of your appsettings json:
{
"EndpointUrl" : "https://localhost"
}
If you want to look for a different name i.e. My_Endpoint you would have to simply rename your property:
public class EndpointConfig
{
public string My_Endpoint {get; set;}
}

How to pass/inject more than one HttpClient parameter to a typed HttpClientClass?

I want to register a typed HttpClient as here Microsoft docs.
Basically, the approach should be
services.AddHttpClient();
normally the pattern of these classes receive only the HttpClient class as a parameter and you implement the logic to call the endpoint. In my case, I need to use 2 HttpClient inside my MyHttpClient, one that pings the endpoint and the other one that talks with an IdentityProvider to discover the refreshEndpoints to refresh my cookies.
public class MyHttpClient : IMyHttpClient
{
public MyHttpClient (HttpClient httpClient,
HttpClient refreshHttpClient)
{
}
}
If I am trying to resolve from a controller an IMyHttpClient, I get an error saying it can't resolve an HttpClient.
In the GitHub code on line 43 AddHttpClient you can see that is calling
DefaultTypedHttpClientFactory.
If you go to the implementation of the DefaultTypedHttpClientFactory implementation you will notice that is a generic type. And when it calls CreateClient it only passes one parameter to the constructor on line 39.
The only workaround I am seeing here is to not create a typed client and register a normal class that receives an IHttpClientFactory and create and configure my clients on the fly, not as typed.
Any other idea?
You can't. You'll either need to inject another service layer or IHttpClientFactory directly
Another service
public class MyRefreshClient
{
private readonly HttpClient _httpClient;
public MyRefreshClient(HttpClient httpClient)
{
_httpClient = httpClient;
}
...
}
public class MyHttpClient : IMyHttpClient
{
private readonly HttpClient _httpClient;
private readonly MyRefreshClient _refreshClient;
public MyHttpClient(HttpClient httpClient, MyRefreshClient refreshClient)
{
_httpClient = httpClient;
_refreshClient = refreshClient;
}
}
Then:
services.AddHttpClient<MyRefreshClient>(c => { ... });
services.AddHttpClient<MyHttpClient>(c => { ... });
Inject IHttpClientFactory (and use named clients):
public class MyHttpClient : IMyHttpClient
{
private readonly HttpClient _httpClient;
private readonly HttpClient _refreshClient;
public MyHttpClient(IHttpClientFactory httpClientFactory)
{
_httpClient = httpClientFactory.CreateClient("MyHttpClient");
_refreshClient = httpClientFactory.CreateClient("MyRefreshClient");
}
}
Then:
services.AddHttpClient("MyHttpClient", c => { ... });
services.AddHttpClient("MyRefreshClient", c=> { ... });

Creating stub for `private static readonly` field

Due on Improper Instantiation problem it is recommended to create private static readonly instance of HttpClient.
Due on lack of time I have injected mocked client into test method with client as their parameter.
The problem is how can I in simple way inject mock into private static readonly HttpClient field of SingleHttpClientInstanceController?
how can I in simple way inject mock into private static readonly
HttpClient field of SingleHttpClientInstanceController?
Answer: There is no simple way.
Suggestion:
Abstract the resource behind an accessor
public interface IHttpClientAccessor {
HttpClient HttpClient { get; }
}
and inject that into the dependent controller.
public class SingleHttpClientInstanceController : ApiController {
private readonly HttpClient HttpClient;
public SingleHttpClientInstanceController(IHttpClientAccessor httpClientAccessor) {
HttpClient = httpClientAccessor.HttpClient;
}
// This method uses the shared instance of HttpClient for every call to GetProductAsync.
public async Task<Product> GetProductAsync(string id) {
var hostName = HttpContext.Current.Request.Url.Host;
var result = await HttpClient.GetStringAsync(string.Format("http://{0}:8080/api/...", hostName));
return new Product { Name = result };
}
}
The same should also be done for accessing HttpContext which is what was recently introduced in Asp.Net-Core's IHttpContextAccessor
An implementation of the IHttpClientAcessor can look something like this
public class HttpClientAccessor : IHttpClientAccessor {
static readonly Lazy<HttpClient> client = new Lazy<HttpClient>(() => new HttpClient());
public HttpClient HttpClient { get { return client.Value; } }
}
So now for tests you can inject mock of the dependency.
If using a DI container remember to register the accessor as a singleton as well.

Categories

Resources