diff --git a/Orm/Xtensive.Orm.Tests.Core/Caching/FastConcurrentLruCacheTest.cs b/Orm/Xtensive.Orm.Tests.Core/Caching/FastConcurrentLruCacheTest.cs new file mode 100755 index 0000000000..4b5d512bb4 --- /dev/null +++ b/Orm/Xtensive.Orm.Tests.Core/Caching/FastConcurrentLruCacheTest.cs @@ -0,0 +1,261 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using NUnit.Framework; +using Xtensive.Caching; +using Xtensive.Conversion; +using Xtensive.Core; +using Xtensive.Orm.Tests; + +#pragma warning disable IDE0058 + +namespace Xtensive.Orm.Tests.Core.Caching +{ + [TestFixture] + public class FastConcurrentLruCacheTest + { + private FastConcurrentLruCache globalCache; + private readonly Random random = RandomManager.CreateRandom((int) DateTime.Now.Ticks); + + private class BadTestClass : + IIdentified, + IHasSize + { + object IIdentified.Identifier + { + get { return Identifier; } + } + + public string Identifier + { + get { return null; } + } + + public long Size + { + get { return 1; } + } + } + + [Test] + public void ConstructorsTest() + { + var cache = new FastConcurrentLruCache( + 1000, + value => value.Text); + + var cache1 = new FastConcurrentLruCache( + 1000, + (value) => value.Text + ); + + + TestClass item = new TestClass("1"); + cache.Add(item); + cache1.Add(item); + Assert.AreEqual(1, cache1.Count); + + for (int i = 0; i < 100000; i++) { + TestClass test = new TestClass("" + i); + cache1.Add(test); + } + } + + [Test] + public void ConstructorDenyTest() + { + Assert.Throws(() => { + var cache = + new FastConcurrentLruCache( + -1, + value => value.Text + ); + }); + } + + [Test] + public void AddRemoveTest() + { + var cache = new FastConcurrentLruCache( + 100, + value => value.Text); + + TestClass item = new TestClass("1"); + cache.Add(item); + Assert.AreEqual(1, cache.Count); + item = new TestClass("2"); + cache.Add(item); + Assert.AreEqual(2, cache.Count); + Assert.AreEqual(item, cache[item.Text, false]); + ICache icache = cache; + Assert.AreEqual(item, icache[item.Text, false]); + Assert.AreEqual(null, icache["3", false]); + cache.Remove(item); + Assert.AreEqual(1, cache.Count); + cache.Clear(); + Assert.AreEqual(0, cache.Count); + } + + [Test] + public void AddDenyTest1() + { + var cache = new FastConcurrentLruCache( + 100, + value => value.Text); + Assert.Throws(() => cache.Add(null)); + } + + [Test] + public void AddDenyTest3() + { + var cache = + new FastConcurrentLruCache( + 100, + value => value.Identifier); + Assert.Throws(() => cache.Add(new BadTestClass())); + } + + [Test] + public void RemoveDenyTest1() + { + var cache = + new FastConcurrentLruCache( + 100, + value => value.Text); + Assert.Throws(() => cache.Remove(null)); + } + + [Test] + public void RemoveDenyTest2() + { + var cache = + new FastConcurrentLruCache( + 100, + value => value.Text); + Assert.Throws(() => cache.RemoveKey(null)); + } + + [Test] + public void RemoveDenyTest3() + { + var cache = + new FastConcurrentLruCache( + 100, + value => value.Identifier); + BadTestClass test1 = new BadTestClass(); + Assert.Throws(() => cache.Remove(test1)); + } + + private static readonly bool canFinish = true; + + [Test] + public void SynchronizationTest() + { + globalCache = + new FastConcurrentLruCache( + 1000, + value => value.Text); + + using (new ThreadPoolThreadsIncreaser(20, 20)) { + var addThreads = new Task[10]; + var removeThreads = new Task[10]; + var cancellationTokenSource = new CancellationTokenSource(); + + for (int i = 0; i < 10; i++) { + addThreads[i] = new Task(() => AddItem(cancellationTokenSource.Token), cancellationTokenSource.Token); + removeThreads[i] = new Task(() => RemoveItem(cancellationTokenSource.Token), cancellationTokenSource.Token); + } + + try { + for (int i = 0; i < 10; i++) { + addThreads[i].Start(); + } + Thread.Sleep(10); + + for (int i = 0; i < 10; i++) { + removeThreads[i].Start(); + } + Thread.Sleep(200); + } + finally { + cancellationTokenSource.Cancel(); + Thread.Sleep(20); + } + } + + Assert.IsTrue(globalCache.Count >= 0); + globalCache = null; + } + + private void AddItem(CancellationToken cancellationToken) + { + int count = random.Next(100000); + int counter = 0; + bool whileCondition = (counter++) < 10 || !cancellationToken.IsCancellationRequested; + while (!cancellationToken.IsCancellationRequested) { + globalCache.Add(new TestClass("item " + count)); + count++; + } + cancellationToken.ThrowIfCancellationRequested(); + } + + private void RemoveItem(CancellationToken cancellationToken) + { + int counter = 0; + while (!cancellationToken.IsCancellationRequested) { + TestClass test = null; + foreach (TestClass testClass in globalCache) { + test = testClass; + break; + } + if (test != null) + globalCache.Remove(test); + } + cancellationToken.ThrowIfCancellationRequested(); + } + + private class ThreadPoolThreadsIncreaser : Disposable + { + private int previousWorkingThreadsCount; + private int previousIOThreadsCount; + + private static Func A; + private static Func B; + + private void Increase(int workingThreadsCount, int ioThreadsCount) + { + int minWorkingThreads; + int minIOTheads; + ThreadPool.GetMinThreads(out minWorkingThreads, out minIOTheads); + previousWorkingThreadsCount = minWorkingThreads; + previousIOThreadsCount = minIOTheads; + + ThreadPool.SetMinThreads(workingThreadsCount, ioThreadsCount); + } + + private static void Decrease(Func workingThreadsCountAcccessor, Func ioThreadsCountAcccessor) + { + ThreadPool.SetMinThreads(workingThreadsCountAcccessor(), ioThreadsCountAcccessor()); + } + + private int Aa() + { + return previousWorkingThreadsCount; + } + + private int Bb() + { + return previousIOThreadsCount; + } + + + public ThreadPoolThreadsIncreaser(int workingThreadsCount, int ioThreadsCount) + : base((disposing) => Decrease(A, B)) + { + Increase(workingThreadsCount, ioThreadsCount); + A = Aa; + B = Bb; + } + } + } +} diff --git a/Orm/Xtensive.Orm.Tests.Core/Caching/LruCacheTest.cs b/Orm/Xtensive.Orm.Tests.Core/Caching/LruCacheTest.cs index 87c8306c0c..262f054307 100644 --- a/Orm/Xtensive.Orm.Tests.Core/Caching/LruCacheTest.cs +++ b/Orm/Xtensive.Orm.Tests.Core/Caching/LruCacheTest.cs @@ -1,3 +1,7 @@ +// Copyright (C) 2021 Xtensive LLC. +// This code is distributed under MIT license terms. +// See the License.txt file in the project root for more information. + using System; using System.Threading; using System.Threading.Tasks; diff --git a/Orm/Xtensive.Orm/Caching/CacheBase.cs b/Orm/Xtensive.Orm/Caching/CacheBase.cs new file mode 100755 index 0000000000..7de2d26058 --- /dev/null +++ b/Orm/Xtensive.Orm/Caching/CacheBase.cs @@ -0,0 +1,56 @@ +// Copyright (C) 2007-2021 Xtensive LLC. +// This code is distributed under MIT license terms. +// See the License.txt file in the project root for more information. + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Diagnostics; +using Xtensive.Core; + +namespace Xtensive.Caching +{ + public abstract class CacheBase : ICache + { + /// + public virtual Converter KeyExtractor { [DebuggerStepThrough]get; protected set; } + + /// + public virtual TItem this[TKey key, bool markAsHit] => TryGetItem(key, markAsHit, out var item) ? item : default; + + /// + public abstract int Count { get; } + + /// + public abstract TItem Add(TItem item, bool replaceIfExists); + + /// + public virtual void Add(TItem item) => Add(item, true); + + /// + public virtual void Clear() => throw new NotImplementedException(); + + /// + public virtual bool ContainsKey(TKey key) => throw new NotImplementedException(); + + /// + public virtual IEnumerator GetEnumerator() => throw new NotImplementedException(); + + /// + public virtual void Remove(TItem item) + { + ArgumentValidator.EnsureArgumentNotNull(item, "item"); + RemoveKey(KeyExtractor(item)); + } + + /// + public abstract void RemoveKey(TKey key); + + /// + public abstract void RemoveKey(TKey key, bool removeCompletely); + + /// + public abstract bool TryGetItem(TKey key, bool markAsHit, out TItem item); + } +} + diff --git a/Orm/Xtensive.Orm/Caching/FastConcurrentLruCache{TKey, TItem}.cs b/Orm/Xtensive.Orm/Caching/FastConcurrentLruCache{TKey, TItem}.cs new file mode 100755 index 0000000000..f0ac9cd116 --- /dev/null +++ b/Orm/Xtensive.Orm/Caching/FastConcurrentLruCache{TKey, TItem}.cs @@ -0,0 +1,77 @@ +// Copyright (C) 2021 Xtensive LLC. +// This code is distributed under MIT license terms. +// See the License.txt file in the project root for more information. + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Diagnostics; +using BitFaster.Caching.Lru; +using Xtensive.Collections; +using Xtensive.Conversion; +using Xtensive.Core; + + +namespace Xtensive.Caching +{ + /// + /// A set of items limited by the maximal amount of memory it can use, or by any other measure. + /// Stores as many most frequently accessed items in memory as long as it is possible + /// while maintaining the total size of cached items less or equal to . + /// + /// The key of the item. + /// The type of the item to cache. + public class FastConcurrentLruCache : + CacheBase + { + private FastConcurrentLru imp; + + /// + public override int Count => imp.Count; + + /// + public long MaxSize { get; private set; } + + /// + public override void Clear() => //TODO: Change to imp.Clear() after updating BitFaster.Caching package to 1.0.4 + imp = new FastConcurrentLru((int)MaxSize); + + /// + public override bool TryGetItem(TKey key, bool markAsHit, out TItem item) => imp.TryGet(key, out item); + + /// + public override bool ContainsKey(TKey key) => imp.TryGet(key, out var _); + + /// + public override TItem Add(TItem item, bool replaceIfExists) + { + ArgumentValidator.EnsureArgumentNotNull(item, "item"); + var key = KeyExtractor(item); + if (replaceIfExists) { + imp.AddOrUpdate(key, item); + return item; + } + else { + return imp.GetOrAdd(key, _ => item); + } + } + + /// + public override void RemoveKey(TKey key) => imp.TryRemove(key); + + /// + public override void RemoveKey(TKey key, bool removeCompletely) => imp.TryRemove(key); + + /// + public override IEnumerator GetEnumerator() => throw new NotImplementedException(); + + public FastConcurrentLruCache(int maxSize, Converter keyExtractor) + { + if (maxSize <= 0) + ArgumentValidator.EnsureArgumentIsInRange(maxSize, 1, int.MaxValue, "maxSize"); + MaxSize = maxSize; + KeyExtractor = keyExtractor; + imp = new FastConcurrentLru(maxSize); + } + } +} diff --git a/Orm/Xtensive.Orm/Caching/InfiniteCache.cs b/Orm/Xtensive.Orm/Caching/InfiniteCache.cs index c0c1a3da7a..0d495331e8 100644 --- a/Orm/Xtensive.Orm/Caching/InfiniteCache.cs +++ b/Orm/Xtensive.Orm/Caching/InfiniteCache.cs @@ -21,37 +21,19 @@ namespace Xtensive.Caching /// The type of the key. /// The type of the item. public sealed class InfiniteCache: - ICache + CacheBase where TItem : class { private readonly Dictionary items; - private readonly Converter keyExtractor; /// - public Converter KeyExtractor - { - get { return keyExtractor; } - } - - /// - public int Count + public override int Count { get { return items.Count; } } /// - public TItem this[TKey key, bool markAsHit] { - get { - TItem item; - if (items.TryGetValue(key, out item)) - return item; - else - return null; - } - } - - /// - public bool TryGetItem(TKey key, bool markAsHit, out TItem item) + public override bool TryGetItem(TKey key, bool markAsHit, out TItem item) { return items.TryGetValue(key, out item); } @@ -63,19 +45,13 @@ public bool Contains(TItem item) } /// - public bool ContainsKey(TKey key) + public override bool ContainsKey(TKey key) { return items.ContainsKey(key); } /// - public void Add(TItem item) - { - Add(item, true); - } - - /// - public TItem Add(TItem item, bool replaceIfExists) + public override TItem Add(TItem item, bool replaceIfExists) { ArgumentValidator.EnsureArgumentNotNull(item, "item"); var key = KeyExtractor(item); @@ -89,52 +65,34 @@ public TItem Add(TItem item, bool replaceIfExists) } /// - public void Remove(TItem item) - { - ArgumentValidator.EnsureArgumentNotNull(item, "item"); - RemoveKey(KeyExtractor(item)); - } - - /// - public void RemoveKey(TKey key) + public override void RemoveKey(TKey key) { if (items.ContainsKey(key)) items.Remove(key); } /// - public void RemoveKey(TKey key, bool removeCompletely) + public override void RemoveKey(TKey key, bool removeCompletely) { RemoveKey(key); } /// - public void Clear() + public override void Clear() { items.Clear(); } - /// - public void Invalidate() - { - Clear(); - } - #region IEnumerable methods /// - public IEnumerator GetEnumerator() + public override IEnumerator GetEnumerator() { foreach (var pair in items) { yield return pair.Value; } } - IEnumerator IEnumerable.GetEnumerator() - { - return GetEnumerator(); - } - #endregion @@ -143,7 +101,7 @@ IEnumerator IEnumerable.GetEnumerator() /// /// Initializes new instance of this type. /// - /// property value. + /// property value. public InfiniteCache(Converter keyExtractor) : this(0, keyExtractor) { @@ -153,14 +111,14 @@ public InfiniteCache(Converter keyExtractor) /// Initializes new instance of this type. /// /// The capacity of cache. - /// property value. + /// property value. /// capacity is out of range. public InfiniteCache(int capacity, Converter keyExtractor) { ArgumentValidator.EnsureArgumentNotNull(keyExtractor, "keyExtractor"); if (capacity < 0) throw new ArgumentOutOfRangeException("capacity", capacity, Strings.ExArgumentValueMustBeGreaterThanOrEqualToZero); - this.keyExtractor = keyExtractor; + this.KeyExtractor = keyExtractor; items = new Dictionary(capacity); } } diff --git a/Orm/Xtensive.Orm/Caching/Interfaces/ICache.cs b/Orm/Xtensive.Orm/Caching/Interfaces/ICache.cs index 296f5a1ed1..d9aa76a80f 100644 --- a/Orm/Xtensive.Orm/Caching/Interfaces/ICache.cs +++ b/Orm/Xtensive.Orm/Caching/Interfaces/ICache.cs @@ -5,7 +5,10 @@ // Created: 2007.05.25 using System; +using System.Collections; using System.Collections.Generic; +using System.Diagnostics; +using Xtensive.Core; namespace Xtensive.Caching { @@ -34,7 +37,7 @@ public interface ICache : IEnumerable, IInvalidatable /// should be marked as hit. /// Item, if found; /// otherwise, . - TItem this[TKey key, bool markAsHit] { get; } + TItem this[TKey key, bool markAsHit] => TryGetItem(key, markAsHit, out var item) ? item : default; /// /// Tries to get cached item by its . @@ -57,7 +60,7 @@ public interface ICache : IEnumerable, IInvalidatable /// if cache contains the specified item; /// otherwise, . /// - bool Contains(TItem item); + bool Contains(TItem item) => ContainsKey(KeyExtractor(item)); /// /// Determines whether cache contains the item with specified key. @@ -69,12 +72,6 @@ public interface ICache : IEnumerable, IInvalidatable /// bool ContainsKey(TKey key); - /// - /// Adds a new item to the cache. If item with this key is already in cache - replaces is with new item. - /// - /// The item to add. - void Add(TItem item); - /// /// Adds a new item to the cache. /// @@ -83,12 +80,23 @@ public interface ICache : IEnumerable, IInvalidatable /// An existing, or a newly added item. TItem Add(TItem item, bool replaceIfExists); + /// + /// Adds a new item to the cache. If item with this key is already in cache - replaces is with new item. + /// + /// The item to add. + void Add(TItem item) => Add(item, true); + /// /// Removes the specified from the cache. /// /// The item to remove. - void Remove(TItem item); - + void Remove(TItem item) + { + ArgumentValidator.EnsureArgumentNotNull(item, "item"); + RemoveKey(KeyExtractor(item)); + } + + /// /// Removes the item with specified from the cache. /// @@ -106,5 +114,11 @@ public interface ICache : IEnumerable, IInvalidatable /// Clears the cache. /// void Clear(); + + void IInvalidatable.Invalidate() => Clear(); + + /// + [DebuggerStepThrough] + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); } -} \ No newline at end of file +} diff --git a/Orm/Xtensive.Orm/Caching/LruCache{TKey, TItem, TCached}.cs b/Orm/Xtensive.Orm/Caching/LruCache{TKey, TItem, TCached}.cs index 244313fc67..4ab9bd12b5 100644 --- a/Orm/Xtensive.Orm/Caching/LruCache{TKey, TItem, TCached}.cs +++ b/Orm/Xtensive.Orm/Caching/LruCache{TKey, TItem, TCached}.cs @@ -24,25 +24,18 @@ namespace Xtensive.Caching /// The type of the item to cache. /// The type of cached representation of the item. public class LruCache : - ICache, + CacheBase, IHasSize where TCached: IIdentified, IHasSize { private readonly long maxSize; private readonly TopDeque deque; - private readonly Converter keyExtractor; private readonly Biconverter cacheConverter; private readonly ICache chainedCache; private long size; #region Properites: KeyExtractor, CacheConverter, ChainedCache, MaxSize, Count, Size - /// - public Converter KeyExtractor { - [DebuggerStepThrough] - get { return keyExtractor; } - } - /// /// Gets the cache converter. /// @@ -66,7 +59,7 @@ public long MaxSize { } /// - public int Count { + public override int Count { [DebuggerStepThrough] get { return deque.Count; } } @@ -80,18 +73,7 @@ public long Size { #endregion /// - public TItem this[TKey key, bool markAsHit] { - get { - TItem item; - if (TryGetItem(key, markAsHit, out item)) - return item; - else - return default(TItem); - } - } - - /// - public virtual bool TryGetItem(TKey key, bool markAsHit, out TItem item) + public override bool TryGetItem(TKey key, bool markAsHit, out TItem item) { TCached cached; if (deque.TryGetValue(key, markAsHit, out cached)) { @@ -117,7 +99,7 @@ public bool Contains(TItem item) } /// - public virtual bool ContainsKey(TKey key) + public override bool ContainsKey(TKey key) { if (deque.Contains(key)) return true; @@ -129,13 +111,7 @@ public virtual bool ContainsKey(TKey key) #region Modification methods: Add, Remove, Clear /// - public void Add(TItem item) - { - Add(item, true); - } - - /// - public virtual TItem Add(TItem item, bool replaceIfExists) + public override TItem Add(TItem item, bool replaceIfExists) { ArgumentValidator.EnsureArgumentNotNull(item, "item"); var key = KeyExtractor(item); @@ -162,20 +138,13 @@ public virtual TItem Add(TItem item, bool replaceIfExists) } /// - public void Remove(TItem item) - { - ArgumentValidator.EnsureArgumentNotNull(item, "item"); - RemoveKey(KeyExtractor(item)); - } - - /// - public virtual void RemoveKey(TKey key) + public override void RemoveKey(TKey key) { RemoveKey(key, false); } /// - public void RemoveKey(TKey key, bool removeCompletely) + public override void RemoveKey(TKey key, bool removeCompletely) { TCached oldCached; if (deque.TryGetValue(key, out oldCached)) { @@ -192,7 +161,7 @@ public void RemoveKey(TKey key, bool removeCompletely) } /// - public virtual void Clear() + public override void Clear() { while (deque.Count > 0) { var cached = deque.PopBottom(); @@ -206,25 +175,12 @@ public virtual void Clear() Cleared(); } - /// - public void Invalidate() - { - Clear(); - } - #endregion #region IEnumerable<...> methods /// - [DebuggerStepThrough] - IEnumerator IEnumerable.GetEnumerator() - { - return GetEnumerator(); - } - - /// - public virtual IEnumerator GetEnumerator() + public override IEnumerator GetEnumerator() { foreach (TCached cachedItem in deque) yield return cacheConverter.ConvertBackward(cachedItem); @@ -247,7 +203,7 @@ protected virtual void Cleared() { } /// Initializes a new instance of this type. /// /// property value. - /// property value. + /// property value. public LruCache(long maxSize, Converter keyExtractor) : this(maxSize, keyExtractor, Biconverter.AsIs) { @@ -257,7 +213,7 @@ public LruCache(long maxSize, Converter keyExtractor) /// Initializes a new instance of this type. /// /// property value. - /// property value. + /// property value. /// property value. public LruCache(long maxSize, Converter keyExtractor, Biconverter cacheConverter) : this(maxSize, keyExtractor, cacheConverter, null) @@ -268,7 +224,7 @@ public LruCache(long maxSize, Converter keyExtractor, Biconverter /// property value. - /// property value. + /// property value. /// property value. public LruCache(long maxSize, Converter keyExtractor, ICache chainedCache) : this(maxSize, keyExtractor, Biconverter.AsIs, chainedCache) @@ -279,7 +235,7 @@ public LruCache(long maxSize, Converter keyExtractor, ICache /// property value. - /// property value. + /// property value. /// property value. /// property value. public LruCache(long maxSize, Converter keyExtractor, @@ -289,7 +245,7 @@ public LruCache(long maxSize, Converter keyExtractor, ArgumentValidator.EnsureArgumentIsInRange(maxSize, 1, long.MaxValue, "maxSize"); ArgumentValidator.EnsureArgumentNotNull(keyExtractor, "keyExtractor"); this.maxSize = maxSize; - this.keyExtractor = keyExtractor; + this.KeyExtractor = keyExtractor; this.cacheConverter = cacheConverter; this.chainedCache = chainedCache; // deque = new TopDeque(1 + (int) maxSize); diff --git a/Orm/Xtensive.Orm/Caching/LruCache{TKey, TItem}.cs b/Orm/Xtensive.Orm/Caching/LruCache{TKey, TItem}.cs index b25a69a9bf..a0e06a916c 100644 --- a/Orm/Xtensive.Orm/Caching/LruCache{TKey, TItem}.cs +++ b/Orm/Xtensive.Orm/Caching/LruCache{TKey, TItem}.cs @@ -23,24 +23,17 @@ namespace Xtensive.Caching /// The key of the item. /// The type of the item to cache. public class LruCache : - ICache, + CacheBase, IHasSize { private readonly long maxSize; private readonly TopDeque> deque; - private readonly Converter keyExtractor; private readonly Func sizeExtractor; private readonly ICache chainedCache; private long size; #region Properites: KeyExtractor, SizeExtractor, ChainedCache, MaxSize, Count, Size - /// - public Converter KeyExtractor { - [DebuggerStepThrough] - get { return keyExtractor; } - } - /// /// Gets the size extractor delegate. /// @@ -65,7 +58,7 @@ public long MaxSize { } /// - public int Count { + public override int Count { [DebuggerStepThrough] get { return deque.Count; } } @@ -79,18 +72,7 @@ public long Size { #endregion /// - public TItem this[TKey key, bool markAsHit] { - get { - TItem item; - if (TryGetItem(key, markAsHit, out item)) - return item; - else - return default(TItem); - } - } - - /// - public virtual bool TryGetItem(TKey key, bool markAsHit, out TItem item) + public override bool TryGetItem(TKey key, bool markAsHit, out TItem item) { KeyValuePair cached; if (deque.TryGetValue(key, markAsHit, out cached)) { @@ -108,15 +90,9 @@ public virtual bool TryGetItem(TKey key, bool markAsHit, out TItem item) } return false; } - + /// - public bool Contains(TItem item) - { - return ContainsKey(KeyExtractor(item)); - } - - /// - public virtual bool ContainsKey(TKey key) + public override bool ContainsKey(TKey key) { if (deque.Contains(key)) return true; @@ -128,13 +104,7 @@ public virtual bool ContainsKey(TKey key) #region Modification methods: TryAdd, Add, Remove, Clear /// - public void Add(TItem item) - { - Add(item, true); - } - - /// - public virtual TItem Add(TItem item, bool replaceIfExists) + public override TItem Add(TItem item, bool replaceIfExists) { ArgumentValidator.EnsureArgumentNotNull(item, "item"); var key = KeyExtractor(item); @@ -161,20 +131,13 @@ public virtual TItem Add(TItem item, bool replaceIfExists) } /// - public void Remove(TItem item) - { - ArgumentValidator.EnsureArgumentNotNull(item, "item"); - RemoveKey(KeyExtractor(item)); - } - - /// - public virtual void RemoveKey(TKey key) + public override void RemoveKey(TKey key) { RemoveKey(key, false); } /// - public void RemoveKey(TKey key, bool removeCompletely) + public override void RemoveKey(TKey key, bool removeCompletely) { KeyValuePair oldCached; if (deque.TryGetValue(key, out oldCached)) { @@ -191,7 +154,7 @@ public void RemoveKey(TKey key, bool removeCompletely) } /// - public virtual void Clear() + public override void Clear() { while (deque.Count > 0) { var cached = deque.PopBottom(); @@ -205,25 +168,12 @@ public virtual void Clear() Cleared(); } - /// - public void Invalidate() - { - Clear(); - } - #endregion #region IEnumerable<...> methods /// - [DebuggerStepThrough] - IEnumerator IEnumerable.GetEnumerator() - { - return GetEnumerator(); - } - - /// - public virtual IEnumerator GetEnumerator() + public override IEnumerator GetEnumerator() { foreach (KeyValuePair cachedItem in deque) yield return cachedItem.Value; @@ -246,7 +196,7 @@ protected virtual void Cleared() { } /// Initializes a new instance of this type. /// /// property value. - /// property value. + /// property value. public LruCache(long maxSize, Converter keyExtractor) : this(maxSize, keyExtractor, i => 1) { @@ -256,7 +206,7 @@ public LruCache(long maxSize, Converter keyExtractor) /// Initializes a new instance of this type. /// /// property value. - /// property value. + /// property value. /// property value. public LruCache(long maxSize, Converter keyExtractor, Func sizeExtractor) : this(maxSize, keyExtractor, sizeExtractor, null) @@ -267,7 +217,7 @@ public LruCache(long maxSize, Converter keyExtractor, Func /// property value. - /// property value. + /// property value. /// property value. public LruCache(long maxSize, Converter keyExtractor, ICache chainedCache) : this(maxSize, keyExtractor, i => 1, chainedCache) @@ -278,7 +228,7 @@ public LruCache(long maxSize, Converter keyExtractor, ICache /// property value. - /// property value. + /// property value. /// property value. /// property value. public LruCache(long maxSize, Converter keyExtractor, @@ -289,7 +239,7 @@ public LruCache(long maxSize, Converter keyExtractor, ArgumentValidator.EnsureArgumentNotNull(keyExtractor, "keyExtractor"); ArgumentValidator.EnsureArgumentNotNull(sizeExtractor, "sizeExtractor"); this.maxSize = maxSize; - this.keyExtractor = keyExtractor; + this.KeyExtractor = keyExtractor; this.sizeExtractor = sizeExtractor; this.chainedCache = chainedCache; // deque = new TopDeque>(1 + (int) maxSize); diff --git a/Orm/Xtensive.Orm/Caching/MfLruCache.cs b/Orm/Xtensive.Orm/Caching/MfLruCache.cs index 8b8aa86a9c..c220da06f3 100644 --- a/Orm/Xtensive.Orm/Caching/MfLruCache.cs +++ b/Orm/Xtensive.Orm/Caching/MfLruCache.cs @@ -22,7 +22,7 @@ namespace Xtensive.Caching /// The key of the item. /// The type of the item to cache. public class MfLruCache : - ICache, + CacheBase, IHasGarbage { /// @@ -42,7 +42,6 @@ public class MfLruCache : private readonly int mfuCapacity; private readonly int capacity; private readonly int efficiencyFactor; - private readonly Converter keyExtractor; private readonly ICache chainedCache; private Dictionary items; private int time; @@ -67,12 +66,6 @@ public CachedItem(TItem item) #region Properites: KeyExtractor, ChainedCache, LruCapacity, MfuCapacity, Capacity, EfficiencyFactor, Count, Size - /// - public Converter KeyExtractor { - [DebuggerStepThrough] - get { return keyExtractor; } - } - /// /// Gets chained cache. /// @@ -113,7 +106,7 @@ public int EfficiencyFactor { } /// - public int Count { + public override int Count { [DebuggerStepThrough] get { return items.Count; } } @@ -121,18 +114,7 @@ public int Count { #endregion /// - public TItem this[TKey key, bool markAsHit] { - get { - TItem item; - if (TryGetItem(key, markAsHit, out item)) - return item; - else - return default(TItem); - } - } - - /// - public virtual bool TryGetItem(TKey key, bool markAsHit, out TItem item) + public override bool TryGetItem(TKey key, bool markAsHit, out TItem item) { OnOperation(); CachedItem cached; @@ -157,13 +139,7 @@ public virtual bool TryGetItem(TKey key, bool markAsHit, out TItem item) } /// - public bool Contains(TItem item) - { - return ContainsKey(KeyExtractor(item)); - } - - /// - public virtual bool ContainsKey(TKey key) + public override bool ContainsKey(TKey key) { if (items.ContainsKey(key)) return true; @@ -175,13 +151,7 @@ public virtual bool ContainsKey(TKey key) #region Modification methods: Add, Remove, Clear /// - public void Add(TItem item) - { - Add(item, true); - } - - /// - public virtual TItem Add(TItem item, bool replaceIfExists) + public override TItem Add(TItem item, bool replaceIfExists) { ArgumentValidator.EnsureArgumentNotNull(item, "item"); OnOperation2(); @@ -205,20 +175,13 @@ public virtual TItem Add(TItem item, bool replaceIfExists) } /// - public void Remove(TItem item) - { - ArgumentValidator.EnsureArgumentNotNull(item, "item"); - RemoveKey(KeyExtractor(item)); - } - - /// - public virtual void RemoveKey(TKey key) + public override void RemoveKey(TKey key) { RemoveKey(key, false); } /// - public void RemoveKey(TKey key, bool removeCompletely) + public override void RemoveKey(TKey key, bool removeCompletely) { CachedItem cached; if (items.TryGetValue(key, out cached)) { @@ -234,7 +197,7 @@ public void RemoveKey(TKey key, bool removeCompletely) } /// - public virtual void Clear() + public override void Clear() { foreach (var pair in items) { var key = pair.Key; @@ -248,12 +211,6 @@ public virtual void Clear() Cleared(); } - /// - public void Invalidate() - { - Clear(); - } - /// public virtual void CollectGarbage() { @@ -351,14 +308,7 @@ public virtual void CollectGarbage() #region IEnumerable<...> methods /// - [DebuggerStepThrough] - IEnumerator IEnumerable.GetEnumerator() - { - return GetEnumerator(); - } - - /// - public virtual IEnumerator GetEnumerator() + public override IEnumerator GetEnumerator() { foreach (var pair in items) yield return pair.Value.Item; @@ -412,7 +362,7 @@ private void OnOperation2() /// /// The property value. /// The property value. - /// property value. + /// property value. public MfLruCache(int lruCapacity, int mfuCapacity, Converter keyExtractor) : this(lruCapacity, mfuCapacity, DefaultEfficiencyFactor, keyExtractor, null) { @@ -424,7 +374,7 @@ public MfLruCache(int lruCapacity, int mfuCapacity, Converter keyEx /// The property value. /// The property value. /// The property value. - /// property value. + /// property value. public MfLruCache(int lruCapacity, int mfuCapacity, int efficiencyFactor, Converter keyExtractor) : this(lruCapacity, mfuCapacity, efficiencyFactor, keyExtractor, null) @@ -436,7 +386,7 @@ public MfLruCache(int lruCapacity, int mfuCapacity, int efficiencyFactor, /// /// The property value. /// The property value. - /// property value. + /// property value. /// property value. public MfLruCache(int lruCapacity, int mfuCapacity, Converter keyExtractor, ICache chainedCache) @@ -450,7 +400,7 @@ public MfLruCache(int lruCapacity, int mfuCapacity, /// The property value. /// The property value. /// The property value. - /// property value. + /// property value. /// property value. public MfLruCache(int lruCapacity, int mfuCapacity, int efficiencyFactor, Converter keyExtractor, ICache chainedCache) @@ -466,7 +416,7 @@ public MfLruCache(int lruCapacity, int mfuCapacity, int efficiencyFactor, this.efficiencyFactor = efficiencyFactor; if (efficiencyFactor<0) timeShift = -efficiencyFactor-1; // Constant timeShift is defined - this.keyExtractor = keyExtractor; + this.KeyExtractor = keyExtractor; this.chainedCache = chainedCache; // items = new Dictionary(1 + capacity); items = new Dictionary(); diff --git a/Orm/Xtensive.Orm/Caching/ThreadSafeCache.cs b/Orm/Xtensive.Orm/Caching/ThreadSafeCache.cs index 85607fe8e5..a445a6690f 100644 --- a/Orm/Xtensive.Orm/Caching/ThreadSafeCache.cs +++ b/Orm/Xtensive.Orm/Caching/ThreadSafeCache.cs @@ -19,7 +19,7 @@ namespace Xtensive.Caching /// /// The type of the key. /// The type of the item. - public sealed class ThreadSafeCache : ICache + public sealed class ThreadSafeCache : CacheBase { private readonly ICache chainedCache; private readonly object syncRoot; @@ -37,7 +37,7 @@ public sealed class ThreadSafeCache : ICache #region IEnumerable /// - public IEnumerator GetEnumerator() + public override IEnumerator GetEnumerator() { lock (syncRoot) { var enumerator = chainedCache.GetEnumerator(); @@ -46,12 +46,6 @@ public IEnumerator GetEnumerator() } } - /// - IEnumerator IEnumerable.GetEnumerator() - { - return GetEnumerator(); - } - #endregion #region IInvalidatable @@ -69,13 +63,13 @@ public void Invalidate() #region ICache /// - public int Count { get { return chainedCache.Count; } } + public override int Count { get { return chainedCache.Count; } } /// - public Converter KeyExtractor { get { return chainedCache.KeyExtractor; } } + public override Converter KeyExtractor { get { return chainedCache.KeyExtractor; } } /// - public TItem this[TKey key, bool markAsHit] { + public override TItem this[TKey key, bool markAsHit] { get { lock (syncRoot) { return chainedCache[key, markAsHit]; @@ -84,7 +78,7 @@ public void Invalidate() } /// - public bool TryGetItem(TKey key, bool markAsHit, out TItem item) + public override bool TryGetItem(TKey key, bool markAsHit, out TItem item) { lock (syncRoot) { return chainedCache.TryGetItem(key, markAsHit, out item); @@ -100,7 +94,7 @@ public bool Contains(TItem item) } /// - public bool ContainsKey(TKey key) + public override bool ContainsKey(TKey key) { lock (syncRoot) { return chainedCache.ContainsKey(key); @@ -108,7 +102,7 @@ public bool ContainsKey(TKey key) } /// - public void Add(TItem item) + public override void Add(TItem item) { lock (syncRoot) { chainedCache.Add(item); @@ -116,7 +110,7 @@ public void Add(TItem item) } /// - public TItem Add(TItem item, bool replaceIfExists) + public override TItem Add(TItem item, bool replaceIfExists) { lock (syncRoot) { return chainedCache.Add(item, replaceIfExists); @@ -124,7 +118,7 @@ public TItem Add(TItem item, bool replaceIfExists) } /// - public void Remove(TItem item) + public override void Remove(TItem item) { lock (syncRoot) { chainedCache.Remove(item); @@ -132,7 +126,7 @@ public void Remove(TItem item) } /// - public void RemoveKey(TKey key) + public override void RemoveKey(TKey key) { lock (syncRoot) { chainedCache.RemoveKey(key); @@ -140,13 +134,13 @@ public void RemoveKey(TKey key) } /// - public void RemoveKey(TKey key, bool removeCompletely) + public override void RemoveKey(TKey key, bool removeCompletely) { RemoveKey(key); } /// - public void Clear() + public override void Clear() { lock (syncRoot) { chainedCache.Clear(); diff --git a/Orm/Xtensive.Orm/Caching/WeakCache.cs b/Orm/Xtensive.Orm/Caching/WeakCache.cs index cbd19b04d3..0f4596956a 100644 --- a/Orm/Xtensive.Orm/Caching/WeakCache.cs +++ b/Orm/Xtensive.Orm/Caching/WeakCache.cs @@ -24,7 +24,7 @@ namespace Xtensive.Caching /// The type of the item to cache. [SecuritySafeCritical] public class WeakCache : - ICache, + CacheBase, IHasGarbage, IDisposable where TItem : class @@ -36,19 +36,11 @@ public class WeakCache : protected const int NoGcCount = 1024; private readonly bool trackResurrection; - private readonly Converter keyExtractor; private Dictionary items; private int time; #region Properites: KeyExtractor, ChainedCache, TrackResurrection, EfficiencyFactor, Count, Size - /// - public Converter KeyExtractor - { - [DebuggerStepThrough] - get => keyExtractor; - } - /// /// Gets a value indicating whether this cache tracks resurrection. /// @@ -59,7 +51,7 @@ public bool TrackResurrection } /// - public int Count + public override int Count { [DebuggerStepThrough] get => items?.Count ?? 0; @@ -67,12 +59,9 @@ public int Count #endregion - /// - public TItem this[TKey key, bool markAsHit] => TryGetItem(key, markAsHit, out var item) ? item : null; - /// [SecuritySafeCritical] - public virtual bool TryGetItem(TKey key, bool markAsHit, out TItem item) + public override bool TryGetItem(TKey key, bool markAsHit, out TItem item) { RegisterOperation(1); if (items != null && items.TryGetValue(key, out var cached)) { @@ -90,19 +79,13 @@ public virtual bool TryGetItem(TKey key, bool markAsHit, out TItem item) } /// - public bool Contains(TItem item) => ContainsKey(KeyExtractor(item)); - - /// - public bool ContainsKey(TKey key) => TryGetItem(key, false, out var _); + public override bool ContainsKey(TKey key) => TryGetItem(key, false, out var _); #region Modification methods: Add, Remove, Clear - /// - public void Add(TItem item) => Add(item, true); - /// [SecuritySafeCritical] - public virtual TItem Add(TItem item, bool replaceIfExists) + public override TItem Add(TItem item, bool replaceIfExists) { ArgumentValidator.EnsureArgumentNotNull(item, nameof(item)); RegisterOperation(2); @@ -126,16 +109,9 @@ public virtual TItem Add(TItem item, bool replaceIfExists) return item; } - /// - public void Remove(TItem item) - { - ArgumentValidator.EnsureArgumentNotNull(item, nameof(item)); - RemoveKey(KeyExtractor(item)); - } - /// [SecuritySafeCritical] - public virtual void RemoveKey(TKey key) + public override void RemoveKey(TKey key) { if (items != null && items.Remove(key, out var cached) == true) { cached.Free(); @@ -143,11 +119,11 @@ public virtual void RemoveKey(TKey key) } /// - public void RemoveKey(TKey key, bool removeCompletely) => RemoveKey(key); + public override void RemoveKey(TKey key, bool removeCompletely) => RemoveKey(key); /// [SecuritySafeCritical] - public virtual void Clear() + public override void Clear() { if (items == null) { return; @@ -166,9 +142,6 @@ public virtual void Clear() } } - /// - public void Invalidate() => Clear(); - /// [SecuritySafeCritical] public virtual void CollectGarbage() @@ -214,11 +187,7 @@ public virtual void CollectGarbage() #region IEnumerable<...> methods /// - [DebuggerStepThrough] - IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); - - /// - public virtual IEnumerator GetEnumerator() + public override IEnumerator GetEnumerator() { foreach (var pair in items ?? Enumerable.Empty>()) { if (ExtractTarget(pair.Value) is TItem item) @@ -253,12 +222,12 @@ private void RegisterOperation(int weight) /// Initializes a new instance of this type. /// /// The property value. - /// property value. + /// property value. public WeakCache(bool trackResurrection, Converter keyExtractor) { ArgumentValidator.EnsureArgumentNotNull(keyExtractor, "keyExtractor"); this.trackResurrection = trackResurrection; - this.keyExtractor = keyExtractor; + this.KeyExtractor = keyExtractor; } // Dispose pattern diff --git a/Orm/Xtensive.Orm/Caching/WeakestCache.cs b/Orm/Xtensive.Orm/Caching/WeakestCache.cs index f482a8a9f4..1140b1c814 100644 --- a/Orm/Xtensive.Orm/Caching/WeakestCache.cs +++ b/Orm/Xtensive.Orm/Caching/WeakestCache.cs @@ -23,7 +23,7 @@ namespace Xtensive.Caching /// The type of the item to cache. [SecuritySafeCritical] public class WeakestCache : - ICache, + CacheBase, IHasGarbage, IDisposable where TKey : class @@ -38,7 +38,6 @@ public class WeakestCache : private const int GcOperationCost = 2; private readonly bool trackKeyResurrection; private readonly bool trackItemResurrection; - private readonly Converter keyExtractor; private Dictionary items; private int time; @@ -176,12 +175,6 @@ public WeakEntryEqualityComparer() #region Properites: KeyExtractor, TrackKeyResurrection, TrackItemResurrection, EfficiencyFactor, Count, Size - /// - public Converter KeyExtractor { - [DebuggerStepThrough] - get { return keyExtractor; } - } - /// /// Gets a value indicating whether this cache tracks key resurrection. /// @@ -199,51 +192,32 @@ public bool TrackItemResurrection { } /// - public int Count { + public override int Count { [DebuggerStepThrough] get { return items.Count; } } #endregion - /// - public TItem this[TKey key, bool markAsHit] { - get { - return GetItemByKeyInternal(key, markAsHit); - } - } - /// [SecuritySafeCritical] - public virtual bool TryGetItem(TKey key, bool markAsHit, out TItem item) + public override bool TryGetItem(TKey key, bool markAsHit, out TItem item) { item = GetItemByKeyInternal(key, markAsHit); return item!=null; } /// - public bool Contains(TItem item) - { - return ContainsKey(KeyExtractor(item)); - } - - /// - public bool ContainsKey(TKey key) + public override bool ContainsKey(TKey key) { return GetItemByKeyInternal(key, false)!=null; } #region Modification methods: Add, Remove, Clear - /// - public void Add(TItem item) - { - Add(item, true); - } - /// [SecuritySafeCritical] - public virtual TItem Add(TItem item, bool replaceIfExists) + public override TItem Add(TItem item, bool replaceIfExists) { ArgumentValidator.EnsureArgumentNotNull(item, "item"); var key = KeyExtractor(item); @@ -266,16 +240,9 @@ public virtual TItem Add(TItem item, bool replaceIfExists) return item; } - /// - public void Remove(TItem item) - { - ArgumentValidator.EnsureArgumentNotNull(item, "item"); - RemoveKey(KeyExtractor(item)); - } - /// [SecuritySafeCritical] - public virtual void RemoveKey(TKey key) + public override void RemoveKey(TKey key) { ArgumentValidator.EnsureArgumentNotNull(key, "key"); WeakEntry entry; @@ -286,14 +253,14 @@ public virtual void RemoveKey(TKey key) } /// - public void RemoveKey(TKey key, bool removeCompletely) + public override void RemoveKey(TKey key, bool removeCompletely) { RemoveKey(key); } /// [SecuritySafeCritical] - public virtual void Clear() + public override void Clear() { try { foreach (var pair in items) @@ -308,12 +275,6 @@ public virtual void Clear() } } - /// - public void Invalidate() - { - Clear(); - } - /// [SecuritySafeCritical] public virtual void CollectGarbage() @@ -356,16 +317,9 @@ public virtual void CollectGarbage() #region IEnumerable<...> methods - /// - [DebuggerStepThrough] - IEnumerator IEnumerable.GetEnumerator() - { - return GetEnumerator(); - } - /// [SecuritySafeCritical] - public virtual IEnumerator GetEnumerator() + public override IEnumerator GetEnumerator() { foreach (var pair in items) { var item = pair.Value.Value.Value; @@ -414,13 +368,13 @@ private TItem GetItemByKeyInternal(TKey key, bool markAsHit) /// /// The property value. /// The property value. - /// property value. + /// property value. public WeakestCache(bool trackKeyResurrection, bool trackItemResurrection, Converter keyExtractor) { ArgumentValidator.EnsureArgumentNotNull(keyExtractor, "keyExtractor"); this.trackKeyResurrection = trackKeyResurrection; this.trackItemResurrection = trackItemResurrection; - this.keyExtractor = keyExtractor; + this.KeyExtractor = keyExtractor; items = new Dictionary(1024, new WeakEntryEqualityComparer()); } diff --git a/Orm/Xtensive.Orm/Orm/Domain.cs b/Orm/Xtensive.Orm/Orm/Domain.cs index c291ca4e34..d6cb1ec86e 100644 --- a/Orm/Xtensive.Orm/Orm/Domain.cs +++ b/Orm/Xtensive.Orm/Orm/Domain.cs @@ -1,4 +1,4 @@ -// Copyright (C) 2007-2020 Xtensive LLC. +// Copyright (C) 2007-2021 Xtensive LLC. // This code is distributed under MIT license terms. // See the License.txt file in the project root for more information. // Created by: Dmitri Maximov @@ -121,7 +121,7 @@ public static Domain Demand() internal ConcurrentDictionary> PrefetchFieldDescriptorCache { get; private set; } - internal ICache> QueryCache { get; private set; } + internal ICache> QueryCache { get; private set; } internal ICache KeyCache { get; private set; } @@ -422,7 +422,7 @@ internal Domain(DomainConfiguration configuration, object upgradeContextCookie, KeyGenerators = new KeyGeneratorRegistry(); PrefetchFieldDescriptorCache = new ConcurrentDictionary>(); KeyCache = new LruCache(Configuration.KeyCacheSize, k => k); - QueryCache = new LruCache>(Configuration.QueryCacheSize, k => k.First); + QueryCache = new FastConcurrentLruCache>(Configuration.QueryCacheSize, k => k.First); PrefetchActionMap = new Dictionary>>(); Extensions = new ExtensionCollection(); UpgradeContextCookie = upgradeContextCookie; diff --git a/Orm/Xtensive.Orm/Orm/Internals/CompiledQueryRunner.cs b/Orm/Xtensive.Orm/Orm/Internals/CompiledQueryRunner.cs index db3de3b23d..ff5ebaf3d0 100644 --- a/Orm/Xtensive.Orm/Orm/Internals/CompiledQueryRunner.cs +++ b/Orm/Xtensive.Orm/Orm/Internals/CompiledQueryRunner.cs @@ -1,4 +1,4 @@ -// Copyright (C) 2012-2020 Xtensive LLC. +// Copyright (C) 2012-2021 Xtensive LLC. // This code is distributed under MIT license terms. // See the License.txt file in the project root for more information. // Created by: Denis Krjuchkov @@ -9,6 +9,7 @@ using System.Linq.Expressions; using System.Threading; using System.Threading.Tasks; +using Xtensive.Caching; using Xtensive.Core; using Xtensive.Orm.Linq; using Xtensive.Orm.Linq.Expressions.Visitors; @@ -196,25 +197,11 @@ private void AllocateParameterAndReplacer() }); } - private ParameterizedQuery GetCachedQuery() - { - var cache = domain.QueryCache; - lock (cache) { - return cache.TryGetItem(queryKey, true, out var item) - ? (ParameterizedQuery) item.Second - : null; - } - } + private ParameterizedQuery GetCachedQuery() => + domain.QueryCache.TryGetItem(queryKey, true, out var item) ? item.Second : null; - private void PutCachedQuery(ParameterizedQuery parameterizedQuery) - { - var cache = domain.QueryCache; - lock (cache) { - if (!cache.TryGetItem(queryKey, false, out _)) { - cache.Add(new Pair(queryKey, parameterizedQuery)); - } - } - } + private void PutCachedQuery(ParameterizedQuery parameterizedQuery) => + domain.QueryCache.Add(new Pair(queryKey, parameterizedQuery)); private ParameterContext CreateParameterContext(ParameterizedQuery query) { diff --git a/Orm/Xtensive.Orm/Orm/Internals/KeyFactory.cs b/Orm/Xtensive.Orm/Orm/Internals/KeyFactory.cs index 391f6eeebc..8e4f3487e2 100644 --- a/Orm/Xtensive.Orm/Orm/Internals/KeyFactory.cs +++ b/Orm/Xtensive.Orm/Orm/Internals/KeyFactory.cs @@ -5,6 +5,7 @@ // Created: 2009.10.09 using System; +using Xtensive.Caching; using Xtensive.Collections; using Xtensive.Core; using Xtensive.Reflection; diff --git a/Orm/Xtensive.Orm/Orm/Internals/Prefetch/PrefetchManager.cs b/Orm/Xtensive.Orm/Orm/Internals/Prefetch/PrefetchManager.cs index 52a9d30470..ad8a903ed3 100644 --- a/Orm/Xtensive.Orm/Orm/Internals/Prefetch/PrefetchManager.cs +++ b/Orm/Xtensive.Orm/Orm/Internals/Prefetch/PrefetchManager.cs @@ -84,7 +84,7 @@ public RootContainerCacheEntry(RootContainerCacheKey key, SortedDictionary graphContainers = new SetSlim(); - private readonly LruCache columnsCache; + private readonly ICache columnsCache; private readonly Fetcher fetcher; private readonly Session session; diff --git a/Orm/Xtensive.Orm/Xtensive.Orm.csproj b/Orm/Xtensive.Orm/Xtensive.Orm.csproj index 915adcf681..4431b7aabf 100644 --- a/Orm/Xtensive.Orm/Xtensive.Orm.csproj +++ b/Orm/Xtensive.Orm/Xtensive.Orm.csproj @@ -41,6 +41,7 @@ +