1
- using Microsoft . Extensions . Caching . Distributed ;
2
- using Microsoft . Extensions . Options ;
3
- using ServiceStack . Redis ;
4
1
using System ;
5
2
using System . Text ;
6
3
using System . Threading ;
7
4
using System . Threading . Tasks ;
5
+ using Microsoft . Extensions . Caching . Distributed ;
6
+ using ServiceStack . Redis ;
8
7
9
8
namespace Microsoft . Extensions . Caching . ServiceStackRedis
10
9
{
11
10
public class ServiceStackRedisCache : IDistributedCache
12
11
{
13
- private readonly IRedisClientsManager _redisManager ;
12
+ private readonly IRedisClientsManager _redisClientsManager ;
14
13
private readonly ServiceStackRedisCacheOptions _options ;
15
14
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 )
17
22
{
18
- if ( optionsAccessor == null )
23
+ if ( key == null )
19
24
{
20
- throw new ArgumentNullException ( nameof ( optionsAccessor ) ) ;
25
+ throw new ArgumentNullException ( nameof ( key ) ) ;
21
26
}
22
27
23
- _options = optionsAccessor . Value ;
28
+ using var client = _redisClientsManager . GetClient ( ) ;
29
+ if ( ! client . ContainsKey ( key ) )
30
+ {
31
+ return null ;
32
+ }
24
33
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 ] ) ;
28
42
}
29
43
30
- public byte [ ] Get ( string key )
44
+ public async Task < byte [ ] > GetAsync ( string key , CancellationToken token = default )
31
45
{
32
46
if ( key == null )
33
47
{
34
48
throw new ArgumentNullException ( nameof ( key ) ) ;
35
49
}
36
50
37
- using ( var client = _redisManager . GetClient ( ) as IRedisNativeClient )
51
+ await using var client = await _redisClientsManager . GetClientAsync ( ) ;
52
+ if ( ! await client . ContainsKeyAsync ( key ) )
38
53
{
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 ) ;
43
62
}
44
- return null ;
63
+
64
+ return Encoding . UTF8 . GetBytes ( values [ 0 ] ) ;
45
65
}
46
66
47
- public Task < byte [ ] > GetAsync ( string key , CancellationToken token = default ( CancellationToken ) )
67
+ public void Set ( string key , byte [ ] value , DistributedCacheEntryOptions options )
48
68
{
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 ) ;
50
87
}
51
88
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 ) )
53
90
{
54
91
if ( key == null )
55
92
{
@@ -66,97 +103,131 @@ public void Set(string key, byte[] value, DistributedCacheEntryOptions options)
66
103
throw new ArgumentNullException ( nameof ( options ) ) ;
67
104
}
68
105
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 )
70
119
{
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 )
73
128
{
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
+ }
76
134
}
77
- else
135
+
136
+ if ( sldExp . HasValue && ttl < sldExp )
78
137
{
79
- client . Set ( key , value ) ;
138
+ client . ExpireEntryIn ( key , sldExp . Value ) ;
80
139
}
81
140
}
82
141
}
83
142
84
- public Task SetAsync ( string key , byte [ ] value , DistributedCacheEntryOptions options , CancellationToken token = default ( CancellationToken ) )
143
+ public async Task RefreshAsync ( string key , CancellationToken token )
85
144
{
86
- return Task . Run ( ( ) => Set ( key , value , options ) ) ;
145
+ await RefreshAsync ( key , null ) ;
87
146
}
88
147
89
- public void Refresh ( string key )
148
+ public async Task RefreshAsync ( string key , TimeSpan ? sldExp )
90
149
{
91
150
if ( key == null )
92
151
{
93
152
throw new ArgumentNullException ( nameof ( key ) ) ;
94
153
}
95
154
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 )
97
158
{
98
- if ( client . Exists ( key ) == 1 )
159
+ if ( ! sldExp . HasValue )
99
160
{
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 ) )
102
163
{
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 ;
108
165
}
109
166
}
167
+
168
+ if ( sldExp . HasValue && ttl < sldExp )
169
+ {
170
+ await client . ExpireEntryInAsync ( key , sldExp . Value ) ;
171
+ }
110
172
}
111
173
}
112
174
113
- public Task RefreshAsync ( string key , CancellationToken token = default ( CancellationToken ) )
175
+ public void Remove ( string key )
114
176
{
115
177
if ( key == null )
116
178
{
117
179
throw new ArgumentNullException ( nameof ( key ) ) ;
118
180
}
119
181
120
- return Task . Run ( ( ) => Refresh ( key ) ) ;
182
+ using var client = _redisClientsManager . GetClient ( ) ;
183
+ client . Remove ( key ) ;
121
184
}
122
185
123
- public void Remove ( string key )
186
+ public async Task RemoveAsync ( string key , CancellationToken token = default )
124
187
{
125
188
if ( key == null )
126
189
{
127
190
throw new ArgumentNullException ( nameof ( key ) ) ;
128
191
}
129
192
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 ) ;
134
195
}
135
196
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 )
142
198
{
143
199
if ( options . SlidingExpiration . HasValue )
144
200
{
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 ) ;
146
204
}
147
205
else if ( options . AbsoluteExpirationRelativeToNow . HasValue )
148
206
{
149
- return ( int ) options . AbsoluteExpirationRelativeToNow . Value . TotalSeconds ;
207
+ client . ExpireEntryAt ( key , DateTime . Now + options . AbsoluteExpirationRelativeToNow . Value ) ;
150
208
}
151
- else
209
+ else if ( options . AbsoluteExpiration . HasValue )
152
210
{
153
- return 0 ;
211
+ client . ExpireEntryAt ( key , options . AbsoluteExpiration . Value . DateTime ) ;
154
212
}
155
213
}
156
214
157
- private string GetExpirationKey ( string key )
215
+ private async Task SetExpirationAsync ( IRedisClientAsync client , string key , DistributedCacheEntryOptions options )
158
216
{
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
+ }
160
231
}
161
232
}
162
233
}
0 commit comments