Blazor, Updating or Refreshing the Display During a Method Call

Full source code available BlazorUpdatingUIEverySecondWasm.

This is a quick post to show how to show how to refresh or update components on a Blazor page asynchronously, for example in response to a task or tasks completing.

In this example I have a button that calls a method, inside the method I have for loop, inside each iteration of the for loop, I increment the counter twice, delay twice, and update the display twice using a call to StateHasChanged().

Here is the code –

@page "/"

<h1>Updating the state of the page every second</h1>

<p>Current count: @currentCount</p>

<button class="btn btn-primary" @onclick="StartCounterAsync">Start counter</button>

@code {
    private int currentCount = 0;
    private async Task StartCounterAsync()
    {
        for (int i = 1; i <= 10;)
        {
            await Task.Delay(1000);
            currentCount = i++;
            StateHasChanged();  
            await Task.Delay(1000);
            currentCount = i++;
            StateHasChanged();  
        }
    }
}

Once you know how to do it’s easy.

There is scenario where using StateHasChanged() won’t work and instead you’ll get an error like –

Exception has occurred: CLR/System.InvalidOperationException
An exception of type 'System.InvalidOperationException' occurred in Microsoft.AspNetCore.Components.dll but was not handled in user code: 'The current thread is not associated with the Dispatcher. Use InvokeAsync() to switch execution to the Dispatcher when triggering rendering or component state.'
   at Microsoft.AspNetCore.Components.Dispatcher.AssertAccess()
   at Microsoft.AspNetCore.Components.RenderTree.Renderer.AddToRenderQueue(Int32 componentId, RenderFragment renderFragment)
   at Microsoft.AspNetCore.Components.ComponentBase.StateHasChanged()

In this case use InvokeAsync(StateHasChanged); instead and it should work.

Full source code available BlazorUpdatingUIEverySecondWasm.

Polly and Blazor, Part 1 – Simple Wait and Retry

Full source code available here.

A few weeks ago I gave a talk on Polly at Dotnetconf (you can check it out here), at the end I got many questions about using Polly with Blazor, I had never tried it, but assumed it would work.

In this blog, part 1 of a few on Polly and Blazor, I’ll show you how to get a simple retry mechanism up and working in a Blazor Server App. I am not concerned with dependency injections, policy registries or anything other fancy things in this post, that will come later..

The setup
I have a .Net 5 Blazor Server app created with

dotnet new blazorserver

I stripped out the Blazor pages I didn’t need, and changed the code of the Index.razor file to make a HTTP call to a controller that generates errors 75% of time. I removed links to the other Blazor pages too.

Around the HTTP call I use the Polly Wait and Retry Policy to retry up three times, with a delay between each request. If you haven’t used Polly before, I have written a LOT about it, and have a Pluralsight course on it.

I made a few updates to the UI to show what the HttpClient and Polly are doing.

The Wait and Retry policy will retry after 2, 4 and 6 seconds. It prints out the to the console and updates the UI with the errors it gets from the remote system and details about the retry.

Because this is my first Blazor app, it took a little longer and may have some less than perfect approaches, but you should be able to take the retry idea and apply it to you application.

Before I show the code for retrying, let me show how I added a familiar Web API controller.

In Configure(..) method of Startup.cs I added endpoints.MapControllers(); to the below block so it looks like this –

app.UseEndpoints(endpoints =>
{
    endpoints.MapControllers(); // added this
    endpoints.MapBlazorHub();
    endpoints.MapFallbackToPage("/_Host");
});

Then I created a Controllers directory and added a new file FaultyController.cs.

Here is its code –

using Microsoft.AspNetCore.Mvc;

namespace WebApiDataDog.Controllers
{
    [ApiController]
    [Route("[controller]")]
    public class FaultyController : ControllerBase
    {
        private static int counter = 0;

        public FaultyController()
        {
        }

        [HttpGet]
        public ActionResult Get()
        {
            if (++counter % 4 == 0)
            {
                return Ok(counter);
            }
            else if(counter % 4 == 1)
            {
                return StatusCode(501);
            }
            else if(counter % 4 == 2)
            {
                return StatusCode(502);
            }
            else
            {
                return StatusCode(503);
            }
        }
    }
}

And here is my Razor page that uses the Polly Wait and Retry policy.

@page "/"
@using Polly

<h1>Polly Test</h1>
<p>This demo uses a HttpClient to call a controller that is setup to fail 75% of the time (first three request will fail, fourth will succeed). The Polly Retry policy will kick in a retry up to three time.</p>

<p>Status: @status</p>
<p>Polly Summary : @pollyMessage</p>

<p>Response Code : @responseCode</p>
<p>Response Body : @number</p>

<button class="btn btn-primary" @onclick="CallRemoteServiceAsync">Call remote service</button>

@code {
   HttpClient httpClient = new HttpClient() {
        BaseAddress =  new Uri("http://localhost:5000")
    };

    private string status;
    private string pollyMessage;
    private string responseCode;
    private string number;

    private async Task CallRemoteServiceAsync()
    {
        IAsyncPolicy<HttpResponseMessage> _httpRequestPolicy = Policy.HandleResult<HttpResponseMessage>(
            r => !r.IsSuccessStatusCode)
            .WaitAndRetryAsync(3,
                retryAttempt => TimeSpan.FromSeconds(retryAttempt * 2), onRetry: (response, retryDelay, retryCount, context) => {
                    pollyMessage = $"Recieved: {response.Result.StatusCode}, retryCount: {retryCount}, delaying: {retryDelay.Seconds} seconds\n";
                    Console.WriteLine(pollyMessage);
                    InvokeAsync(StateHasChanged);
        });

        status = "Calling remote service...";
        string requestEndpoint = "faulty/";

        HttpResponseMessage httpResponse = await _httpRequestPolicy.ExecuteAsync(() => httpClient.GetAsync(requestEndpoint));
        
        responseCode = httpResponse.StatusCode.ToString();
        number = await httpResponse.Content.ReadAsStringAsync();
        status = "Finished";
        pollyMessage = "";
    }
}

As I said above, I’m going to write a few more posts on Polly and Blazor to show some other scenarios, like using the Polly Context to pass data around and dependency injection (hopefully).

Full source code available here.

Indexing the Works of Shakespeare in ElasticSearch – Part 4, Searching via Web API in .NET 5

Full source code available here.

This is part four of my four part series on indexing the works of Shakespeare in ElasticSearch.

In this I’ll show how to use the ElasticSearch “low level client” to perform the search. In the past I wrote a blog showing how to use a HttpClient to perform the search using Json, and this works fine, but Steve Gordon suggested I try to the Elastic client as it supports things like connection pooling and still lets me use Json directly with ElasticSearch.

To go along with the Elastic “low level client” there is a “high level client” called NEST, I have tried both and prefer to stick with Json, but you may find them more useful.

Because I develop on a few languages, Json is the natural choice for me. I use it when querying from Node.js, inside a HTTP client (Fiddler, Rest Client, etc) when figuring out my queries and I want to use it in .NET.

But Json and C# don’t go together very well, you have to jump through hoops to make it work with escaping. Or, as I have doe, use a creative approach to deserializing via dynamic objects (I know some people won’t like this), I find this much more convenient than converting my Json queries to the Elastic client syntaxes.

This examples shows how to use the a Web API application to search for a piece of text in isolation or within specific play.

The setup
There is very little to do here.

In Startup.cs add the following to the ConfigureServices(..) method –

services.AddSingleton<ElasticLowLevelClient>(new ElasticLowLevelClient(new ConnectionConfiguration(new Uri("http://localhost:9200"))));

In the SearchController add the following to pass the ElasticSearch client in via dependency injection –

public class SearchController : ControllerBase
{
    private readonly ElasticLowLevelClient _lowLevelClient;
    public SearchController(ElasticLowLevelClient lowLevelClient)
    {
        _lowLevelClient = lowLevelClient;
    }
//snip ..

I have two action methods, one to search for a play and line, and one to search for a line across all plays (I know they could be combined into a single action method, I want keep things simple) –

[HttpGet("Line")]
public ActionResult Line(string text)
{
    string queryWithParams = GetLineQuery(text);
    var lines = PerformQuery(queryWithParams);
    
    return Ok(lines);
}

[HttpGet("PlayAndLine")]
public ActionResult PlayAndLine(string play, string text)
{
    string queryWithParams = GetPlayAndLineQuery(play, text);
    var lines = PerformQuery(queryWithParams);

    return Ok(lines);
}

All very straightforward so far, but now comes the “creative” approach to handling the Json problems.

I put my ElasticSearch queries into their own files. The first is Line.Json

{
    "query": {
        "match_phrase_prefix" :{
            "Line": ""
        }
    }
} 

And the second is PlayAndLine.Json

{
    "query":{
        "bool": {
            "must": [
                { "match": { "Play": "" } }
               ,{ "match_phrase_prefix": { "Line": "" } }
            ]
        }
    }
}

These Json queries are loaded into dynamic objects and the relevant values are set in C#.
See lines 5 and 14 & 15.

private string GetLineQuery(string text)
{
    string elasticSearchQuery = System.IO.File.ReadAllText($"Queries/Line.json");
    dynamic workableElasticSearchQuery = JsonConvert.DeserializeObject(elasticSearchQuery);
    workableElasticSearchQuery.query.match_phrase_prefix.Line = text;

    return workableElasticSearchQuery.ToString();
}

private string GetPlayAndLineQuery(string play, string text)
{
    string elasticSearchQuery = System.IO.File.ReadAllText($"Queries/PlayAndLine.json");
    dynamic workableElasticSearchQuery = JsonConvert.DeserializeObject(elasticSearchQuery);
    workableElasticSearchQuery.query.@bool.must[0].match.Play = play;
    workableElasticSearchQuery.query.@bool.must[1].match_phrase_prefix.Line = text;

    return workableElasticSearchQuery.ToString();
}

The strings the above methods return are the queries that will be sent to ElasticSearch.

The below method makes the request, and deserializes the response into the ESResponse class. That class was generated by https://json2csharp.com/.

private ESResponse PerformQuery(string queryWithParams)
{
    var response = _lowLevelClient.Search<StringResponse>("shakespeare", queryWithParams);
    ESResponse esResponse = System.Text.Json.JsonSerializer.Deserialize<ESResponse>(response.Body);
    return esResponse;
}

You might have noticed that I use System.Text.Json and Newtonsoft Json, this is because System.Text.Json does not support dynamic deserialization, see this discussion – https://github.com/dotnet/runtime/issues/29690.

That’s it, searching, and parsing of ElasticSearch results via a Web API application, feels a bit messy, but hope it helps.

Full source code available here.