Last active
December 18, 2020 05:36
-
-
Save poojarsn/0c5aef92428f96e154eda06822c374b7 to your computer and use it in GitHub Desktop.
Azure Redis cache asp.net provider
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public interface ICacheProvider | |
{ | |
T Get<T>(string key, Func<T> fetch) where T : class; | |
IEnumerable<T> GetAll<T>(IReadOnlyCollection<string> keys, Func<IEnumerable<T>> fetch, Func<T, string, string> keyGen, string keyPrefix) where T : class; | |
void Clear(string key); | |
} | |
//Encryt and decrypt | |
//Logging service | |
//Key for different env same shared redis cache | |
public class RedisCacheProvider : ICacheProvider | |
{ | |
private readonly Func<IDatabase> _connectionResolver; | |
private const double CacheTimeMaxDeviationFraction = 0.2; | |
private readonly ICryptoService _cyptoService; | |
private readonly ILoggerService _loggerService; | |
private readonly string _environment; | |
private readonly string _encryptionKey = ConfigurationManager.AppSettings[AppSettingKeys.Common.RedisEncryptionKey]; | |
private static readonly Random Random = new Random(Guid.NewGuid().GetHashCode()); | |
public RedisCacheProvider(bool enableCacheEncryption, int cacheExpiration, string environment, Func<IDatabase> connectionResolver, ICryptoService cyptoService, | |
ILoggerService loggerService) | |
{ | |
_connectionResolver = connectionResolver; | |
CacheExpiration = cacheExpiration; | |
EnableCacheEncryption = enableCacheEncryption; | |
_cyptoService = cyptoService; | |
_loggerService = loggerService; | |
_environment = environment; | |
} | |
private int CacheExpiration { get; set; } | |
private bool EnableCacheEncryption { get; set; } | |
public T Get<T>(string key, Func<T> fetch) where T : class | |
{ | |
return Get(key, fetch, CacheExpiration, EnableCacheEncryption); | |
} | |
public IEnumerable<T> GetAll<T>(IReadOnlyCollection<string> keys, Func<IEnumerable<T>> fetch, | |
Func<T, string, string> keyGen, string keyPrefix) where T : class | |
{ | |
return GetAll(keys, fetch, keyGen, CacheExpiration, EnableCacheEncryption, keyPrefix); | |
} | |
private IEnumerable<T> GetAll<T>(IReadOnlyCollection<string> keys, Func<IEnumerable<T>> fetch, | |
Func<T, string, string> keyGen, int minutesToExpiry, bool enableCacheEncryption, string keyPrefix) where T : class | |
{ | |
var redisKeys = keys.Select(key => (RedisKey)GetKey(key)).ToArray(); | |
try | |
{ | |
var redisValue = _connectionResolver().StringGet(redisKeys)? | |
.Where(r => r.HasValue) | |
.Select(r => JsonConvert.DeserializeObject<T>(enableCacheEncryption ? _cyptoService.Decrypt(r, _encryptionKey) : r.ToString())); | |
if (redisValue == null) return null; | |
var redisValueArr = redisValue as T[] ?? redisValue.ToArray(); | |
if (redisValueArr.Count() == keys.Count) | |
{ | |
_loggerService.Info($"Cache Hit {keyPrefix}: keys - {keys.Count}"); | |
return redisValueArr; | |
} | |
_loggerService.Info($"Cache Miss {keyPrefix}: Search keys - {keys.Count} Redis Keys -{redisValueArr.Count()}"); | |
} | |
catch (Exception ex) | |
{ | |
_loggerService.Error($"Error while getting cache from redis. Key: {keyPrefix}, Message: {ex.Message} : {ex}"); | |
return null; | |
} | |
var values = fetch(); | |
if (values == null) return null; | |
var valuesFeatched = values as T[] ?? values.ToArray(); | |
foreach (var value in valuesFeatched) | |
{ | |
var featchedValue = (enableCacheEncryption) ? _cyptoService.Encrypt(JsonConvert.SerializeObject(value), _encryptionKey) : JsonConvert.SerializeObject(value); | |
_connectionResolver().StringSet(GetKey(keyGen(value, keyPrefix)), featchedValue, GetCacheAbsoluteExpiration(minutesToExpiry)); | |
} | |
_loggerService.Info($"Cache Added {keyPrefix}: Search keys - {keys.Count} Featched Keys -{valuesFeatched.Count()}"); | |
return valuesFeatched; | |
} | |
} | |
public void Clear(string key) | |
{ | |
throw new NotImplementedException(); | |
} | |
private T Get<T>(string key, Func<T> fetch, int minutesToExpiry, bool enableCacheEncryption) where T : class | |
{ | |
var redisValue = _connectionResolver().StringGet(GetKey(key)); | |
if (redisValue.HasValue) | |
{ | |
redisValue = enableCacheEncryption ? _cyptoService.Decrypt(redisValue, _encryptionKey) : redisValue.ToString(); | |
return JsonConvert.DeserializeObject<T>(redisValue); | |
} | |
var value = fetch(); | |
if (value == null) return null; | |
_connectionResolver().StringSet(GetKey(key), enableCacheEncryption ? _cyptoService.Encrypt(JsonConvert.SerializeObject(value), _encryptionKey) : JsonConvert.SerializeObject(value), GetCacheAbsoluteExpiration(minutesToExpiry)); | |
return value; | |
} | |
private static TimeSpan? GetCacheAbsoluteExpiration(int requestedMinutes) | |
{ | |
var maxDeviation = requestedMinutes * CacheTimeMaxDeviationFraction; | |
var deviation = (Random.NextDouble() - 0.5) * 2 * maxDeviation; | |
return TimeSpan.FromMinutes(requestedMinutes + deviation); | |
} | |
private string GetKey(string key) | |
{ | |
return string.IsNullOrWhiteSpace(_environment) ? $"{key}" : $"{_environment}:{key}"; | |
} | |
} | |
public static class RedisReferenceDataConnnection | |
{ | |
/// <summary> | |
/// To use a different App Setting you will need to write your own connection resolver and register it | |
/// via AutoFac (an example can be seen where RedisCache is registered in ServiceQuotesModule). | |
/// </summary> | |
private static readonly string ConnectionString = ConfigurationManager.ConnectionStrings["redis"].ConnectionString; | |
/// <summary> | |
/// Static property initialization and lazy loading to simulate connection pooling. | |
/// This has the constraint of forcing us into a static App Settings key ie 'redisCache'. | |
/// </summary> | |
private static readonly Lazy<ConnectionMultiplexer> Connection = | |
new Lazy<ConnectionMultiplexer>( | |
() => ConnectionMultiplexer.Connect(ConnectionString)); | |
public static IDatabase GetConnection(int databaseId) | |
{ | |
return Connection.Value.GetDatabase(databaseId); | |
} | |
} | |
public class AutofacServiceProviderBuilder | |
: BaseServiceProviderBuilder | |
{ | |
protected override IServiceProvider BuildServiceProvider(IServiceCollection serviceCollection) | |
{ | |
var builder = new ContainerBuilder(); | |
/*------------------------------------------------------------------------------------------ | |
* Register Sitecore services in Autofac. | |
* It goes through all existing services (registered so far by Sitecore) and | |
* registers them in Autofac, which makes them available in your DI container. If | |
* Sitecore registers more services later on, they will be registered in Autofac through | |
* the IServiceProvider adaptor we returned from BuildServiceProvider(). | |
*------------------------------------------------------------------------------------------ | |
*/ | |
builder.Populate(serviceCollection); | |
// Register our custom services via a module. | |
builder.RegisterModule<ServicesModule>(); | |
// Register all MVC controllers in the current assembly. | |
builder.RegisterControllers(Assembly.GetExecutingAssembly()); | |
RegisterServices(builder); | |
//builder.RegisterFilterProvider(); | |
IContainer container = builder.Build(); | |
Platform.ServiceLocator.SetLocator(container); | |
return container.Resolve<IServiceProvider>(); | |
} | |
private static void RegisterServices(ContainerBuilder builder) | |
{ | |
var cryptoService = new CryptoService(); | |
builder.RegisterInstance<ICryptoService>(cryptoService); | |
builder.Register(ctx => | |
{ | |
var cacheExpirationintvalue = 0; | |
var enableCacheEncryption = false; | |
cacheExpirationintvalue = (cacheExpirationintvalue == 0) ? 2000 : cacheExpirationintvalue; | |
var databaseId = int.Parse(System.Configuration.ConfigurationManager.AppSettings["refDataId"]); | |
var environment = System.Configuration.ConfigurationManager.AppSettings["Env"]; | |
return new RedisCacheProvider(enableCacheEncryption, cacheExpirationintvalue, environment, | |
() => RedisReferenceDataConnnection.GetConnection(databaseId), new CryptoService(), | |
new LoggerService("abc")); | |
}).As<ICacheProvider>(); | |
; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment