Skip to content

Commit 188dcce

Browse files
committed
CA fixes
1 parent 1082b30 commit 188dcce

10 files changed

Lines changed: 64 additions & 65 deletions

File tree

Neolution.Extensions.Caching.Abstractions/CacheKeyAttribute.cs

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
namespace Neolution.Extensions.Caching.Abstractions
1+
namespace Neolution.Extensions.Caching.Abstractions
22
{
33
using System;
44

@@ -17,14 +17,9 @@ public sealed class CacheKeyAttribute : Attribute
1717
/// <exception cref="ArgumentException">Thrown when key is empty or whitespace.</exception>
1818
public CacheKeyAttribute(string key)
1919
{
20-
if (key == null)
21-
{
22-
throw new ArgumentNullException(nameof(key));
23-
}
24-
2520
if (string.IsNullOrWhiteSpace(key))
2621
{
27-
throw new ArgumentException("Cache key cannot be empty or whitespace.", nameof(key));
22+
throw new ArgumentException("Cache key cannot be null, empty or whitespace.", nameof(key));
2823
}
2924

3025
this.Key = key;

Neolution.Extensions.Caching.Abstractions/DistributedCache.cs

Lines changed: 37 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -20,16 +20,23 @@ public abstract class DistributedCache<TCacheId> : IDistributedCache<TCacheId>
2020
private const int MaxCacheKeyBytes = 250;
2121

2222
/// <summary>
23-
/// Gets the name of the cache.
23+
/// Indicates whether cache keys should be URL-encoded.
2424
/// </summary>
25-
/// <value>
26-
/// The name of the cache.
27-
/// </value>
28-
private static string CacheIdName => typeof(TCacheId).Name;
29-
3025
private readonly bool enableKeyEncoding;
26+
27+
/// <summary>
28+
/// Indicates whether cache key length validation is enabled.
29+
/// </summary>
3130
private readonly bool enableKeyLengthValidation;
31+
32+
/// <summary>
33+
/// The cache key version for invalidation purposes.
34+
/// </summary>
3235
private readonly int? version;
36+
37+
/// <summary>
38+
/// The environment prefix for cache key isolation.
39+
/// </summary>
3340
private readonly string? environmentPrefix;
3441

3542
/// <summary>
@@ -71,127 +78,135 @@ protected DistributedCache(IOptions<DistributedCacheOptionsBase> optionsAccessor
7178
/// </summary>
7279
protected string? EnvironmentPrefix => this.environmentPrefix;
7380

81+
/// <summary>
82+
/// Gets the name of the cache.
83+
/// </summary>
84+
/// <value>
85+
/// The name of the cache.
86+
/// </value>
87+
private static string CacheIdName => typeof(TCacheId).Name;
88+
7489
/// <inheritdoc />
7590
public T? Get<T>(TCacheId id)
7691
where T : class
7792
{
78-
var cacheKey = CreateCacheKey(id);
93+
var cacheKey = this.CreateCacheKey(id);
7994
return this.GetCacheObject<T>(cacheKey);
8095
}
8196

8297
/// <inheritdoc />
8398
public T? Get<T>(TCacheId id, string key)
8499
where T : class
85100
{
86-
var cacheKey = CreateCacheKey(id, key);
101+
var cacheKey = this.CreateCacheKey(id, key);
87102
return this.GetCacheObject<T>(cacheKey);
88103
}
89104

90105
/// <inheritdoc />
91106
public Task<T?> GetAsync<T>(TCacheId id, CancellationToken token = default)
92107
where T : class
93108
{
94-
var cacheKey = CreateCacheKey(id);
109+
var cacheKey = this.CreateCacheKey(id);
95110
return this.GetCacheObjectAsync<T>(cacheKey, token);
96111
}
97112

98113
/// <inheritdoc />
99114
public Task<T?> GetAsync<T>(TCacheId id, string key, CancellationToken token = default)
100115
where T : class
101116
{
102-
var cacheKey = CreateCacheKey(id, key);
117+
var cacheKey = this.CreateCacheKey(id, key);
103118
return this.GetCacheObjectAsync<T>(cacheKey, token);
104119
}
105120

106121
/// <inheritdoc />
107122
public void Set<T>(TCacheId id, T value)
108123
where T : class
109124
{
110-
var cacheKey = CreateCacheKey(id);
125+
var cacheKey = this.CreateCacheKey(id);
111126
this.SetCacheObject(cacheKey, value, new CacheEntryOptions());
112127
}
113128

114129
/// <inheritdoc />
115130
public void Set<T>(TCacheId id, string key, T value)
116131
where T : class
117132
{
118-
var cacheKey = CreateCacheKey(id, key);
133+
var cacheKey = this.CreateCacheKey(id, key);
119134
this.SetCacheObject(cacheKey, value, new CacheEntryOptions());
120135
}
121136

122137
/// <inheritdoc />
123138
public Task SetAsync<T>(TCacheId id, T value, CancellationToken token = default)
124139
where T : class
125140
{
126-
var cacheKey = CreateCacheKey(id);
141+
var cacheKey = this.CreateCacheKey(id);
127142
return this.SetCacheObjectAsync(cacheKey, value, new CacheEntryOptions(), token);
128143
}
129144

130145
/// <inheritdoc />
131146
public Task SetAsync<T>(TCacheId id, string key, T value, CancellationToken token = default)
132147
where T : class
133148
{
134-
var cacheKey = CreateCacheKey(id, key);
149+
var cacheKey = this.CreateCacheKey(id, key);
135150
return this.SetCacheObjectAsync(cacheKey, value, new CacheEntryOptions(), token);
136151
}
137152

138153
/// <inheritdoc />
139154
public void SetWithOptions<T>(TCacheId id, T value, CacheEntryOptions? options)
140155
where T : class
141156
{
142-
var cacheKey = CreateCacheKey(id);
157+
var cacheKey = this.CreateCacheKey(id);
143158
this.SetCacheObject(cacheKey, value, options);
144159
}
145160

146161
/// <inheritdoc />
147162
public void SetWithOptions<T>(TCacheId id, string key, T value, CacheEntryOptions? options)
148163
where T : class
149164
{
150-
var cacheKey = CreateCacheKey(id, key);
165+
var cacheKey = this.CreateCacheKey(id, key);
151166
this.SetCacheObject(cacheKey, value, options);
152167
}
153168

154169
/// <inheritdoc />
155170
public Task SetWithOptionsAsync<T>(TCacheId id, T value, CacheEntryOptions? options, CancellationToken token = default)
156171
where T : class
157172
{
158-
var cacheKey = CreateCacheKey(id);
173+
var cacheKey = this.CreateCacheKey(id);
159174
return this.SetCacheObjectAsync(cacheKey, value, options, token);
160175
}
161176

162177
/// <inheritdoc />
163178
public Task SetWithOptionsAsync<T>(TCacheId id, string key, T value, CacheEntryOptions? options, CancellationToken token = default)
164179
where T : class
165180
{
166-
var cacheKey = CreateCacheKey(id, key);
181+
var cacheKey = this.CreateCacheKey(id, key);
167182
return this.SetCacheObjectAsync(cacheKey, value, options, token);
168183
}
169184

170185
/// <inheritdoc />
171186
public void Remove(TCacheId id)
172187
{
173-
var cacheKey = CreateCacheKey(id);
188+
var cacheKey = this.CreateCacheKey(id);
174189
this.RemoveCacheObject(cacheKey);
175190
}
176191

177192
/// <inheritdoc />
178193
public void Remove(TCacheId id, string key)
179194
{
180-
var cacheKey = CreateCacheKey(id, key);
195+
var cacheKey = this.CreateCacheKey(id, key);
181196
this.RemoveCacheObject(cacheKey);
182197
}
183198

184199
/// <inheritdoc />
185200
public Task RemoveAsync(TCacheId id, CancellationToken token = default)
186201
{
187-
var cacheKey = CreateCacheKey(id);
202+
var cacheKey = this.CreateCacheKey(id);
188203
return this.RemoveCacheObjectAsync(cacheKey, token);
189204
}
190205

191206
/// <inheritdoc />
192207
public Task RemoveAsync(TCacheId id, string key, CancellationToken token = default)
193208
{
194-
var cacheKey = CreateCacheKey(id, key);
209+
var cacheKey = this.CreateCacheKey(id, key);
195210
return this.RemoveCacheObjectAsync(cacheKey, token);
196211
}
197212

Neolution.Extensions.Caching.Abstractions/DistributedCacheOptionsBase.cs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,15 @@
44
/// Base class for distributed cache configuration options.
55
/// Provides common configuration properties shared across all distributed cache implementations.
66
/// </summary>
7-
public abstract class DistributedCacheOptionsBase
7+
public class DistributedCacheOptionsBase
88
{
9+
/// <summary>
10+
/// Initializes a new instance of the <see cref="DistributedCacheOptionsBase"/> class.
11+
/// </summary>
12+
protected DistributedCacheOptionsBase()
13+
{
14+
}
15+
916
/// <summary>
1017
/// Gets or sets the cache key version for invalidation purposes.
1118
/// If null, version is not included in the cache key.

Neolution.Extensions.Caching.Distributed/ServiceCollectionExtensions.cs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,8 +50,7 @@ public static IServiceCollection AddSerializedDistributedCache(this IServiceColl
5050
An IDistributedCache provider must be registered before calling AddSerializedDistributedCache().
5151
Register a provider such as Redis (AddStackExchangeRedisCache), SQL Server (AddDistributedSqlServerCache),
5252
or Memory (AddDistributedMemoryCache) first.
53-
"""
54-
);
53+
""");
5554
}
5655

5756
services.AddOptions();

Neolution.Extensions.Caching.UnitTests/CacheKeyImprovementsTests.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ public void MemoryCacheAcceptsVeryLongKeys()
8181
// Arrange
8282
using var serviceProvider = CreateServiceCollection().BuildServiceProvider();
8383
var cache = GetCache(serviceProvider);
84+
8485
// In-memory cache has no length restriction
8586
var longKey = new string('x', 500);
8687
const string value = "test-value";

Neolution.Extensions.Caching.UnitTests/CacheKeyVersioningTests.cs

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -39,15 +39,18 @@ public void MessagePackCacheKeyWithoutVersionByDefault()
3939
}
4040

4141
/// <summary>
42-
/// Tests if cache key includes version by default
42+
/// Tests if cache key includes version when configured
4343
/// </summary>
4444
[Fact]
45-
public void MessagePackCacheKeyIncludesDefaultVersion()
45+
public void MessagePackCacheKeyIncludesConfiguredVersion()
4646
{
4747
// Arrange
4848
var services = new ServiceCollection();
4949
services.AddDistributedMemoryCache();
50-
services.AddSerializedDistributedCache();
50+
services.AddSerializedDistributedCache(options =>
51+
{
52+
options.Version = 1;
53+
});
5154

5255
using var serviceProvider = services.BuildServiceProvider();
5356
var cache = serviceProvider.GetRequiredService<IDistributedCache<TestCacheId>>();
@@ -56,7 +59,7 @@ public void MessagePackCacheKeyIncludesDefaultVersion()
5659
// Act
5760
cache.Set(TestCacheId.Foobar, testValue);
5861

59-
// Assert - Verify we can retrieve the value (key format is correct)
62+
// Assert - Verify we can retrieve the value (version is included in key)
6063
cache.Get<string>(TestCacheId.Foobar).ShouldBe(testValue);
6164
}
6265

@@ -203,6 +206,7 @@ public void MessagePackCacheKeyWithOptionalKeyAndVersion()
203206
/// <summary>
204207
/// Tests if null or empty environment prefix is handled correctly
205208
/// </summary>
209+
/// <param name="environmentPrefix">The environment prefix to test.</param>
206210
[Theory]
207211
[InlineData(null)]
208212
[InlineData("")]

Neolution.Extensions.Caching.UnitTests/DistributedCacheKeyImprovementsTests.cs

Lines changed: 3 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ public void CacheKeyThrowsExceptionWhenTooLong(IServiceCollection serviceCollect
9090
// Arrange
9191
using var serviceProvider = serviceCollection.BuildServiceProvider();
9292
var cache = GetCache(serviceProvider);
93+
9394
// Create a very long key that exceeds 250 bytes
9495
var longKey = new string('x', 300);
9596

@@ -114,6 +115,7 @@ public void CacheKeyAcceptsMaximumAllowedLength(IServiceCollection serviceCollec
114115
// Arrange
115116
using var serviceProvider = serviceCollection.BuildServiceProvider();
116117
var cache = GetCache(serviceProvider);
118+
117119
// Test that we can use keys up to the limit
118120
// Assuming enum name + structure is ~50 bytes, test with ~180 char key
119121
var maxKey = new string('a', 180);
@@ -139,6 +141,7 @@ public void CacheKeyValidationAccountsForUnicodeBytes(IServiceCollection service
139141
// Arrange
140142
using var serviceProvider = serviceCollection.BuildServiceProvider();
141143
var cache = GetCache(serviceProvider);
144+
142145
// Unicode characters can be multiple bytes in UTF-8
143146
// 100 Chinese characters = ~300 bytes in UTF-8
144147
var unicodeKey = new string('中', 100);
@@ -194,31 +197,6 @@ public void CacheKeyFallsBackToEnumNameWhenNoAttribute(IServiceCollection servic
194197
result.ShouldBe(value);
195198
}
196199

197-
/// <summary>
198-
/// Tests that cache key with attribute is refactor-safe
199-
/// </summary>
200-
/// <param name="serviceCollection">The service collection.</param>
201-
[Theory]
202-
[ClassData(typeof(ServiceCollectionTestDataCollection))]
203-
public void CacheKeyWithAttributeIsRefactorSafe(IServiceCollection serviceCollection)
204-
{
205-
// Arrange
206-
using var serviceProvider = serviceCollection.BuildServiceProvider();
207-
var cache = GetCache(serviceProvider);
208-
const string value = "test-value";
209-
210-
// This test documents the behavior:
211-
// Even if we rename "UserProfile" to "User",
212-
// the cache key remains "user-profile" due to the attribute
213-
214-
// Act
215-
cache.Set(TestCacheId.UserProfile, value);
216-
217-
// Assert - The actual cache key should contain "user-profile", not "UserProfile"
218-
var result = cache.Get<string>(TestCacheId.UserProfile);
219-
result.ShouldBe(value);
220-
}
221-
222200
/// <summary>
223201
/// Tests that cache key with attribute and optional key works correctly
224202
/// </summary>

Neolution.Extensions.Caching.UnitTests/Models/TestObject.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
namespace Neolution.Extensions.Caching.UnitTests.Models
1+
namespace Neolution.Extensions.Caching.UnitTests.Models
22
{
33
/// <summary>
44
/// A simple test object for unit testing.

Neolution.Extensions.Caching.UnitTests/MsgPackSerializerTests.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -159,7 +159,7 @@ private static TestObject CreateLargeTestObject()
159159
return new TestObject
160160
{
161161
Name = largeString,
162-
Value = 999
162+
Value = 999,
163163
};
164164
}
165165
}

Neolution.Extensions.Caching.UnitTests/RedisHybridCacheOptionsTests.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ public void EnableCompression_CanBeSetToTrue()
3333
// Arrange
3434
var options = new RedisHybridCacheOptions
3535
{
36-
EnableCompression = true
36+
EnableCompression = true,
3737
};
3838

3939
// Act & Assert
@@ -103,7 +103,7 @@ public void RedisHybridCacheOptions_InheritsFromBase()
103103
Version = 5,
104104
EnvironmentPrefix = "prod",
105105
EnableKeyEncoding = false,
106-
EnableKeyLengthValidation = false
106+
EnableKeyLengthValidation = false,
107107
};
108108

109109
// Assert

0 commit comments

Comments
 (0)