Skip to content

Commit c099c74

Browse files
committed
Proper handling of null values on GetOrSet methods
1 parent 5ea7569 commit c099c74

11 files changed

+130
-87
lines changed

samples/Sample.Application/WeatherService.cs

+6
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,12 @@ public class WeatherService : IWeatherService
1313

1414
public async Task<WeatherForecast> FetchForecastAsync(DateTime date, CancellationToken cancellationToken)
1515
{
16+
// Value factories that return null don't get cached by GetOrSet extensions.
17+
if (RNG.NextDouble() > 0.5)
18+
{
19+
return null;
20+
}
21+
1622
// Simulate access to a database or third party service.
1723
await Task.Delay(100, cancellationToken);
1824

src/SimpleConcepts.Extensions.Caching.Abstractions/Distributed/DistributedCacheGetOrSetExtensions.cs

+31-19
Original file line numberDiff line numberDiff line change
@@ -20,19 +20,19 @@ public static Task SetAsync(this IDistributedCache cache, string key, byte[] val
2020
}
2121

2222

23-
public static byte[] GetOrSet(this IDistributedCache cache, string key, Func<byte[]> valueFactory)
23+
public static byte[]? GetOrSet(this IDistributedCache cache, string key, Func<byte[]?> valueFactory)
2424
{
2525
return cache.GetOrSet(key, valueFactory, new DistributedCacheEntryOptions());
2626
}
2727

28-
public static byte[] GetOrSet(this IDistributedCache cache, string key, Func<byte[]> valueFactory,
28+
public static byte[]? GetOrSet(this IDistributedCache cache, string key, Func<byte[]?> valueFactory,
2929
TimeSpan absoluteExpirationRelativeToNow)
3030
{
3131
return cache.GetOrSet(key, valueFactory,
3232
new DistributedCacheEntryOptions { AbsoluteExpirationRelativeToNow = absoluteExpirationRelativeToNow });
3333
}
3434

35-
public static byte[] GetOrSet(this IDistributedCache cache, string key, Func<byte[]> valueFactory,
35+
public static byte[]? GetOrSet(this IDistributedCache cache, string key, Func<byte[]?> valueFactory,
3636
DistributedCacheEntryOptions options)
3737
{
3838
var cached = cache.Get(key);
@@ -44,26 +44,29 @@ public static byte[] GetOrSet(this IDistributedCache cache, string key, Func<byt
4444

4545
var value = valueFactory();
4646

47-
cache.Set(key, value, options);
47+
if (value != null)
48+
{
49+
cache.Set(key, value, options);
50+
}
4851

4952
return value;
5053
}
5154

5255

53-
public static Task<byte[]> GetOrSetAsync(this IDistributedCache cache, string key, Func<Task<byte[]>> valueFactory,
56+
public static Task<byte[]?> GetOrSetAsync(this IDistributedCache cache, string key, Func<Task<byte[]?>> valueFactory,
5457
CancellationToken token = default)
5558
{
5659
return cache.GetOrSetAsync(key, valueFactory, new DistributedCacheEntryOptions(), token);
5760
}
5861

59-
public static Task<byte[]> GetOrSetAsync(this IDistributedCache cache, string key, Func<Task<byte[]>> valueFactory,
62+
public static Task<byte[]?> GetOrSetAsync(this IDistributedCache cache, string key, Func<Task<byte[]?>> valueFactory,
6063
TimeSpan absoluteExpirationRelativeToNow, CancellationToken token = default)
6164
{
6265
return cache.GetOrSetAsync(key, valueFactory,
6366
new DistributedCacheEntryOptions { AbsoluteExpirationRelativeToNow = absoluteExpirationRelativeToNow }, token);
6467
}
6568

66-
public static async Task<byte[]> GetOrSetAsync(this IDistributedCache cache, string key, Func<Task<byte[]>> valueFactory,
69+
public static async Task<byte[]?> GetOrSetAsync(this IDistributedCache cache, string key, Func<Task<byte[]?>> valueFactory,
6770
DistributedCacheEntryOptions options, CancellationToken token = default)
6871
{
6972
var cached = await cache.GetAsync(key, token);
@@ -75,25 +78,28 @@ public static async Task<byte[]> GetOrSetAsync(this IDistributedCache cache, str
7578

7679
var value = await valueFactory();
7780

78-
await cache.SetAsync(key, value, options, token);
81+
if (value != null)
82+
{
83+
await cache.SetAsync(key, value, options, token);
84+
}
7985

8086
return value;
8187
}
8288

8389

84-
public static string GetOrSetString(this IDistributedCache cache, string key, Func<string> valueFactory)
90+
public static string? GetOrSetString(this IDistributedCache cache, string key, Func<string?> valueFactory)
8591
{
8692
return cache.GetOrSetString(key, valueFactory, new DistributedCacheEntryOptions());
8793
}
8894

89-
public static string GetOrSetString(this IDistributedCache cache, string key, Func<string> valueFactory,
95+
public static string? GetOrSetString(this IDistributedCache cache, string key, Func<string?> valueFactory,
9096
TimeSpan absoluteExpirationRelativeToNow)
9197
{
9298
return cache.GetOrSetString(key, valueFactory,
9399
new DistributedCacheEntryOptions { AbsoluteExpirationRelativeToNow = absoluteExpirationRelativeToNow });
94100
}
95101

96-
public static string GetOrSetString(this IDistributedCache cache, string key, Func<string> valueFactory,
102+
public static string? GetOrSetString(this IDistributedCache cache, string key, Func<string?> valueFactory,
97103
DistributedCacheEntryOptions options)
98104
{
99105
var cached = cache.GetString(key);
@@ -105,27 +111,30 @@ public static string GetOrSetString(this IDistributedCache cache, string key, Fu
105111

106112
var value = valueFactory();
107113

108-
cache.SetString(key, value, options);
114+
if (value != null)
115+
{
116+
cache.SetString(key, value, options);
117+
}
109118

110119
return value;
111120
}
112121

113122

114-
public static Task<string> GetOrSetStringAsync(this IDistributedCache cache, string key,
115-
Func<Task<string>> valueFactory, CancellationToken token = default)
123+
public static Task<string?> GetOrSetStringAsync(this IDistributedCache cache, string key,
124+
Func<Task<string?>> valueFactory, CancellationToken token = default)
116125
{
117126
return cache.GetOrSetStringAsync(key, valueFactory, new DistributedCacheEntryOptions(), token);
118127
}
119128

120-
public static Task<string> GetOrSetStringAsync(this IDistributedCache cache, string key,
121-
Func<Task<string>> valueFactory, TimeSpan absoluteExpirationRelativeToNow, CancellationToken token = default)
129+
public static Task<string?> GetOrSetStringAsync(this IDistributedCache cache, string key,
130+
Func<Task<string?>> valueFactory, TimeSpan absoluteExpirationRelativeToNow, CancellationToken token = default)
122131
{
123132
return cache.GetOrSetStringAsync(key, valueFactory,
124133
new DistributedCacheEntryOptions { AbsoluteExpirationRelativeToNow = absoluteExpirationRelativeToNow }, token);
125134
}
126135

127-
public static async Task<string> GetOrSetStringAsync(this IDistributedCache cache, string key,
128-
Func<Task<string>> valueFactory, DistributedCacheEntryOptions options, CancellationToken token = default)
136+
public static async Task<string?> GetOrSetStringAsync(this IDistributedCache cache, string key,
137+
Func<Task<string?>> valueFactory, DistributedCacheEntryOptions options, CancellationToken token = default)
129138
{
130139
var cached = await cache.GetStringAsync(key, token);
131140

@@ -136,7 +145,10 @@ public static async Task<string> GetOrSetStringAsync(this IDistributedCache cach
136145

137146
var value = await valueFactory();
138147

139-
await cache.SetStringAsync(key, value, options, token);
148+
if (value != null)
149+
{
150+
await cache.SetStringAsync(key, value, options, token);
151+
}
140152

141153
return value;
142154
}

src/SimpleConcepts.Extensions.Caching.Abstractions/Distributed/DistributedCacheJsonExtensions.cs

+33-27
Original file line numberDiff line numberDiff line change
@@ -35,50 +35,50 @@ public static class DistributedCacheJsonExtensions
3535

3636

3737

38-
public static void SetJsonObject<T>(this IDistributedCache cache, string key, T? value,
38+
public static void SetJsonObject<T>(this IDistributedCache cache, string key, T value,
3939
JsonSerializerOptions? serializerOptions = null) where T : class
4040
{
4141
cache.SetJsonObject(key, value, new DistributedCacheEntryOptions(), serializerOptions);
4242
}
4343

44-
public static void SetJsonObject<T>(this IDistributedCache cache, string key, T? value,
44+
public static void SetJsonObject<T>(this IDistributedCache cache, string key, T value,
4545
TimeSpan absoluteExpirationRelativeToNow, JsonSerializerOptions? serializerOptions = null) where T : class
4646
{
4747
cache.SetJsonObject(key, value,
4848
new DistributedCacheEntryOptions { AbsoluteExpirationRelativeToNow = absoluteExpirationRelativeToNow },
4949
serializerOptions);
5050
}
5151

52-
public static void SetJsonObject<T>(this IDistributedCache cache, string key, T? value,
52+
public static void SetJsonObject<T>(this IDistributedCache cache, string key, T value,
5353
DistributedCacheEntryOptions entryOptions, JsonSerializerOptions? serializerOptions = null) where T : class
5454
{
55-
var bytes = value != null ? JsonSerializer.SerializeToUtf8Bytes(value, serializerOptions) : null;
55+
var bytes = JsonSerializer.SerializeToUtf8Bytes(value, serializerOptions);
5656

5757
cache.Set(key, bytes, entryOptions);
5858
}
5959

6060

61-
public static Task SetJsonObjectAsync<T>(this IDistributedCache cache, string key, T? value,
61+
public static Task SetJsonObjectAsync<T>(this IDistributedCache cache, string key, T value,
6262
CancellationToken token = default) where T : class
6363
{
6464
return cache.SetJsonObjectAsync(key, value, new DistributedCacheEntryOptions(), null, token);
6565
}
6666

67-
public static Task SetJsonObjectAsync<T>(this IDistributedCache cache, string key, T? value,
67+
public static Task SetJsonObjectAsync<T>(this IDistributedCache cache, string key, T value,
6868
JsonSerializerOptions? serializerOptions, CancellationToken token = default) where T : class
6969
{
7070
return cache.SetJsonObjectAsync(key, value, new DistributedCacheEntryOptions(), serializerOptions, token);
7171
}
7272

73-
public static Task SetJsonObjectAsync<T>(this IDistributedCache cache, string key, T? value,
73+
public static Task SetJsonObjectAsync<T>(this IDistributedCache cache, string key, T value,
7474
TimeSpan absoluteExpirationRelativeToNow, CancellationToken token = default) where T : class
7575
{
7676
return cache.SetJsonObjectAsync(key, value,
7777
new DistributedCacheEntryOptions { AbsoluteExpirationRelativeToNow = absoluteExpirationRelativeToNow },
7878
null, token);
7979
}
8080

81-
public static Task SetJsonObjectAsync<T>(this IDistributedCache cache, string key, T? value,
81+
public static Task SetJsonObjectAsync<T>(this IDistributedCache cache, string key, T value,
8282
TimeSpan absoluteExpirationRelativeToNow, JsonSerializerOptions? serializerOptions,
8383
CancellationToken token = default) where T : class
8484
{
@@ -87,33 +87,33 @@ public static Task SetJsonObjectAsync<T>(this IDistributedCache cache, string ke
8787
serializerOptions, token);
8888
}
8989

90-
public static Task SetJsonObjectAsync<T>(this IDistributedCache cache, string key, T? value,
90+
public static Task SetJsonObjectAsync<T>(this IDistributedCache cache, string key, T value,
9191
DistributedCacheEntryOptions entryOptions, JsonSerializerOptions? serializerOptions = null,
9292
CancellationToken token = default) where T : class
9393
{
94-
var bytes = value != null ? JsonSerializer.SerializeToUtf8Bytes(value, serializerOptions) : null;
94+
var bytes = JsonSerializer.SerializeToUtf8Bytes(value, serializerOptions);
9595

9696
return cache.SetAsync(key, bytes, entryOptions, token);
9797
}
9898

9999

100100

101-
public static T GetOrSetJsonObject<T>(this IDistributedCache cache, string key, Func<T> valueFactory,
101+
public static T? GetOrSetJsonObject<T>(this IDistributedCache cache, string key, Func<T?> valueFactory,
102102
JsonSerializerOptions? serializerOptions = null) where T : class
103103
{
104104
return cache.GetOrSetJsonObject(key, valueFactory, new DistributedCacheEntryOptions(), serializerOptions);
105105
}
106106

107107

108-
public static T GetOrSetJsonObject<T>(this IDistributedCache cache, string key, Func<T> valueFactory,
108+
public static T? GetOrSetJsonObject<T>(this IDistributedCache cache, string key, Func<T?> valueFactory,
109109
TimeSpan absoluteExpirationRelativeToNow, JsonSerializerOptions? serializerOptions = null) where T : class
110110
{
111111
return cache.GetOrSetJsonObject<T>(key, valueFactory,
112112
new DistributedCacheEntryOptions { AbsoluteExpirationRelativeToNow = absoluteExpirationRelativeToNow },
113113
serializerOptions);
114114
}
115115

116-
public static T GetOrSetJsonObject<T>(this IDistributedCache cache, string key, Func<T> valueFactory,
116+
public static T? GetOrSetJsonObject<T>(this IDistributedCache cache, string key, Func<T?> valueFactory,
117117
DistributedCacheEntryOptions entryOptions, JsonSerializerOptions? serializerOptions = null) where T : class
118118
{
119119
var cached = cache.GetJsonObject<T>(key, serializerOptions);
@@ -125,50 +125,53 @@ public static T GetOrSetJsonObject<T>(this IDistributedCache cache, string key,
125125

126126
var value = valueFactory();
127127

128-
cache.SetJsonObject(key, value, entryOptions, serializerOptions);
128+
if (value != null)
129+
{
130+
cache.SetJsonObject(key, value, entryOptions, serializerOptions);
131+
}
129132

130133
return value;
131134
}
132135

133136

134-
public static Task<T> GetOrSetJsonObjectAsync<T>(this IDistributedCache cache, string key,
135-
Func<Task<T>> valueFactory, CancellationToken token = default) where T : class
137+
public static Task<T?> GetOrSetJsonObjectAsync<T>(this IDistributedCache cache, string key,
138+
Func<Task<T?>> valueFactory, CancellationToken token = default) where T : class
136139
{
137140
return cache.GetOrSetJsonObjectAsync(key, valueFactory, new DistributedCacheEntryOptions(), null, token);
138141
}
139-
public static Task<T> GetOrSetJsonObjectAsync<T>(this IDistributedCache cache, string key,
140-
Func<Task<T>> valueFactory, JsonSerializerOptions? serializerOptions,
142+
public static Task<T?> GetOrSetJsonObjectAsync<T>(this IDistributedCache cache, string key,
143+
Func<Task<T?>> valueFactory, JsonSerializerOptions? serializerOptions,
141144
CancellationToken token = default) where T : class
142145
{
143146
return cache.GetOrSetJsonObjectAsync(key, valueFactory, new DistributedCacheEntryOptions(), serializerOptions, token);
144147
}
145148

146-
public static Task<T> GetOrSetJsonObjectAsync<T>(this IDistributedCache cache, string key,
147-
Func<Task<T>> valueFactory, TimeSpan absoluteExpirationRelativeToNow, JsonSerializerOptions? serializerOptions,
149+
public static Task<T?> GetOrSetJsonObjectAsync<T>(this IDistributedCache cache, string key,
150+
Func<Task<T?>> valueFactory, TimeSpan absoluteExpirationRelativeToNow, JsonSerializerOptions? serializerOptions,
148151
CancellationToken token = default) where T : class
149152
{
150153
return cache.GetOrSetJsonObjectAsync(key, valueFactory,
151154
new DistributedCacheEntryOptions { AbsoluteExpirationRelativeToNow = absoluteExpirationRelativeToNow },
152155
serializerOptions, token);
153156
}
154157

155-
public static Task<T> GetOrSetJsonObjectAsync<T>(this IDistributedCache cache, string key,
156-
Func<Task<T>> valueFactory, TimeSpan absoluteExpirationRelativeToNow, CancellationToken token = default) where T : class
158+
public static Task<T?> GetOrSetJsonObjectAsync<T>(this IDistributedCache cache, string key,
159+
Func<Task<T?>> valueFactory, TimeSpan absoluteExpirationRelativeToNow, CancellationToken token = default) where T : class
157160
{
158161
return cache.GetOrSetJsonObjectAsync(key, valueFactory,
159162
new DistributedCacheEntryOptions { AbsoluteExpirationRelativeToNow = absoluteExpirationRelativeToNow },
160163
null, token);
161164
}
162165

163-
public static Task<T> GetOrSetJsonObjectAsync<T>(this IDistributedCache cache, string key,
164-
Func<Task<T>> valueFactory, DistributedCacheEntryOptions entryOptions,
166+
public static Task<T?> GetOrSetJsonObjectAsync<T>(this IDistributedCache cache, string key,
167+
Func<Task<T?>> valueFactory, DistributedCacheEntryOptions entryOptions,
165168
CancellationToken token = default) where T : class
166169
{
167170
return cache.GetOrSetJsonObjectAsync<T>(key, valueFactory, entryOptions, null, token);
168171
}
169172

170-
public static async Task<T> GetOrSetJsonObjectAsync<T>(this IDistributedCache cache, string key,
171-
Func<Task<T>> valueFactory, DistributedCacheEntryOptions entryOptions,
173+
public static async Task<T?> GetOrSetJsonObjectAsync<T>(this IDistributedCache cache, string key,
174+
Func<Task<T?>> valueFactory, DistributedCacheEntryOptions entryOptions,
172175
JsonSerializerOptions? serializerOptions = null, CancellationToken token = default) where T : class
173176
{
174177
var cached = await cache.GetJsonObjectAsync<T>(key, serializerOptions, token);
@@ -180,7 +183,10 @@ public static async Task<T> GetOrSetJsonObjectAsync<T>(this IDistributedCache ca
180183

181184
var value = await valueFactory();
182185

183-
await cache.SetJsonObjectAsync(key, value, entryOptions, serializerOptions, token);
186+
if (value != null)
187+
{
188+
await cache.SetJsonObjectAsync(key, value, entryOptions, serializerOptions, token);
189+
}
184190

185191
return value;
186192
}

src/SimpleConcepts.Extensions.Caching.Abstractions/IValueSerializer.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ namespace SimpleConcepts.Extensions.Caching
44
{
55
public interface IValueSerializer
66
{
7-
byte[]? Serialize(object? value);
7+
byte[] Serialize(object value);
88
object? Deserialize(byte[]? bytes, Type type);
99
}
1010
}

0 commit comments

Comments
 (0)