redis localhost:6379> keys *
39) "foo:rand:000000000016"
40) "foo:rand:000000000017"
41) "foo:rand:000000000018"
42) "foo:rand:000000000019"
43) "mykey-f9d21bca-48f6-4cf8-b68d-1652b9533633"
44) "mykey-43d5b08a-5b60-4c72-8820-dcb6f6372b4b"
redis localhost:6379> del mykey-0001
redis localhost:6379> flushall
localhost:6379> set key001 value001
Ok
localhost:6379> get key001
"value001"
Redis has a .NET package which wraps up the underlying TCP/IP communications and gives us a nice IDistributedCache to work with.
-
[ClassInitialize]
public static void Init(TestContext context)
{
var builder = new ConfigurationBuilder();
builder.AddJsonFile("settings.json", optional: false);
Config = builder.Build();
ServiceCollection coll = new ServiceCollection();
coll.AddStackExchangeRedisCache(options =>
{
string server = Config["redis-server"];
string port = Config["redis-port"];
string cnstring = $"{server}:{port}";
options.Configuration = cnstring;
});
var sprovider = coll.BuildServiceProvider();
//Use sprovder to create an instance of IDistributedCache
var cache = sprovider.GetService();
}
//The overridable Configure method of a Azure function Startup class
public override void Configure(IFunctionsHostBuilder builder)
{
var configurationBuilder = new ConfigurationBuilder();
configurationBuilder.AddEnvironmentVariables();
IConfiguration config = configurationBuilder.Build();
builder.Services.AddSingleton(config);
builder.Services.AddSingleton(provider => new RedisConfiguration
{
ConnectionStringTxn = provider.GetRequiredService()["REDISDEMO_CNSTRING"]
});
builder.Services.AddSingleton(this.CreateRedisServerCallBack);
}
private REDIS.IServer CreateRedisServerCallBack(IServiceProvider provider)
{
var redisConfig = provider.GetService();
var cnstringAdmin = redisConfig.ConnectionStringAdmin;
//You need allowAdmin=true to call methods .FlushDatabase and .Keys()
//https://stackexchange.github.io/StackExchange.Redis/Basics.html
var redis = REDIS.ConnectionMultiplexer.Connect(cnstringAdmin);
var firstEndPoint = redis.GetEndPoints().FirstOrDefault();
if (firstEndPoint == null)
{
throw new ArgumentException("The endpoints collection was empty. Could not get an end point from Redis connection multiplexer.");
}
return redis.GetServer(firstEndPoint);
}
public class RedisConfiguration
{
public string ConnectionStringAdmin => $"{this.ConnectionStringTxn},allowAdmin=true";
public string ConnectionStringTxn { get; internal set; }
public override string ToString()
{
return $"{ConnectionStringTxn}";
}
}
The interface IDistributedCache will address the requirements if you key-value pair caching is all you need. However, if you want to manage the Redis cache server itself (e.g. flush the cache, enumerate keys, etc.) then the interface IServer is more powerful
The class MemoryDistributedCache provides an inproc implementation of IDistributedCache and very useful during local development and debugging.
[TestMethod]
public void ExampleTestMethod()
{
var expectedData = new byte[] { 100, 200 };
var opts = Options.Create(new MemoryDistributedCacheOptions());
IDistributedCache cache = new MemoryDistributedCache(opts);
cache.Set("key1", expectedData);
var cachedData = cache.Get("key1");
Assert.AreEqual(expectedData, cachedData);
//Use the variable cache as an input to any class which expects IDistributedCache
}
You are using the key-value caching functionality of Redis. You are coding an Azure function (could be an ASP.NET Core Web app) and you want to develop and debug the code. Do you need to install Redis locally? Not neccessary. Through a couple of lines of clever DI, you can “fool” your classes to use the MemoryDistributedCache implementation of the interface IDistributedCache.
In this snippet we are checking for the the value of an environment variable and conditionally injecting the desired implementation of IDistributedCache
services.AddStackExchangeRedisCache(options =>
{
options.Configuration = "localhost:6000";
});
4.Implementing caching code snippet in Get call of WeatherForeCastController,
[HttpGet]
public async Task < IActionResult > Get() {
var cacheKey = "weatherList";
string serializedCustomerList;
List < string > weatherList = new List < string > ();
var redisCustomerList = await _distributedCache.GetAsync(cacheKey);
if (redisCustomerList != null) {
serializedCustomerList = Encoding.UTF8.GetString(redisCustomerList);
weatherList = JsonConvert.DeserializeObject < List < string >> (serializedCustomerList);
} else {
weatherList = GetFromDb();
serializedCustomerList = JsonConvert.SerializeObject(weatherList);
redisCustomerList = Encoding.UTF8.GetBytes(serializedCustomerList);
var options = new DistributedCacheEntryOptions().SetAbsoluteExpiration(DateTime.Now.AddMinutes(10)).SetSlidingExpiration(TimeSpan.FromMinutes(2));
await _distributedCache.SetAsync(cacheKey, redisCustomerList, options);
}
return Ok(weatherList);
}
We check if the key has a value in Redis, then convert it to a weather list and send back the data and if the value does not exist in Redis, then access the database via efcore (in above code value is hardcoded), get the data and set it to Redis.
If the key has value then, the data will be stored in Redis as a byte array. We will be converting this array of a string which will convert the string to an object of type, List
DistributedCacheEntryOptions,
SetAbsoluteExpiration
Here you can set the expiration time of the cached object.
SetSlidingExpiration
This is similar to Absolute Expiration. It expires as a cached object if it not being requested for a defined amount of time period. Note that Sliding Expiration should always be set lower than the absolute expiration