Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@ public partial interface ICacheEntry : System.IDisposable
System.Collections.Generic.IList<Microsoft.Extensions.Caching.Memory.PostEvictionCallbackRegistration> PostEvictionCallbacks { get; }
Microsoft.Extensions.Caching.Memory.CacheItemPriority Priority { get; set; }
long? Size { get; set; }
bool ShouldNotStore { get; set; }
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is adding new public API. It needs an approved API proposal first

https://github.com/dotnet/runtime/blob/main/docs/project/api-review-process.md

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Understood. Just spiking out and experimenting at the moment.

System.TimeSpan? SlidingExpiration { get; set; }
object? Value { get; set; }
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- https://learn.microsoft.com/dotnet/fundamentals/package-validation/diagnostic-ids -->
<Suppressions xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:Microsoft.Extensions.Caching.Memory.ICacheEntry.get_ShouldNotStore</Target>
<Left>ref/net10.0/Microsoft.Extensions.Caching.Abstractions.dll</Left>
<Right>lib/net10.0/Microsoft.Extensions.Caching.Abstractions.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:Microsoft.Extensions.Caching.Memory.ICacheEntry.set_ShouldNotStore(System.Boolean)</Target>
<Left>ref/net10.0/Microsoft.Extensions.Caching.Abstractions.dll</Left>
<Right>lib/net10.0/Microsoft.Extensions.Caching.Abstractions.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:Microsoft.Extensions.Caching.Memory.ICacheEntry.get_ShouldNotStore</Target>
<Left>ref/net462/Microsoft.Extensions.Caching.Abstractions.dll</Left>
<Right>lib/net462/Microsoft.Extensions.Caching.Abstractions.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:Microsoft.Extensions.Caching.Memory.ICacheEntry.set_ShouldNotStore(System.Boolean)</Target>
<Left>ref/net462/Microsoft.Extensions.Caching.Abstractions.dll</Left>
<Right>lib/net462/Microsoft.Extensions.Caching.Abstractions.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:Microsoft.Extensions.Caching.Memory.ICacheEntry.get_ShouldNotStore</Target>
<Left>ref/net8.0/Microsoft.Extensions.Caching.Abstractions.dll</Left>
<Right>lib/net8.0/Microsoft.Extensions.Caching.Abstractions.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:Microsoft.Extensions.Caching.Memory.ICacheEntry.set_ShouldNotStore(System.Boolean)</Target>
<Left>ref/net8.0/Microsoft.Extensions.Caching.Abstractions.dll</Left>
<Right>lib/net8.0/Microsoft.Extensions.Caching.Abstractions.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:Microsoft.Extensions.Caching.Memory.ICacheEntry.get_ShouldNotStore</Target>
<Left>ref/net9.0/Microsoft.Extensions.Caching.Abstractions.dll</Left>
<Right>lib/net9.0/Microsoft.Extensions.Caching.Abstractions.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:Microsoft.Extensions.Caching.Memory.ICacheEntry.set_ShouldNotStore(System.Boolean)</Target>
<Left>ref/net9.0/Microsoft.Extensions.Caching.Abstractions.dll</Left>
<Right>lib/net9.0/Microsoft.Extensions.Caching.Abstractions.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:Microsoft.Extensions.Caching.Memory.ICacheEntry.get_ShouldNotStore</Target>
<Left>ref/netstandard2.0/Microsoft.Extensions.Caching.Abstractions.dll</Left>
<Right>lib/netstandard2.0/Microsoft.Extensions.Caching.Abstractions.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:Microsoft.Extensions.Caching.Memory.ICacheEntry.set_ShouldNotStore(System.Boolean)</Target>
<Left>ref/netstandard2.0/Microsoft.Extensions.Caching.Abstractions.dll</Left>
<Right>lib/netstandard2.0/Microsoft.Extensions.Caching.Abstractions.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0006</DiagnosticId>
<Target>P:Microsoft.Extensions.Caching.Memory.ICacheEntry.ShouldNotStore</Target>
<Left>ref/net10.0/Microsoft.Extensions.Caching.Abstractions.dll</Left>
<Right>lib/net10.0/Microsoft.Extensions.Caching.Abstractions.dll</Right>
</Suppression>
</Suppressions>
Original file line number Diff line number Diff line change
Expand Up @@ -59,5 +59,12 @@ public interface ICacheEntry : IDisposable
/// Gets or set the size of the cache entry value.
/// </summary>
long? Size { get; set; }

/// <summary>
/// Gets or sets a value indicating whether the cache entry should not be stored in the cache.
/// When set to <c>true</c>, the entry will not be added to the cache and subsequent
/// requests will re-execute the factory method.
/// </summary>
bool ShouldNotStore { get; set; }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ internal sealed partial class CacheEntry : ICacheEntry
private bool _isDisposed;
private bool _isExpired;
private bool _isValueSet;
private bool _shouldNotStore;
private byte _evictionReason;
private byte _priority = (byte)CacheItemPriority.Normal;

Expand Down Expand Up @@ -170,6 +171,12 @@ public TimeSpan? SlidingExpiration
}
}

public bool ShouldNotStore
{
get => _shouldNotStore;
set => _shouldNotStore = value;
}

public object Key { get; }

public object? Value
Expand All @@ -196,7 +203,7 @@ public void Dispose()
{
CommitWithTracking();
}
else if (_isValueSet)
else if (_isValueSet && !_shouldNotStore)
{
_cache.SetEntry(this);
}
Expand All @@ -211,7 +218,7 @@ private void CommitWithTracking()
// Don't commit or propagate options if the CacheEntry Value was never set.
// We assume an exception occurred causing the caller to not set the Value successfully,
// so don't use this entry.
if (_isValueSet)
if (_isValueSet && !_shouldNotStore)
{
_cache.SetEntry(this);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -865,6 +865,134 @@ public void MixedKeysUsage()
Assert.Equal("decimal value", cache.Get(key1));
}

[Theory]
[InlineData(true)]
[InlineData(false)]
public void GetOrCreate_WithShouldNotStore_DoesNotCache(bool trackLinkedCacheEntries)
{
var cache = CreateCache(trackLinkedCacheEntries);
string key = "myKey";
int callCount = 0;

var result1 = cache.GetOrCreate(key, entry =>
{
callCount++;
entry.ShouldNotStore = true;
return "value1";
});

Assert.Equal("value1", result1);
Assert.Equal(1, callCount);
Assert.False(cache.TryGetValue(key, out _));

var result2 = cache.GetOrCreate(key, entry =>
{
callCount++;
return "value2";
});

Assert.Equal("value2", result2);
Assert.Equal(2, callCount);
Assert.True(cache.TryGetValue(key, out string cachedValue));
Assert.Equal("value2", cachedValue);
}

[Theory]
[InlineData(true)]
[InlineData(false)]
public async Task GetOrCreateAsync_WithShouldNotStore_DoesNotCache(bool trackLinkedCacheEntries)
{
var cache = CreateCache(trackLinkedCacheEntries);
string key = "myKey";
int callCount = 0;

var result1 = await cache.GetOrCreateAsync(key, async entry =>
{
callCount++;
entry.ShouldNotStore = true;
await Task.Yield();
return "value1";
});

Assert.Equal("value1", result1);
Assert.Equal(1, callCount);
Assert.False(cache.TryGetValue(key, out _));

var result2 = await cache.GetOrCreateAsync(key, async entry =>
{
callCount++;
await Task.Yield();
return "value2";
});

Assert.Equal("value2", result2);
Assert.Equal(2, callCount);
Assert.True(cache.TryGetValue(key, out string cachedValue));
Assert.Equal("value2", cachedValue);
}

[Theory]
[InlineData(true)]
[InlineData(false)]
public void GetOrCreate_WithoutShouldNotStore_DoesCache(bool trackLinkedCacheEntries)
{
var cache = CreateCache(trackLinkedCacheEntries);
string key = "myKey";
int callCount = 0;

var result1 = cache.GetOrCreate(key, entry =>
{
callCount++;
return "value1";
});

Assert.Equal("value1", result1);
Assert.Equal(1, callCount);
Assert.True(cache.TryGetValue(key, out string cachedValue));
Assert.Equal("value1", cachedValue);

var result2 = cache.GetOrCreate(key, entry =>
{
callCount++;
return "value2";
});

Assert.Equal("value1", result2);
Assert.Equal(1, callCount);
}

[Theory]
[InlineData(true)]
[InlineData(false)]
public async Task GetOrCreateAsync_WithoutShouldNotStore_DoesCache(bool trackLinkedCacheEntries)
{
var cache = CreateCache(trackLinkedCacheEntries);
string key = "myKey";
int callCount = 0;

var result1 = await cache.GetOrCreateAsync(key, async entry =>
{
callCount++;
await Task.Yield();
return "value1";
});

Assert.Equal("value1", result1);
Assert.Equal(1, callCount);
Assert.True(cache.TryGetValue(key, out string cachedValue));
Assert.Equal("value1", cachedValue);

var result2 = await cache.GetOrCreateAsync(key, async entry =>
{
callCount++;
await Task.Yield();
return "value2";
});

Assert.Equal("value1", result2);
Assert.Equal(1, callCount);
}

private class TestKey
{
public override bool Equals(object obj) => true;
Expand Down
Loading