SignalR C#— Atualizando Clients a partir do BackEnd.

SignalR é uma biblioteca criada pela equipe de desenvolvimento da Microsoft e que tem por finalidade adicionar funções em aplicações Web do lado do servidor que ao atualizar algum conteúdo os clientes também recebem ao mesmo tempo essa atualização, isso mesmo, a partir de qualquer atualização de algum dado os que estiverem com essa aplicação serão automaticamente atualizadas. Segundo docs Microsoft: A funcionalidade da Web em tempo real permite que o código do lado do servidor envie conteúdo aos clientes instantaneamente.
Para demonstrar um exemplo básico e funcional onde os clientes tenha um atualização no número de registro registrados para uma determinada tabela, instale os seguintes pacotes:
- Microsoft.AspNetCore.SignalR
- Microsoft.AspNetCore.SignalR.Client
- Microsoft.EntityFrameworkCore.Sqlite
- Microsoft.EntityFrameworkCore.Tools
sendo que os dois primeiros responsável pela SignalR e os dois restantes para a camada com Entity Framework Core
Vamos configurar a camada de persistência da seguinte forma:
Model: Todo
[Table("todos")]
public class Todo
{
[Key]
public int Id { get; set; } [Required()]
[MaxLength(150)]
public string Description { get; set; } [Required()]
public bool Done { get; set; }
}
e a camada DbContextTodo:
public class DbContextTodo: DbContext
{
public DbContextTodo(DbContextOptions options)
: base(options) { }
public DbSet<Todo> Todo { get; set; }
}
na sua aplicação Web no arquivo Startup.cs no ConfigureServices adicione as seguintes linhas:
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<DbContextTodo>(
options => options.UseSqlite("Data Source = ./base.db")
);
services.AddSignalR();
services.AddControllersWithViews();
}
tendo as configurações da camada de persistência e a adição do método padrão do SignalR.
Antes de partir para a próxima configuração precisamos criar o código responsável por essas atualizações em tempo real:
public class TodosHub : Hub
{
public DbContextTodo Db { get; }
public TodosHub(DbContextTodo db)
{
Db = db;
} public async Task GetServerCount()
{
var count = await Db.Todo.CountAsync();
await Clients.All.SendAsync("GetClientCount", count);
}
}
essa classe tem a função de pegar a quantidade de registros de uma tabela da base de dados e mostrar aos clientes que estão fazendo o uso do aplicativo e que saberão a quantidade atualizada desses registros em tempo real, com o método GetServerCount()
.
Voltando as configurações no arquivo Startup.cs no método Configure adicionando a linha dentro da configuração app.UseEndpoints: endpoints.MapHub<TodosHub>("/todohub")
, exemplo completo:
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
endpoints.MapHub<TodosHub>("/todohub");
});
Finalizado aqui a parte de configuração do aplicativo Web, agora no controller
Home adicione mais um método que tem o objetivo de carregar uma página com o contador atualizado de registros:
public IActionResult Status()
{
return View();
}
onde é gerado uma página com o seguinte código:
@{ ViewData["Title"] = "Status"; }
<h1>Status</h1>
<div>
<h3>Quantidade de registros de Todos: <span id="statusTodos">Carregando ...</span></h3>
</div>@section Scripts {
<script src="~/lib/microsoft/signalr/dist/browser/signalr.js">
</script>
<script>
$(document).ready(function () {
connection = new signalR.HubConnectionBuilder()
.withUrl("/todohub")
.configureLogging(signalR.LogLevel.Information)
.withAutomaticReconnect()
.build();
connection.start()
.then(function () {
connection.on('GetClientCount', function (count) {
document.getElementById("statusTodos").innerHTML = count;
});
connection.invoke("GetServerCount");
});
})
</script>
}
contendo nesse caso uma simples página onde a mesma por javascript
tem o objetivo de conectar em tempo real ao serviço Hub
configurado no aplicativo Web. Baixe o SignalR ou siga o tutorial de instalação e escreva o seguinte código para se conectar ao serviço:
connection = new signalR.HubConnectionBuilder()
.withUrl("/todohub")
.configureLogging(signalR.LogLevel.Information)
.withAutomaticReconnect()
.build();
connection.start()
.then(function () {
connection.on('GetClientCount', function (count) {
document.getElementById("statusTodos").innerHTML = count;
});
connection.invoke("GetServerCount");
});
Isso configura o FrontEnd responsável pelo dado em tempo real, para que funcione isso, precisa que o BackEnd avise para que seja atualizado as telas dos clientes, o código do Backend seria o seguinte:
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create(
[Bind("Id,Description,Done")] Todo todo
)
{
if (ModelState.IsValid)
{
_context.Add(todo);
await _context.SaveChangesAsync(); HubConnection hubConnection = new HubConnectionBuilder()
.WithUrl("http://localhost:53043/todohub")
.Build(); await hubConnection.StartAsync();
await hubConnection.InvokeAsync("GetServerCount");
await hubConnection.DisposeAsync(); return RedirectToAction(nameof(Index));
}
return View(todo);
}
funcionando da seguinte forma ao concluir a inserção pela camada de persistência foi criado um HubConnection sendo responsável em invocar o método GetServerCount
, que vai disparar para todos os clientes que estiverem na página Status terão a atualização da quantidade dos registros da tabela Todos, exemplo:

ou seja, em tempo real a tela de status recebe as atualizações feitas na tabela onde a inserção de um novo registro que acontece no backend é mostrado a quantidade todos os registro no frontend.
Projeto completo: https://github.com/fulviocanducci/WebAppHub
Referencias