AlvinfromDiaspar
AlvinfromDiaspar

Reputation: 6834

Blazor HttpClient injection into a ViewModel constructor

I am following the referenced blog/article below, and I am unable to inject an HttpClient to my ViewModel. It works fine the default way (@inject in the razor file). But i am trying to inject into the ViewModel instead.

If i add to the services like the following, then the default injection doesnt work for other razor views which has the @inject HttpClient.

// manually add HttpClient to services.
services.AddTransient<IFetchViewModel, FetchViewModel>();

Question:

How can i inject the default injected HttpClient to my various ViewModels?

Note that I am getting an exception:

WASM: Unhandled exception rendering component:
WASM: System.Reflection.TargetParameterCountException: Number of parameters specified does not match the expected number.

Reference:

https://itnext.io/a-simple-mvvm-implementation-in-client-side-blazor-8c875c365435

Update

After making suggested changes and then digging deeper during debugging, i can see that there is something wrong with the json deserialization. Could this be an issue? https://github.com/aspnet/Blazor/issues/225

Note that deeper down the exception stack trace, i see the following:

WASM: at SimpleJson.SimpleJson.DeserializeObject (System.String json, System.Type type, SimpleJson.IJsonSerializerStrategy jsonSerializerStrategy) <0x2ebc4f0 + 0x00068> in <8f8c03446dbf45f5bbcb1e109a064f6e>:0 WASM: at SimpleJson.SimpleJson.DeserializeObject[T] (System.String json) <0x2ef2490 + 0x0000a> in <8f8c03446dbf45f5bbcb1e109a064f6e>:0 WASM:
at Microsoft.JSInterop.Json.Deserialize[T] (System.String json) <0x2ef2458 + 0x00004> in <8f8c03446dbf45f5bbcb1e109a064f6e>:0 WASM:
at Microsoft.AspNetCore.Components.HttpClientJsonExtensions.GetJsonAsync[T] (System.Net.Http.HttpClient httpClient, System.String requestUri) <0x33182e0 + 0x000fa> in <13ab8f8dacb6489b93c9655168c56037>:0 WASM:
at WebUI.Features.Fetch.FetchViewModel.LoadAsync () <0x3300de0 + 0x00102> in :0

Updated 2

So i can confirm now that i was barking up the wrong tree. Essentially, i had a deserialization issue. Once i resolved that issue, everything is working fine. Not sure if i had a DI issue from the beginning or not. Nonetheless, my issue resolved now. Thanks for all the enlightening perspectives.

Upvotes: 3

Views: 3428

Answers (2)

Henk Holterman
Henk Holterman

Reputation: 273701

The pattern is easy.

I got it working like this, starting from the standard starter template.
In FetchData.razor:

@page "/fetchdata"
@using ClientBlazor1.ViewModels
@inject FetchDataViewModel vm

... the html

protected override async Task OnInitAsync()
{
    forecasts = await vm.GetForecasts();
}

And the ViewModel is below. You seem to be missing the constructor (-injection) part here.
Using an interface is optional, I didn't.

public class FetchDataViewModel
{
    private HttpClient _httpClient;

    public FetchDataViewModel(HttpClient httpClient)
    {
        _httpClient = httpClient;
    }

    public async Task<WeatherForecast[]> GetForecasts()
    {
        return await _httpClient.GetJsonAsync<WeatherForecast[]>("sample-data/weather.json");
    }
}

and to finish it up, the registration part in Startup.cs :

public void ConfigureServices(IServiceCollection services)
{
    services.AddTransient<FetchDataViewModel>();
}

In general this should not be a Singleton.

Upvotes: 2

enet
enet

Reputation: 45754

This is not really an answer to your question; without complete display of your code, no answer is really possible. But let me relate to the following code snippet; perhaps the problem lies there:

// manually add HttpClient to services.
services.AddTransient<IFetchViewModel, FetchViewModel>(); 

HttpClient service is provided as a Singleton (CSB) by the Blazor framework. Thus you cannot inject HttpClient into a service which you add to your app as Transient. Your service should also be added as Singleton...

Hope this helps...

[Edit]

How can i inject the default injected HttpClient 
to my various ViewModels?
  • If your ViewModels are Components, you may use the @inject directive like this:

@inject HttpClient httpClient

  • If your ViewModels are ordinary classes (.cs), you can either pass a reference to an HttpClient object from your calling component methods or inject the HttpClient service to your ViewModels' constructors. Don't forget to add your Services or ViewModels in the Startup class: services.AddSingleton<IFetchViewModel, FetchViewModel>();

Once again, use AddSingleton

No, your problem has got nothing to do with issue 225. This issue is very old, and all the bugs referred to in this issue were rectified long before I've heard of Blazor...

Note: The exception stack trace clearly points out in the direction of HttpClient being the culprit. Do what I've suggested above, and tell us if the issue is still persisting.

Why don't you display your code, as others ask you to do. Please look for instruction on how to ask question in stack overflow.

Upvotes: 3

Related Questions