Real time App with .NET MAUI Blazor and Azure SignalR service

This is my entry to the  Csadvent Calendar for this year This has become a tradition to submit my article to the advent run by awesome Matt. Thank you Matt for this wonderful initiative.

In this article let’s see how we can add the real time capabilities to the MAUI apps using Azure SignalR service and Azure functions. In this demo let’s build a small temperature dashboard which will keep on updating the UI every 2 seconds to build this let’s build MAUI Blazor App one Http Triggered function app and one timer trigger function which will broadcast the data to the UI.

Let’s divide our topic in following section

  • What is MAUI
  • .NET Project setup
  • Setup Backend services
  • Demo

What is MAUI ?

.NET MAUI (Multi-platform App UI) is a cross platform framework to develop the native apps for mobiles and desktops. We can develop apps that can run on desktop, web and mobiles by using this platform.

.NET MAUI

image source Microsoft documentation

MAUI make sure that all the native app API are unified in the single API and that make sure that we write code once and run it everywhere across the platforms. New versions of the .NET (6 and above) provide platform specific APIs which all have access to the .NET BCL (base class library) this BCL make sure that every platform gets the needed execution environment based on the .NET runtime of the apps for example Android and iOS run time will be provided by mono and for windows .NET core CLR will be used as a run time execution.

Following image shows all the needed component that .NET MAUI architecture

image source Microsoft documentation

While BCL will give us the execution environment we can create the native controls using the appropriate platform specific UI framework like .NET for Android and .NET for iOS or Winui3 for the control’s development.

More on the .NET MAUI can be found here on official microsoft documentation

So far, we saw what the architecture of the MAUI apps is and how can we use them now let’s see how can create the real time app using MAUI and SignalR service. In next section lets try to setup the MAUI Project and add some code to it

MAUI Project Setup

To Add the MAUI Project, make sure you have MAUI workload is installed on your machine if not update your visual studio to latest version and select the workload like below

Once we have this workload installed we can add the MAUI application like below

In this we can see three options for the MAUI apps out of which we will select the .NET MAUI Blazor app for our demo other two options lets explore in coming articles. After giving proper name to the project we can go to the last step where we will pick up the latest version of the .NET like below

Now we are ready with the basic setup of the application lets try to understand little bit about .NET MAUI Blazor and what it offers in terms of development.

What is .NET MAUI Blazor ?

There are two ways we can build UI in the MAUI first one using the Xaml and other we can use Blazor to do that in short, we use the blazor to build UI which will run natively on all the operating system which are available in MAUI.

This ability to run Blazor apps in MAUI is made possible with control like BlazorWebView. This enables us to host the blazor app to be hosted inside the MAUI app and it doesn’t make use of the web assembly or any other web technology. Only Web part of this type of the application is UI which is built using Html and CSS rest of all the part of the application will run natively on the execution platform.

Ok i guess we have enough discussion about the platform lets dive into the code and see how it will work and see the demo so lets get started

  1. Set up Client App

Once we are done with this step let’s try to setup the client app which is actually a MAUI app and add some

@page "/Dashboard"
@using Microsoft.AspNetCore.SignalR.Client
@using ChartJs.Blazor.Common
@using ChartJs.Blazor.Util
@using ChartJs.Blazor.BarChart
@using ChartJs.Blazor.Common.Enums
@using ChartJs.Blazor
@using ChartJs.Blazor.Common.Axes
@using ChartJs.Blazor.LineChart

<h1>Counter</h1>


<Chart Config="Config" @ref="_chart"></Chart>


@code {
    private const int InitialCount = 7;

    protected Chart _chart;

    private HubConnection HubConnection; //for connecting to SignalR

    protected LineConfig Config;

    private readonly string FunctionAppBaseUri = "<Replace with URL>"; //URL for function app. Leave this as is for now.



    protected override async Task OnInitializedAsync()
    {
        SetConfig();

        HubConnection = new HubConnectionBuilder()
            .WithUrl(FunctionAppBaseUri)
            .Build();

        //_config = GetConfig();

        Connect();
        
        await HubConnection.StartAsync(); //start connection!

        foreach (var time in SampleUtils.TimeofTheDay)
        {
            Config.Data.Labels.Add(time);
        }

        Config.Data.Datasets.Add(dataset1);
        Config.Data.Datasets.Add(dataset2);
    }
    
    IDataset<int> dataset1 = new LineDataset<int>(new List<int>(InitialCount))
    {
        Label = "Pune",
        BackgroundColor = ColorUtil.FromDrawingColor(System.Drawing.Color.White),
        BorderColor = ColorUtil.FromDrawingColor(System.Drawing.Color.Crimson)
    };


    readonly IDataset<int> dataset2 = new LineDataset<int>(new List<int>(InitialCount))
    {
        Label = "Mumbai",
        BackgroundColor = ColorUtil.FromDrawingColor(System.Drawing.Color.White),
        BorderColor = ColorUtil.FromDrawingColor(System.Drawing.Color.DarkBlue)
    };
    private void SetConfig()
    {
        Config = new LineConfig
        {
            Options = new LineOptions
            {
                Responsive = true,
                Title = new OptionsTitle
                {
                    Display = true,
                    Text = "Temprature"
                },
                Tooltips = new Tooltips
                {
                    Mode = InteractionMode.Nearest,
                    Intersect = true
                },
                Hover = new Hover
                {
                    Mode = InteractionMode.Nearest,
                    Intersect = true
                },
                Scales = new Scales
                {
                    XAxes = new List<CartesianAxis>
                    {
                        new CategoryAxis
                        {
                            ScaleLabel = new ScaleLabel
                            {
                                LabelString = "Time"
                            }
                        }
                    },
                    YAxes = new List<CartesianAxis>
                    {
                        new LinearCartesianAxis
                        {
                            ScaleLabel = new ScaleLabel
                            {
                                LabelString = "Temp (in Celsius ) "
                            }
                        }
                    }
                }
            }
        };
    }
    
    private void AddData()
    {
        if (Config.Data.Datasets.Count == 0)
            return;

        var month = SampleUtils.TimeofTheDay[Config.Data.Labels.Count % SampleUtils.TimeofTheDay.Count];

        Config.Data.Labels.Add(month);
    }

    private void Connect()
    {

        HubConnection.On<List<DashboardMessage>>("dashboardmessage", (clientMessage) =>
        {
            foreach (var message in clientMessage)
            {
                switch (message.Id)
                {
                    case "1":
                        dataset1.Add(Convert.ToInt16(message.Details));
                        break;
                    case "2":
                        dataset2.Add(Convert.ToInt16(message.Details));
                        break;
                }
            }

            AddData();

            InvokeAsync(StateHasChanged); //This tells Blazor that the UI needs to be updated
        });
    }
}

Above is the component which is being used for building chart and rendering the chart on the UI. This code is performing two things basically 1. Build the chart configuration and other build the SignalR service to connect to the Hub and receive data from hub. more explanation about this code you can find on this article

Integrating the Azure SignalR service with the Blazor application involves few steps .

1.Install the SignalR service client Package

First step  to get started with this is to install the client package in the UI application in order to install the package just go to the package manager console and use following command

           PM> Install-Package Microsoft.AspNetCore.SignalR.Client

2Connect to Hub

To establish the connection with the Azure SignalR service we use HubConnectionBuilder and call Build method the code for the same will be like below

private HubConnection SignalRHubConnection; //for connecting to Signal
          // Build the Hub connection
            SignalRHubConnection = new HubConnectionBuilder()
                .WithUrl(”<<URL for the Azure SignalR service negotiate function goes here >>”)
          .Build();


Here we can use the Build method to configure various options like URL, Protocol log levels for the SignalR Connection.

3Register the Handler

In this case we will use the HubConnection On() method to register a handler which can be invoked whenever the hub method dashboard message is called

SignalRHubConnection.On<List<DashboardMessage>>("dashboardMessage", (clientMessage)=>
    {
        // Code to receive the message and update UI  goes here 
    });


4Start the Connection

Once we have setup all the stuff now it is time to start the connection and initiate the communication with our SignalR service to start the connection we will use following code

	
await SignalRHubConnection.StartAsync(); //start connection!

Once we are done with the Azure SignalR service now its time to integrate the chart component which be used for the visualisation of the data which is coming from the service.

Once we have the UI ready we will add the backend services like below

using Microsoft.AspNetCore.Http;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Extensions.Http;
using Microsoft.Azure.WebJobs.Extensions.SignalRService;

namespace MAUIRealTimeFunctionsApp
{
    public static class Negotiator
    {
        
        [FunctionName("negotiate")]
        public static SignalRConnectionInfo GetConnectionInfo(
            [HttpTrigger(AuthorizationLevel.Anonymous, "post")]
            HttpRequest req,
            [SignalRConnectionInfo(HubName = "Dashboard")]
            SignalRConnectionInfo connectionInfo)
        {
            return connectionInfo;
        }
    }
}

This is a azure function which will be used to negotiate or connection info with the clients . this is simple HttpTriggered function which will be called by client when connecting to the server. in Return this function will return the connection info and token to the clients which will be used by the client in the next requests.

Next function we will have is the Brodcaster function

using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Extensions.SignalRService;
using Microsoft.Azure.WebJobs.Host;
using Microsoft.Extensions.Logging;

namespace MAUIRealTimeFunctionsApp
{
    public class DashboardBroadcaster
    {
        [FunctionName("Dashboard")]
        public static Task Run([TimerTrigger("*/2 * * * * *")] TimerInfo myTimer,
            [SignalR(HubName = "Dashboard")] IAsyncCollector<SignalRMessage> signalrMessageForDashboard,
            ILogger log)
        {

            var message = new List<DashboardMessage>()
            {
                new DashboardMessage
                {
                    Id = "1",
                    Details = new Random().Next(1, 45).ToString()
                },
                 new DashboardMessage
                {
                    Id = "2",
                    Details = new Random().Next(1, 45).ToString()
                }
            };

            log.LogInformation($"C# Timer trigger function executed at: {DateTime.Now}");


            // target name and method name on client must match
            // datatype of the argument in Hub and client must match

            return signalrMessageForDashboard.AddAsync(new SignalRMessage
            {
                Target = "dashboardmessage",
                Arguments = new[] { message
                }
            });


        }
    }
}
public class DashboardMessage
{
    public string Id { get; set; }
    public string Details { get; set; }
}

This is timer triggered function which will just broadcast the random number which we will interpret as temperature in the client app this function will be triggered every two seconds and broadcast the data to the client

Once we have these component ready and running, we can run the application and see the output

You can see the demo of the sample app like below

References

https://learn.microsoft.com/en-us/dotnet/maui/?view=net-maui-7.0

4 comments

  1. […] Whereas the cloud helps with backend providers pumping out real-time information, consumer experiences could be lit up with fashionable cross-platform applied sciences—.NET MAUI can play the right host for cell/desktop real-time experiences. Mangesh Gaherwar wrote up an article combining the most effective of cloud and consumer experiences—real-time apps with .NET MAUI Blazor and Azure SignalR service. […]

    Like

  2. […] While the cloud helps with backend services pumping out real-time data, client experiences can be lit up with modern cross-platform technologies—.NET MAUI can play the perfect host for mobile/desktop real-time experiences. Mangesh Gaherwar wrote up an article combining the best of cloud and client experiences—real-time apps with .NET MAUI Blazor and Azure SignalR service. […]

    Like

  3. […] While the cloud helps with backend services pumping out real-time data, client experiences can be lit up with modern cross-platform technologies—.NET MAUI can play the perfect host for mobile/desktop real-time experiences. Mangesh Gaherwar wrote up an article combining the best of cloud and client experiences—real-time apps with .NET MAUI Blazor and Azure SignalR service. […]

    Like

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s