Ejercicio: Conexión de una aplicación a la caché

Ahora que se ha creado una caché de Redis en Azure, se creará una aplicación para usarla. Asegúrese de que dispone de la información de la cadena de conexión de Azure Portal.

Nota

La instancia de Cloud Shell integrada está disponible en la parte derecha. Puede usar ese símbolo del sistema para crear y ejecutar el código de ejemplo que estamos compilando aquí o realizar estos pasos localmente si tiene una instalación del entorno de desarrollo de .NET Core.

Creación de una aplicación de consola

Vamos a usar una sencilla aplicación de consola para que podemos centrarnos en la implementación de Redis.

  1. En Cloud Shell, cree una nueva aplicación de consola de .NET Core y asígnele el nombre "SportsStatsTracker"

    dotnet new console --name SportsStatsTracker
    
  2. Esto creará una carpeta para el proyecto, siga adelante y cambie el directorio actual.

    cd SportsStatsTracker
    

Incorporación de la cadena de conexión

Vamos a agregar la cadena de conexión que obtuvimos de Azure Portal al código. Nunca almacene credenciales como esta en el código fuente. Para simplificar este ejemplo, vamos a usar un archivo de configuración. Un enfoque mejor para una aplicación en el lado de servidor en Azure sería usar Azure Key Vault con certificados.

  1. Cree un nuevo archivo appsettings.json para agregarlo al proyecto.

    touch appsettings.json
    
  2. Abra el editor de código escribiendo code . en la carpeta del proyecto. Si está trabajando localmente, le recomendamos que use Visual Studio Code. Los pasos que se indican a continuación están relacionados con su uso.

  3. Seleccione el archivo appsettings.json en el editor y agregue el siguiente texto. Pegue la cadena de conexión en el valor de la configuración.

    {
      "CacheConnection": "[value-goes-here]"
    }
    
  4. Guarde los cambios.

    Importante

    Cada vez que pegue o cambie código en un archivo en el editor, no olvide guardarlo posteriormente mediante el menú "..." o la tecla de aceleración (CTRL+S en Windows y Linux o Cmd+S en macOS).

  5. Haga clic en el archivo SportsStatsTracker.csproj en el editor para abrirlo.

  6. Agregue el siguiente bloque de configuración de <ItemGroup> en el elemento raíz <Project> para incluir el nuevo archivo en el proyecto y cópielo en la carpeta de salida. Esto garantiza que el archivo de configuración de la aplicación se coloca en el directorio de salida cuando la aplicación se compila o crea.

    <Project Sdk="Microsoft.NET.Sdk">
       ...
        <ItemGroup>
            <None Update="appsettings.json">
              <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
            </None>
        </ItemGroup>
    </Project>
    
  7. Guarde el archivo. (Asegúrese de hacer esto o perderá los cambios cuando se agregue el paquete siguiente).

Compatibilidad para leer un archivo de configuración JSON

Las aplicaciones de .NET Core requieren paquetes NuGet adicionales para leer los archivos de configuración JSON.

  1. En el símbolo del sistema de la ventana, agregue una referencia al paquete NuGet Microsoft.Extensions.Configuration.Json.

    dotnet add package Microsoft.Extensions.Configuration.Json
    

Incorporación de código para leer el archivo de configuración

Ahora que hemos agregado las bibliotecas necesarias para permitir la lectura de la configuración, debemos permitir esa funcionalidad en nuestra aplicación de consola.

  1. Seleccione Program.cs en el editor.

  2. En la parte superior del archivo, aparece una línea using System. Debajo de esa línea, agregue las líneas de código siguientes:

    using Microsoft.Extensions.Configuration;
    using System.IO;
    
  3. Reemplace el contenido del método Main por el siguiente código. Este código inicializa el sistema de configuración para leer desde el archivo appsettings.json.

    var config = new ConfigurationBuilder()
        .SetBasePath(Directory.GetCurrentDirectory())
        .AddJsonFile("appsettings.json")
        .Build();
    

El archivo Program.cs ahora debería tener este aspecto:

using System;
using Microsoft.Extensions.Configuration;
using System.IO;

namespace SportsStatsTracker
{
    class Program
    {
        static void Main(string[] args)
        {
            var config = new ConfigurationBuilder()
                .SetBasePath(Directory.GetCurrentDirectory())
                .AddJsonFile("appsettings.json")
                .Build();
        }
    }
}

Obtención de la cadena de conexión de la configuración

  1. En Program.cs, al final del método Main, use la nueva variable config para recuperar la cadena de conexión y almacenarla en una nueva variable denominada connectionString.

    • La variable config tiene un indexador en el que puede pasar una cadena para recuperar desde su archivo appSettings.json.
    string connectionString = config["CacheConnection"];
    

Compatibilidad con el cliente de .NET para la caché de Redis

A continuación, vamos a configurar la aplicación de consola para utilizar el cliente StackExchange.Redis para .NET.

  1. Agregue el paquete NuGet StackExchange.Redis al proyecto mediante el símbolo del sistema en la parte inferior del editor de Cloud Shell.

    dotnet add package StackExchange.Redis
    
  2. Seleccione Program.cs en el editor y agregue un using para el espacio de nombres StackExchange.Redis

    using StackExchange.Redis;
    

Una vez completada la instalación, el cliente de caché de Redis estará disponible para su uso con el proyecto.

Conexión a la memoria caché

Vamos a agregar el código para conectarse a la memoria caché.

  1. Seleccione Program.cs en el editor.

  2. Cree un ConnectionMultiplexer mediante ConnectionMultiplexer.Connect pasando la cadena de conexión. Asigne al valor devuelto el nombre cache.

  3. Como la conexión que se creó se puede desechar, encapsúlela en un bloque using. El código debería ser similar al siguiente:

    string connectionString = config["CacheConnection"];
    
    using (var cache = ConnectionMultiplexer.Connect(connectionString))
    {
    
    }
    

Nota

La clase ConnectionMultiplexer administra la conexión con Azure Redis Cache. Esta clase debe compartirse y reutilizarse en toda la aplicación cliente. No queremos crear una nueva conexión para cada operación. En vez de eso, queremos almacenarla como un campo de nuestra clase y reutilizarla para cada operación. Aquí solo vamos a usarla en el método Main, pero en una aplicación de producción se debería almacenar en un campo de la clase o un singleton.

Incorporación de un valor a la memoria caché

Ahora que tenemos la conexión, vamos a agregar un valor a la memoria caché.

  1. Una vez creada la conexión, en el bloque using, use el método GetDatabase para recuperar una instancia de IDatabase.

    IDatabase db = cache.GetDatabase();
    
  2. Llame a StringSet en el objeto IDatabase para establecer la clave "test:key" en el valor "algún valor".

    • El valor que se devuelve de StringSet es un bool que indica si se ha agregado la clave.
  3. Muestre el valor que se ha devuelto desde StringSet en la consola.

    bool setValue = db.StringSet("test:key", "some value");
    Console.WriteLine($"SET: {setValue}");
    

Obtención de un valor de la memoria caché

  1. A continuación, recupere el valor mediante StringGet. Esto toma la clave para recuperar y devolver el valor.

  2. Genere el valor devuelto.

    string getValue = db.StringGet("test:key");
    Console.WriteLine($"GET: {getValue}");
    
  3. El código debe ser similar al siguiente:

    using System;
    using Microsoft.Extensions.Configuration;
    using System.IO;
    using StackExchange.Redis;
    
    namespace SportsStatsTracker
    {
        class Program
        {
            static void Main(string[] args)
            {
                var config = new ConfigurationBuilder()
                    .SetBasePath(Directory.GetCurrentDirectory())
                    .AddJsonFile("appsettings.json")
                    .Build();
    
                string connectionString = config["CacheConnection"];
    
                using (var cache = ConnectionMultiplexer.Connect(connectionString))
                {
                    IDatabase db = cache.GetDatabase();
    
                    bool setValue = db.StringSet("test:key", "some value");
                    Console.WriteLine($"SET: {setValue}");
    
                    string getValue = db.StringGet("test:key");
                    Console.WriteLine($"GET: {getValue}");
                }
            }
        }
    }
    
  4. Ejecute la aplicación para ver el resultado. Escriba dotnet run en la ventana del terminal debajo del editor. Asegúrese de que se encuentra en la carpeta del proyecto o no encontrará el código para compilar y ejecutar.

    dotnet run
    

Sugerencia

Si el programa no hace lo previsto, pero realiza la compilación, puede que no haya guardado los cambios en el editor. Recuerde siempre guardar los cambios cuando cambie entre las ventanas del terminal y del editor.

Uso de las versiones asincrónicas de los métodos

Hemos sido capaces de obtener y establecer valores de la memoria caché, pero estamos usando las versiones sincrónicas antiguas. En las aplicaciones del lado del servidor, estas no contribuyen a un uso eficaz de nuestros subprocesos. En su lugar, queremos usar las versiones asincrónicas de los métodos. Puede reconocerlas fácilmente: todas terminan en Async.

Para que sea fácil trabajar con estos métodos, podemos usar las palabras clave async y await de C#. Sin embargo, tendremos que usar al menos C# 7.1 para poder aplicar estas palabras clave a nuestro método Main.

Cambio a C# 7.1

Las palabras clave async y await de C# no eran palabras clave válidas de los métodos Main hasta la versión C# 7.1. Podemos cambiar fácilmente a ese compilador mediante una marca en el archivo .csproj.

  1. Abra el archivo SportsStatsTracker.csproj en el editor.

  2. Agregue <LangVersion>7.1</LangVersion> en el primer PropertyGroup del archivo de compilación. Cuanto termine, debería tener el siguiente aspecto.

    <Project Sdk="Microsoft.NET.Sdk">
    
      <PropertyGroup>
        <OutputType>Exe</OutputType>
        <TargetFramework>netcoreapp2.0</TargetFramework>
        <LangVersion>7.1</LangVersion> 
      </PropertyGroup>
    ...
    

Aplicación de la palabra clave async

A continuación, aplique la palabra clave async al método Main. Debemos hacer tres cosas.

  1. Agregar la palabra clave async a la firma del método Main.
  2. Cambiar el tipo de valor devuelto de void a Task.
  3. Agregar una instrucción using que incluya System.Threading.Tasks.
using System;
using Microsoft.Extensions.Configuration;
using System.IO;
using StackExchange.Redis;
using System.Threading.Tasks;

namespace SportsStatsTracker
{
    class Program
    {
        static async Task Main(string[] args)
        {
        ...

Obtención y establecimiento de valores de forma asincrónica

Podemos dejar los métodos sincrónicos como están, y vamos a agregar una llamada a los métodos StringSetAsync y StringGetAsync para agregar otro valor a la caché. Establezca "counter" en el valor "100".

  1. Use los métodos StringSetAsync y StringGetAsync para establecer y recuperar una clave llamada "counter". Establezca el valor en "100".

  2. Aplique la palabra clave await para obtener los resultados de cada método.

  3. Genere los resultados en la ventana de la consola al igual que hizo con las versiones sincrónicas.

    // Simple get and put of integral data types into the cache
    setValue = await db.StringSetAsync("test", "100");
    Console.WriteLine($"SET: {setValue}");
    
    getValue = await db.StringGetAsync("test");
    Console.WriteLine($"GET: {getValue}");
    
  4. Ejecute la aplicación de nuevo. Esta debería seguir funcionando y tiene ahora dos valores.

Aumento del valor

  1. Use el método StringIncrementAsync para incrementar el valor de counter. Pase el número 50 para agregar al contador.

    • Observe que el método toma la clave and ya sea un long o double.
    • Según los parámetros que se pasen, devuelve long o double.
  2. Genere los resultados del método en la consola.

    long newValue = await db.StringIncrementAsync("counter", 50);
    Console.WriteLine($"INCR new value = {newValue}");
    

Otras operaciones

Por último, vamos a intentar ejecutar algunos métodos adicionales con el apoyo de ExecuteAsync.

  1. Haga "PING" para probar la conexión al servidor. La respuesta debe ser "PONG".
  2. Ejecute "FLUSHDB" para borrar los valores de la base de datos. La respuesta debe ser "OK".
var result = await db.ExecuteAsync("ping");
Console.WriteLine($"PING = {result.Type} : {result}");

result = await db.ExecuteAsync("flushdb");
Console.WriteLine($"FLUSHDB = {result.Type} : {result}");

El código final debería ser parecido al siguiente:

using System;
using Microsoft.Extensions.Configuration;
using System.IO;
using StackExchange.Redis;
using System.Threading.Tasks;

namespace SportsStatsTracker
{
    class Program
    {
        static async Task Main(string[] args)
        {
            var config = new ConfigurationBuilder()
                .SetBasePath(Directory.GetCurrentDirectory())
                .AddJsonFile("appsettings.json")
                .Build();

            string connectionString = config["CacheConnection"];

            using (var cache = ConnectionMultiplexer.Connect(connectionString))
            {
                IDatabase db = cache.GetDatabase();

                bool setValue = db.StringSet("test:key", "some value");
                Console.WriteLine($"SET: {setValue}");

                string getValue = db.StringGet("test:key");
                Console.WriteLine($"GET: {getValue}");

                setValue = await db.StringSetAsync("test", "100");
                Console.WriteLine($"SET: {setValue}");

                getValue = await db.StringGetAsync("test");
                Console.WriteLine($"GET: {getValue}");

                var result = await db.ExecuteAsync("ping");
                Console.WriteLine($"PING = {result.Type} : {result}");
                
                result = await db.ExecuteAsync("flushdb");
                Console.WriteLine($"FLUSHDB = {result.Type} : {result}");
            }
        }
    }
}

Desafío

A modo de desafío, intente serializar un tipo de objeto en la memoria caché. Estos son los pasos básicos.

  1. Cree un nuevo class con algunas propiedades públicas. Puede inventar uno propio ("Person" o "Car" son habituales), o use el ejemplo de "GameStats" que se proporcionó en la unidad anterior.

  2. Agregue compatibilidad al paquete NuGet Newtonsoft.Json mediante dotnet add package.

  3. Agregue un using para el espacio de nombres Newtonsoft.Json.

  4. Cree uno de los objetos.

  5. Serialícelo con JsonConvert.SerializeObject y use StringSetAsync para insertarlo en la memoria caché.

  6. Sáquelo de nuevo de la caché con StringGetAsync y, después, deserialícelo con JsonConvert.DeserializeObject<T>.