Snabbstart: Använda Azure Cache for Redis i .NET Framework
I den här snabbstarten införlivar du Azure Cache for Redis i en .NET Framework-app för att få åtkomst till en säker, dedikerad cache som kan nås från alla program i Azure. Du använder specifikt StackExchange.Redis-klienten med C#-kod i en .NET-konsolapp.
Hoppa till koden på GitHub
Om du vill hoppa direkt till koden kan du gå .NET Framework snabbstarten på GitHub.
Förutsättningar
- Azure-prenumeration – skapa en kostnadsfritt
- Visual Studio 2019
- .NET Framework 4 ellersenare , vilket krävs av StackExchange.Redis-klienten.
Skapa en cache
Om du vill skapa ett cacheminne loggar du in Azure Portal och väljer Skapa en resurs.
På sidan Nytt väljer du Databaser och väljer sedan Azure Cache for Redis.
På sidan Ny Redis Cache konfigurerar du inställningarna för den nya cachen.
Inställning Välj ett värde Beskrivning Prenumeration I listrutan väljer du din prenumeration. Prenumerationen där den nya instansen ska skapas Azure Cache for Redis instansen. Resursgrupp Välj en resursgrupp i listrutan eller välj Skapa ny och ange ett nytt resursgruppnamn. Namnet på den resursgrupp där du vill skapa ditt cacheminne och andra resurser. Genom att placera alla dina appresurser i en resursgrupp kan du enkelt hantera eller ta bort dem tillsammans. DNS-namn Ange ett unikt namn. Cachenamnet måste vara en sträng mellan 1 och 63 tecken som endast innehåller siffror, bokstäver eller bindestreck. Namnet måste börja och sluta med en siffra eller bokstav och får inte innehålla efterföljande bindestreck. Cacheinstansens värdnamn blir <DNS name> .redis.cache.windows.net. Plats Välj en plats i listrutan. Välj en region nära andra tjänster som ska använda ditt cacheminne. Cachetyp I listrutan väljer du en nivå. Nivån avgör storlek, prestanda och funktioner som är tillgängliga för cachen. Mer information finns i Översikt över Azure Cache for Redis. Välj fliken Nätverk eller välj knappen Nätverk längst ned på sidan.
På fliken Nätverk väljer du din anslutningsmetod.
Välj fliken Nästa: Avancerat eller välj knappen Nästa: Avancerat längst ned på sidan.
På fliken Avancerat för en basic- eller standard-cacheinstans väljer du aktivera om du vill aktivera en icke-TLS-port. Du kan också välja vilken Redis-version du vill använda, antingen 4 eller 6.
På fliken Avancerat för premiumcacheinstansen konfigurerar du inställningarna för icke-TLS-port, klustring och datapersistence. Du kan också välja vilken Redis-version du vill använda, antingen 4 eller 6.
Välj fliken Nästa: Taggar eller välj knappen Nästa: Taggar längst ned på sidan.
Du kan också ange namn och värde på fliken Taggar om du vill kategorisera resursen.
Välj Granska + skapa. Du kommer till fliken Granska + skapa där Azure verifierar din konfiguration.
När det gröna meddelandet Valideringen har godkänts visas väljer du Skapa.
Det tar en stund innan cacheminnet skapas. Du kan övervaka förloppet på Azure Cache for Redis översiktssidan. När Status visas som Körs är cacheminnet klart att användas.
Hämta värdnamn, portar och åtkomstnycklar från Azure Portal
För att kunna ansluta Azure Cache for Redis en instans behöver cacheklienter värdnamnet, portarna och en nyckel för cachen. Vissa klienter kan hänvisa till dessa objekt med namn som skiljer sig något. Du kan hämta värdnamn, portar och nycklar från Azure Portal.
Om du vill hämta åtkomstnycklarna går du till det vänstra navigeringsfönstret i cacheminnet och väljer Åtkomstnycklar.

Om du vill hämta värdnamnet och portarna går du till det vänstra navigeringsfönstret i cacheminnet och väljer Egenskaper. Värdnamnet har formen <DNS name> .redis.cache.windows.net.

Skapa en fil på datorn med namnet CacheSecrets.config och placera den på en plats där den inte checkas in med källkoden för exempelappen. För den här snabbstarten finns filen CacheSecrets.config här: C:\AppSecrets\CacheSecrets.config.
Redigera filen CacheSecrets.config och lägg till följande innehåll:
<appSettings>
<add key="CacheConnection" value="<host-name>,abortConnect=false,ssl=true,allowAdmin=true,password=<access-key>"/>
</appSettings>
Ersätt <host-name> med din cachens värdnamn.
Ersätt <access-key> med primärnyckeln för cachen.
Skapa en konsolapp
I Visual Studio väljer du Arkiv > ny > Project.
Välj Konsolapp (.NET Framework) och Nästa för att konfigurera din app. Skriv ett Project, kontrollera att .NET Framework 4.6.1 eller senare har valts och välj sedan Skapa för att skapa ett nytt konsolprogram.
Konfigurera cacheklienten
I det här avsnittet konfigurerar du konsolprogrammet att använda StackExchange.Redis-klienten för .NET.
I Visual Studio väljer du Verktyg NuGet Package Manager Package Manager Console och kör > > följande kommando från package manager-konsolfönstret.
Install-Package StackExchange.Redis
När installationen är klar är StackExchange.Redis-cacheklienten tillgänglig för användning med ditt projekt.
Ansluta till cachen
Öppna din App.config-fil i Visual Studio och uppdatera den så att den inlkuderar ett appSettings file-attribut som refererar till filen CacheSecrets.config.
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.7.2" />
</startup>
<appSettings file="C:\AppSecrets\CacheSecrets.config"></appSettings>
</configuration>
I Solution Explorer högerklickar du på Referenser och väljer Lägg till en referens. Lägg till en referens i sammansättningen System.Configuration.
Lägg till följande using-instruktioner i Program.cs:
using StackExchange.Redis;
using System.Configuration;
Anslutningen till Azure Cache for Redis hanteras av ConnectionMultiplexer-klassen. Den här klassen delas och återanvändas i hela klientprogrammet. Skapa inte en ny anslutning för varje åtgärd.
Lagra aldrig autentiseringsuppgifterna i källkoden. Om du vill hålla det här exemplet enkelt använder jag endast en config-fil för externa hemligheter. En bättre metod är att använda Azure Key Vault med certifikat.
I Program.cs, lägger du till följande medlemmar i Program-klassen för ditt konsolprogram:
private static Lazy<ConnectionMultiplexer> lazyConnection = CreateConnection();
public static ConnectionMultiplexer Connection
{
get
{
return lazyConnection.Value;
}
}
private static Lazy<ConnectionMultiplexer> CreateConnection()
{
return new Lazy<ConnectionMultiplexer>(() =>
{
string cacheConnection = ConfigurationManager.AppSettings["CacheConnection"].ToString();
return ConnectionMultiplexer.Connect(cacheConnection);
});
}
Den här metoden för att dela en ConnectionMultiplexer-instans i ditt program använder en statisk egenskap som returnerar en ansluten instans. Koden ger ett trådsäkert sätt att endast initiera en enda ansluten ConnectionMultiplexer-instans. abortConnect är inställt på falskt, vilket innebär att anropet lyckas även om en anslutning till Azure Cache for Redis inte har etablerats. En viktig egenskap i ConnectionMultiplexer är att anslutningen till cachen återställs automatiskt när nätverksproblemet eller andra fel har åtgärdats.
Värdet för appinställningen CacheConnection används för att referera till cache-anslutningssträngen från Azure-portalen som lösenordsparameter.
Hantera RedisConnectionException och SocketException genom att ansluta igen
En rekommenderad metod när du anropar metoder på är att försöka lösa undantag automatiskt genom att stänga ConnectionMultiplexer RedisConnectionException och återupprätta SocketException anslutningen.
Lägg till följande using-instruktioner i Program.cs:
using System.Net.Sockets;
using System.Threading;
I Program.cs lägger du till följande medlemmar i Program klassen :
private static long _lastReconnectTicks = DateTimeOffset.MinValue.UtcTicks;
private static DateTimeOffset _firstErrorTime = DateTimeOffset.MinValue;
private static DateTimeOffset _previousErrorTime = DateTimeOffset.MinValue;
private static SemaphoreSlim _reconnectSemaphore = new SemaphoreSlim(initialCount: 1, maxCount: 1);
private static SemaphoreSlim _initSemaphore = new SemaphoreSlim(initialCount: 1, maxCount: 1);
private static ConnectionMultiplexer _connection;
private static bool _didInitialize = false;
// In general, let StackExchange.Redis handle most reconnects,
// so limit the frequency of how often ForceReconnect() will
// actually reconnect.
public static TimeSpan ReconnectMinInterval => TimeSpan.FromSeconds(60);
// If errors continue for longer than the below threshold, then the
// multiplexer seems to not be reconnecting, so ForceReconnect() will
// re-create the multiplexer.
public static TimeSpan ReconnectErrorThreshold => TimeSpan.FromSeconds(30);
public static TimeSpan RestartConnectionTimeout => TimeSpan.FromSeconds(15);
public static int RetryMaxAttempts => 5;
public static ConnectionMultiplexer Connection { get { return _connection; } }
private static async Task InitializeAsync()
{
if (_didInitialize)
{
throw new InvalidOperationException("Cannot initialize more than once.");
}
_connection = await CreateConnectionAsync();
_didInitialize = true;
}
// This method may return null if it fails to acquire the semaphore in time.
// Use the return value to update the "connection" field
private static async Task<ConnectionMultiplexer> CreateConnectionAsync()
{
if (_connection != null)
{
// If we already have a good connection, let's re-use it
return _connection;
}
try
{
await _initSemaphore.WaitAsync(RestartConnectionTimeout);
}
catch
{
// We failed to enter the semaphore in the given amount of time. Connection will either be null, or have a value that was created by another thread.
return _connection;
}
// We entered the semaphore successfully.
try
{
if (_connection != null)
{
// Another thread must have finished creating a new connection while we were waiting to enter the semaphore. Let's use it
return _connection;
}
// Otherwise, we really need to create a new connection.
string cacheConnection = ConfigurationManager.AppSettings["CacheConnection"].ToString();
return await ConnectionMultiplexer.ConnectAsync(cacheConnection);
}
finally
{
_initSemaphore.Release();
}
}
private static async Task CloseConnectionAsync(ConnectionMultiplexer oldConnection)
{
if (oldConnection == null)
{
return;
}
try
{
await oldConnection.CloseAsync();
}
catch (Exception)
{
// Ignore any errors from the oldConnection
}
}
/// <summary>
/// Force a new ConnectionMultiplexer to be created.
/// NOTES:
/// 1. Users of the ConnectionMultiplexer MUST handle ObjectDisposedExceptions, which can now happen as a result of calling ForceReconnectAsync().
/// 2. Call ForceReconnectAsync() for RedisConnectionExceptions and RedisSocketExceptions. You can also call it for RedisTimeoutExceptions,
/// but only if you're using generous ReconnectMinInterval and ReconnectErrorThreshold. Otherwise, establishing new connections can cause
/// a cascade failure on a server that's timing out because it's already overloaded.
/// 3. The code will:
/// a. wait to reconnect for at least the "ReconnectErrorThreshold" time of repeated errors before actually reconnecting
/// b. not reconnect more frequently than configured in "ReconnectMinInterval"
/// </summary>
public static async Task ForceReconnectAsync()
{
var utcNow = DateTimeOffset.UtcNow;
long previousTicks = Interlocked.Read(ref _lastReconnectTicks);
var previousReconnectTime = new DateTimeOffset(previousTicks, TimeSpan.Zero);
TimeSpan elapsedSinceLastReconnect = utcNow - previousReconnectTime;
// If multiple threads call ForceReconnectAsync at the same time, we only want to honor one of them.
if (elapsedSinceLastReconnect < ReconnectMinInterval)
{
return;
}
try
{
await _reconnectSemaphore.WaitAsync(RestartConnectionTimeout);
}
catch
{
// If we fail to enter the semaphore, then it is possible that another thread has already done so.
// ForceReconnectAsync() can be retried while connectivity problems persist.
return;
}
try
{
utcNow = DateTimeOffset.UtcNow;
elapsedSinceLastReconnect = utcNow - previousReconnectTime;
if (_firstErrorTime == DateTimeOffset.MinValue)
{
// We haven't seen an error since last reconnect, so set initial values.
_firstErrorTime = utcNow;
_previousErrorTime = utcNow;
return;
}
if (elapsedSinceLastReconnect < ReconnectMinInterval)
{
return; // Some other thread made it through the check and the lock, so nothing to do.
}
TimeSpan elapsedSinceFirstError = utcNow - _firstErrorTime;
TimeSpan elapsedSinceMostRecentError = utcNow - _previousErrorTime;
bool shouldReconnect =
elapsedSinceFirstError >= ReconnectErrorThreshold // Make sure we gave the multiplexer enough time to reconnect on its own if it could.
&& elapsedSinceMostRecentError <= ReconnectErrorThreshold; // Make sure we aren't working on stale data (e.g. if there was a gap in errors, don't reconnect yet).
// Update the previousErrorTime timestamp to be now (e.g. this reconnect request).
_previousErrorTime = utcNow;
if (!shouldReconnect)
{
return;
}
_firstErrorTime = DateTimeOffset.MinValue;
_previousErrorTime = DateTimeOffset.MinValue;
ConnectionMultiplexer oldConnection = _connection;
await CloseConnectionAsync(oldConnection);
_connection = null;
_connection = await CreateConnectionAsync();
Interlocked.Exchange(ref _lastReconnectTicks, utcNow.UtcTicks);
}
finally
{
_reconnectSemaphore.Release();
}
}
// In real applications, consider using a framework such as
// Polly to make it easier to customize the retry approach.
private static async Task<T> BasicRetryAsync<T>(Func<T> func)
{
int reconnectRetry = 0;
int disposedRetry = 0;
while (true)
{
try
{
return func();
}
catch (Exception ex) when (ex is RedisConnectionException || ex is SocketException)
{
reconnectRetry++;
if (reconnectRetry > RetryMaxAttempts)
throw;
await ForceReconnectAsync();
}
catch (ObjectDisposedException)
{
disposedRetry++;
if (disposedRetry > RetryMaxAttempts)
throw;
}
}
}
public static Task<IDatabase> GetDatabaseAsync()
{
return BasicRetryAsync(() => Connection.GetDatabase());
}
public static Task<System.Net.EndPoint[]> GetEndPointsAsync()
{
return BasicRetryAsync(() => Connection.GetEndPoints());
}
public static Task<IServer> GetServerAsync(string host, int port)
{
return BasicRetryAsync(() => Connection.GetServer(host, port));
}
Kör cachekommandon
Lägg till följande kod för procedur Main för klass Program för ditt konsolprogram:
static void Main(string[] args)
{
IDatabase cache = GetDatabase();
// Perform cache operations using the cache object...
// Simple PING command
string cacheCommand = "PING";
Console.WriteLine("\nCache command : " + cacheCommand);
Console.WriteLine("Cache response : " + cache.Execute(cacheCommand).ToString());
// Simple get and put of integral data types into the cache
cacheCommand = "GET Message";
Console.WriteLine("\nCache command : " + cacheCommand + " or StringGet()");
Console.WriteLine("Cache response : " + cache.StringGet("Message").ToString());
cacheCommand = "SET Message \"Hello! The cache is working from a .NET console app!\"";
Console.WriteLine("\nCache command : " + cacheCommand + " or StringSet()");
Console.WriteLine("Cache response : " + cache.StringSet("Message", "Hello! The cache is working from a .NET console app!").ToString());
// Demonstrate "SET Message" executed as expected...
cacheCommand = "GET Message";
Console.WriteLine("\nCache command : " + cacheCommand + " or StringGet()");
Console.WriteLine("Cache response : " + cache.StringGet("Message").ToString());
// Get the client list, useful to see if connection list is growing...
// Note that this requires allowAdmin=true in the connection string
cacheCommand = "CLIENT LIST";
Console.WriteLine("\nCache command : " + cacheCommand);
var endpoint = (System.Net.DnsEndPoint)GetEndPoints()[0];
IServer server = GetServer(endpoint.Host, endpoint.Port);
ClientInfo[] clients = server.ClientList();
Console.WriteLine("Cache response :");
foreach (ClientInfo client in clients)
{
Console.WriteLine(client.Raw);
}
CloseConnection(lazyConnection);
}
Azure Cache for Redis har ett konfigurerbart antal databaser (16 är standard) som kan användas för att logiskt separera data i ett Azure Cache for Redis. Koden ansluter till standarddatabasen DB 0. Mer information finns i What are Redis databases? (Vad är Redis-databaser?) och Default Redis server configuration (Standardkonfiguration av Redis-server).
Cacheobjekt kan lagras och hämtas med hjälp av metoderna StringSet och StringGet.
Redis lagrar de flesta data som Redis-strängar, men dessa strängar kan innehålla flera typer av data, inklusive serialiserade binära data som kan användas när .NET-objekt lagras i cacheminnet.
Tryck på Ctrl+F5 för att skapa och köra konsolprogrammet.
I exemplet nedan ser du att Message-nyckeln tidigare hade ett cachelagrat värde som angavs med Redis-konsolen i Azure Portal. Appen uppdatera det cachelagrade värdet. Appen körde även kommandona PING och CLIENT LIST.

Arbeta med .NET-objekt i cachen
Azure Cache for Redis kan cachelagra både .NET-objekt och basdatatyper, men .NET-objekt måste serialiseras innan de kan cachelagras. Den här .NET-objektserialiseringen är programutvecklarens ansvar och ger utvecklaren flexibilitet i valet av serialiserare.
Ett enkelt sätt att serialisera objekt är att använda JsonConvert-serialiseringsmetoderna i Newtonsoft.Json och serialisera till och från JSON. I det här avsnittet ska du lägga till ett .NET-objekt till cachen.
I Visual Studio väljer du Verktyg NuGet Package Manager Package Manager Console och kör > > följande kommando från package manager-konsolfönstret.
Install-Package Newtonsoft.Json
Lägg till följande using-uttryck högst upp i Program.cs:
using Newtonsoft.Json;
Lägg till följande Employee-klassdefinition i Program.cs:
class Employee
{
public string Id { get; set; }
public string Name { get; set; }
public int Age { get; set; }
public Employee(string employeeId, string name, int age)
{
Id = employeeId;
Name = name;
Age = age;
}
}
Längst ned i Main()-procedur i Program.cs, och innan anropet till CloseConnection(), lägger du till följande rader med kod i cachen och hämtar ett serialiserat .NET-objekt:
// Store .NET object to cache
Employee e007 = new Employee("007", "Davide Columbo", 100);
Console.WriteLine("Cache response from storing Employee .NET object : " +
cache.StringSet("e007", JsonConvert.SerializeObject(e007)));
// Retrieve .NET object from cache
Employee e007FromCache = JsonConvert.DeserializeObject<Employee>(cache.StringGet("e007"));
Console.WriteLine("Deserialized Employee .NET object :\n");
Console.WriteLine("\tEmployee.Name : " + e007FromCache.Name);
Console.WriteLine("\tEmployee.Id : " + e007FromCache.Id);
Console.WriteLine("\tEmployee.Age : " + e007FromCache.Age + "\n");
Tryck på Ctrl + F5 att skapa och köra konsolappen om du vill testa serialisering av .NET-objekt.

Rensa resurser
Om du ska fortsätta till nästa självstudie kan du behålla resurserna som du har skapat i den här självstudien och använda dem igen.
Om du är klar med exempelappen för snabbstart kan du ta bort Azure-resurserna som du skapade i snabbstarten för att undvika kostnader.
Viktigt
Det går inte att ångra borttagningen av en resursgrupp och att resursgruppen och alla resurser i den tas bort permanent. Kontrollera att du inte av misstag tar bort fel resursgrupp eller resurser. Om du har skapat resurserna som är värdar för det här exemplet i en befintlig resursgrupp som innehåller resurser som du vill behålla, kan du ta bort varje resurs individuellt till vänster i stället för att ta bort resursgruppen.
Logga in på Azure Portal och välj Resursgrupper.
Skriv namnet på din resursgrupp i textrutan Filter by name... (Filtrera efter namn...). Anvisningarna för den här artikeln använde en resursgrupp med namnet TestResources. I resursgruppen i resultatlistan väljer du ... och sedan Ta bort resursgrupp.

Du blir ombedd att bekräfta borttagningen av resursgruppen. Skriv namnet på resursgruppen för att bekräfta och välj Ta bort.
Efter en liten stund tas resursgruppen och resurser som finns i den bort.
Nästa steg
I den här snabbstarten du har lärt dig hur du använder Azure Cache for Redis från ett .NET-program. Fortsätt till nästa snabbstart om du vill använda Azure Cache for Redis med en ASP.NET-webbapp.
Vill du optimera och spara på dina molnutgifter?