Scenario:
Create a Sign on service to allow creation of users in our app. The web App calls this service to get all users. Cache the data into memory and serve the next request from cache till it expires.Solution:
Implement ASP.NET core MemoryCache.- VS 2019 web project -> Add CacheKeys.cs
- Add ICache.cs
- Add ICacheService.cs
- Add InMemoryCacheService.cs
- In Startup.cs
- Add HomeController.cs
1 2 3 4 5 6 7 8 | namespace Web.Services { public static class CacheKeys { public static string Users { get { return "_allUsers"; } } public static string CallbackMessage { get { return "_callbackMessage"; } } } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 | namespace Web.Services { public interface ICache<TValue> { bool TrySet(string key, TValue value); bool TryGet(string key, out TValue value); void Remove(string key); void RemoveAll(); } } |
1 2 3 4 5 6 7 | namespace Web.Services { public interface ICacheService { ICache<TValue> GetCache<TValue>(string name); } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 | using Microsoft.Extensions.Caching.Memory; using Microsoft.Extensions.Primitives; using System; using System.Collections.Generic; using System.Threading; namespace Web.Services { //InMemory cache public class InMemoryCacheService : ICacheService { private static readonly Dictionary<string, object> _repo; private readonly ReaderWriterLockSlim Lock = new ReaderWriterLockSlim(); static InMemoryCacheService() { //create cache repo _repo = new Dictionary<string, object>(); } public ICache<TValue> GetCache<TValue>(string name) { object cache = null; Lock.EnterUpgradeableReadLock(); try { Lock.EnterWriteLock(); if (!_repo.TryGetValue(name, out cache)) { cache = new InMemoryCache<TValue>(); _repo[name] = cache; } } finally { Lock.ExitWriteLock(); Lock.ExitUpgradeableReadLock(); } return (ICache<TValue>)cache; } } public class InMemoryCache<TValue> : ICache<TValue> { private static IMemoryCache _cache = new MemoryCache(new MemoryCacheOptions()); private readonly ReaderWriterLockSlim _lock = new ReaderWriterLockSlim(); static InMemoryCache() { } public void Remove(string key) { _lock.EnterWriteLock(); try { _cache.Remove(key); } finally { _lock.ExitWriteLock(); } } public bool TryGet(string key, out TValue value) { _lock.EnterUpgradeableReadLock(); try { if (_cache.TryGetValue(key, out value)) { return true; } } catch (Exception) { value = default(TValue); } finally { _lock.ExitUpgradeableReadLock(); } return false; } public bool TrySet(string key, TValue value) { _lock.EnterWriteLock(); if (!_cache.TryGetValue(key, out var result)) { try { var cts = new CancellationTokenSource(TimeSpan.FromSeconds(10)); var options = new MemoryCacheEntryOptions(). AddExpirationToken(new CancellationChangeToken(cts.Token)). SetSlidingExpiration(TimeSpan.FromSeconds(10)). //SetAbsoluteExpiration(TimeSpan.FromSeconds(60)). SetPriority(CacheItemPriority.NeverRemove). RegisterPostEvictionCallback(callback: EvictionCallBack, state: this); //.SetSize(1024); _cache.Set(key, value, options); return true; } catch (Exception e) { return false; } finally { _lock.ExitWriteLock(); } } return true; } public void RemoveAll() { _lock.EnterWriteLock(); try { _cache.Dispose(); } catch (Exception) { } finally { _lock.ExitWriteLock(); } } private static void EvictionCallBack(object key, object value, EvictionReason evictionReason, object state) { var message = $"Entry {key} was evicted. Reason: {evictionReason}."; try { var options = new MemoryCacheEntryOptions(); //.SetSize(1024); _cache.Set(CacheKeys.CallbackMessage, message, options); } } } } |
1 2 3 4 5 | public void ConfigureServices(IServiceCollection services) { services.AddSingleton<ICacheService, InMemoryCacheService>(); services.AddSingleton<ICache<object>, InMemoryCache<object>>(); } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 | public class HomeController : Controller { private ILogger _logger; private readonly ICache<List<string>> _userCache; public HomeController(ILoggerFactory loggerFactory, SignOn.SignOnClient signOnClient, ICacheService cacheService, IMemoryCache memoryCache) { _logger = loggerFactory.CreateLogger<InMemoryCacheService>(); _userCache = cacheService.GetCache<List<string>>("cache_users"); _cache = memoryCache; } private async Task<Tuple<bool, List<string>>> GetUsers() { var userlist = new List<string>(); string name = string.Empty; var cachekey = CacheKeys.Users; _usersLock.EnterReadLock(); try { if (_userCache.TryGet(cachekey, out userlist)) { return new Tuple<bool, List<string>>(true, userlist); } if (_userCache.TryGet(CacheKeys.CallbackMessage, out var message)) { _logger.LogDebug(message.First()); } } finally { _usersLock.ExitReadLock(); } try { _usersLock.EnterWriteLock(); using (var call = _signOnClient.GetAllUsers(new UserRequest())) { userlist = new List<string>(); while (await call.ResponseStream.MoveNext()) { var user = call.ResponseStream.Current; userlist.Add(user.Name); } } if (_userCache.TrySet(cachekey,userlist)) { return new Tuple<bool, List<string>>(true, userlist); } return new Tuple<bool, List<string>>(false, null); } catch (Exception) { return new Tuple<bool, List<string>>(false, null); } finally { _usersLock.ExitWriteLock(); } } try { if (!all) { _userCache.Remove(CacheKeys.Users); } else { _userCache.RemoveAll(); } } catch (Exception e) { throw e; } return View(); } } |
No comments:
Post a Comment