Use SignalR to actively push alert logs from the server to various terminals (desktop, mobile, web)

WeChat public address: Dotnet9 Website: Dotnet9 , questions or suggestions: Please leave a message on the website
If it helps you: Welcome appreciation.

Use SignalR to actively push alert logs from the server to various terminals (desktop, mobile, web)

Reading navigation

  1. Background of this article
  2. code implementation
  3. Reference in this article

1. Background

There is a business at work. Net Core WebAPI, as the server, needs to classify the logs generated in the operation process and push them to various terminals in real time for alarm. The terminals include desktop (WPF), mobile (Xamarin.Forms), website (Angular.JS), etc. and use SignalR to push the alarm logs.

Here is the test effect of desktop:

2. Code implementation

The whole system is composed of server, desktop, website and mobile terminal, with the structure as follows:

2.1 log entity classes used by both server and client

For a simple log definition, the server will actively push the latest log to each terminal through the AlarmLogItem instance:

/// <summary>
///Alarm log
/// </summary>
public class AlarmLogItem
{
    public string Id { get; set; }
    /// <summary>
    ///Log type
    /// </summary>
    public AlarmLogType Type { get; set; }
    /// <summary>
    ///Log name
    /// </summary>
    public string Text { get; set; }
    /// <summary>
    ///Log details
    /// </summary>
    public string Description { get; set; }
    /// <summary>
    ///Log update time
    /// </summary>
    public string UpdateTime { get; set; }
}

public enum AlarmLogType
{
    Info,
    Warn,
    Error
}

2.2 server

Web API project built with. Net Core 2.2

2.2.1 AlarmLogHub.cs of hub class

Define the Hub hub class AlarmLogHub, which inherits from Hub and is used for SignalR communication. Look at the following code without adding any methods. You are right:

public class AlarmLogHub : Hub
{}

2.2.2 Startup.cs

You need to register the SignalR pipeline and services in this class, which are used in the following two key methods. The B/S backend friends are very familiar with it.

  1. ConfigureServices method

Add a SignalR pipeline :

services.AddSignalR(options => { options.EnableDetailedErrors = true; });
  1. Configure method register SignalR service address

Port 8022, the client access address is: http://localhost:8022/alarmlog

app.UseSignalR(routes =>
{
    routes.MapHub<AlarmLogHub>("/alarmlog");
});

2.2.3 SignalRTimedHostedService.cs

This is a key class, which is used by the server to actively push logs. Baidu and Google found it for a long time. The webmaster's technology stack is mainly C/S, and B/S does not do much. No one can point it out. Sorry. Refer to the website: How do I push data from hub to client every second using SignalR.

This class inherits from IHostedService. As a service self startup (nonsense), it obtains an instance of ihubcontext < alarmloghub > through the constructor dependency injection of SignalRTimedHostedService, which is used by the server to push logs to clients (open the timer in StartAsync method to simulate the active push of alert logs by the server, see the DoWork method):

internal class SignalRTimedHostedService : IHostedService, IDisposable
{
    private readonly IHubContext<AlarmLogHub> _hub;
    private Timer _timer;

    //Analog send alarm log            
    List<AlarmLogItem> lstLogs = new List<AlarmLogItem> {
            new AlarmLogItem{ Type=AlarmLogType.Error,Text="OK WebSocket Broken even",Description="Attempt to connect 50 times, failed to reconnect!"},
            new AlarmLogItem{ Type=AlarmLogType.Warn,Text="OK WebSocket Disconnection and reconnection",Description="Try to connect 5 times, successfully reconnected!"},
            new AlarmLogItem{ Type=AlarmLogType.Warn,Text="OK Restfull Broken even",Description="Try to connect 30 times, successfully reconnected!"},
            new AlarmLogItem{ Type=AlarmLogType.Error,Text="OK WebSocket Broken even",Description="Disconnect for the first time!"},
            new AlarmLogItem{ Type=AlarmLogType.Info,Text="OK WebSocket Successful connection",Description="First successful connection!"},
            new AlarmLogItem{ Type=AlarmLogType.Error,Text="OK WebSocket Broken even",Description="Attempt to connect the 7th time, failed to reconnect!"}
        };

    Random rd = new Random(DateTime.Now.Millisecond);

    public SignalRTimedHostedService(IHubContext<AlarmLogHub> hub)
    {
        _hub = hub;
    }

    public Task StartAsync(CancellationToken cancellationToken)
    {

        _timer = new Timer(DoWork, null, TimeSpan.Zero,
            TimeSpan.FromSeconds(1));

        return Task.CompletedTask;
    }

    private void DoWork(object state)
    {
        if (DateTime.Now.Second % rd.Next(1, 3) == 0)
        {
            AlarmLogItem log = lstLogs[rd.Next(lstLogs.Count)];
            log.UpdateTime = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff");
            _hub.Clients.All.SendAsync("ReceiveAlarmLog", log);
        }
    }

    public Task StopAsync(CancellationToken cancellationToken)
    {

        _timer?.Change(Timeout.Infinite, 0);

        return Task.CompletedTask;
    }

    public void Dispose()
    {
        _timer?.Dispose();
    }
}

The SignalRTimedHostedService class, as the Host service (inherited from IHostedService), needs to register the pipeline in the ConfigureServices method of Startup.cs (right? Do you have a good B/S book recommendation? The webmaster plans to study hard when he has time):

services.AddHostedService<SignalRTimedHostedService>();

All the key codes of the server have been delivered. The following is mainly about the codes of the desktop and the mobile. In fact, the codes of the two are similar.

2.3 website

Reference resources index.html

2.4 desktop (WPF)

For the WFP project created with. Net Core 3.0, Nuget package needs to be introduced: Microsoft.AspNetCore.SignalR.Client

The interface uses a ListView to display the received logs:

<Grid>
    <ListBox x:Name="messagesList"  RenderTransformOrigin="-0.304,0.109" BorderThickness="1" BorderBrush="Gainsboro"/>
</Grid>

Simple background writing, directly connect the server SignalR address in the form constructor: http://localhost:8022/alarmlog , listen to the server alarm log push: ReceiveAlarmLog.

using AppClient.Models;
using Microsoft.AspNetCore.SignalR.Client;
using System;
using System.Threading.Tasks;
using System.Windows;

namespace SignalRChatClientCore
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        HubConnection connection;
        public MainWindow()
        {
            InitializeComponent();

            connection = new HubConnectionBuilder()
                .WithUrl("http://localhost:8022/alarmlog")
                .Build();

            connection.Closed += async (error) =>
            {
                await Task.Delay(new Random().Next(0, 5) * 1000);
                await connection.StartAsync();
            };
            connection.On<AlarmLogItem>("ReceiveAlarmLog", (message) =>
            {
                this.Dispatcher.Invoke(() =>
                {
                    messagesList.Items.Add(message.Description);
                });
            });

            try
            {
                connection.StartAsync();
                messagesList.Items.Add("Connection started");
            }
            catch (Exception ex)
            {
                messagesList.Items.Add(ex.Message);
            }
        }
    }
}

2.4 mobile terminal

In fact, the mobile terminal is similar to the desktop, because the. Net Core 3.0 used by the desktop and the. NET Standard 2.0 used by the mobile terminal need to introduce Nuget package: Microsoft.AspNetCore.SignalR.Client.

The interface uses ListView to display logs, so there's no code to paste. In MVVM mode, paste ViewModel code directly. Let's just look at it, don't get tangled up with specific code, refer to desktop. cs code, is it the same?

using AppClient.Models;
using AppClient.Views;
using Microsoft.AspNetCore.SignalR.Client;
using System;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Threading.Tasks;
using Xamarin.Forms;
using System.Linq;

namespace AppClient.ViewModels
{
    /// <summary>
    ///Alarm log VM
    /// </summary>
    public class AlarmItemsViewModel : BaseViewModel
    {
        private ViewState _state = ViewState.Disconnected;

        /// <summary>
        ///Alarm log list
        /// </summary>
        public ObservableCollection<AlarmLogItem> AlarmItems { get; set; }
        public Command LoadItemsCommand { get; set; }

        //Connect to alarm server
        private HubConnection _connection;

        public AlarmItemsViewModel()
        {
            Title = "Alarm log";
            AlarmItems = new ObservableCollection<AlarmLogItem>();
            LoadItemsCommand = new Command(async () => await ExecuteLoadItemsCommand());

            //Receive login success notification
            MessagingCenter.Subscribe<LoginViewModel, LoginUser>(this, "LoginSuccess", async (sender, userInfo) =>
             {
                 //DisplayAlert("login succeeded", userInfo.UserName, "OK");
             });
            MessagingCenter.Subscribe<NewItemPage, AlarmLogItem>(this, "Add item", async (obj, item) =>
            {
                var newItem = item as AlarmLogItem;
                AlarmItems.Add(newItem);
                await DataStore.AddItemAsync(newItem);
            });

            ConnectAlarmServer();
        }

        async Task ExecuteLoadItemsCommand()
        {
            if (IsBusy)
                return;

            IsBusy = true;

            try
            {
                AlarmItems.Clear();
                var items = await DataStore.GetItemsAsync(true);
                foreach (var item in items)
                {
                    AlarmItems.Add(item);
                }
            }
            catch (Exception ex)
            {
                Debug.WriteLine(ex);
            }
            finally
            {
                IsBusy = false;
            }
        }

        private async Task ConnectAlarmServer()
        {
            if (_state == ViewState.Connected)
            {
                try
                {
                    await _connection.StopAsync();
                }
                catch (Exception ex)
                {
                    return;
                }
                _state = ViewState.Disconnected;
            }
            else
            {
                try
                {
                    _connection = new HubConnectionBuilder()
                      .WithUrl(App.Setting.AlarmHost)
                      .Build();
                    _connection.On<AlarmLogItem>("ReceiveAlarmLog", async (newItem) =>
                    {
                        AlarmItems.Add(newItem);
                        await DataStore.AddItemAsync(newItem);
                    });
                    _connection.Closed += async (error) =>
                    {
                        await Task.Delay(new Random().Next(0, 5) * 1000);
                        await _connection.StartAsync();
                    };
                    await _connection.StartAsync();
                }
                catch (Exception ex)
                {
                    return;
                }
                _state = ViewState.Connected;
            }
        }

        private enum ViewState
        {
            Disconnected,
            Connecting,
            Connected,
            Disconnecting
        }
    }
}

The key code has been pasted. I hope it can help you.

3. reference

  1. . NET client SignalR ASP.NET Core
  2. SignalR-samples
  3. How do I push data from hub to client every second using SignalR

Unless otherwise noted, articles are written by Dotnet9 Organize and release, welcome to reprint.

For reprint, please indicate the address: https://dotnet9.com/6913.html

Welcome to scan the WeChat public number below the two-dimensional code concern Dotnet9, this site will push the latest technical articles in time.

Dotnet9

Tags: C# Mobile angular Google Windows

Posted on Fri, 10 Jan 2020 08:17:49 -0500 by assessino