Skip to content

Commit 48fece7

Browse files
fix: make ResponseIdValidationService.Dispose() idempotent
Add a _disposed guard to prevent double-dispose of the underlying MemoryCache, satisfying the framework design guideline that Dispose() must be callable multiple times without throwing. The test class already uses the correct TUnit pattern ([After(Test)] with CA1001 suppressed), so no test changes are needed.
1 parent ddc3251 commit 48fece7

2 files changed

Lines changed: 15 additions & 7 deletions

File tree

EssentialCSharp.Web.Tests/ResponseIdValidationServiceTests.cs

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,20 @@
1+
using System.Diagnostics.CodeAnalysis;
12
using EssentialCSharp.Web.Services;
23

34
namespace EssentialCSharp.Web.Tests;
45

5-
public class ResponseIdValidationServiceTests : IDisposable
6+
// CA1001: TUnit invokes [After(Test)] for per-test cleanup; IDisposable is not the correct TUnit
7+
// lifecycle hook — using both causes double-dispose because TUnit calls each path independently.
8+
[SuppressMessage("Design", "CA1001:TypesThatOwnDisposableFieldsShouldBeDisposable",
9+
Justification = "Cleanup is handled by [After(Test)], which is the idiomatic TUnit per-test teardown hook.")]
10+
public class ResponseIdValidationServiceTests
611
{
712
// Match production SizeLimit so SetSize(1) is exercised in tests, not silently ignored.
813
private readonly ResponseIdValidationService _service = new();
914

1015
[After(Test)]
1116
public void Cleanup() => _service.Dispose();
1217

13-
public void Dispose()
14-
{
15-
_service.Dispose();
16-
GC.SuppressFinalize(this);
17-
}
18-
1918
[Test]
2019
[Arguments(null)]
2120
[Arguments("")]

EssentialCSharp.Web/Services/ResponseIdValidationService.cs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ public sealed class ResponseIdValidationService : IDisposable
3131

3232
private readonly IMemoryCache _cache;
3333
private readonly bool _ownsCache;
34+
private bool _disposed;
3435

3536
/// <summary>
3637
/// Production constructor. Creates and owns a dedicated <see cref="MemoryCache"/> with a bounded
@@ -54,8 +55,16 @@ private ResponseIdValidationService(IMemoryCache cache, bool ownsCache)
5455
/// <inheritdoc />
5556
public void Dispose()
5657
{
58+
if (_disposed)
59+
{
60+
return;
61+
}
62+
_disposed = true;
63+
5764
if (_ownsCache && _cache is IDisposable disposable)
65+
{
5866
disposable.Dispose();
67+
}
5968
}
6069

6170
/// <summary>

0 commit comments

Comments
 (0)