O SignalR é uma implementação open source para facilitar a comunicação em tempo real de forma assíncrona.

Esta tecnologia foi desenvolvida por dois funcionários do time do asp.net chamados Damian Edwards e David Fowler, grandes nomes do asp.net atualmente e principalmente nas melhorias contínuas do asp.net.

 

SignalR

SignalR

Antes do SignalR, programar aplicações em tempo real eram extremamente complexos, controlar todos os clientes exibindo a mesma informações na tela não é uma tarefa fácil. Normalmente as aplicações são feitas para que cada usuário que está conectado façam requisições e busquem no servidor novas informações.

A idéia central é mudar a forma de trafegar os dados, ao invés da aplicação buscar os dados no servidor, deixar que o servidor informe os clientes da atualização. Existem algumas tecnologias atuais que permitem este tipo de programação, utilizando Long Polling, Web Sockets, WebRTC, NodeJS, Flash, Applets, cada uma possuí complexidade e limitações especificas.

Com o SignalR toda essa complexidade primária foi esquecida, trazendo uma implementação muito simplificada da coisa.

O SignalR em si são bibliotecas do lado do servidor  (Asp.Net) e bibliotecas do lado do cliente (JavaScript), com isso esta tecnolgia pode ser integrada a diversas outras, como Silverlight, projetos em windows phone e claro aplicações asp.net.

Aplicações em TempoReal, é uma aplicação que os dados chegam para seus clientes no mesmo tempo que ele os recebe. Para isso a aplicaçaõ deve conhecer todos os seus clientes ativos e ter uma canal de comunicação para notifica-los.

 O transporte dos dados são feito por diversos protocolos internamente implementado.

 

Transport Protocols

Transport Protocols

Cada protocolo destes é utilizado conforme o ambiente do cliente. O recomendável é utilizar o WebSockects porém clientes com browsers desatualizados e ambientes mais restritivos, o SignalR tomará a ação de abstrair outro protocolo e manter o cliente atualizado de forma transparente.

O gerenciamento da conexão, o SignalR possue controladores internos que configuram e controlam as conexões existente, atribuindo para cada página aberta um ID próprio e único, este gerenciador sabe quando o usuário está ativo, reconectados e fechados. Quando uma nova aba é aberta do browser, o SignalR atribui um novo ID para a conexão.

Quando é preciso gerenciar as conexões e fechamento de conexões para alguma tarefa especifica, implementa-se uma classe herdando de PersistentConnection, sobrescrevendo alguns métodos virtuais, fica o controle total da conexão no projeto. Esta interface é utilizando quando a escolha de persisteconnection é feita e não a utilização dos Hubs.

 Segue exemplo deste código:

 

public class ConnPersistent : PersistentConnection 
       { 
         /// Quando uma nova conexão for criada
         protected override Task OnConnectedAsync (IRequest request, string connectionId)
          {                          
            //Sua implementação Aqui
            return null; 
           } 
          /// Quando uma conexão existente for reconectada 
         protected override Task OnReconnectedAsync (IRequest request, IEnumerable groups, string clientId) 
           { 
            //Sua implementação Aqui
            return null;
           }
          /// Quando uma nova conexão for disconectada
          protected override Task OnDisconnectAsync(string connectionId)
            { 
             //Sua implementação Aqui
             return null;        
            } 
    }

Quando implementar Hubs, obrigatoriamente use as interfaces: IConnected and IDisconnected

Segue exemplo:

 

  public class ConexaoHub : Hub, IConnected, IDisconnect
    {
        /// Invocado quando uma nova conexão for criada
        public Task Connect()
        {
            // Obtém o Id da conexão
            string idConexao = Context.ConnectionId;
            //Sua implementaçao aqui
            return null;
        }
        /// Invocado quanto uma conexão existente for reconectada devido
        /// ao seu timeout (Default 110 segundos por conexão)
        public Task Reconnect(IEnumerable groups)
        {
            // Obtém o Id da conexão
            string idConexao = Context.ConnectionId;
            //Sua implementaçao aqui
            return null;
        }
        /// Invocado quando uma nova conexão for disconectada
        public Task Disconnect()
        {
            // Obtém o Id da conexão
            string idConexao = Context.ConnectionId;
            //Sua Implementaçao Aqui
            return null;
        }
    }

Para começar é necessário o Visual Studio 2013, inicie um novo projeto  web, utilizando o framework 4.5. Selecione o template em branco conforme as imagens abaixo.

 

 

Signalr Visual Studio

Signalr Visual Studio

Visual Studio MVC

Visual Studio MVC

O nuget, para utilizar o SignalR e alguns itens necessários para finalizar este projeto, devemos instalar alguns pacotes. São eles, SignalR e modernizr.

Encontre a linha de comandos do nuget diretamente no site deles, conforme abaixo:

nuget.org

nuget.org

No visual studio, abra o Nuget Package manager e coloque os comandos abaixo. Aguade até finalizar a equalização dos pacotes principais e dependentes.

 

Install-Package Microsoft.Aspnet.SignalR
Install-Package Modernizr
Install-Package Microsoft.Owin
Install-Package Microsoft.Owin.Security

Quando o processo finalizar, na pasta scripts do projeto web, estarão presentes todos os scripts do signal e seus dependentes.

 

 

Scripts Visual Studio

Scripts Visual Studio

Inicie agora um projeto Class Library, para incluir os projetos  de Entidade, serviço, interfaces e enumeradores do nosso projeto.

 

Visual Studio Class Library

Visual Studio Class Library

Crie uma entidade Pedido, esta classe POCO terá todos os campos necessários para o envio de pedido a nossa cozinha.

A nossa entidade terá um construtor da Entidade garçom.

namespace Restaurante.Dominio.Entidade
{
   public class Pedido
    {
        public Pedido(Garcom _garcom)
        {
            Garcom = _garcom;
        }
        public int Codigo { get; set; }
        public Garcom Garcom { get; private set; }
        public string Items { get; set; }
    }
}

Abaixo está a definição da entidade Garçom.

namespace Restaurante.Dominio.Entidade
{
     public class Garcom    
       {
          public int Codigo { get; set; }        
          public string Nome { get; set; }       
        }
}

As entidades do domínio foram definidas para atender nossas necessidades.
O Hub, este conceito implemetando pelo SignalR tem uma abstração de alto nível, da implementação PersisteConnection com chamadas remotas de método direto entre Servidor e Cliente. Para implementar um Hub é recomendável criar uma classe especifica que herde de Hub, dentro do projeto crie uma pasta para armazenar estas classes. O persistentConnection, é a abatração do endpoint de baixo nível, possibilitando o desenvolvedor trabalhar similarmente ao Socktes, a conexão pode ser feita com um único destinatário, agrupamentos ou broadcasts. Este modo serve para aplicações onde existirá comunicações em modo privado, facilitando gerar canais e destes trafegar informações controlada. Esta herança é obrigatória para o proxy do SignalR criar a configuração e as chamadas de métodos do Cliente para o Servidor, todas estas referências são realizadas internamente pelas bibliotecas dele.

namespace Restaurante.Web.Hub
{
    public class Cozinha : Microsoft.AspNet.SignalR.Hub
    {
    }
}

Crie um método do lado do servidor chamado InformarCozinha,  estes métodos serão disparados  aos clientes da cozinha e informar que um pedido chegou, em seguida  listar na tela do cozinheiro, sem que ele tenha que clicar F5 ou que a view tenha a inteligência de ficar requisitando em uma base de dados as novas informações.   A implementação, como citado no inicio, é muito fácil, utilizando a implementação Hub, a declaração é bem fácil de ler, totalmente intuiva e o trabalho realizado internamente é enorme. Segue código:

 public void InformarCozinha(dynamic pedido)
        {
            //Posso Fazer alguma coisa aqui...           
            Clients.All.AtualizarCozinheiros(pedido);
        }

Poucas linhas de código, nenhuma curva de aprendizado e uma nova experiência para o usuário final.   Explicando alguns pontos das linhas acima:

Clients.All [/code]

São métodos do SignalR que consegue identificar todos (all) os clientes (Clients) que estão conectados neste momento e enviar a atualização pelo método InformarCozinheiros (Método Client ex: JavaScript function).   Lado Cliente, o server já está informando os clientes das novas informações, agora o cliente terá a interligência de receber esses dados e notificar os clientes ativos no Hub. O proxy do SignalR fica a cargo de chamar o cliente Cozinha e os hubs ligados.   Na pasta Js do projeto, crie um arquivo javascript chamado Cozinha e outro arquivo chamado arquivo app-connect-signalr.js, este arquivo tem as definições de conexão do Cliente com o proxy e ficará aguardando as chamadas do servidor. Esta chamada irá refletir em um método jquery ou puro javascript.   Segue o código:

var connector = connector || {};
$(function() {    
      appCozinha = $.connection.cozinha;    
      $.connection.hub.start();
});

Na pasta Js, crie a pasta Cozinha e dentro dela crie o arquivo Cozinha.js, este arquivo armazena o código do lado do cliente, são métodos callback do proxy, que quando chamados pelo servidor, respondem a chamada e efetuam os métodos escritos.   O código cozinha segue abaixo:

var Cozinha = Cozinha || {};

Cozinha.Application = function () {
    var self = this;

    self.InformarCozinha = function (pedido) {
        connector.server.informarCozinha(pedido);
    }

    self.AtualizarCozinheiros = function ($container, data) {
        var pedido = $.parseJSON(data);
        var html = '<div class="container-fluid">' + '<div class="row">' +
            '<div class="col-sm-8" style="background-color: lavender;">Pedido: ' + pedido.Itens + ' Garcom: ' + pedido.Garcom.Nome + '</div> </div>' +
            '</div>   <div class="container-fluid"></div>';
        $($container).append(html);
    }
}

O arquivo de envio dos pedidos, chama-se Garcom.js, dentro da pasta Js no nosso projeto Web. Este arquivo tem a responsabilidade de enviar o pedido para o servidor que irá notificar os cozinheiros.

var Garcom = Garcom || {};
var pedido = pedido || {}; 

Garcom.InformarCozinha = function () {    
    var form = Garcom.GetPedido();   
    connector.server.informarCozinha(JSON.stringify(form));
} 

Garcom.GetPedido = function () {    
    var form = $("#formPedido").serializeArray();    
    var pedido = {};    
    pedido.Garcom = {};    

    $.each(form, function (i, field) {  
        //Montar arquivo de pedido em JS para serializar e enviar o objeto.    
        if (field.name === 'Pedido.Garcom.Codigo')     
            pedido.Garcom.Codigo = field.value;      

        if (field.name === 'Pedido.Garcom.Nome')            
            pedido.Garcom.Nome = field.value;        

        if (field.name === 'Pedido.Codigo')           
            pedido.Codigo = field.value;        

        if (field.name === 'Pedido.Items')        
            pedido.Itens = field.value;    
    });     

    return pedido;
} 

$("#btnSalvar").click(function () {   
    //Chamar o servidor para informar os cozinheiros.   
    Garcom.InformarCozinha();
});

Durante o desenvolvimento tipo problema com os tipos complexos no SignalR, Classes aninhadas não deserializavam para o json. Foi então que usei o tipo dynamic que recebe o Json e estes dados serão trafegados nativamente, ou seja, uma ponta envia Json e a outra ponta receberá a mesma informação sem nenhum tratamento. Para iniciar as views, inclua no projeto o bootstrap, framework de front-end e deixar o design das páginas bem legais.

Install-Package bootstrap

A view onde será informada o pedido, será criado dentro da /Home/Index, assim sendo, a controller Home é definida, seguindo os padrões do asp.net mvc.   Segue o código:

using System.Web.Mvc;
namespace Restaurante.Web.Controllers
{
    public class HomeController : Controller
    {
        public ActionResult Index()
        {
            return View();
        }
    }
}

e abaixo a view Index.cshtml.

@model Restaurante.Web.ViewModel.PedidoViewModel
@{
    ViewBag.Title = "Crie seu Pedido";
}
<form id="formPedido">
    @Html.HiddenFor(x =&gt; x.Pedido.Garcom.Codigo)     @Html.HiddenFor(x =&gt; x.Pedido.Garcom.Nome)InformarCozinheiros     @Html.HiddenFor(x =&gt; x.Pedido.Codigo)
    <div id="content" class="container-fluid">
        <div class="row">
            <div class="label">Descrição do Pedido</div>
            <div class="input-lg">@Html.TextBoxFor(x =&gt; x.Pedido.Items)</div>
        </div>
        <div class="row"></div>
    </div>
</form>

Atenta-se a alguns detalhes do cshtml, pois são fundamentais para o funcionamento do SignalR.
Segue linha do cshtml acima:

<script src="/signalr/hubs" type="text/javascript"></script>

Esta linha é o que faz tudo funcionar, o visual studio irá informar que o caminho não existe, em tempo de execução. Este espaço é criado em tempo de execução e hospedado junto ao seu site ao final, nele estará todas as instruções do Proxy e Hubs do SignalR,  utilizando a ferramenta de desenvolvedor dos browsers atuais e procure este endereço, todo o código gerado pelo SignalR e os proxys  e hubs poderão ser visualizados e debugado durante a execução.

Crie uma viewmodel para tratar alguns detalhes da view e ajudar no trafego dos dados entre a controller e a view.

using Restaurante.Dominio.Entidade;
namespace Restaurante.Web.ViewModel
{
    public class PedidoViewModel
    {
        private Pedido _pedido;
        public PedidoViewModel(Pedido pedido)
        {
            _pedido = pedido;
        }
        public Pedido Pedido
        {
            get { return _pedido; }
        }
    }
}

Agora algo importante para que o sistema rode neste momento, devemos criar a nossa classe Startup, para que o Owin possa rodar e instanciar o SignalR. Esta classe registra no pipe do owin as referências para o SignalR ser executado corretamente. Caso esqueça desta declaração o sistema apresentará a exceção e irá obrigar a declaração.

using Owin;
namespace Restaurante.Web
{
    public class Startup
    {
        public void Configuration(IAppBuilder app)
        {
            app.MapSignalR();
        }
    }
}

 

A tela de pedidos irá receber os dados do garçom e utilizando os javascripts escritos, irá informar os cozinheiros que houve um novo pedido, sem que a aplicação fique requisitando nada, apenas receba o push do servidor.

 

A baixo o código da view para os pedidos:

 

@{
    ViewBag.Title = "Cozinha - Recebendo Pedidos";
}
<h2>Pedidos Atualizados</h2>

<div id="listOrders" class=""></div>

<script>
    $(function () {
        var conn = connector;
        var app = new Cozinha.Application();
        conn.client.AtualizarCozinheiros = function (pedido) {
            app.AtualizarCozinheiros($("#listOrders"), pedido);
        }
    });
</script>

O SignalR é uma implementação incrivel que facilitou um trabalho árduo para desenvolver aplicações em tempo real, trocando dificies implementações para uma forma declarativa simples.

 

Esta tecnologia abriu infinitas possibilidades de desenvolvimento, o exemplo mais simples de implementação é o de Chat, existem diversos exemplos e aplicações finais que utilizam SignalR como tecnologia para atualizar os usuários.

 

Este projeto desenvolvido está no github e a missão agora é melhorar o código e aprender juntos com novas implementações. Mantendo como core de comunicação o SignalR, podemos evoluir até a um nível de produto.

Referências de pesquisa

Restaurante RealTime – Codigo Fonte

SignalR

SignalR CookBook – Amostra Grátis

Install-Package Microsoft.AspNet.SignalR.Sample [Nuget Comand SignalR Samples]

WebSockets

Github Signal

Nadador de maratonas aquática aposentado, Pai, Programador .net e do que estiver por ai. Trabalho a alguns anos, grande parte deste fazendo “projetinhos”. Assumido um programador, não gosta de ver a glória sem participar da guerra. Sonhando um dia acertar no projeto perfeito e viver eternamente sendo dono da sua própria intelectualidade.

Facebook Twitter LinkedIn Google+ Skype 

Comentários

comentarios