No ASP.NET Core existe a possibilidade de se criar as Schedule Task (tarefas em segundo plano), que funcionam como se fossem serviços hospedados junto com a aplicação. A grande vantagem de se utilizar esse conceito é que podemos disparar execuções que vão ser processadas pelo lado do servidor de forma independente e sem dar lock na aplicação.
O SignalR é uma biblioteca de software livre que simplifica a adição da funcionalidade da Web em tempo real a aplicativos. A funcionalidade da Web em tempo real permite que o código do lado do servidor envie conteúdo aos clientes instantaneamente.
Implementação do Schedule Task
Para implementação do Schedule Task, vamos criar um projeto web no Visual Studio 2019:
Vamos deixar os dados da aplicação padrão, visto que nosso intuito aqui é somente mostrar o funcionamento do Schedule Task e do SingalR:
E na próxima tela vamos selecionar o tipo de aplicação, então selecionamos o Web Application (MVC).
Pronto, estamos com a base do projeto criada, mas precisamos primeiro entender como funciona o Schedule Task.
As implementações do Schedule Task funcionam a partir da implementação da interface IHostedService, ela faz com que você tenha basicamente duas implementações, a StartAsyn e StopAsync, onde a primeira é executada quando a aplicação é levantada e está pronta para iniciar o serviço, a segunda é executada quando ocorre o desligamento normal da aplicação. Basicamente ao levantar a aplicação é executado o StartAsync e ao parar executa-se o StopAsync.
Os métodos consistem basicamente em iniciar e parar o serviço, no método de início é feita a chamada para o método que vai trabalhar no serviço. No exemplo vamos criar uma classe HostedService ela vai trabalhar de forma para ser herdada pelas implementações dos serviços que vamos implementar. Todos as classes que por sua vez herdarem de HostedService vão ter que implementar o método ExecuteAsync que é o responsável por executar alguma ação na aplicação.
Vamos criar uma pasta na aplicação chamada Service.
Logo vamos adicionar uma nova classe HostedService clicando com o botão direito do mouse sobre a pasta.
Agora na classe vamos ter o seguinte código:
Implementação do Serviço
Agora com nossa classe base implementada, vamos criar a classe que vai implementar nosso serviço, essa classe vai herdar de HostedService e implementar o método ExecuteAsync, vamos adicionar uma nova classe em Services chamada DataRefreshService.
Logo vamos fazer a seguinte implementação na nossa classe:
Basicamente nesse momento o código vai executar um serviço que vai ser chamado a cada cinco segundos. Vamos deixa um break point no método para visualizarmos a execução dele.
O próximo passo é configurar nosso Startup.cs para executar o serviço que foi criado, para isso vamos fazer o seguinte ajuste no Startup no método ConfigureServices:
Vamos adicionar uma chamada Singleton ao nosso service que vai controlar nossa Injeção de Dependência, definindo como Singleton isso vai garantir que só vamos ter uma instância da classe sendo executada na aplicação.
Agora já podemos executar a aplicação, ao terminar de executar o carregamento podemos observar que o código vai ser chamado a cada cinco segundos, como colocamos o break point, podemos ficar pressionando o F5 que vamos ver que ele vai passar diversas vezes no código.
Com isso vimos que nosso serviço está sendo executado, agora podemos implementar o restante do código.
Implementação do serviço de String dinâmica
Agora vamos implementar um serviço que vai gerar uma String dinâmica a cada vez que o serviço for solicitado, para isso vamos criar uma pasta Provider, onde vai ficar a nossa classe RandomStringProvider.
Nessa classe vamos implementar um HttpClient que vai conectar ao serviço do site www.random.org que gera strings dinamicamente a cada requisição, nessa classe vamos ter uma propriedade pública que vai retornar expor a string a aplicação, abaixo temos o código da implementação:
Após a implementação realizada, devemos adicionar a classe a nossa Injeção de Dependências, vamos adicionar ela como Singleton também para só ter uma instância em toda a aplicação.
Após ter injetado a classe nas dependências, vamos ajustar nosso DataRefreshService para consumir o RandomStringProvider, para isso vamos criar uma variável readonly e alimentar ela pelo construtor, e depois no nosso ExecuteAsync vamos executar o UpdateString, vamos ver o código completo abaixo:
Pronto, agora com o break point ainda na linha que estava anteriormente vamos executar a aplicação, e ver que a cada chamada a string retornada na variável criada vai ser diferente, veja:
Próxima execução:
Assim podemos ver que nosso serviço está atualizando nossa variável a cada cinco segundos com o novo valor, e como foi definido como Singleton na nossa Injeção de Dependências ele vai o mesmo para todos na aplicação.
Próximo passo agora é implementar o SignalR para notificar a todos os clientes conectados a modificação de nossa string e o mesmo atualizar todos os clientes.
Implementação do SignalR
Com o SignalR podemos notificar todos os nossos clientes que a aplicação foi atualizada, e podemos enviar do servidor para o cliente que os mesmos executem alguma ação após essa notificação.
No nosso projeto vamos enviar uma mensagem com a atualização que foi implementada no serviço, então todos que estiverem conectados no site no momento vão atualizar com o novo texto.
O SignalR possui dois modelos de comunicação, que são os Persistentes e Hubs. No nosso projeto vamos utilizar o Hub (para conhecer mais sobre o SignalR consulte a documentação oficial clicando aqui).
O Hub permite que seu cliente e servidor chamem métodos uns dos outros diretamente. Ou seja, através do seu browser você pode invocar um método no servidor e o servidor pode chamar funções javascript dos clientes conectados no memento. Isso tudo acontece devido a um websocket (para saber mais sobre websockets clique aqui) que é aberto entre o browser e o servidor.
Vamos iniciar nossa implementação criando uma pasta Hubs e criando uma classe UpdateHub, essa classe não vai ter nenhuma implementação no nosso exemplo, ela vai tratar única e exclusivamente para realizar a abertura da conexão entre nosso cliente com o servidor. Vejamos como vai ficar:
Após criada a classe, vamos ao nosso Startup.cs pois precisamos criar um endpoit para a aplicação apontando para essa classe, para que o browser possa fazer a conexão ao websocket, isso tudo é definido no Configure do Startup, junto com as rotas da aplicação, veja como fica:
Precisamos também ajustar nosso Startup para ele utilizar o SignalR, no ConfigureServices precisamos adicionar o SignalR, conforme abaixo:
Após feito o ajuste, agora precisamos instalar o javascript do SignalR, para isso vamos clicar com o botão direito do mouse na pasta wwwroot e seguir até a opção Add e depois em Client-Side Library:
Na caixa que vai aparecer, definimos o Provider e na Library informamos @microsoft/signalr@latest. Então selecionamos Chose Specífic File, e é selecionado signalr.js e por fim colocamos o Target Location wwwroot/js/signalr/. Abaixo podemos ver como fica o processo:
O próximo passo é adicionar a referência do javascript ao nosso site, para isso vamos seguir View\Shared\_Layout.cshtml, e vamos adicionar a referência ao fim do arquivo (~/js/signalr/dist/browser/signalr.js), conforme a imagem abaixo:
A referência foi adicionada antes do site.js, pois a nossa implementação vai ficar nele e elas depende dos arquivos do SignalR já carregados. Após feita a referência vamos implementar nosso site.js, ao abrir o arquivo vamos criar o seguinte código:
Como podemos ver é criada uma variável connection e ela vai se definir uma conexão com o endpoint que foi definido no Startup. Foi criado um evento ReceiveMessage que sempre que for invocado pelo servidor vai atualizar um elemento com id update. Por último devemos dar o start na conexão com o SignalR.
Devemos criar um elemento no nosso html com id update, no exemplo foi colocado dentro do próprio _Layout.cshmlt antes do RenderBody:
Para finalizar nossa implementação agora devemos fazer nosso Sechedule Task atualizar os clientes conectados com a string dinâmica, para isso vamos ajustar novamente nosso DataRefreshService.
Vamos adicionar referência do UpdateHub e vamos criar um Método (UpdateHub) para disparar as mensagens a todos os clientes, confira o código completo:
O método UpdateHub vai ser o responsável por notificar os clientes. Ele executa o método SendAsync onde existem 2 parâmetros, um é o nome do método que vai ser chamado nos clientes, e o segundo o parâmetro que vai ser enviado para esse método. No exemplo vai ser o texto que vai atualizar a div.
Assim o resultado final é que a cada cinco segundos o serviço vai atualizar a string e logo após vai notificar todos os clientes conectados via SignalR, abaixo temos o exemplo em execução.
Conclusão
O Schedule Task abre possibilidade de você implementar diversos serviços para seu produto o melhor de tudo é que ele vai ficar rodando junto a sua aplicação, assim não dependendo de implementação de um serviço externo. O SignalR cabe muito bem a implementação do Schedule Task para você informar as pessoas conectadas que determinada rotina foi executada.
Espero que com o exemplo básico possa dar noção ao que pode ser implementado com as duas tecnologias em conjunto.
Até a próxima!
Código do exemplo
https://github.com/tborgesvieira/Schedule
Referências
https://www.stevejgordon.co.uk/asp-net-core-2-ihostedservice
https://docs.microsoft.com/pt-br/aspnet/core/signalr/introduction?view=aspnetcore-3.1
Olá Thiago, parabéns pelo artigo,
eu uso uma aplicação M2M com Mqtt, em resumo de um pc me comunico com outro,
pequenas mensagns, latencia de 20ms.
gotaria de te perguntar se com SignalR, implementando u hub como vc fez, em website,
para ele servir de Servidor, para que dois ou mais app se comunique(ponte) parecido com
broker de mqtt.
sem presse se puder me responda,
obrigado,
Carlos.
Olá Carlos, o Mqtt é diferente do SignalR, pois o broker do Mqtt vai garantir a entrega das mensagens aos subscriber. Já o SignalR só notifica as conexões ativas no momento, caso um cliente caia ele não vai receber a mensagem.