1- using Microsoft . Extensions . Caching . Distributed ;
2- using Microsoft . Extensions . Options ;
3- using ServiceStack . Redis ;
41using System ;
52using System . Text ;
63using System . Threading ;
74using System . Threading . Tasks ;
5+ using Microsoft . Extensions . Caching . Distributed ;
6+ using ServiceStack . Redis ;
87
98namespace Microsoft . Extensions . Caching . ServiceStackRedis
109{
1110 public class ServiceStackRedisCache : IDistributedCache
1211 {
13- private readonly IRedisClientsManager _redisManager ;
12+ private readonly IRedisClientsManager _redisClientsManager ;
1413 private readonly ServiceStackRedisCacheOptions _options ;
1514
16- public ServiceStackRedisCache ( IOptions < ServiceStackRedisCacheOptions > optionsAccessor )
15+ public ServiceStackRedisCache ( IRedisClientsManager redisClientsManager )
16+ {
17+ RedisConfig . VerifyMasterConnections = false ;
18+ _redisClientsManager = redisClientsManager ;
19+ }
20+
21+ public byte [ ] Get ( string key )
1722 {
18- if ( optionsAccessor == null )
23+ if ( key == null )
1924 {
20- throw new ArgumentNullException ( nameof ( optionsAccessor ) ) ;
25+ throw new ArgumentNullException ( nameof ( key ) ) ;
2126 }
2227
23- _options = optionsAccessor . Value ;
28+ using var client = _redisClientsManager . GetClient ( ) ;
29+ if ( ! client . ContainsKey ( key ) )
30+ {
31+ return null ;
32+ }
2433
25- var host = $ "{ _options . Password } @{ _options . Host } :{ _options . Port } ";
26- RedisConfig . VerifyMasterConnections = false ;
27- _redisManager = new RedisManagerPool ( host ) ;
34+ var values = client . GetValuesFromHash ( key , nameof ( CacheEntry . Value ) , nameof ( CacheEntry . SlidingExpiration ) ) ;
35+
36+ if ( TimeSpan . TryParse ( values [ 1 ] , out var sldExp ) )
37+ {
38+ Refresh ( key , sldExp ) ;
39+ }
40+
41+ return Encoding . UTF8 . GetBytes ( values [ 0 ] ) ;
2842 }
2943
30- public byte [ ] Get ( string key )
44+ public async Task < byte [ ] > GetAsync ( string key , CancellationToken token = default )
3145 {
3246 if ( key == null )
3347 {
3448 throw new ArgumentNullException ( nameof ( key ) ) ;
3549 }
3650
37- using ( var client = _redisManager . GetClient ( ) as IRedisNativeClient )
51+ await using var client = await _redisClientsManager . GetClientAsync ( ) ;
52+ if ( ! await client . ContainsKeyAsync ( key ) )
3853 {
39- if ( client . Exists ( key ) == 1 )
40- {
41- return client . Get ( key ) ;
42- }
54+ return null ;
55+ }
56+
57+ var values = await client . GetValuesFromHashAsync ( key , nameof ( CacheEntry . Value ) , nameof ( CacheEntry . SlidingExpiration ) ) ;
58+
59+ if ( TimeSpan . TryParse ( values [ 1 ] , out var slbExp ) )
60+ {
61+ await RefreshAsync ( key , slbExp ) ;
4362 }
44- return null ;
63+
64+ return Encoding . UTF8 . GetBytes ( values [ 0 ] ) ;
4565 }
4666
47- public Task < byte [ ] > GetAsync ( string key , CancellationToken token = default ( CancellationToken ) )
67+ public void Set ( string key , byte [ ] value , DistributedCacheEntryOptions options )
4868 {
49- return Task . FromResult ( Get ( key ) ) ;
69+ if ( key == null )
70+ {
71+ throw new ArgumentNullException ( nameof ( key ) ) ;
72+ }
73+
74+ if ( value == null )
75+ {
76+ throw new ArgumentNullException ( nameof ( value ) ) ;
77+ }
78+
79+ if ( options == null )
80+ {
81+ throw new ArgumentNullException ( nameof ( options ) ) ;
82+ }
83+
84+ using var client = _redisClientsManager . GetClient ( ) ;
85+ client . SetEntryInHash ( key , nameof ( CacheEntry . Value ) , Encoding . UTF8 . GetString ( value ) ) ;
86+ SetExpiration ( client , key , options ) ;
5087 }
5188
52- public void Set ( string key , byte [ ] value , DistributedCacheEntryOptions options )
89+ public async Task SetAsync ( string key , byte [ ] value , DistributedCacheEntryOptions options , CancellationToken token = default ( CancellationToken ) )
5390 {
5491 if ( key == null )
5592 {
@@ -66,97 +103,131 @@ public void Set(string key, byte[] value, DistributedCacheEntryOptions options)
66103 throw new ArgumentNullException ( nameof ( options ) ) ;
67104 }
68105
69- using ( var client = _redisManager . GetClient ( ) as IRedisNativeClient )
106+ await using var client = await _redisClientsManager . GetClientAsync ( ) ;
107+ await client . SetEntryInHashAsync ( key , nameof ( CacheEntry . Value ) , Encoding . UTF8 . GetString ( value ) ) ;
108+ await SetExpirationAsync ( client , key , options ) ;
109+ }
110+
111+ public void Refresh ( string key )
112+ {
113+ Refresh ( key , null ) ;
114+ }
115+
116+ public void Refresh ( string key , TimeSpan ? sldExp )
117+ {
118+ if ( key == null )
70119 {
71- var expireInSeconds = GetExpireInSeconds ( options ) ;
72- if ( expireInSeconds > 0 )
120+ throw new ArgumentNullException ( nameof ( key ) ) ;
121+ }
122+
123+ using var client = _redisClientsManager . GetClient ( ) ;
124+ var ttl = client . GetTimeToLive ( key ) ;
125+ if ( ttl . HasValue )
126+ {
127+ if ( ! sldExp . HasValue )
73128 {
74- client . SetEx ( key , expireInSeconds , value ) ;
75- client . SetEx ( GetExpirationKey ( key ) , expireInSeconds , Encoding . UTF8 . GetBytes ( expireInSeconds . ToString ( ) ) ) ;
129+ var sldExpStr = client . GetValueFromHash ( key , nameof ( CacheEntry . SlidingExpiration ) ) ;
130+ if ( TimeSpan . TryParse ( sldExpStr , out var cachedSldExp ) )
131+ {
132+ sldExp = cachedSldExp ;
133+ }
76134 }
77- else
135+
136+ if ( sldExp . HasValue && ttl < sldExp )
78137 {
79- client . Set ( key , value ) ;
138+ client . ExpireEntryIn ( key , sldExp . Value ) ;
80139 }
81140 }
82141 }
83142
84- public Task SetAsync ( string key , byte [ ] value , DistributedCacheEntryOptions options , CancellationToken token = default ( CancellationToken ) )
143+ public async Task RefreshAsync ( string key , CancellationToken token )
85144 {
86- return Task . Run ( ( ) => Set ( key , value , options ) ) ;
145+ await RefreshAsync ( key , null ) ;
87146 }
88147
89- public void Refresh ( string key )
148+ public async Task RefreshAsync ( string key , TimeSpan ? sldExp )
90149 {
91150 if ( key == null )
92151 {
93152 throw new ArgumentNullException ( nameof ( key ) ) ;
94153 }
95154
96- using ( var client = _redisManager . GetClient ( ) as IRedisNativeClient )
155+ await using var client = await _redisClientsManager . GetClientAsync ( ) ;
156+ var ttl = await client . GetTimeToLiveAsync ( key ) ;
157+ if ( ttl . HasValue )
97158 {
98- if ( client . Exists ( key ) == 1 )
159+ if ( ! sldExp . HasValue )
99160 {
100- var value = client . Get ( key ) ;
101- if ( value != null )
161+ var sldExpStr = await client . GetValueFromHashAsync ( key , nameof ( CacheEntry . SlidingExpiration ) ) ;
162+ if ( TimeSpan . TryParse ( sldExpStr , out var cachedSldExp ) )
102163 {
103- var expirationValue = client . Get ( GetExpirationKey ( key ) ) ;
104- if ( expirationValue != null )
105- {
106- client . Expire ( key , int . Parse ( Encoding . UTF8 . GetString ( expirationValue ) ) ) ;
107- }
164+ sldExp = cachedSldExp ;
108165 }
109166 }
167+
168+ if ( sldExp . HasValue && ttl < sldExp )
169+ {
170+ await client . ExpireEntryInAsync ( key , sldExp . Value ) ;
171+ }
110172 }
111173 }
112174
113- public Task RefreshAsync ( string key , CancellationToken token = default ( CancellationToken ) )
175+ public void Remove ( string key )
114176 {
115177 if ( key == null )
116178 {
117179 throw new ArgumentNullException ( nameof ( key ) ) ;
118180 }
119181
120- return Task . Run ( ( ) => Refresh ( key ) ) ;
182+ using var client = _redisClientsManager . GetClient ( ) ;
183+ client . Remove ( key ) ;
121184 }
122185
123- public void Remove ( string key )
186+ public async Task RemoveAsync ( string key , CancellationToken token = default )
124187 {
125188 if ( key == null )
126189 {
127190 throw new ArgumentNullException ( nameof ( key ) ) ;
128191 }
129192
130- using ( var client = _redisManager . GetClient ( ) as IRedisNativeClient )
131- {
132- client . Del ( key ) ;
133- }
193+ await using var client = await _redisClientsManager . GetClientAsync ( ) ;
194+ await client . RemoveAsync ( key ) ;
134195 }
135196
136- public Task RemoveAsync ( string key , CancellationToken token = default ( CancellationToken ) )
137- {
138- return Task . Run ( ( ) => Remove ( key ) ) ;
139- }
140-
141- private int GetExpireInSeconds ( DistributedCacheEntryOptions options )
197+ private void SetExpiration ( IRedisClient client , string key , DistributedCacheEntryOptions options )
142198 {
143199 if ( options . SlidingExpiration . HasValue )
144200 {
145- return ( int ) options . SlidingExpiration . Value . TotalSeconds ;
201+ var sldExp = options . SlidingExpiration . Value ;
202+ client . SetEntryInHash ( key , nameof ( CacheEntry . SlidingExpiration ) , sldExp . ToString ( ) ) ;
203+ client . ExpireEntryIn ( key , sldExp ) ;
146204 }
147205 else if ( options . AbsoluteExpirationRelativeToNow . HasValue )
148206 {
149- return ( int ) options . AbsoluteExpirationRelativeToNow . Value . TotalSeconds ;
207+ client . ExpireEntryAt ( key , DateTime . Now + options . AbsoluteExpirationRelativeToNow . Value ) ;
150208 }
151- else
209+ else if ( options . AbsoluteExpiration . HasValue )
152210 {
153- return 0 ;
211+ client . ExpireEntryAt ( key , options . AbsoluteExpiration . Value . DateTime ) ;
154212 }
155213 }
156214
157- private string GetExpirationKey ( string key )
215+ private async Task SetExpirationAsync ( IRedisClientAsync client , string key , DistributedCacheEntryOptions options )
158216 {
159- return key + $ "-{ nameof ( DistributedCacheEntryOptions ) } ";
217+ if ( options . SlidingExpiration . HasValue )
218+ {
219+ var sldExp = options . SlidingExpiration . Value ;
220+ await client . SetEntryInHashAsync ( key , nameof ( CacheEntry . SlidingExpiration ) , sldExp . ToString ( ) ) ;
221+ await client . ExpireEntryInAsync ( key , sldExp ) ;
222+ }
223+ else if ( options . AbsoluteExpirationRelativeToNow . HasValue )
224+ {
225+ await client . ExpireEntryAtAsync ( key , DateTime . Now + options . AbsoluteExpirationRelativeToNow . Value ) ;
226+ }
227+ else if ( options . AbsoluteExpiration . HasValue )
228+ {
229+ await client . ExpireEntryAtAsync ( key , options . AbsoluteExpiration . Value . DateTime ) ;
230+ }
160231 }
161232 }
162233}
0 commit comments