|
2 | 2 | using AspNetCoreCacheKit.Models; |
3 | 3 | using Microsoft.Extensions.Caching.Memory; |
4 | 4 | using Microsoft.Extensions.Options; |
| 5 | +using Microsoft.Extensions.Primitives; |
5 | 6 |
|
6 | 7 | namespace AspNetCoreCacheKit |
7 | 8 | { |
8 | 9 | public class CacheService : ICacheService |
9 | 10 | { |
10 | 11 | private readonly IMemoryCache _memoryCache; |
11 | 12 | private readonly CacheOptions _cacheOptions; |
12 | | - private readonly TimeSpan _expiration; |
13 | 13 |
|
14 | | - public CacheService(IMemoryCache memoryCache, IOptionsSnapshot<CacheOptions> cacheOptions) |
| 14 | + public CacheService(IMemoryCache memoryCache, IOptions<CacheOptions> cacheOptions) |
15 | 15 | { |
| 16 | + ArgumentNullException.ThrowIfNull(memoryCache); |
| 17 | + ArgumentNullException.ThrowIfNull(cacheOptions); |
| 18 | + |
16 | 19 | _memoryCache = memoryCache; |
17 | 20 | _cacheOptions = cacheOptions.Value; |
18 | | - _expiration = _cacheOptions.Duration; |
19 | | - |
20 | | - ArgumentNullException.ThrowIfNull(_memoryCache); |
21 | | - ArgumentNullException.ThrowIfNull(_cacheOptions); |
22 | 21 | } |
23 | 22 |
|
24 | | - public void Set(string groupKey, string key, object value) |
| 23 | + public void Set(string groupKey, string key, object value, TimeSpan? duration = null) |
25 | 24 | { |
26 | 25 | if (_cacheOptions.IsEnabled) |
27 | 26 | { |
28 | 27 | var fullKey = GetFullKey(key, groupKey); |
29 | 28 | _memoryCache.Set(fullKey, value, new MemoryCacheEntryOptions |
30 | 29 | { |
31 | | - AbsoluteExpirationRelativeToNow = _expiration |
| 30 | + AbsoluteExpirationRelativeToNow = ResolveExpiration(groupKey, duration) |
32 | 31 | }); |
33 | 32 | } |
34 | 33 | } |
35 | 34 |
|
36 | | - public async Task<T> GetOrCreateAsync<T>(string groupKey, string key, Func<Task<T>> createFunc) |
| 35 | + public async Task<T?> GetOrCreateAsync<T>( |
| 36 | + string groupKey, |
| 37 | + string key, |
| 38 | + Func<ICacheEntry, Task<T>> createFunc, |
| 39 | + TimeSpan? duration = null, |
| 40 | + CancellationToken cancellationToken = default) |
37 | 41 | { |
38 | 42 | if (!_cacheOptions.IsEnabled) |
39 | | - return await createFunc(); |
| 43 | + return await createFunc(new NullCacheEntry()); |
40 | 44 |
|
41 | | - var fullKey = GetFullKey(key, groupKey); |
| 45 | + cancellationToken.ThrowIfCancellationRequested(); |
42 | 46 |
|
43 | | - return await _memoryCache.GetOrCreate(fullKey, entry => |
44 | | - { |
45 | | - entry.AbsoluteExpirationRelativeToNow = _expiration; |
46 | | - return createFunc(); |
47 | | - }); |
| 47 | + var expiration = ResolveExpiration(groupKey, duration); |
| 48 | + |
| 49 | + return await _memoryCache.GetOrCreateAsync( |
| 50 | + GetFullKey(groupKey, key), |
| 51 | + entry => |
| 52 | + { |
| 53 | + entry.AbsoluteExpirationRelativeToNow = expiration; |
| 54 | + return createFunc(entry); |
| 55 | + }); |
48 | 56 | } |
49 | 57 |
|
50 | | - public T GetOrCreate<T>(string groupKey, string key, Func<T> createFunc) |
| 58 | + public T? GetOrCreate<T>( |
| 59 | + string groupKey, |
| 60 | + string key, |
| 61 | + Func<T> createFunc, |
| 62 | + TimeSpan? duration = null) |
51 | 63 | { |
52 | 64 | if (!_cacheOptions.IsEnabled) |
53 | 65 | return createFunc(); |
54 | 66 |
|
55 | | - var fullKey = GetFullKey(key, groupKey); |
| 67 | + var expiration = ResolveExpiration(groupKey, duration); |
56 | 68 |
|
57 | | - return _memoryCache.GetOrCreate(fullKey, entry => |
58 | | - { |
59 | | - entry.AbsoluteExpirationRelativeToNow = _expiration; |
60 | | - return createFunc(); |
61 | | - }); |
| 69 | + return _memoryCache.GetOrCreate( |
| 70 | + GetFullKey(groupKey, key), |
| 71 | + entry => |
| 72 | + { |
| 73 | + entry.AbsoluteExpirationRelativeToNow = expiration; |
| 74 | + return createFunc(); |
| 75 | + }); |
62 | 76 | } |
63 | 77 |
|
64 | 78 | public void Delete(string groupKey, string key) |
65 | 79 | { |
66 | 80 | if (_cacheOptions.IsEnabled) |
67 | | - _memoryCache.Remove(GetFullKey(key, groupKey)); |
| 81 | + _memoryCache.Remove(GetFullKey(groupKey, key)); |
68 | 82 | } |
69 | 83 |
|
70 | | - public void Set(string key, object value) => Set(string.Empty, key, value); |
| 84 | + public void Set(string key, object value, TimeSpan? duration = null) |
| 85 | + => Set(string.Empty, key, value, duration); |
| 86 | + |
| 87 | + public async Task<T?> GetOrCreateAsync<T>( |
| 88 | + string key, |
| 89 | + Func<ICacheEntry, Task<T>> createFunc, |
| 90 | + TimeSpan? duration = null, |
| 91 | + CancellationToken cancellationToken = default) |
| 92 | + => await GetOrCreateAsync(string.Empty, key, createFunc, duration, cancellationToken); |
71 | 93 |
|
72 | | - public async Task<T> GetOrCreateAsync<T>(string key, Func<Task<T>> createFunc) => await GetOrCreateAsync(string.Empty, key, createFunc); |
| 94 | + public T? GetOrCreate<T>(string key, Func<T> createFunc, TimeSpan? duration = null) |
| 95 | + => GetOrCreate(string.Empty, key, createFunc, duration); |
73 | 96 |
|
74 | | - public T GetOrCreate<T>(string key, Func<T> createFunc) => GetOrCreate(string.Empty, key, createFunc); |
| 97 | + public void Delete(string key) |
| 98 | + => Delete(string.Empty, key); |
75 | 99 |
|
76 | | - public void Delete(string key) => Delete(string.Empty, key); |
| 100 | + private static string GetFullKey(string groupKey, string key) |
| 101 | + => string.IsNullOrEmpty(groupKey) ? key : $"{groupKey}:{key}"; |
77 | 102 |
|
78 | | - private static string GetFullKey(string groupKey, string key) => $"{groupKey}:{key}"; |
| 103 | + private TimeSpan ResolveExpiration(string groupKey, TimeSpan? duration) |
| 104 | + => duration ?? _cacheOptions.GetGroupDuration(groupKey) ?? _cacheOptions.Duration; |
| 105 | + |
| 106 | + private sealed class NullCacheEntry : ICacheEntry |
| 107 | + { |
| 108 | + public object Key => string.Empty; |
| 109 | + public object? Value { get; set; } |
| 110 | + public DateTimeOffset? AbsoluteExpiration { get; set; } |
| 111 | + public TimeSpan? AbsoluteExpirationRelativeToNow { get; set; } |
| 112 | + public TimeSpan? SlidingExpiration { get; set; } |
| 113 | + public IList<IChangeToken> ExpirationTokens { get; } = []; |
| 114 | + public IList<PostEvictionCallbackRegistration> PostEvictionCallbacks { get; } = []; |
| 115 | + public CacheItemPriority Priority { get; set; } |
| 116 | + public long? Size { get; set; } |
| 117 | + public void Dispose() { } |
| 118 | + } |
79 | 119 | } |
80 | 120 | } |
0 commit comments