Saltar al contenido

Artículos, tutoriales, trucos, curiosidades, reflexiones y links sobre programación web ASP.NET Core, MVC, Blazor, SignalR, Entity Framework, C#, Azure, Javascript... y lo que venga ;)

17 años online

el blog de José M. Aguilar

Inicio El autor Contactar

Artículos, tutoriales, trucos, curiosidades, reflexiones y links sobre programación web
ASP.NET Core, MVC, Blazor, SignalR, Entity Framework, C#, Azure, Javascript...

¡Microsoft MVP!
martes, 12 de mayo de 2015
ASP.NET CoreSeguimos hablando de novedades que acompañarán a ASP.NET Core MVC, y esta vez le toca el turno a otra de los grandes ausencias que podemos notar cuando creamos desde cero una aplicación utilizando estos frameworks: las variables de sesión.

Bueno, en realidad no se trata tanto de una ausencia como de un desplazamiento de esta característica. Resumidamente, las variables de sesión seguirán existiendo, pero no son parte del core de ASP.NET, como ocurría desde la primera versión de ASP.NET, sino un componente totalmente opcional implementado en forma de middleware que deberá ser instalado y configurado de forma independiente.

Pero antes de continuar, recordad que tanto MVC como ASP.NET Core siguen estando en proceso de desarrollo, por lo que algunas de las cosas que contaremos podrían cambiar en las versiones definitivas, aunque espero que no demasiado.

Y dicho esto, vamos al lío: ¿cómo utilizamos variables de sesión en aplicaciones basadas en ASP.NET Core?

1. Descarga e instalación del middleware proveedor de sesión

Siguiendo la nueva filosofía modular de ASP.NET Core, los mecanismos de almacenamiento y recuperación de variables de sesión son ahora un componente que podemos añadir a nuestras aplicaciones de forma opcional.

Como es habitual, la descarga e instalación la realizaremos a través del interfaz de Nuget en Visual Studio, o bien añadiendo manualmente la referencia al archivo project.json. El nombre del paquete que nos interesa es Microsoft.AspNetCore.Session, como se muestra en la siguiente captura de pantalla:

Reference session package

(Nota: los espacios de nombres mostrados en la captura anterior no son válidos debido al cambio de nombre a ASP.NET Core)

Una vez hecho esto, ya tenemos en nuestro proyecto los componentes básicos que necesitamos para poder usar variables de sesión. Continuamos el proceso…

2. Añadir el middleware al pipeline

Session middleware in the pipelineEl estado de sesión es proporcionado por el middleware SessionMiddleware, presente en el paquete que acabamos de descargar. Este middleware, insertado en el pipeline de proceso de las peticiones, ofrecerá a los frameworks o componentes posicionados tras él la capacidad de usar variables de sesión.

El hecho de posicionarse en el pipeline y ser “atravesado” por la petición brinda a este middleware la posibilidad de establecer la cookie de sesión que distinguirá de forma única al usuario, preparar el acceso a sus datos y almacenar los cambios producidos durante el proceso de la petición.

Y ojo, que esto es importante: hay que añadir el middleware que proporciona el estado de sesión antes que el propio framework MVC. De no ser así, la petición será procesada por MVC antes de el middleware de sesión sea ejecutado, por lo que desde nuestras aplicaciones no tendremos acceso a las variables de sesión.

El siguiente ejemplo muestra cómo introducir el middleware en el pipeline, utilizando la memoria como almacenamiento para el estado de sesión:

UseInMemorySession

En aplicaciones pequeñas y sin demasiadas aspiraciones, almacenar las variables de sesión en memoria es una solución efectiva y razonable. Sin embargo, cuando la aplicación debe crecer a varios servidores o se necesita una solución menos volátil, podemos usar otros mecanismos de persistencia, utilizando el extensor UseDistributedSession() en lugar de UseInMemorySession() visto anteriormente. No entraremos en más detalle en este post, lo dejaremos para más adelante, pero es muy interesante el hecho de que esto descanse sobre el sistema de caching de ASP.NET, del que ha hablado el gran Unai hace poco.

Como suele ser habitual a la hora de añadir módulos al pipeline, es posible indicar opciones de configuración:

Middleware options

3. Establecer, obtener y eliminar variables de sesión

En primer lugar, es importante tener en cuenta que los pasos anteriores son obligatorios si queremos utilizar variables de sesión. Cualquier intento de utilización de las mismas sin haber instalado y configurado previamente los componentes que las proporcionan provocarán la aparición de un error:

Error trying to use Session vars without installing or configuring the middleware

El acceso a las variables de sesión se realiza utilizando la propiedad Context.Session, es decir, la clase Controller no proporciona ya una propiedad Session como en versiones anteriores de MVC, aunque obviamente siempre podríamos crearnos un atajo (por cierto, fijaos qué limpio queda usando las nuevas expresiones lambda en miembros de función de C# 6):

Session property
Los que ya habéis usado antes variables de sesión, seguro que os sorprende esos métodos GetInt() y SetInt() de la propiedad Session. Pues sí, esto otro cambio importante sobre las versiones anteriores del framework.

Hasta ahora podíamos introducir cualquier tipo de objeto en variables de sesión, y el abuso de esta capacidad ha generado muchísimos problemas. He llegado a ver incluso contextos de datos o conexiones a bases de datos mantenidas en memoria de esta forma, creando unos problemas terribles en tiempo de ejecución. También ha sido tradicionalmente un limitante de la posibilidad de escalado de las aplicaciones (aunque puede solventarse usando algunos trucos de balanceado), y un problema para la fiabilidad  de los sistemas (recordad que un reseteo del servidor, de IIS o incluso un reciclado del pool limpia la memoria).

Session vars are byte arraysPara evitar esto, a partir de ASP.NET Core sólo podremos guardar en variables de sesión objetos serializados, y esto se manifiesta en el tipo de dato que es ahora el diccionario Session: un array de bytes. Con esto, se acabó el establecer o leer objetos completos directos como hemos hecho siempre, tendremos que serializarlos a un array de bytes para guardarlos el almacén de sesión, así como deserializarlos para poder acceder a ellos. De esta forma, simplificamos el uso de almacenamientos distintos a la memoria local para esta información, facilitando la escalabilidad y mejorando la fiabilidad.

El framework nos proporciona métodos extensores para facilitar las tareas de serialización y deserialización en escenarios comunes, como el almacenamiento y recuperación de variables de sesión de tipo entero y cadenas de texto, mediante los métodos GetInt(), SetInt(), GetString() y SetString(), como se muestra en el siguiente ejemplo:

Session extensions

Si por cualquier motivo queremos guardar un objeto o grafo complejo, tendremos que crear nuestro propio mecanismo de serialización. Aunque la primera tentación puede ser crear un serializador binario usando la clase BinaryFormatter de toda la vida, resulta que la serialización binaria no está soportada en .NET Core, supongo que debido al carácter cross platform de este framework.

Pero sí podríamos, por ejemplo, serializarlo a JSON y guardarlo como string en la variable de sesión y realizar el proceso inverso al recuperar el valor, o bien crear nuestras propias funciones extensoras para facilitarnos la vida en el futuro:

Serialization helpers

Así, ya podríamos usar construcciones más sencillas como las siguientes:

Helper usage

Nota: existen conversaciones en Github sobre la inclusión de métodos genéricos Set<T>() y Get<T>() que permitan indicar un componente de serialización como Protobuf o Bond, así que posiblemente las versiones finales de ASP.NET Core encontremos algo de ello.

Por otra parte, para eliminar variables de sesión podemos seguir utilizando el método Remove(), al igual que hemos hecho siempre:

Context.Session.Remove()

4. Otros cambios respecto a versiones anteriores

Por último, vamos a comentar un par de detalles que han sido modificados respecto a versiones anteriores de ASP.NET.

Comenzaremos diciendo que en ASP.NET Core no existe (al menos de momento) un método Abandon(). En cambio, se añade un método Clear() que permite eliminar toda la información de sesión del usuario conectado.

Y una última cosa interesante a tener en cuenta es que ya no están disponibles los eventos Session_Start() y Session_End() que podíamos capturar en el Global.asax en versiones anteriores del framework.

Bueno, pues de momento lo dejaremos aquí. Espero que lo visto os sea de utilidad para continuar interiorizando los cambio que se avecinan con las nuevas plataformas y que la transición a ellas sea un poco menos dolorosa ;)

Publicado en Variable not found.

7 Comentarios:

Juan Irigoyen dijo...

Hola Jose María, tengo varias preguntas sobre este artículo que después de estos años todavía no tengo muy claras y me gustaría conocer tu opinión a este respecto, he oído varias veces que no es muy aconsejable utilizar objetos de sesión, para minimizar el uso de cookies, en Mvc introdujeron el diccionario TempDat que internamente utiliza una variable de sesión para almacenar su estado, ViewBag o ViewData permiten almacenar datos para el request actual y la utilización de patrones como singleton se pueden aplicar para evitar o minimizar el uso de la sesión, bajo tu punto de vista, ¿cuándo se deberían utilizar las variables de sesión?, ¿son necesarias?, ¿podemos evitarlas?, ¿si nuestro sistema crece, que problemas pueden plantearnos?, ya sé que son muchas preguntas, pero los sistemas que he desarrollado siempre han sido para pocos clientes concurrentes y nunca he observado problemas utilizándolas o no, me gustaría conocer cuáles son las mejores prácticas a este respecto. Gracias.

Anónimo dijo...

Deberías buscar mas información sobre el uso de sesiones y TempDat.
El usar una variable de tipo sesión depende del contexto en donde te encuentres.

José María Aguilar dijo...

Hola!

En aplicaciones pequeñitas, con poca carga de usuarios y siempre que no te importe que los datos de sesión se pierdan si se reinicia el servidor, puedes usarlas con tranquilidad usando la memoria como almacenamiento (la opción por defecto de ASP.NET).

El problema viene cuando quieres que crezcan esas aplicaciones que empezaron siendo pequeñas. En ese momento se te complican algo más las cosas porque las variables de sesión tienen algunas limitaciones (bloqueos de hilos, dificultad para compartirlas entre servidores...)

Personalmente, llevo años si usarlas y tampoco las echo en falta especialmente.


Saludos!

Julio A dijo...

Hola José, como siempre un excelente post.

Una alternativa cuando nuestro sistema comienza a crecer es tener esas sesiones es otro sistema, algo que permita escalar y compartirse entre diferentes instancias/servidores, un buen sistema puede ser Redis

Saludos!

josé M. Aguilar dijo...

Hola!

Sí, sin duda. Para escalar y para tener persistencia más allá de la memoria Redis es una de las opciones más recomendables.

Y además, en ASP.NET 5 la posibilidad de usar Redis viene incluida de serie :)

Muchas gracias por comentar!

Unknown dijo...

Hola,

He intentado hacer la clase SessionExtensions y me genera 2 errores
    -uno en GetObject
    -otro en SetObject

Estoy usando ASP .NET MVC Core 1.0

José María Aguilar dijo...

Hola!

Las cosas han cambiando bastante estos últimos meses... :(

Aquí tienes información más actualizada:

https://docs.asp.net/en/latest/fundamentals/app-state.html#installing-and-configuring-session

Saludos!