Interact with Azure Cache for Redis by using .NET
Typically, a client application uses a client library to form requests and execute commands on a Redis cache. You can get a list of client libraries directly from the Redis clients page.
Executing commands on the Redis cache
A popular high-performance Redis client for the .NET language is StackExchange.Redis. The package is available through NuGet and can be added to your .NET code using the command line or IDE. Following are examples of how to use the client.
Connecting to your Redis cache with StackExchange.Redis
Recall that we use the host address, port number, and an access key to connect to a Redis server. Azure also offers a connection string for some Redis clients that bundles this data together into a single string. It looks something like the following (with the cache-name
and password-here
fields filled in with real values):
[cache-name].redis.cache.windows.net:6380,password=[password-here],ssl=True,abortConnect=False
You can pass this string to StackExchange.Redis to create a connection the server.
Notice that there are two more parameters at the end:
- ssl - ensures that communication is encrypted.
- abortConnection - allows a connection to be created even if the server is unavailable at that moment.
There are several other optional parameters you can append to the string to configure the client library.
Creating a connection
The main connection object in StackExchange.Redis is the StackExchange.Redis.ConnectionMultiplexer
class. This object abstracts the process of connecting to a Redis server (or group of servers). It's optimized to manage connections efficiently and intended to be kept around while you need access to the cache.
You create a ConnectionMultiplexer
instance using the static ConnectionMultiplexer.Connect
or ConnectionMultiplexer.ConnectAsync
method, passing in either a connection string or a ConfigurationOptions
object.
Here's a simple example:
using StackExchange.Redis;
...
var connectionString = "[cache-name].redis.cache.windows.net:6380,password=[password-here],ssl=True,abortConnect=False";
var redisConnection = ConnectionMultiplexer.Connect(connectionString);
Once you have a ConnectionMultiplexer
, there are three primary things you might want to do:
- Access a Redis Database.
- Make use of the publisher/subscriber features of Redis, which is outside the scope of this module.
- Access an individual server for maintenance or monitoring purposes.
Accessing a Redis database
The IDatabase
type represents the Redis database. You can retrieve one using the GetDatabase()
method:
IDatabase db = redisConnection.GetDatabase();
Tip
The object returned from GetDatabase
is a lightweight object, and does not need to be stored. Only the ConnectionMultiplexer
needs to be kept alive.
Once you have a IDatabase
object, you can execute methods to interact with the cache. All methods have synchronous and asynchronous versions that return Task
objects to make them compatible with the async
and await
keywords.
Following is an example of storing a key/value in the cache:
bool wasSet = db.StringSet("favorite:flavor", "i-love-rocky-road");
The StringSet
method returns a bool
indicating whether the value was set (true
) or not (false
). We can then retrieve the value with the StringGet
method:
string value = db.StringGet("favorite:flavor");
Console.WriteLine(value); // displays: ""i-love-rocky-road""
Getting and Setting binary values
Recall that Redis keys and values are binary safe. These same methods can be used to store binary data. There are implicit conversion operators to work with byte[]
types so you can work with the data naturally:
byte[] key = ...;
byte[] value = ...;
db.StringSet(key, value);
byte[] key = ...;
byte[] value = db.StringGet(key);
StackExchange.Redis represents keys using the RedisKey
type. This class has implicit conversions to and from both string
and byte[]
, allowing both text and binary keys to be used without any complication. Values are represented by the RedisValue
type. As with RedisKey
, there are implicit conversions in place to allow you to pass string
or byte[]
.
Other common operations
The IDatabase
interface includes several other methods to work with the Redis cache. There are methods to work with hashes, lists, sets, and ordered sets.
Here are some of the more common ones that work with single keys, you can read the source code for the interface to see the full list.
Method | Description |
---|---|
CreateBatch |
Creates a group of operations to be sent to the server as a single unit, but not necessarily processed as a unit. |
CreateTransaction |
Creates a group of operations to be sent to the server as a single unit and processed on the server as a single unit. |
KeyDelete |
Delete the key/value. |
KeyExists |
Returns whether the given key exists in cache. |
KeyExpire |
Sets a time-to-live (TTL) expiration on a key. |
KeyRename |
Renames a key. |
KeyTimeToLive |
Returns the TTL for a key. |
KeyType |
Returns the string representation of the type of the value stored at key. The different types that can be returned are: string, list, set, zset, and hash. |
Executing other commands
The IDatabase
object has an Execute
and ExecuteAsync
method that can be used to pass textual commands to the Redis server. For example:
var result = db.Execute("ping");
Console.WriteLine(result.ToString()); // displays: "PONG"
The Execute
and ExecuteAsync
methods return a RedisResult
object that is a data holder that includes two properties:
Resp2Type
that returns astring
indicating the type of the result -STRING
,INTEGER
, etc.IsNull
a true/false value to detect when the result isnull
.
You can then use ToString()
on the RedisResult
to get the actual return value.
You can use Execute
to perform any supported commands - for example, we can get all the clients connected to the cache ("CLIENT LIST"):
var result = await db.ExecuteAsync("client", "list");
Console.WriteLine($"Type = {result.Resp2Type}\r\nResult = {result}");
This outputs all the connected clients:
Type = BulkString
Result = id=9469 addr=16.183.122.154:54961 fd=18 name=DESKTOP-AAAAAA age=0 idle=0 flags=N db=0 sub=1 psub=0 multi=-1 qbuf=0 qbuf-free=0 obl=0 oll=0 omem=0 ow=0 owmem=0 events=r cmd=subscribe numops=5
id=9470 addr=16.183.122.155:54967 fd=13 name=DESKTOP-BBBBBB age=0 idle=0 flags=N db=0 sub=0 psub=0 multi=-1 qbuf=0 qbuf-free=32768 obl=0 oll=0 omem=0 ow=0 owmem=0 events=r cmd=client numops=17
Storing more complex values
Redis is oriented around binary safe strings, but you can cache off object graphs by serializing them to a textual format - typically XML or JSON. For example, perhaps for our statistics, we have a GameStats
object that looks like:
public class GameStat
{
public string Id { get; set; }
public string Sport { get; set; }
public DateTimeOffset DatePlayed { get; set; }
public string Game { get; set; }
public IReadOnlyList<string> Teams { get; set; }
public IReadOnlyList<(string team, int score)> Results { get; set; }
public GameStat(string sport, DateTimeOffset datePlayed, string game, string[] teams, IEnumerable<(string team, int score)> results)
{
Id = Guid.NewGuid().ToString();
Sport = sport;
DatePlayed = datePlayed;
Game = game;
Teams = teams.ToList();
Results = results.ToList();
}
public override string ToString()
{
return $"{Sport} {Game} played on {DatePlayed.Date.ToShortDateString()} - " +
$"{String.Join(',', Teams)}\r\n\t" +
$"{String.Join('\t', Results.Select(r => $"{r.team } - {r.score}\r\n"))}";
}
}
We could use the Newtonsoft.Json library to turn an instance of this object into a string:
var stat = new GameStat("Soccer", new DateTime(2019, 7, 16), "Local Game",
new[] { "Team 1", "Team 2" },
new[] { ("Team 1", 2), ("Team 2", 1) });
string serializedValue = Newtonsoft.Json.JsonConvert.SerializeObject(stat);
bool added = db.StringSet("event:1950-world-cup", serializedValue);
We could retrieve it and turn it back into an object using the reverse process:
var result = db.StringGet("event:2019-local-game");
var stat = Newtonsoft.Json.JsonConvert.DeserializeObject<GameStat>(result.ToString());
Console.WriteLine(stat.Sport); // displays "Soccer"
Cleaning up the connection
When the connection is no longer needed, you can Dispose
the ConnectionMultiplexer
. This closes all connections and shutdown the communication to the server.
redisConnection.Dispose();
redisConnection = null;