Search code examples
c#wpfasp.net-core-mvcsignalrasp.net-core-6.0

.NET Core 6 - WPF, MVC and SignalR


I have an WPF app that sends a request to an ASP.NET Core 6 MVC app when a button is clicked. The action method in the MVC controller that receives the request, calls a method that runs a long task inside a for loop, and I am trying to use SignalR to update a label in the WPF app with the number of the loop iteration being processed.

The problem: after I click the button, the label does not change at all for the duration of the task, and only when the task ends it changes to show the number of the last iteration.

I guess its a problem with synchronous/asynchronous, but I am a noob at it and still can't seem to understand it...

This is the WPF button handler:

private async void myButton_Click(object sender, RoutedEventArgs e) {
    HubConnection connection = new HubConnectionBuilder().WithUrl("https://localhost:7036/miHub").Build();
    await connection.StartAsync();
    connection.On<string>("Notify", (str) => { lblProgress.Content = str; });

    //calls mvc controller action 
    string url = "https://localhost:7036/Sincro/procesa";

    using (var httpClient = new HttpClient()) {
        string json = await httpClient.GetStringAsync(url);
    }
}

This is the controller:

public class SincroController : Controller {
    private readonly IHubContext<MiHub> _hubContext;
    
    //ctor
    public SincroController(IHubContext<MiHub> hubContext) {
        _hubContext = hubContext;
    }
    
    public async Task<JsonResult> procesa() {
        await longProcess();
        
        var json = new { foo = "bar" };
        return Json(json);
    }
    
    private async Task<int> longProcess() {
        for(int row=0; row<5000; row++) {
            await _hubContext.Clients.All.SendAsync("Notify", $"Processing thing {row}");
        }
        return 0;
    }
}

Solution

  • In your longProcess code, the for loop is actually very fast, try Thread.Sleep(1500);.

    Test Result

    Optimized test code

    enter image description here

    My Test Result

    enter image description here

    Your code test result

    enter image description here

    enter image description here

    Sample Code

    MainWindow.xaml

    <Window x:Class="WpfApp1.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
            xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
            xmlns:local="clr-namespace:WpfApp1"
            mc:Ignorable="d"
            Title="MainWindow" Height="450" Width="800">
        <Grid>
            <Button x:Name="button" Content="Invoke Api" HorizontalAlignment="Left" Margin="13,15,0,0" VerticalAlignment="Top" Click="button_Click"/>
            <Label x:Name="lblSignalrStatus" Content="" HorizontalAlignment="Left" Margin="115,13,0,0" VerticalAlignment="Top" Width="367"/>
            <Label x:Name="lblProgress" Content="" HorizontalAlignment="Left" Margin="205,45,0,0" VerticalAlignment="Top" Width="367"/>
            <TextBox x:Name="textBoxProgress" AcceptsReturn="True" HorizontalAlignment="Left" Margin="12,87,0,0" TextWrapping="Wrap" Text="" VerticalAlignment="Top" Width="744" Height="327"/>
            <Label x:Name="label" Content="Label Content" HorizontalAlignment="Left" Margin="110,43,0,0" VerticalAlignment="Top"/>
            <Label x:Name="label1" Content="TextBox Content" HorizontalAlignment="Left" Margin="12,55,0,0" VerticalAlignment="Top"/>
    
        </Grid>
    </Window>
    

    MainWindow.xaml.cs

    using Microsoft.AspNetCore.SignalR.Client;
    using Microsoft.Extensions.Logging;
    using System;
    using System.Collections.Generic;
    using System.Data.Common;
    using System.Diagnostics;
    using System.Linq;
    using System.Net.Http;
    using System.Text;
    using System.Threading;
    using System.Threading.Tasks;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Data;
    using System.Windows.Documents;
    using System.Windows.Input;
    using System.Windows.Media;
    using System.Windows.Media.Imaging;
    using System.Windows.Navigation;
    using System.Windows.Shapes;
    
    namespace WpfApp1
    {
        /// <summary>
        /// Interaction logic for MainWindow.xaml
        /// </summary>
        public partial class MainWindow : Window
        {
            HubConnection _connection;
            public MainWindow()
            {
                InitializeComponent();
    
                Connect();
            }
    
            private async void button_Click(object sender, RoutedEventArgs e)
            {
    
                //calls mvc controller action 
                string url = "https://localhost:44356/Test/procesa";
                using (var httpClient = new HttpClient())
                {
                    string json = await httpClient.GetStringAsync(url);
                }
            }
    
            public async Task Connect()
            {
                _connection = new HubConnectionBuilder()
                    .WithUrl($"https://localhost:44356/mainHub")
                    .WithAutomaticReconnect()
                    .Build();
    
                await _connection.StartAsync();
    
                lblSignalrStatus.Content = "Connection started";
    
                _connection.On<string>("Notify", OnNotifyHandler);
    
                _connection.Closed += async (error) =>
                {
                    await Task.Delay(new Random().Next(0, 5) * 1000);
                    await _connection.StartAsync();
                };
            }
            public Func<string, Task> NotifyHandler { get; set; }
            private void OnNotifyHandler(string str) // => NotifyHandler?.Invoke(name);
            {
                this.Dispatcher.Invoke(() =>
                {
                    string dtime = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") + "   ";
                    lblProgress.Content = dtime + str;
                    textBoxProgress.AppendText(dtime + str + "\r\n");
                    textBoxProgress.ScrollToEnd();
                });
                
            }
        }
    }