How to Perform Real-Time Updates in Blazor WASM apps with SignalR

When data is updated on the server, how can we listen for those changes and instantly update our client application?

We will explore that question as we show how to get live or “real-time” updates in a client-side Blazor web application. This topic is ideal for C# developers with little knowledge of ASP.NET MVC.

Blazor WASM (WebAssembly) applications run completely on the client. However, the data is still on the server. We need a way to handle this communication (as illustrated below). For this scenario, Microsoft has provided the SignalR library.

What is SignalR?

SignalR simplifies the process of adding real-time web functionality to applications. It enables the server to push content to connected clients instantly as it becomes available, rather than having the server wait for a client to request new data. This works through the implementation of a SignalR Hub.

But SignalR is used by ASP.NET MVC developers. If you are developing with Blazor, you may have come from a .NET desktop development background. We help bridge this learning gap with the ComponentOne DataCollection (C1DataCollection) library that now has built-in SignalR communication features. You’ll still need to configure the hub, but C1DataCollection manages the send and receive code.

Deliver enterprise-level applications faster with the most flexible .NET UI controls. Download ComponentOne Today!

What is C1DataCollection?

The ComponentOne DataCollection (C1DataCollection) library provides filtering, grouping, and sorting services for your data collection. It makes it easy to enable cursor and paging-based data virtualization for incrementally loading large data sets and displaying real-time updates from SQL Server.

When we consider showing extensive collections of data, it is mandatory to include data virtualization in the discussion; otherwise, we can end up with tons of data being sent and received through the network. This involves sending the data partially to the client app, which brings other consequences like sort or filter operations having to be made on the server, desirably delegating this to SQL Server.

To resolve this scenario, we added a new component to the library named C1ProxyDataCollection. This client-side collection will be the shadow of another collection on the server and will receive the data as needed and update when the server-side collection changes. Additionally, the filter and sort will pass through up to the server collection.

To implement this, we will make use of ASP.NET SignalR libraries. The solution involves two new sub-libraries: C1.DataCollection.SignalR.Client and C1.DataCollection.SignalR.Server. The first one will contain C1ProxyDataCollection, and the second will have C1DataCollectionHub, which will serve the data and notify of changes.

C1DataCollectionHub

Since the Hub will serve the data of another collection, we will inherit from C1DataCollectionHub and configure the ASP.NET server to inject the collection into the hub.

Next, let’s walk through the steps to create this in a Blazor WASM app.

Creating a Blazor WASM App with Real-Time Updates

Open Visual Studio and create a Blazor WebAssembly App named "SQLServerRealTimeUpdates". This will give you three projects within the solution: Server, Client, and Shared.

In the shared project, SQLServerRealTimeUpdates.Shared add a new class named "Product.cs".

namespace SQLServerRealTimeUpdates.Shared
{
    public class Product
    {
        public string Code { get; set; }
        public string Name { get; set; }
        public decimal Price { get; set; }
    }
}

In the server project, SQLServerRealTimeUpdates.Server, perform the following steps:

Add the nuget packages C1.DataCollection.SignalR.Server, Microsoft.Data.SqlClient and SqlTableDependency.

Add a new folder named "Data" and add the SQLServerDataCollection.cs file from this previous post.

Add a new folder named "Hubs"

Hubs

Create a new file named “ProductsHub.cs" in the Hubs folder.

using C1.DataCollection;
using C1.DataCollection.SignalR.Server;
using Microsoft.AspNetCore.SignalR;
using SQLServerRealTimeUpdates.Data;
using SQLServerRealTimeUpdates.Shared;

namespace SQLServerRealTimeUpdates.Server.Hubs
{
    public class ProductsHub : C1DataCollectionHub<Product>
    {
        private IConfiguration Configuration { get; }

        public ProductsHub(IHubContext<ProductsHub> context, IConfiguration configuration)
            : base(context.Clients)
        {
            Configuration = configuration;
        }

        protected override IDataCollection<Product> GetCollection(HubCallerContext context)
        {
            var sqlConnection = new Microsoft.Data.SqlClient.SqlConnection(Configuration["ConnectionString"]);
            return new SQLServerDataCollection<Product>(sqlConnection, "Products", new ProductComparer());
        }
    }
}

 

Open the Program.cs file and edit the following lines:

  • Configure JSON serializers to be used by SignalR (After the CreateBuilder line)
using C1.DataCollection.Serialization;
using System.Text.Json;

builder.Services.AddSignalR().AddJsonProtocol(options =>
{
    options.PayloadSerializerOptions = new JsonSerializerOptions
    {
        Converters =
        {
            new FilterExpressionJsonConverter(),
            new SortDescriptionJsonConverter(),
            new NotifyCollectionChangedEventArgsJsonConverter()
        }
    };
});

Map the hub with the URL path

using SQLServerRealTimeUpdates.Server.Hubs;

app.MapHub<ProductsHub>("/productsHub");

In the client project, we will also use the ComponentOne FlexGrid, a Blazor datagrid control, but you could use any data view control. In the client project, SQLServerRealTimeUpdates.Client, perform the following steps:

Add the NuGet packages C1.DataCollection.SignalR.Client and C1.Blazor.Grid.

Then, add the file "Index.razor" and replace it with the following content:

@page "/"
@using C1.Blazor.Grid
@using C1.DataCollection.SignalR.Client
@using SQLServerRealTimeUpdates.Shared
@inject NavigationManager NavigationManager

<FlexGrid ItemsSource="Products" AutoGenerateColumns="false" HeadersVisibility="GridHeadersVisibility.All" NewRowPosition="GridNewRowPosition.Bottom" Style="@("width:100%;")">
    <FlexGridColumns>
        <GridColumn Binding="Code"></GridColumn>
        <GridColumn Binding="Name" Width="GridLength.Star"></GridColumn>
        <GridColumn Binding="Price"></GridColumn>
    </FlexGridColumns>
</FlexGrid>

@code {
    C1ProxyDataCollection<Product> Products;

    protected override void OnInitialized()
    {
        var url = NavigationManager.ToAbsoluteUri("productsHub");
        Products = new C1ProxyDataCollection<Product>(url);
    }
}

Here in the client-side WASM application, we created a C1ProxyDataCollection which will be connected to the hub. Since this collection implements standard .NET collection interfaces like IEnumerable and IReadOnly list, this can be used like a normal collection. Still, it also implements common C1DataCollection interfaces that allow sorting and filtering of the collection.

When we bind the collection to our Blazor FlexGrid, the data virtualization will make the application load very quickly. The final user will receive more data as the grid is scrolled. Also, the user will see real-time updates to the data as it changes in the SQL Server instance. Users can even filter and sort the data as they need.

Blazor WASM

The C1ProxyDataCollection was added in the ComponentOne 2022 v2 release. In a previous post, Data Binding Blazor FlexGrid to SQL Server with Real-Time Updates, we also demonstrated how to perform real-time updates for a Blazor Server application. Blazor Server does not depend upon the new proxy data collection classes to provide SignalR communication.

You can find the ComponentOne Service Components Samples here.

Deliver enterprise-level applications faster with the most flexible .NET UI controls. Download ComponentOne Today!

comments powered by Disqus