44using System ;
55using System . Threading . Tasks ;
66using Microsoft . Extensions . Caching . Distributed ;
7+ using Microsoft . Extensions . Logging ;
78using Microsoft . Extensions . Options ;
89using StackExchange . Redis ;
910
@@ -30,11 +31,16 @@ public class RedisCache : IDistributedCache, IDisposable
3031
3132 private ConnectionMultiplexer _connection ;
3233 private IDatabase _cache ;
34+ private readonly ILogger _logger ;
3335
3436 private readonly RedisCacheOptions _options ;
3537 private readonly string _instance ;
3638
3739 public RedisCache ( IOptions < RedisCacheOptions > optionsAccessor )
40+ : this ( optionsAccessor , loggerFactory : null )
41+ { }
42+
43+ public RedisCache ( IOptions < RedisCacheOptions > optionsAccessor , ILoggerFactory loggerFactory )
3844 {
3945 if ( optionsAccessor == null )
4046 {
@@ -45,6 +51,7 @@ public RedisCache(IOptions<RedisCacheOptions> optionsAccessor)
4551
4652 // This allows partitioning a single backend cache for use with multiple apps/services.
4753 _instance = _options . InstanceName ?? string . Empty ;
54+ _logger = loggerFactory ? . CreateLogger < RedisCache > ( ) ;
4855 }
4956
5057 public byte [ ] Get ( string key )
@@ -84,20 +91,27 @@ public void Set(string key, byte[] value, DistributedCacheEntryOptions options)
8491 throw new ArgumentNullException ( nameof ( options ) ) ;
8592 }
8693
87- Connect ( ) ;
94+ try
95+ {
96+ Connect ( ) ;
8897
89- var creationTime = DateTimeOffset . UtcNow ;
98+ var creationTime = DateTimeOffset . UtcNow ;
9099
91- var absoluteExpiration = GetAbsoluteExpiration ( creationTime , options ) ;
100+ var absoluteExpiration = GetAbsoluteExpiration ( creationTime , options ) ;
92101
93- var result = _cache . ScriptEvaluate ( SetScript , new RedisKey [ ] { _instance + key } ,
94- new RedisValue [ ]
95- {
102+ var result = _cache . ScriptEvaluate ( SetScript , new RedisKey [ ] { _instance + key } ,
103+ new RedisValue [ ]
104+ {
96105 absoluteExpiration ? . Ticks ?? NotPresent ,
97106 options . SlidingExpiration ? . Ticks ?? NotPresent ,
98107 GetExpirationInSeconds ( creationTime , absoluteExpiration , options ) ?? NotPresent ,
99108 value
100- } ) ;
109+ } ) ;
110+ }
111+ catch ( Exception ex )
112+ {
113+ LogSuppressedException ( ex ) ;
114+ }
101115 }
102116
103117 public async Task SetAsync ( string key , byte [ ] value , DistributedCacheEntryOptions options )
@@ -117,20 +131,27 @@ public async Task SetAsync(string key, byte[] value, DistributedCacheEntryOption
117131 throw new ArgumentNullException ( nameof ( options ) ) ;
118132 }
119133
120- await ConnectAsync ( ) ;
134+ try
135+ {
136+ await ConnectAsync ( ) ;
121137
122- var creationTime = DateTimeOffset . UtcNow ;
138+ var creationTime = DateTimeOffset . UtcNow ;
123139
124- var absoluteExpiration = GetAbsoluteExpiration ( creationTime , options ) ;
140+ var absoluteExpiration = GetAbsoluteExpiration ( creationTime , options ) ;
125141
126- await _cache . ScriptEvaluateAsync ( SetScript , new RedisKey [ ] { _instance + key } ,
127- new RedisValue [ ]
128- {
142+ await _cache . ScriptEvaluateAsync ( SetScript , new RedisKey [ ] { _instance + key } ,
143+ new RedisValue [ ]
144+ {
129145 absoluteExpiration ? . Ticks ?? NotPresent ,
130146 options . SlidingExpiration ? . Ticks ?? NotPresent ,
131147 GetExpirationInSeconds ( creationTime , absoluteExpiration , options ) ?? NotPresent ,
132148 value
133- } ) ;
149+ } ) ;
150+ }
151+ catch ( Exception ex )
152+ {
153+ LogSuppressedException ( ex ) ;
154+ }
134155 }
135156
136157 public void Refresh ( string key )
@@ -178,34 +199,41 @@ private byte[] GetAndRefresh(string key, bool getData)
178199 throw new ArgumentNullException ( nameof ( key ) ) ;
179200 }
180201
181- Connect ( ) ;
182-
183- // This also resets the LRU status as desired.
184- // TODO: Can this be done in one operation on the server side? Probably, the trick would just be the DateTimeOffset math.
185- RedisValue [ ] results ;
186- if ( getData )
202+ try
187203 {
188- results = _cache . HashMemberGet ( _instance + key , AbsoluteExpirationKey , SlidingExpirationKey , DataKey ) ;
189- }
190- else
191- {
192- results = _cache . HashMemberGet ( _instance + key , AbsoluteExpirationKey , SlidingExpirationKey ) ;
193- }
204+ Connect ( ) ;
194205
195- // TODO: Error handling
196- if ( results . Length >= 2 )
197- {
198- // Note we always get back two results, even if they are all null.
199- // These operations will no-op in the null scenario.
200- DateTimeOffset ? absExpr ;
201- TimeSpan ? sldExpr ;
202- MapMetadata ( results , out absExpr , out sldExpr ) ;
203- Refresh ( key , absExpr , sldExpr ) ;
204- }
206+ // This also resets the LRU status as desired.
207+ // TODO: Can this be done in one operation on the server side? Probably, the trick would just be the DateTimeOffset math.
208+ RedisValue [ ] results ;
209+ if ( getData )
210+ {
211+ results = _cache . HashMemberGet ( _instance + key , AbsoluteExpirationKey , SlidingExpirationKey , DataKey ) ;
212+ }
213+ else
214+ {
215+ results = _cache . HashMemberGet ( _instance + key , AbsoluteExpirationKey , SlidingExpirationKey ) ;
216+ }
205217
206- if ( results . Length >= 3 && results [ 2 ] . HasValue )
218+ // TODO: Error handling
219+ if ( results . Length >= 2 )
220+ {
221+ // Note we always get back two results, even if they are all null.
222+ // These operations will no-op in the null scenario.
223+ DateTimeOffset ? absExpr ;
224+ TimeSpan ? sldExpr ;
225+ MapMetadata ( results , out absExpr , out sldExpr ) ;
226+ Refresh ( key , absExpr , sldExpr ) ;
227+ }
228+
229+ if ( results . Length >= 3 && results [ 2 ] . HasValue )
230+ {
231+ return results [ 2 ] ;
232+ }
233+ }
234+ catch ( Exception ex )
207235 {
208- return results [ 2 ] ;
236+ LogSuppressedException ( ex ) ;
209237 }
210238
211239 return null ;
@@ -218,34 +246,41 @@ private async Task<byte[]> GetAndRefreshAsync(string key, bool getData)
218246 throw new ArgumentNullException ( nameof ( key ) ) ;
219247 }
220248
221- await ConnectAsync ( ) ;
222-
223- // This also resets the LRU status as desired.
224- // TODO: Can this be done in one operation on the server side? Probably, the trick would just be the DateTimeOffset math.
225- RedisValue [ ] results ;
226- if ( getData )
227- {
228- results = await _cache . HashMemberGetAsync ( _instance + key , AbsoluteExpirationKey , SlidingExpirationKey , DataKey ) ;
229- }
230- else
249+ try
231250 {
232- results = await _cache . HashMemberGetAsync ( _instance + key , AbsoluteExpirationKey , SlidingExpirationKey ) ;
233- }
251+ await ConnectAsync ( ) ;
234252
235- // TODO: Error handling
236- if ( results . Length >= 2 )
237- {
238- // Note we always get back two results, even if they are all null.
239- // These operations will no-op in the null scenario.
240- DateTimeOffset ? absExpr ;
241- TimeSpan ? sldExpr ;
242- MapMetadata ( results , out absExpr , out sldExpr ) ;
243- await RefreshAsync ( key , absExpr , sldExpr ) ;
244- }
253+ // This also resets the LRU status as desired.
254+ // TODO: Can this be done in one operation on the server side? Probably, the trick would just be the DateTimeOffset math.
255+ RedisValue [ ] results ;
256+ if ( getData )
257+ {
258+ results = await _cache . HashMemberGetAsync ( _instance + key , AbsoluteExpirationKey , SlidingExpirationKey , DataKey ) ;
259+ }
260+ else
261+ {
262+ results = await _cache . HashMemberGetAsync ( _instance + key , AbsoluteExpirationKey , SlidingExpirationKey ) ;
263+ }
264+
265+ // TODO: Error handling
266+ if ( results . Length >= 2 )
267+ {
268+ // Note we always get back two results, even if they are all null.
269+ // These operations will no-op in the null scenario.
270+ DateTimeOffset ? absExpr ;
271+ TimeSpan ? sldExpr ;
272+ MapMetadata ( results , out absExpr , out sldExpr ) ;
273+ await RefreshAsync ( key , absExpr , sldExpr ) ;
274+ }
245275
246- if ( results . Length >= 3 && results [ 2 ] . HasValue )
276+ if ( results . Length >= 3 && results [ 2 ] . HasValue )
277+ {
278+ return results [ 2 ] ;
279+ }
280+ }
281+ catch ( Exception ex )
247282 {
248- return results [ 2 ] ;
283+ LogSuppressedException ( ex ) ;
249284 }
250285
251286 return null ;
@@ -258,10 +293,16 @@ public void Remove(string key)
258293 throw new ArgumentNullException ( nameof ( key ) ) ;
259294 }
260295
261- Connect ( ) ;
296+ try
297+ {
298+ Connect ( ) ;
262299
263- _cache . KeyDelete ( _instance + key ) ;
264- // TODO: Error handling
300+ _cache . KeyDelete ( _instance + key ) ;
301+ }
302+ catch ( Exception ex )
303+ {
304+ LogSuppressedException ( ex ) ;
305+ }
265306 }
266307
267308 public async Task RemoveAsync ( string key )
@@ -270,11 +311,16 @@ public async Task RemoveAsync(string key)
270311 {
271312 throw new ArgumentNullException ( nameof ( key ) ) ;
272313 }
314+ try
315+ {
316+ await ConnectAsync ( ) ;
273317
274- await ConnectAsync ( ) ;
275-
276- await _cache . KeyDeleteAsync ( _instance + key ) ;
277- // TODO: Error handling
318+ await _cache . KeyDeleteAsync ( _instance + key ) ;
319+ }
320+ catch ( Exception ex )
321+ {
322+ LogSuppressedException ( ex ) ;
323+ }
278324 }
279325
280326 private void MapMetadata ( RedisValue [ ] results , out DateTimeOffset ? absoluteExpiration , out TimeSpan ? slidingExpiration )
@@ -380,6 +426,9 @@ private async Task RefreshAsync(string key, DateTimeOffset? absExpr, TimeSpan? s
380426 return absoluteExpiration ;
381427 }
382428
429+ private void LogSuppressedException ( Exception ex )
430+ => _logger ? . ExceptionSuppressed ( ex ) ;
431+
383432 public void Dispose ( )
384433 {
385434 if ( _connection != null )
0 commit comments