Implementando SignalR en ASP.NET MVC

En la entrada anterior vimos algunos puntos asociados al funcionamiento general de SignalR y el planteo que propone. Lo que veremos en este nuevo post es cómo implementarlo en una aplicación ASP.NET MVC y lograr al menos una interacción básica.

Lo primero que haremos es definir el funcionamiento de nuestra aplicación. Lo que queremos lograr es armar un check-list compartido entre distintos usuarios, donde cuando uno de ellos marque un elemento como realizado o no, a los otros también les aparezca dicho estado actualizado. Para este ejemplo haremos algo muy básico, lo cual con el correr de los post’s de esta serie iremos haciendo crecer.

Comenzaremos con una aplicación ASP.NET MVC básica, instalando el paquete Nuget asociado a SignalR:

Install-Package Microsoft.AspNet.SignalR

Una vez finalizada la instalación, deberemos crear el archivo Startup.cs dentro de la raíz de nuestro proyecto web. Es en el mismo donde mediante OWIN estableceremos la inicialización de los mapeos necesarios para la interacción con SignalR, quedando el archivo de la siguiente forma:

ImplementacionSignalR - ClaseStartup
Definición de clase Startup usando OWIN

Hasta allí está lo que es la configuración inicial del proyecto. Lo posterior a hacer será crear el Hub que se encargará de la comunicación con los clientes. Para ello crearemos la carpeta Hubs dentro de la raíz del proyecto web, y dentro de la misma el elemento “SignalR Hub Class” con nombre NotificationHub:

ImplementacionSignalR - AgregarHub
Creación de la clase de tipo Hub

Nota: Como consideración, en general se define el nombre de forma de que termine con el posfijo “Hub“.

Una vez finalizada la creación tendremos nuestro Hub de la siguiente forma:

ImplementacionSignalR - HubOriginal
Hub creado por defecto

Como se puede apreciar nuestra clase hereda de Hub, que es la clase base de SignalR que tiene el comportamiento base. A su vez también tenemos el método de ejemplo Hello, el cual nos da un ejemplo mínimo de funcionamiento. Será en esta clase donde tendremos definidos todos los métodos del servidor que los distintos clientes pueden invocar, con sus parámetros. Como mencionamos en el post anterior, podremos usar tanto objetos simples como complejos.

Luego en la implementación de cada método estará lo que necesitemos hacer como reacción a lo que nos ha indicado el cliente, pudiendo no hacer nada o comunicarse con algún cliente particular para indicarle una acción o información. En el método de ejemplo al crear la clase – Hello – lo que está haciendo es indicarle a todos los clientes que ejecuten la función hello definida en el contexto del cliente. Ese funcionamiento es algo que todavía no hemos visto, ya llegaremos en breve a ese punto.

Lo siguiente que haremos será modificar esta clase para que tenga el comportamiento que nosotros estamos deseando, obteniendo el siguiente resultado:

ImplementacionSignalR - HubModificado
Hub modificado con nuesta lógica de aplicación
  1. Definimos el método Notificate, el cual recibe como parámetros el identificador de la tarea y un booleano indicando su resultado.
  2. En la implementación de dicho método no tendremos lógica adicional, indicándoles directamente a todos los clientes que ejecuten la función markDone con la información recibida en los parámetros.

Así de simple es la lógica desde el lado del servidor. Veamos que hay que hacer para la ejecución del lado del cliente.

Para este ejemplo crearemos la vista /Home/Notification, donde agregaremos el siguiente código que simula nuestras tareas a marcar cómo resueltas o no:

ImplementacionSignalR - DesarrolloHtml

Básicamente tenemos un listado de inputs de tipo checkbox, con un atributo para poder referenciarlos y saber cuál es el seleccionado (podríamos haber usado el atributo id, pero soy partidario de separar el propósito de uso de cada atributo).

Ahora en lo que debemos trabajar es en la lógica del lado del cliente, es decir en el código JavaScript que se encargará de enviar la información al servidor y dejar disponibles los métodos necesarios para que el servidor los pueda invocar. En nuestro ejemplo nos quedará de la siguiente forma:

ImplementacionSignalR - DesarrolloScripts
Código JavaScript asociado a SignalR

Hay mucho que aclarar sobre esto, veámoslo en detalle:

  1. Incluimos la referencia a los scripts de SignalR, los cuales se agregaron al instalar el paquete Nuget como una de sus dependencias.
  2. Esta referencia es fija siempre, y contiene el código autogenerado de cada uno de los Hub’s que tengamos en nuestra aplicación, con sus métodos. Esto es el resultado de la configuración realizada en la clase Startup anteriormente.
  3. Referenciamos al Hub, utilizando el nombre definido en la clase pero en el estilo lower camel case.
  4. Sobre la referencia de nuestro Hub tendremos el objeto client, donde iremos agregando todos los métodos que nuestros clientes expongan para ser invocados por el servidor. Como se puede ver, tendremos el método que establecimos invocar desde nuestro Hub con los parámetros necesarios y su implementación.
  5. Realizamos la inicialización del Hub. A su vez, en el evento “done” del mismo estableceremos la lógica para invocar al servidor.
  6. Similar a lo establecido en el punto 4, tendremos el objeto server, donde podremos ir invocando a los métodos expuestos (nuevamente usando lower camel case).
  7. Pasamos los parámetros definidos en el método correspondiente del Hub.

Y con esto ya tenemos todo listo para hacer la primer prueba de ejecución. Obviamente que para validar el funcionamiento vamos a tener que contar con más de un cliente, lo cual en esta prueba haremos con dos navegadores trabajando en paralelo.

ImplementacionSignalR - Resultado
Ejecución de la aplicación

Excelente, ya logramos el objetivo inicial!! 🙂

Ahora indaguemos un poco en lo que es el aspecto de comunicación entre el servidor y los clientes. En la entrada anterior vimos los mecanismos disponibles para esta interacción, estableciendo una “jerarquía de elección” para lograr la técnica de conexión más óptima pero sin perder funcionalidad. Para ello agregaremos la siguiente línea dentro de nuestro script JS:

$.connection.hub.logging = true;

Con la misma establecemos que en la consola de los clientes se vaya registrando la información asociada a la conexión, entre ellas el mecanismo utilizado. Para lograr una contraposición en las técnicas vamos a probar con Chrome e IE 8 (emulado), con las cuales esperamos una diferencia significativa.

ImplementacionSignalR - ConexionesNavegadores
Resultado de la determinación de la técnica adecuada para cada navegador

Como se puede apreciar, en Chrome se está usando WebSockets como era de esperar. En cambio en IE 8 al no haber soporte se usa HTTP Long Polling. Lo que nos queda validar es el comportamiento que se logra a nivel usuario con esta combinación de técnicas, lo cual se puede ver a continuación:

ImplementacionSignalR - ResultadoIE8
Ejecución de la aplicación en IE 8

Y efectivamente todo sigue funcionando de forma correcta, incluso con un navegador de generación anterior como IE 8. Como se puede ver en la pestaña de red de IE 8 están los sucesivos request’s que quedan pendientes hasta que el servidor tiene algo que notificar, mientras que en el caso de Chrome no vemos ninguna interacción, ya que va todo por la conexión WebSockets establecida inicialmente.

Otra cosa que es interesante es lo que mencionábamos en el punto 2 del código JavaScript de nuestros clientes:

Esta referencia es fija siempre, y contiene el código autogenerado de cada uno de los Hub’s que tengamos en nuestra aplicación, con sus métodos…

Así que indaguemos un poco en la misma, la cual efectivamente encontraremos sobre la ruta~/signalr/hubs. Entre varias porciones de código genéricas del funcionamiento, lo que encontraremos es la definición del Hub que tenemos creado en el servidor, con las firmas de los métodos correspondientes:

ImplementacionSignalR - HubAutogeneradoCliente
Código autogenerado con la definición del Hub

 

Conclusiones

Como pudimos apreciar en este post, SignalR realmente logra lo que habíamos mencionado en la entrada anterior. De forma realmente rápida hemos logrado establecer una funcionalidad en tiempo real sin problemas. Detengámonos tan solo unos minutos a pensar todo lo que deberíamos hacer para lograr hacer funcionar este ejemplo sin SignalR, por trivial que sea su funcionamiento.

Además de lo rápido que logramos hacer el desarrollo hay otra cuestión muy importante a tener en cuenta: es robusto. Más allá de que el ejemplo es básico, lo hemos probado en IE 8 (una de las peores alternativas para esto, en el sentido de que está totalmente obsoleto a la fecha) y el resultado de funcionamiento es perfecto.

Finalmente lo prolijo que ha quedado nuestro código, ya que claramente quedan bien separadas las responsabilidades del servidor y del cliente. Solamente debemos ser prolijos y cuidadosos con las firmas de los métodos que expone cada parte, ya que es allí donde está el acoplamiento y enlace entre las dos partes.

 

Les comento que he subido el proyecto de este ejemplo a GitHub, para que el que quiera pueda bajarlo, validar algunos puntos y hacer sus propias pruebas: https://github.com/diegobersano/SignalR-Demo

En las siguientes entradas seguiremos profundizando en algunos aspectos que tenemos disponibles con SignalR, los cuales iremos aplicando sobre el mismo proyecto y por consiguiente actualizando el repositorio de GitHub.

Gracias por leer!

Anuncios

8 comentarios en “Implementando SignalR en ASP.NET MVC

  1. Buenos días, muy intresante tu articulo sigo mucho tu blog. Una pregunta yo nunca he trbajado con SignalR, pero mi trabajo tengo la necesidad de implementar, y tengo la siguiente dudad para ver si hay la posiblidad que ma adelante toques el tema, son lo siguiente:
    1. Como implementar SignalR con WebApi.
    2. Cuando un cambio en la base de datos como un registro nuevo, o que un Stock esta en lo minimo lance una notificación y hacer notificaciones por grupo de usuario.
    3. que buena practica nos recomienda, para implementar notificaciones no llenarnos de notificaciones y como deberia ser bien estructurada.
    Gracias por tus articulos muy interesante..

    Me gusta

    • Buenas noches Erwing, muchas gracias!
      Si, la idea es ir haciendo varios post’s que completen la serie de SignalR.
      Por el punto 1, me lo anoto como idea para tratarlo de forma detallada en uno.
      Por el segundo, justo estoy escribiendo sobre cómo diferenciar a quien enviar una determinada notificación. En las próximas semanas seguro que ya se encuentra publicado.
      Y sobre el tercero, dependerá mucho del ámbito de la aplicación que estemos realizando. La clave reside en usar realmente SignalR cuando necesitamos una comunicación en dos vías, y no usarlo sin una justificación o necesidad real. Es parte del objetivo a lograr con el correr de los post’s de la serie.

      Cualquier cosa me la comentas, saludos!

      Me gusta

  2. Buenas tardes Diego, yo trabajo con signalR con sql pero solo logro traer notificaciones de una tabla, deseo saber como hacer para traer notificaciones a partir de un storedprocedure o hacerlo con una consulta para traer data relacionada tipo maestro detalle

    Me gusta

    • Hola Alexis.
      Por lo del Store Procedure no le encuentro una limitante, tan solo deberías ejecutarlo dentro de uno de los métodos de tu Hub (puede que se me esté escapando alguna particularidad en cómo estás desarrollando la solución).
      Por el maestro de detalle, puedes retornar cualquier entidad que se serializará a JSON. Por lo cual podes retornar un elemento padre que tenga una lista de sus hijos (o una lista de padres también).

      Cualquier cosa me avisas, saludos!

      Me gusta

  3. Tengo una aplicación web que requiere ande en ie9 y chrome, de entrada ocupo realizar 80 llamadas ajax async, dado que el browser me limita a seis llamadas paralelas, viendo este tema se me ocurre hacer esas llamadas a los metodos de una y que los metodos de recepción del lado del cliente esperen dicha repuesta, si se da esto ocuparia esa respuesta solo al cliente que la solicito, que me recomiendan

    Me gusta

    • Hola!
      Sin conocer del dominio del problema que estás necesitando resolver, lo primero en lo que me enfocaría es en la cantidad de llamadas ajax y si son todas necesarias.
      Dependiendo de esa respuesta podes tomar distintos cursos de acción, incluido el usar SignalR para solicitar dicha información.
      Saludos!

      Me gusta

  4. Si el tema es que las llamadas son para la carga de filtros de una aplicación, cosa que aún no entiendo el porque quieren tantos filtros, justifica el cliente que por la cantidad de usuarios que manejan la aplicación terminan por usar unos u otros, pero todos son necesarios, se puede configurar SignalR, para hacer mas de 5 peticiones en paralelo, hice un prueba con este ejemplo mas hace de 5 en 5

    Me gusta

    • Bien, allí se me ocurren las siguientes alternativas, las cuales se pueden combinar entre sí:
      – A partir del tipo de usuario restringir los filtros que pueden hacerse, siempre y cuando es algo fijo y que puedas determinar de tu parte.
      – Agrupar varias llamadas Ajax en una sola y en el lado del servidor (controller) la búsqueda de la información de forma paralela con distintas tareas (con un Task.WaitAll). De esta forma el cliente hace menos llamadas y servidor paraleliza la carga de búsqueda de la información.
      – Aplicar cache en las acciones que retornan la información (https://diegobersano.com.ar/2014/08/13/outputcache-cache-de-acciones-en-asp-net-mvc/). Si bien se hacen las llamadas, la respuesta es casi inmediata porque retorna contenido estático.

      Saludos!

      Me gusta

Responder

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión / Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión / Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión / Cambiar )

Google+ photo

Estás comentando usando tu cuenta de Google+. Cerrar sesión / Cambiar )

Conectando a %s