Note
This simple solution is not suitable for all use cases. Feel free to adapt it.
Some time ago I want to have progress bar for long time operation into Web application. As I primary did it for Desktop applications before it was not a trivial task. After some researching I have found that the best way is to using SignalR with AJAX. In addition, there is a differences between SignalR Core 3.0 implementation and previous SignalR versions. So some sample can't be used at all as they was wriiten for previous version.
I decided to share my investigation in this Demo application.
Complete description you can found here. I have copied here important steps only.
-
In Solution Explorer, right-click the project, and select Add > Client-Side Library.
-
In the Add Client-Side Library dialog, for Provider select unpkg.
-
For Library, enter @microsoft/signalr@latest.
-
Select Choose specific files, expand the dist/browser folder, and select signalr.js and signalr.min.js.
-
Set Target Location to wwwroot/js/signalr/, and select Install.
A hub is a class that serves as a high-level pipeline that handles client-server communication. We will call hub member functions from Java script and give back notification to Java script
-
In the Demo project folder, create a Hubs folder.
-
In the Hubs folder, create a LongOperationHub class:
- It must be inherited from Hub class
- It must have the constructor with parameter IHubContext hubContext. Normally Hub is short live time object, with hub context we could use it longer.
- It could have some member functions. We have main function Start and test function SendMessage. In Addition we could use OnConnectedAsync and OnDisconnectedAsync. Note
We send notification back for one client only, that why we need connection id.
public class LongOperationHub : Hub
{
private readonly IHubContext<LongOperationHub> mHubContext;
public LongOperationHub(IHubContext<LongOperationHub> hubContext)
{
mHubContext = hubContext;
}
public async Task SendMessage(string user, string message)
{
await Clients.All.SendAsync("ReceiveMessage", user, message);
}
public async Task Start(string connectionId)
{
await LongOperationTask(connectionId);
}
//simulate a long task
private async Task LongOperationTask(string connectionId)
{
DateTime start = DateTime.UtcNow;
int maxCount = 200;
for (int i = 0; i < maxCount; i++)
{
//Do operation slowly
Thread.Sleep(100);
TimeSpan duration = DateTime.UtcNow - start;
await mHubContext.Clients.Client(connectionId).SendAsync("ReportProgress", duration.ToString("g"), i * 100 / (maxCount - 1));
}
await mHubContext.Clients.Client(connectionId).SendAsync("ReportFinish");
}
}
The SignalR server must be configured to pass SignalR requests to SignalR.
- Add the some code to the Startup.cs file:
- to the function public void ConfigureServices(IServiceCollection services) add line services.AddSignalR();_
- to the function public void Configure(IApplicationBuilder app, IWebHostEnvironment env) to the block
_app.UseEndpoints(endpoints =>_
add endpoints.MapHub<LongOperationHub>("/longOperationHub");
app.UseEndpoints(
endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
endpoints.MapHub<LongOperationHub>("/longOperationHub");
});
- Add to the your page for starting long operation (in our demo it is
\Views\Home\Start.cshtml
) the next code:
<button onclick="StartProcess(1)" type="button" id="startButton" class="btn btn-primary btn-danger">Start the process</button>
<a asp-action="Index">Back to List</a>
<script src="~/js/signalr/dist/browser/signalr.js"></script>
<script src="~/js/longoperation_support.js"></script>
Where is longoperation_support.js could be the next code:
"use strict";
var connection = new signalR.HubConnectionBuilder()
.withUrl("/longOperationHub")
.build();
//alert(connection);
connection.on("ReceiveMessage",
(user, message) => {
var msg = message.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
var encodedMsg = `${user} says ${msg}`;
alert(encodedMsg);
});
connection.on("ReportProgress",
(message, percentage) => {
ProgressBarModal("show", message + ":" + percentage + "%");
$('#ProgressMessage').width(percentage + "%");
});
connection.on("ReportFinish",
(message, percentage) => {
ProgressBarModal();
location.href = "Index";
});
function StartProcess(parameter) {
//alert("Start");
connection.start()
.then(function() {
console.log('Now connected, connection ID=' + connection.id);
connection.invoke('start', parameter);
})
.catch(error =>
console.error('Could not connect', error)
);
}
Where is connection.on
subscription to callback functions and connection.invoke
called function defined in hub class.
Error console will be visible into web browser.
Note
Press F12 for calling debug console in Firefox
We add progress bar component globally into site layout \Views\Shared\_Layout.cshtml
For this you must add the script reference and component creating
<script src="~/js/progress_bar_helper.js" type="text/javascript"></script>
<div>
@await Component.InvokeAsync("ProgressBar")
</div>
Differences between ASP.NET SignalR and ASP.NET Core SignalR
Use hubs in SignalR for ASP.NET Core
SignalR Progress Bar Simple Example - Tutorial - progress bar implementation based on this project
Not finished, first commit