What is Streaming?
Streaming is a technique for transmitting data in the form of a steady continuous stream.
Usage Scenarios for Streaming
In some scenarios, if the amount of data returned by the server is larger and the waiting time is longer, the client has to wait for the server to return all data before other corresponding operations can be performed. Luckily, the server data can be fragmented by using streaming. Only the completed part will be transmitted once each of the data fragments finished being read, so you don’t need to wait for all data to be read.
How to Enable Streaming in ASP.NET Core SignalR
In ASP.NET Core SignalR, when the returned value of a Hub
method is ChannelReader
or Task<ChannelReader>
, the Hub
method will become a streaming Hub
method automatically.
Let’s take a look at a simple example below.
Create an ASP.NET Core Web Application
First let’s create an ASP.NET Core Web application by using Visual Studio 2017.
Then choose to create a Web Application of ASP.NET Core 2.1.
Create a Hub
Let’s add a StreamHub
class, and the code is as follows.
public class StreamHub : Hub
{
public ChannelReader<int> DelayCounter(int delay)
{
var channel = Channel.CreateUnbounded<int>();
_ = WriteItems(channel.Writer, 20, delay);
return channel.Reader;
}
private async Task WriteItems(ChannelWriter<int> writer, int count, int delay)
{
for (var i = 0; i < count; i++)
{
await writer.WriteAsync(i);
await Task.Delay(delay);
}
writer.TryComplete();
}
}
DelayCounter
is a streaming method, which defines a delay parameterdelay
, and thedelay
parameter defines the interval between pushing data fragments.WriteItems
is a private method, and it returns aTask
object- The last line
writer.TryComplete()
of theWriteItems
method indicates that the streaming has been completed.
Configure SignalR
First let’s add the SignalR service to the ConfigureService
method of the Startup
class.
services.AddSignalR();
Then we need to add a route to the SignalR streaming, so let’s add the following code in the Configure
method of the Startup
class:
app.UseSignalR(routes =>
{
routes.MapHub<StreamHub>("/streamHub");
});
Add the SignalR Client Script Library
In this step we need to add the SignalR JS library into the client.
Here we need to use npm to download the SignalR JS library.
npm install @aspnet/signalr
Copy the signalr.js from the <projectfolder>\node_modules@aspnet\signalr\dist\browser
directory to the wwwroot\lib\signalr
directory manually after installation.
Write Page
Copy the following code into Index.cshtml
.
@page
@model IndexModel
@{
ViewData["Title"] = "Home page";
}
<div class="container">
<div class="row"> </div>
<div class="row">
<div class="col-6"> </div>
<div class="col-6">
<input type="button" id="streamButton" value="Start Streaming" />
</div>
</div>
<div class="row">
<div class="col-12">
<hr />
</div>
</div>
<div class="row">
<div class="col-6"> </div>
<div class="col-6">
<ul id="messagesList"></ul>
</div>
</div>
</div>
<script src="~/lib/signalr/signalr.js"></script>
<script src="~/js/signalrstream.js"></script>
Enable Streaming in JavaScript
Create a new file signalrstream.js
in the wwwroot\js
directory with the following code:
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var connection = new signalR.HubConnectionBuilder()
.withUrl("/streamHub")
.build();
document.getElementById("streamButton").addEventListener("click", (event) => __awaiter(this, void 0, void 0, function* () {
try {
connection.stream("DelayCounter", 500)
.subscribe({
next: (item) => {
var li = document.createElement("li");
li.textContent = item;
document.getElementById("messagesList").appendChild(li);
},
complete: () => {
var li = document.createElement("li");
li.textContent = "Stream completed";
document.getElementById("messagesList").appendChild(li);
},
error: (err) => {
var li = document.createElement("li");
li.textContent = err;
document.getElementById("messagesList").appendChild(li);
},
});
}
catch (e) {
console.error(e.toString());
}
event.preventDefault();
}));
(() => __awaiter(this, void 0, void 0, function* () {
try {
yield connection.start();
}
catch (e) {
console.error(e.toString());
}
}))();
code interpretation
Unlike the typical SignalR, here we use a different syntax to create a SignalR connection.
var connection = new signalR.HubConnectionBuilder()
.withUrl("/streamHub")
.build();
For the common SignalR connection, we use the connection.on
method to add the listener. However, we need to use the connection.stream
method when using streaming, and this method has 2 parameters:
- the name of the
Hub
method, and in this case it’sDelayCounter
. - the parameter of the
Hub
method, and in this case it’s500
.
connection.stream("DelayCounter", 500)
.subscribe({
next: (item) => {
var li = document.createElement("li");
li.textContent = item;
document.getElementById("messagesList").appendChild(li);
},
complete: () => {
var li = document.createElement("li");
li.textContent = "Stream completed";
document.getElementById("messagesList").appendChild(li);
},
error: (err) => {
var li = document.createElement("li");
li.textContent = err;
document.getElementById("messagesList").appendChild(li);
},
});
There is a subscribe
method in the returned object from the connection.stream
method. You can register the following 3 events in the method:
next
– to be executed when getting one data fragmentcomplete
– to be executed when streaming is completeerror
– to be executed when the streaming occurs an exception
Final effect
Sum up
Streaming is not a new concept, but it is a great feature for ASP.NET Core SignalR. Streaming can be used to guarantee a smooth user experience and reduce the server stress.
Most programmers know that SignalR can’t transfer too much data, but after using streaming, the client doesn’t need to wait for the server to return all the data at once, so if your project has a large amount of data for a single request, you’re recommended to consider using streaming for SignalR to improve user experience and reduce server stress.
Source Code: https://github.com/lamondlu/StreamingInSignalR