diff --git a/BitFaster.Caching.UnitTests/ScopedAsyncCacheTestBase.cs b/BitFaster.Caching.UnitTests/ScopedAsyncCacheTestBase.cs index be7cdd7b..02cbd16a 100644 --- a/BitFaster.Caching.UnitTests/ScopedAsyncCacheTestBase.cs +++ b/BitFaster.Caching.UnitTests/ScopedAsyncCacheTestBase.cs @@ -1,4 +1,5 @@ using System; +using System.Collections; using System.Collections.Generic; using System.Linq; using System.Text; @@ -169,6 +170,52 @@ public void WhenKeyExistsTryUpdateReturnsTrue() this.cache.TryUpdate(1, new Disposable()).Should().BeTrue(); } + [Fact] + public void WhenItemsAddedKeysContainsTheKeys() + { + cache.Count.Should().Be(0); + cache.AddOrUpdate(1, new Disposable()); + cache.AddOrUpdate(2, new Disposable()); + cache.Keys.Should().BeEquivalentTo(new[] { 1, 2 }); + } + + [Fact] + public void WhenItemsAddedGenericEnumerateContainsKvps() + { + var d1 = new Disposable(); + var d2 = new Disposable(); + + cache.Count.Should().Be(0); + cache.AddOrUpdate(1, d1); + cache.AddOrUpdate(2, d2); + cache + .Select(kvp => new KeyValuePair(kvp.Key, kvp.Value.CreateLifetime().Value)) + .Should().BeEquivalentTo(new[] { new KeyValuePair(1, d1), new KeyValuePair(2, d2) }); + } + + [Fact] + public void WhenItemsAddedEnumerateContainsKvps() + { + var d1 = new Disposable(); + var d2 = new Disposable(); + + cache.Count.Should().Be(0); + cache.AddOrUpdate(1, d1); + cache.AddOrUpdate(2, d2); + + var enumerable = (IEnumerable)cache; + + var list = new List>(); + + foreach (var i in enumerable) + { + var kvp = (KeyValuePair>)i; + list.Add(new KeyValuePair(kvp.Key, kvp.Value.CreateLifetime().Value)); + } + + list.Should().BeEquivalentTo(new[] { new KeyValuePair(1, d1), new KeyValuePair(2, d2) }); + } + protected void OnItemRemoved(object sender, ItemRemovedEventArgs> e) { this.removedItems.Add(e); diff --git a/BitFaster.Caching.UnitTests/ScopedCacheTestBase.cs b/BitFaster.Caching.UnitTests/ScopedCacheTestBase.cs index 539fa390..1ac22135 100644 --- a/BitFaster.Caching.UnitTests/ScopedCacheTestBase.cs +++ b/BitFaster.Caching.UnitTests/ScopedCacheTestBase.cs @@ -1,4 +1,5 @@ using System; +using System.Collections; using System.Collections.Generic; using System.Linq; using System.Text; @@ -169,6 +170,52 @@ public void WhenKeyExistsTryUpdateReturnsTrue() this.cache.TryUpdate(1, new Disposable()).Should().BeTrue(); } + [Fact] + public void WhenItemsAddedKeysContainsTheKeys() + { + cache.Count.Should().Be(0); + cache.AddOrUpdate(1, new Disposable()); + cache.AddOrUpdate(2, new Disposable()); + cache.Keys.Should().BeEquivalentTo(new[] { 1, 2 }); + } + + [Fact] + public void WhenItemsAddedGenericEnumerateContainsKvps() + { + var d1 = new Disposable(); + var d2 = new Disposable(); + + cache.Count.Should().Be(0); + cache.AddOrUpdate(1, d1); + cache.AddOrUpdate(2, d2); + cache + .Select(kvp => new KeyValuePair(kvp.Key, kvp.Value.CreateLifetime().Value)) + .Should().BeEquivalentTo(new[] { new KeyValuePair(1, d1), new KeyValuePair(2, d2) }); + } + + [Fact] + public void WhenItemsAddedEnumerateContainsKvps() + { + var d1 = new Disposable(); + var d2 = new Disposable(); + + cache.Count.Should().Be(0); + cache.AddOrUpdate(1, d1); + cache.AddOrUpdate(2, d2); + + var enumerable = (IEnumerable)cache; + + var list = new List>(); + + foreach (var i in enumerable) + { + var kvp = (KeyValuePair>)i; + list.Add(new KeyValuePair(kvp.Key, kvp.Value.CreateLifetime().Value)); + } + + list.Should().BeEquivalentTo(new[] { new KeyValuePair(1, d1), new KeyValuePair(2, d2) }); + } + protected void OnItemRemoved(object sender, ItemRemovedEventArgs> e) { this.removedItems.Add(e); diff --git a/BitFaster.Caching.UnitTests/Synchronized/AtomicFactoryAsyncCacheTests.cs b/BitFaster.Caching.UnitTests/Synchronized/AtomicFactoryAsyncCacheTests.cs index c4793d1b..152b19d4 100644 --- a/BitFaster.Caching.UnitTests/Synchronized/AtomicFactoryAsyncCacheTests.cs +++ b/BitFaster.Caching.UnitTests/Synchronized/AtomicFactoryAsyncCacheTests.cs @@ -1,4 +1,5 @@ using System; +using System.Collections; using System.Collections.Generic; using System.Linq; using System.Text; @@ -147,6 +148,35 @@ public void WhenKeyExistsTryUpdateReturnsTrue() value.Should().Be(2); } + [Fact] + public void WhenItemsAddedKeysContainsTheKeys() + { + cache.Count.Should().Be(0); + cache.AddOrUpdate(1, 1); + cache.AddOrUpdate(2, 2); + cache.Keys.Should().BeEquivalentTo(new[] { 1, 2 }); + } + + [Fact] + public void WhenItemsAddedGenericEnumerateContainsKvps() + { + cache.Count.Should().Be(0); + cache.AddOrUpdate(1, 1); + cache.AddOrUpdate(2, 2); + cache.Should().BeEquivalentTo(new[] { new KeyValuePair(1, 1), new KeyValuePair(2, 2) }); + } + + [Fact] + public void WhenItemsAddedEnumerateContainsKvps() + { + cache.Count.Should().Be(0); + cache.AddOrUpdate(1, 1); + cache.AddOrUpdate(2, 2); + + var enumerable = (IEnumerable)cache; + enumerable.Should().BeEquivalentTo(new[] { new KeyValuePair(1, 1), new KeyValuePair(2, 2) }); + } + private void OnItemRemoved(object sender, ItemRemovedEventArgs e) { this.removedItems.Add(e); diff --git a/BitFaster.Caching.UnitTests/Synchronized/AtomicFactoryCacheTests.cs b/BitFaster.Caching.UnitTests/Synchronized/AtomicFactoryCacheTests.cs index 722882d1..10dc7611 100644 --- a/BitFaster.Caching.UnitTests/Synchronized/AtomicFactoryCacheTests.cs +++ b/BitFaster.Caching.UnitTests/Synchronized/AtomicFactoryCacheTests.cs @@ -1,4 +1,5 @@ using System; +using System.Collections; using System.Collections.Generic; using System.Linq; using System.Text; @@ -147,6 +148,35 @@ public void WhenKeyExistsTryUpdateReturnsTrue() value.Should().Be(2); } + [Fact] + public void WhenItemsAddedKeysContainsTheKeys() + { + cache.Count.Should().Be(0); + cache.AddOrUpdate(1, 1); + cache.AddOrUpdate(2, 2); + cache.Keys.Should().BeEquivalentTo(new[] { 1, 2 }); + } + + [Fact] + public void WhenItemsAddedGenericEnumerateContainsKvps() + { + cache.Count.Should().Be(0); + cache.AddOrUpdate(1, 1); + cache.AddOrUpdate(2, 2); + cache.Should().BeEquivalentTo(new[] { new KeyValuePair(1, 1), new KeyValuePair(2, 2) }); + } + + [Fact] + public void WhenItemsAddedEnumerateContainsKvps() + { + cache.Count.Should().Be(0); + cache.AddOrUpdate(1, 1); + cache.AddOrUpdate(2, 2); + + var enumerable = (IEnumerable)cache; + enumerable.Should().BeEquivalentTo(new[] { new KeyValuePair(1, 1), new KeyValuePair(2, 2) }); + } + private void OnItemRemoved(object sender, ItemRemovedEventArgs e) { this.removedItems.Add(e); diff --git a/BitFaster.Caching/IAsyncCache.cs b/BitFaster.Caching/IAsyncCache.cs index 7f837876..a5d46ecc 100644 --- a/BitFaster.Caching/IAsyncCache.cs +++ b/BitFaster.Caching/IAsyncCache.cs @@ -11,7 +11,7 @@ namespace BitFaster.Caching /// /// The type of keys in the cache. /// The type of values in the cache. - public interface IAsyncCache + public interface IAsyncCache : IEnumerable> { /// /// Gets the total number of items that can be stored in the cache. @@ -33,6 +33,11 @@ public interface IAsyncCache /// ICacheEvents Events { get; } + /// + /// Gets a collection containing the keys in the cache. + /// + ICollection Keys { get; } + /// /// Attempts to get the value associated with the specified key from the cache. /// diff --git a/BitFaster.Caching/ICache.cs b/BitFaster.Caching/ICache.cs index dbb9e892..5ecb804e 100644 --- a/BitFaster.Caching/ICache.cs +++ b/BitFaster.Caching/ICache.cs @@ -11,7 +11,7 @@ namespace BitFaster.Caching /// /// The type of keys in the cache. /// The type of values in the cache. - public interface ICache + public interface ICache : IEnumerable> { /// /// Gets the total number of items that can be stored in the cache. @@ -33,6 +33,11 @@ public interface ICache /// ICacheEvents Events { get; } + /// + /// Gets a collection containing the keys in the cache. + /// + ICollection Keys { get; } + /// /// Attempts to get the value associated with the specified key from the cache. /// diff --git a/BitFaster.Caching/IScopedAsyncCache.cs b/BitFaster.Caching/IScopedAsyncCache.cs index 32ba7150..ae5e08cd 100644 --- a/BitFaster.Caching/IScopedAsyncCache.cs +++ b/BitFaster.Caching/IScopedAsyncCache.cs @@ -11,7 +11,7 @@ namespace BitFaster.Caching /// /// The type of keys in the cache. /// The type of values in the cache. - public interface IScopedAsyncCache where V : IDisposable + public interface IScopedAsyncCache : IEnumerable>> where V : IDisposable { /// /// Gets the total number of items that can be stored in the cache. @@ -37,6 +37,11 @@ public interface IScopedAsyncCache where V : IDisposable /// ICacheEvents> Events { get; } + /// + /// Gets a collection containing the keys in the cache. + /// + ICollection Keys { get; } + /// /// Attempts to create a lifetime for the value associated with the specified key from the cache /// diff --git a/BitFaster.Caching/IScopedCache.cs b/BitFaster.Caching/IScopedCache.cs index e06f11c1..7c3687d0 100644 --- a/BitFaster.Caching/IScopedCache.cs +++ b/BitFaster.Caching/IScopedCache.cs @@ -11,7 +11,7 @@ namespace BitFaster.Caching /// /// The type of keys in the cache. /// The type of values in the cache. - public interface IScopedCache where V : IDisposable + public interface IScopedCache : IEnumerable>> where V : IDisposable { /// /// Gets the total number of items that can be stored in the cache. @@ -37,6 +37,11 @@ public interface IScopedCache where V : IDisposable /// ICacheEvents> Events { get; } + /// + /// Gets a collection containing the keys in the cache. + /// + ICollection Keys { get; } + /// /// Attempts to create a lifetime for the value associated with the specified key from the cache /// diff --git a/BitFaster.Caching/ScopedAsyncCache.cs b/BitFaster.Caching/ScopedAsyncCache.cs index f15dbe53..1fd00183 100644 --- a/BitFaster.Caching/ScopedAsyncCache.cs +++ b/BitFaster.Caching/ScopedAsyncCache.cs @@ -1,4 +1,5 @@ using System; +using System.Collections; using System.Collections.Generic; using System.Linq; using System.Text; @@ -40,6 +41,9 @@ public ScopedAsyncCache(IAsyncCache> cache) /// public ICacheEvents> Events => this.cache.Events; + /// + public ICollection Keys => this.cache.Keys; + /// public void AddOrUpdate(K key, V value) { @@ -107,5 +111,18 @@ public bool TryUpdate(K key, V value) { return this.cache.TryUpdate(key, new Scoped(value)); } + + public IEnumerator>> GetEnumerator() + { + foreach (var kvp in this.cache) + { + yield return kvp; + } + } + + IEnumerator IEnumerable.GetEnumerator() + { + return ((ScopedAsyncCache)this).GetEnumerator(); + } } } diff --git a/BitFaster.Caching/ScopedCache.cs b/BitFaster.Caching/ScopedCache.cs index 8d34e229..2c8476c0 100644 --- a/BitFaster.Caching/ScopedCache.cs +++ b/BitFaster.Caching/ScopedCache.cs @@ -1,4 +1,5 @@ using System; +using System.Collections; using System.Collections.Generic; using System.Linq; using System.Text; @@ -40,6 +41,9 @@ public ScopedCache(ICache> cache) /// public ICacheEvents> Events => this.cache.Events; + /// + public ICollection Keys => this.cache.Keys; + /// public void AddOrUpdate(K key, V value) { @@ -107,5 +111,18 @@ public bool TryUpdate(K key, V value) { return this.cache.TryUpdate(key, new Scoped(value)); } + + public IEnumerator>> GetEnumerator() + { + foreach (var kvp in this.cache) + { + yield return kvp; + } + } + + IEnumerator IEnumerable.GetEnumerator() + { + return ((ScopedCache)this).GetEnumerator(); + } } } diff --git a/BitFaster.Caching/Synchronized/AtomicFactoryAsyncCache.cs b/BitFaster.Caching/Synchronized/AtomicFactoryAsyncCache.cs index 467d7f6b..1c8d0cd3 100644 --- a/BitFaster.Caching/Synchronized/AtomicFactoryAsyncCache.cs +++ b/BitFaster.Caching/Synchronized/AtomicFactoryAsyncCache.cs @@ -1,4 +1,5 @@ using System; +using System.Collections; using System.Collections.Generic; using System.Linq; using System.Text; @@ -30,6 +31,8 @@ public AtomicFactoryAsyncCache(ICache> cache) public ICacheEvents Events => this.eventProxy; + public ICollection Keys => this.cache.Keys; + public void AddOrUpdate(K key, V value) { cache.AddOrUpdate(key, new AsyncAtomicFactory(value)); @@ -76,6 +79,19 @@ public bool TryUpdate(K key, V value) return cache.TryUpdate(key, new AsyncAtomicFactory(value)); } + public IEnumerator> GetEnumerator() + { + foreach (var kvp in this.cache) + { + yield return new KeyValuePair(kvp.Key, kvp.Value.ValueIfCreated); + } + } + + IEnumerator IEnumerable.GetEnumerator() + { + return ((AtomicFactoryAsyncCache)this).GetEnumerator(); + } + private class EventProxy : CacheEventProxyBase, V> { public EventProxy(ICacheEvents> inner) diff --git a/BitFaster.Caching/Synchronized/AtomicFactoryCache.cs b/BitFaster.Caching/Synchronized/AtomicFactoryCache.cs index e84a74c3..1883dd6b 100644 --- a/BitFaster.Caching/Synchronized/AtomicFactoryCache.cs +++ b/BitFaster.Caching/Synchronized/AtomicFactoryCache.cs @@ -1,4 +1,5 @@ using System; +using System.Collections; using System.Collections.Generic; using System.Linq; using System.Text; @@ -30,6 +31,8 @@ public AtomicFactoryCache(ICache> cache) public ICacheEvents Events => this.eventProxy; + public ICollection Keys => this.cache.Keys; + public void AddOrUpdate(K key, V value) { this.cache.AddOrUpdate(key, new AtomicFactory(value)); @@ -76,6 +79,19 @@ public bool TryUpdate(K key, V value) return cache.TryUpdate(key, new AtomicFactory(value)); } + public IEnumerator> GetEnumerator() + { + foreach (var kvp in this.cache) + { + yield return new KeyValuePair(kvp.Key, kvp.Value.ValueIfCreated); + } + } + + IEnumerator IEnumerable.GetEnumerator() + { + return ((AtomicFactoryCache)this).GetEnumerator(); + } + private class EventProxy : CacheEventProxyBase, V> { public EventProxy(ICacheEvents> inner) diff --git a/BitFaster.Caching/Synchronized/AtomicFactoryScopedAsyncCache.cs b/BitFaster.Caching/Synchronized/AtomicFactoryScopedAsyncCache.cs index 9ce1ecc8..ffc802ce 100644 --- a/BitFaster.Caching/Synchronized/AtomicFactoryScopedAsyncCache.cs +++ b/BitFaster.Caching/Synchronized/AtomicFactoryScopedAsyncCache.cs @@ -1,4 +1,5 @@ using System; +using System.Collections; using System.Collections.Generic; using System.Linq; using System.Text; @@ -31,6 +32,9 @@ public AtomicFactoryScopedAsyncCache(ICache> c public ICacheEvents> Events => this.eventProxy; + /// + public ICollection Keys => this.cache.Keys; + public void AddOrUpdate(K key, V value) { this.cache.AddOrUpdate(key, new ScopedAsyncAtomicFactory(value)); @@ -94,6 +98,19 @@ public bool TryUpdate(K key, V value) return this.cache.TryUpdate(key, new ScopedAsyncAtomicFactory(value)); } + public IEnumerator>> GetEnumerator() + { + foreach (var kvp in this.cache) + { + yield return new KeyValuePair>(kvp.Key, kvp.Value.ScopeIfCreated); + } + } + + IEnumerator IEnumerable.GetEnumerator() + { + return ((AtomicFactoryScopedAsyncCache)this).GetEnumerator(); + } + private class EventProxy : CacheEventProxyBase, Scoped> { public EventProxy(ICacheEvents> inner) diff --git a/BitFaster.Caching/Synchronized/AtomicFactoryScopedCache.cs b/BitFaster.Caching/Synchronized/AtomicFactoryScopedCache.cs index 746d7670..309cf56f 100644 --- a/BitFaster.Caching/Synchronized/AtomicFactoryScopedCache.cs +++ b/BitFaster.Caching/Synchronized/AtomicFactoryScopedCache.cs @@ -1,4 +1,5 @@ using System; +using System.Collections; using System.Collections.Generic; using System.Linq; using System.Text; @@ -31,6 +32,9 @@ public AtomicFactoryScopedCache(ICache> cache) public ICacheEvents> Events => this.eventProxy; + /// + public ICollection Keys => this.cache.Keys; + public void AddOrUpdate(K key, V value) { this.cache.AddOrUpdate(key, new ScopedAtomicFactory(value)); @@ -92,6 +96,19 @@ public bool TryUpdate(K key, V value) return this.cache.TryUpdate(key, new ScopedAtomicFactory(value)); } + public IEnumerator>> GetEnumerator() + { + foreach (var kvp in this.cache) + { + yield return new KeyValuePair>(kvp.Key, kvp.Value.ScopeIfCreated); + } + } + + IEnumerator IEnumerable.GetEnumerator() + { + return ((AtomicFactoryScopedCache)this).GetEnumerator(); + } + private class EventProxy : CacheEventProxyBase, Scoped> { public EventProxy(ICacheEvents> inner)