DotNet Core, Coravel & SignalR

in Uncategorized

DotNet Core, Coravel & SignalR

Coravel è una libreria .net core che consente di implementare funzionalità legate allo scheduling (e non solo), promettendo di mantenere semplice la configurazione. La descrizione del progetto su github riporta: 

Near-zero config .NET Core library that makes Task Scheduling, Caching, Queuing, Mailing, Event Broadcasting (and more) a breeze!

L’utilizzo di SignalR con Coravel è piuttosto naturale e consente, ad esempio, di inviare notifiche di un progress Task,  verso l’interfaccia web di un’applicazione. Ovviamente non è l’unica libreria adatta a questo scopo, ma sicuramente è una di quelle più semplici da integrare. 

L’integrazione all’interno di un’applicazione .NET Core MVC passa dall’inclusione della libreria tramite nuget (linea di comando / interfaccia grafica)

dotnet add package Coravel

e dalla successiva attivazione del services all’interno del file Startup.cs: 

public void ConfigureServices(IServiceCollection services)
    {
        //...

        services.AddQueue();
    }

Sfruttando la dependency injection è possibile ottenere un’instanza della classe Queue semplicemente inserendo all’interno del costruttore della controller una dipendenza all’interfaccia IQueue:

public class HomeController : Controller
{
    private readonly IQueue _queue;

    public HomeController(IQueue queue)
    {
        _queue = queue;
    }

    //...
}

Ovviamente, il tutto può essere replicato anche all’interno di una Razor Pages, impostando, allo stesso modo il costruttore. Per default, Coravel esegue task schedulati ogni 30 secondi: è comunque possibile modificare questo intervallo di tempo agendo sulla configurazione, come riportato all’interno della guida ufficiale.

Il metodo QueueAsyncTask consente di aggiungere in maniera asincrona un task alla coda di esecuzion. E’ possibile utilizzare la seguente sintassi:

 _queue.QueueAsyncTask(async () => ... )

inserendo all’interno del corpo della funzione il codice che dovrà essere eseguito.

All’interno del controller è possibile creare una particolare Action invocata al momento della schedulazione e del run del processo da eseguire in background. Coravel accoderà il task da eseguire (in maniera asincrona) e potrà effettuare un redirect verso una pagina specifica: la pagina in cui mostrare la progress bar. Per mantere un riferimento al task da eseguire viene generato un guid per ogni nuova chiamata (e relativo accodamento del task nella coda).

public IActionResult StartProgress()
    {
        string jobId = Guid.NewGuid().ToString("N");
        _queue.QueueAsyncTask(() => PerformBackgroundJob(jobId));
        return RedirectToAction("ProgressAction", new {jobId});
    }

L’id del task appena creato (guid) verrà successivamente riportato all’interno della pagina corrispondente alla action ProgressAction, per essere utilizzato al momento della ricezione delle notifiche dell’avanzamento del task.

Per poter ricevere le notifiche all’interno di una view, verrà utilizzato Signal-R, ovviamente nella versione presente in .NET Core e di cui si è già parlato in un precedente post.

Integrazione con Signal-R

Il primo passo per poter utilizzare signal-r è quello di includerlo all’interno del progetto, aggiungendo il service all’interno del file Startup.cs:

public class Startup
{
    public void ConfigureServices(IServiceCollection services)
    {
        //...
        services.AddSignalR();
    }
}

in questo modo Signal-R sarà disponibile all’interno del progetto. La configurazione di Signal-R non è oggetto di questo post, rimando quindi alla relativa documentazione. L’obiettivo dell’applicazione è quello di poter inviare all’utente una notifica dell’avanzamento del processo eseguito in background: per questo scopo è necessario creare una classe Hub:

public class JobProgressHub : Hub
{
    public async Task AssociateJob(string jobId)
    {
        await Groups.AddToGroupAsync(Context.ConnectionId, jobId);
    }
}

Il metodo asincrono AssociateJob consente di creare un Signal-R group assiociato all’ id del job (creato in precedenza) . Questo metodo consentirà di inviare un messaggio a tutti i client che hanno fatto join al gruppo.

All’interno del metodo Configure, sempre all’interno di Startup.cs è necessario associare una route specifica per l’utilizzo di Signal-R:

app.UseSignalR(routes => { routes.MapHub<JobProgressHub>("/jobprogress"); 

In questo caso la route si riferisce a /jobprogress ed utilizza come Hub, la classe JobProgressHub creata in precedenza.

Inviare un messaggio

Per poter inviare un messaggio a tutti i client registrati è necessario utilizzare il metodo asincrono SendAsync all’interno di una action :

await _hubContext.Clients.Group(jobId).SendAsync("progress", i);

Nell’implementazione dell’interfaccia client (in javascript) lo stato del progress verrà notificato all’interno della variabile progress.

_hubContext è un instanza di un oggetto (che può essere ottenuto tramite DI nel costruttore del controller) :

IHubContext<JobProgressHub> _hubContext; 

Creazione dell’interfaccia client

Per l’implementazione del client è necessario includere nel progetto la libreria client di Signal-R. All’interno della pagina in cui visualizzare la progress includiamo il seguente script:

@section Scripts
{
    <script src="~/lib/signalr/signalr.js"></script>
    <script>
        var connection = new signalR.HubConnectionBuilder()
            .withUrl("/jobprogress")
            .configureLogging(signalR.LogLevel.Information)
            .build();
        connection.on("progress",
            (percent) => {
                if (percent === 100) {
                    document.getElementById("job-status").innerText = "Finished!";
                } else {
                    document.getElementById("job-status").innerText = `${percent}%`;
                }
            });
        connection.start()
            .then(_ => connection.invoke("AssociateJob", "@ViewBag.JobId"))
            .catch(err => console.error(err.toString()));
    </script>
}

definendo la connection da utilizzare e le operazioni che dovranno essere eseguite quando si verifica l’evento “progress”, lo stesso definito al momento della definizione dell’Hub. In questo caso, sfruttando javascript viene aggiornato il valore del DOM html corrispondente all’ID job-status.

Questo post è un’introduzione alla libreria Coravel che consente di utilizzare servizi Queue (e molto altro) all’interno di applicazioni ASPNET.Core.