diff --git a/src/NHibernate.DomainModel/CustomPersister.cs b/src/NHibernate.DomainModel/CustomPersister.cs index a4f91c22644..ad4838dd37a 100644 --- a/src/NHibernate.DomainModel/CustomPersister.cs +++ b/src/NHibernate.DomainModel/CustomPersister.cs @@ -29,12 +29,20 @@ public partial class CustomPersister : IEntityPersister private static readonly bool[] Nullability = new bool[] { true }; private readonly ISessionFactoryImplementor factory; + [Obsolete] public CustomPersister(PersistentClass model, ICacheConcurrencyStrategy cache, ISessionFactoryImplementor factory, IMapping mapping) { this.factory = factory; } + //TODO 6.0: Uncomment + //public CustomPersister(PersistentClass model, Func cache, ISessionFactoryImplementor factory, + // IMapping mapping) + //{ + // this.factory = factory; + //} + #region IEntityPersister Members public ISessionFactoryImplementor Factory diff --git a/src/NHibernate.Test/Async/CacheTest/BatchableCacheFixture.cs b/src/NHibernate.Test/Async/CacheTest/BatchableCacheFixture.cs index 741992bf116..b4044554fcd 100644 --- a/src/NHibernate.Test/Async/CacheTest/BatchableCacheFixture.cs +++ b/src/NHibernate.Test/Async/CacheTest/BatchableCacheFixture.cs @@ -16,6 +16,8 @@ using NHibernate.Cfg; using NHibernate.Linq; using NHibernate.Multi; +using NHibernate.Persister.Collection; +using NHibernate.Persister.Entity; using NHibernate.Test.CacheTest.Caches; using NUnit.Framework; using Environment = NHibernate.Cfg.Environment; @@ -108,8 +110,8 @@ protected override void OnTearDown() public async Task MultipleGetReadOnlyCollectionTestAsync() { var persister = Sfi.GetCollectionPersister($"{typeof(ReadOnly).FullName}.Items"); - Assert.That(persister.Cache.Cache, Is.Not.Null); - Assert.That(persister.Cache.Cache, Is.TypeOf()); + Assert.That(persister.GetCache(null).Cache, Is.Not.Null); + Assert.That(persister.GetCache(null).Cache, Is.TypeOf()); var ids = new List(); using (var s = Sfi.OpenSession()) @@ -218,8 +220,8 @@ public async Task MultipleGetReadOnlyCollectionTestAsync() public async Task MultipleGetReadOnlyTestAsync() { var persister = Sfi.GetEntityPersister(typeof(ReadOnly).FullName); - Assert.That(persister.Cache.Cache, Is.Not.Null); - Assert.That(persister.Cache.Cache, Is.TypeOf()); + Assert.That(persister.GetCache(null).Cache, Is.Not.Null); + Assert.That(persister.GetCache(null).Cache, Is.TypeOf()); int[] getIds; int[] loadIds; @@ -380,8 +382,8 @@ public async Task MultipleGetReadOnlyTestAsync() public async Task MultipleGetReadOnlyItemTestAsync() { var persister = Sfi.GetEntityPersister(typeof(ReadOnlyItem).FullName); - Assert.That(persister.Cache.Cache, Is.Not.Null); - Assert.That(persister.Cache.Cache, Is.TypeOf()); + Assert.That(persister.GetCache(null).Cache, Is.Not.Null); + Assert.That(persister.GetCache(null).Cache, Is.TypeOf()); int[] getIds; int[] loadIds; @@ -542,9 +544,9 @@ public async Task MultipleGetReadOnlyItemTestAsync() public async Task MultiplePutReadWriteTestAsync() { var persister = Sfi.GetEntityPersister(typeof(ReadWrite).FullName); - Assert.That(persister.Cache.Cache, Is.Not.Null); - Assert.That(persister.Cache.Cache, Is.TypeOf()); - var cache = (BatchableCache) persister.Cache.Cache; + Assert.That(persister.GetCache(null).Cache, Is.Not.Null); + Assert.That(persister.GetCache(null).Cache, Is.TypeOf()); + var cache = (BatchableCache) persister.GetCache(null).Cache; var ids = new List(); await (cache.ClearAsync(CancellationToken.None)); @@ -592,9 +594,9 @@ public async Task MultiplePutReadWriteTestAsync() public async Task MultiplePutReadWriteItemTestAsync() { var persister = Sfi.GetCollectionPersister($"{typeof(ReadWrite).FullName}.Items"); - Assert.That(persister.Cache.Cache, Is.Not.Null); - Assert.That(persister.Cache.Cache, Is.TypeOf()); - var cache = (BatchableCache) persister.Cache.Cache; + Assert.That(persister.GetCache(null).Cache, Is.Not.Null); + Assert.That(persister.GetCache(null).Cache, Is.TypeOf()); + var cache = (BatchableCache) persister.GetCache(null).Cache; var ids = new List(); await (cache.ClearAsync(CancellationToken.None)); @@ -869,7 +871,7 @@ public async Task QueryCacheTestAsync() public async Task QueryEntityBatchCacheTestAsync(bool clearEntityCacheAfterQuery, CancellationToken cancellationToken = default(CancellationToken)) { var persister = Sfi.GetEntityPersister(typeof(ReadOnlyItem).FullName); - var cache = (BatchableCache) persister.Cache.Cache; + var cache = (BatchableCache) persister.GetCache(null).Cache; var queryCache = GetDefaultQueryCache(); Sfi.Statistics.Clear(); @@ -945,9 +947,9 @@ public async Task QueryCacheTestAsync() var persister = Sfi.GetEntityPersister(typeof(ReadOnly).FullName); var itemPersister = Sfi.GetEntityPersister(typeof(ReadOnlyItem).FullName); var collectionPersister = Sfi.GetCollectionPersister($"{typeof(ReadOnly).FullName}.Items"); - var cache = (BatchableCache) persister.Cache.Cache; - var itemCache = (BatchableCache) itemPersister.Cache.Cache; - var collectionCache = (BatchableCache) collectionPersister.Cache.Cache; + var cache = (BatchableCache) persister.GetCache(null).Cache; + var itemCache = (BatchableCache) itemPersister.GetCache(null).Cache; + var collectionCache = (BatchableCache) collectionPersister.GetCache(null).Cache; var queryCache = GetDefaultQueryCache(); int middleId; @@ -1089,8 +1091,8 @@ public async Task QueryCacheTestAsync() var persister = Sfi.GetEntityPersister(typeof(ReadOnlyItem).FullName); var parentPersister = Sfi.GetEntityPersister(typeof(ReadOnly).FullName); - var cache = (BatchableCache) persister.Cache.Cache; - var parentCache = (BatchableCache) parentPersister.Cache.Cache; + var cache = (BatchableCache) persister.GetCache(null).Cache; + var parentCache = (BatchableCache) parentPersister.GetCache(null).Cache; var queryCache = GetDefaultQueryCache(); int middleId; @@ -1217,7 +1219,7 @@ private async Task AssertMultipleCacheCallsAsync(IEnumerable loadI where TEntity : CacheEntity { var persister = Sfi.GetEntityPersister(typeof(TEntity).FullName); - var cache = (BatchableCache) persister.Cache.Cache; + var cache = (BatchableCache) persister.GetCache(null).Cache; await (cache.ClearAsync(cancellationToken)); if (cacheBeforeLoadFn != null) @@ -1283,7 +1285,7 @@ private void AssertEquivalent(List ids, int[][] expectedIdIndexes, List ids, int idIndex, int[][] fetchedIdIndexes, int[] putIdIndexes, Func cacheBeforeLoadFn = null, CancellationToken cancellationToken = default(CancellationToken)) { var persister = Sfi.GetCollectionPersister($"{typeof(ReadOnly).FullName}.Items"); - var cache = (BatchableCache) persister.Cache.Cache; + var cache = (BatchableCache) persister.GetCache(null).Cache; await (cache.ClearAsync(cancellationToken)); if (cacheBeforeLoadFn != null) diff --git a/src/NHibernate.Test/Async/CacheTest/BatchableCacheSubclassFixture.cs b/src/NHibernate.Test/Async/CacheTest/BatchableCacheSubclassFixture.cs index 7ac6d663aee..328660bcfb1 100644 --- a/src/NHibernate.Test/Async/CacheTest/BatchableCacheSubclassFixture.cs +++ b/src/NHibernate.Test/Async/CacheTest/BatchableCacheSubclassFixture.cs @@ -8,13 +8,12 @@ //------------------------------------------------------------------------------ -using System; -using System.Collections; using System.Collections.Generic; using System.Linq; using NHibernate.Cache; using NHibernate.Cfg; using NHibernate.DomainModel; +using NHibernate.Persister.Entity; using NHibernate.Test.CacheTest.Caches; using NUnit.Framework; @@ -91,14 +90,14 @@ protected override void OnTearDown() public async Task BatchableRootEntityTestAsync() { var persister = Sfi.GetEntityPersister(typeof(Foo).FullName); - Assert.That(persister.Cache.Cache, Is.Not.Null); - Assert.That(persister.Cache.Cache, Is.TypeOf()); - var fooCache = (BatchableCache) persister.Cache.Cache; + Assert.That(persister.GetCache(null).Cache, Is.Not.Null); + Assert.That(persister.GetCache(null).Cache, Is.TypeOf()); + var fooCache = (BatchableCache) persister.GetCache(null).Cache; persister = Sfi.GetEntityPersister(typeof(Bar).FullName); - Assert.That(persister.Cache.Cache, Is.Not.Null); - Assert.That(persister.Cache.Cache, Is.TypeOf()); - var barCache = (BatchableCache) persister.Cache.Cache; + Assert.That(persister.GetCache(null).Cache, Is.Not.Null); + Assert.That(persister.GetCache(null).Cache, Is.TypeOf()); + var barCache = (BatchableCache) persister.GetCache(null).Cache; Assert.That(barCache, Is.EqualTo(fooCache)); diff --git a/src/NHibernate.Test/Async/CacheTest/BuildCacheFixture.cs b/src/NHibernate.Test/Async/CacheTest/BuildCacheFixture.cs index d944b825c81..70f198c69a0 100644 --- a/src/NHibernate.Test/Async/CacheTest/BuildCacheFixture.cs +++ b/src/NHibernate.Test/Async/CacheTest/BuildCacheFixture.cs @@ -15,6 +15,8 @@ using NHibernate.Cache; using NHibernate.Cfg; using NHibernate.Engine; +using NHibernate.Persister.Collection; +using NHibernate.Persister.Entity; using NHibernate.Util; using NUnit.Framework; using Environment = NHibernate.Cfg.Environment; diff --git a/src/NHibernate.Test/Async/FilterTest/DynamicFilterTest.cs b/src/NHibernate.Test/Async/FilterTest/DynamicFilterTest.cs index 85853191c30..d9fcc732a56 100644 --- a/src/NHibernate.Test/Async/FilterTest/DynamicFilterTest.cs +++ b/src/NHibernate.Test/Async/FilterTest/DynamicFilterTest.cs @@ -15,6 +15,7 @@ using NHibernate.Cache; using NHibernate.Cache.Entry; using NHibernate.Criterion; +using NHibernate.Persister.Collection; using NHibernate.Transform; using NUnit.Framework; @@ -54,7 +55,7 @@ public async Task SecondLevelCachedCollectionsFilteringAsync() var sp = (Salesperson) await (session.LoadAsync(typeof(Salesperson), testData.steveId)); await (NHibernateUtil.InitializeAsync(sp.Orders)); Assert.IsTrue(persister.HasCache, "No cache for collection"); - cachedData = (CollectionCacheEntry) await (persister.Cache.Cache.GetAsync(cacheKey, CancellationToken.None)); + cachedData = (CollectionCacheEntry) await (persister.GetCache(null).Cache.GetAsync(cacheKey, CancellationToken.None)); Assert.IsNotNull(cachedData, "collection was not in cache"); } @@ -66,7 +67,7 @@ public async Task SecondLevelCachedCollectionsFilteringAsync() .UniqueResultAsync()); Assert.AreEqual(1, sp.Orders.Count, "Filtered-collection not bypassing 2L-cache"); - CollectionCacheEntry cachedData2 = (CollectionCacheEntry) await (persister.Cache.Cache.GetAsync(cacheKey, CancellationToken.None)); + CollectionCacheEntry cachedData2 = (CollectionCacheEntry) await (persister.GetCache(null).Cache.GetAsync(cacheKey, CancellationToken.None)); Assert.IsNotNull(cachedData2, "collection no longer in cache!"); Assert.AreSame(cachedData, cachedData2, "Different cache values!"); } diff --git a/src/NHibernate.Test/Async/LazyGroup/LazyGroupFixture.cs b/src/NHibernate.Test/Async/LazyGroup/LazyGroupFixture.cs index 149e7b001f7..007356b2d13 100644 --- a/src/NHibernate.Test/Async/LazyGroup/LazyGroupFixture.cs +++ b/src/NHibernate.Test/Async/LazyGroup/LazyGroupFixture.cs @@ -14,6 +14,7 @@ using System.Threading.Tasks; using NHibernate.Cache; using NHibernate.Cfg; +using NHibernate.Persister.Entity; using NUnit.Framework; namespace NHibernate.Test.LazyGroup @@ -135,7 +136,7 @@ public async Task TestGroupsAsync() public async Task TestCacheAsync() { var persister = Sfi.GetEntityPersister(typeof(Person).FullName); - var cache = (HashtableCache) persister.Cache.Cache; + var cache = (HashtableCache) persister.GetCache(null).Cache; await (cache.ClearAsync(CancellationToken.None)); using (var s = OpenSession()) @@ -171,7 +172,7 @@ public async Task TestCacheAsync() public async Task TestInitializeFromCacheAsync() { var persister = Sfi.GetEntityPersister(typeof(Person).FullName); - var cache = (HashtableCache) persister.Cache.Cache; + var cache = (HashtableCache) persister.GetCache(null).Cache; await (cache.ClearAsync(CancellationToken.None)); Sfi.Statistics.Clear(); diff --git a/src/NHibernate.Test/BulkManipulation/BulkOperationCleanupActionFixture.cs b/src/NHibernate.Test/BulkManipulation/BulkOperationCleanupActionFixture.cs index 0b5b8d2eadf..ce1a97b6e78 100644 --- a/src/NHibernate.Test/BulkManipulation/BulkOperationCleanupActionFixture.cs +++ b/src/NHibernate.Test/BulkManipulation/BulkOperationCleanupActionFixture.cs @@ -51,20 +51,20 @@ public void AfterTransactionCompletionProcess_EvictsFromCache(string querySpaces if (expectedEntityEvictionCount > 0) { - _factory.Received(1).EvictEntity(Arg.Is>(x => x.Count() == expectedEntityEvictionCount)); + _factory.Received(1).EvictEntity(Arg.Is>(x => x.Count() == expectedEntityEvictionCount), null); } else { - _factory.DidNotReceive().EvictEntity(Arg.Any>()); + _factory.DidNotReceive().EvictEntity(Arg.Any>(), null); } if (expectedCollectionEvictionCount > 0) { - _factory.Received(1).EvictCollection(Arg.Is>(x => x.Count() == expectedCollectionEvictionCount)); + _factory.Received(1).EvictCollection(Arg.Is>(x => x.Count() == expectedCollectionEvictionCount), null); } else { - _factory.DidNotReceive().EvictCollection(Arg.Any>()); + _factory.DidNotReceive().EvictCollection(Arg.Any>(), null); } } } diff --git a/src/NHibernate.Test/CacheTest/BatchableCacheFixture.cs b/src/NHibernate.Test/CacheTest/BatchableCacheFixture.cs index 093cddf61bf..b5382ae8e64 100644 --- a/src/NHibernate.Test/CacheTest/BatchableCacheFixture.cs +++ b/src/NHibernate.Test/CacheTest/BatchableCacheFixture.cs @@ -6,6 +6,8 @@ using NHibernate.Cfg; using NHibernate.Linq; using NHibernate.Multi; +using NHibernate.Persister.Collection; +using NHibernate.Persister.Entity; using NHibernate.Test.CacheTest.Caches; using NUnit.Framework; using Environment = NHibernate.Cfg.Environment; @@ -96,8 +98,8 @@ protected override void OnTearDown() public void MultipleGetReadOnlyCollectionTest() { var persister = Sfi.GetCollectionPersister($"{typeof(ReadOnly).FullName}.Items"); - Assert.That(persister.Cache.Cache, Is.Not.Null); - Assert.That(persister.Cache.Cache, Is.TypeOf()); + Assert.That(persister.GetCache(null).Cache, Is.Not.Null); + Assert.That(persister.GetCache(null).Cache, Is.TypeOf()); var ids = new List(); using (var s = Sfi.OpenSession()) @@ -206,8 +208,8 @@ public void MultipleGetReadOnlyCollectionTest() public void MultipleGetReadOnlyTest() { var persister = Sfi.GetEntityPersister(typeof(ReadOnly).FullName); - Assert.That(persister.Cache.Cache, Is.Not.Null); - Assert.That(persister.Cache.Cache, Is.TypeOf()); + Assert.That(persister.GetCache(null).Cache, Is.Not.Null); + Assert.That(persister.GetCache(null).Cache, Is.TypeOf()); int[] getIds; int[] loadIds; @@ -368,8 +370,8 @@ public void MultipleGetReadOnlyTest() public void MultipleGetReadOnlyItemTest() { var persister = Sfi.GetEntityPersister(typeof(ReadOnlyItem).FullName); - Assert.That(persister.Cache.Cache, Is.Not.Null); - Assert.That(persister.Cache.Cache, Is.TypeOf()); + Assert.That(persister.GetCache(null).Cache, Is.Not.Null); + Assert.That(persister.GetCache(null).Cache, Is.TypeOf()); int[] getIds; int[] loadIds; @@ -530,9 +532,9 @@ public void MultipleGetReadOnlyItemTest() public void MultiplePutReadWriteTest() { var persister = Sfi.GetEntityPersister(typeof(ReadWrite).FullName); - Assert.That(persister.Cache.Cache, Is.Not.Null); - Assert.That(persister.Cache.Cache, Is.TypeOf()); - var cache = (BatchableCache) persister.Cache.Cache; + Assert.That(persister.GetCache(null).Cache, Is.Not.Null); + Assert.That(persister.GetCache(null).Cache, Is.TypeOf()); + var cache = (BatchableCache) persister.GetCache(null).Cache; var ids = new List(); cache.Clear(); @@ -580,9 +582,9 @@ public void MultiplePutReadWriteTest() public void MultiplePutReadWriteItemTest() { var persister = Sfi.GetCollectionPersister($"{typeof(ReadWrite).FullName}.Items"); - Assert.That(persister.Cache.Cache, Is.Not.Null); - Assert.That(persister.Cache.Cache, Is.TypeOf()); - var cache = (BatchableCache) persister.Cache.Cache; + Assert.That(persister.GetCache(null).Cache, Is.Not.Null); + Assert.That(persister.GetCache(null).Cache, Is.TypeOf()); + var cache = (BatchableCache) persister.GetCache(null).Cache; var ids = new List(); cache.Clear(); @@ -857,7 +859,7 @@ public void QueryCacheTest() public void QueryEntityBatchCacheTest(bool clearEntityCacheAfterQuery) { var persister = Sfi.GetEntityPersister(typeof(ReadOnlyItem).FullName); - var cache = (BatchableCache) persister.Cache.Cache; + var cache = (BatchableCache) persister.GetCache(null).Cache; var queryCache = GetDefaultQueryCache(); Sfi.Statistics.Clear(); @@ -933,9 +935,9 @@ public void QueryFetchCollectionBatchCacheTest(bool clearEntityCacheAfterQuery, var persister = Sfi.GetEntityPersister(typeof(ReadOnly).FullName); var itemPersister = Sfi.GetEntityPersister(typeof(ReadOnlyItem).FullName); var collectionPersister = Sfi.GetCollectionPersister($"{typeof(ReadOnly).FullName}.Items"); - var cache = (BatchableCache) persister.Cache.Cache; - var itemCache = (BatchableCache) itemPersister.Cache.Cache; - var collectionCache = (BatchableCache) collectionPersister.Cache.Cache; + var cache = (BatchableCache) persister.GetCache(null).Cache; + var itemCache = (BatchableCache) itemPersister.GetCache(null).Cache; + var collectionCache = (BatchableCache) collectionPersister.GetCache(null).Cache; var queryCache = GetDefaultQueryCache(); int middleId; @@ -1077,8 +1079,8 @@ public void QueryFetchEntityBatchCacheTest(bool clearEntityCacheAfterQuery, bool var persister = Sfi.GetEntityPersister(typeof(ReadOnlyItem).FullName); var parentPersister = Sfi.GetEntityPersister(typeof(ReadOnly).FullName); - var cache = (BatchableCache) persister.Cache.Cache; - var parentCache = (BatchableCache) parentPersister.Cache.Cache; + var cache = (BatchableCache) persister.GetCache(null).Cache; + var parentCache = (BatchableCache) parentPersister.GetCache(null).Cache; var queryCache = GetDefaultQueryCache(); int middleId; @@ -1205,7 +1207,7 @@ private void AssertMultipleCacheCalls(IEnumerable loadIds, IReadO where TEntity : CacheEntity { var persister = Sfi.GetEntityPersister(typeof(TEntity).FullName); - var cache = (BatchableCache) persister.Cache.Cache; + var cache = (BatchableCache) persister.GetCache(null).Cache; cache.Clear(); if (cacheBeforeLoadFn != null) @@ -1271,7 +1273,7 @@ private void AssertEquivalent(List ids, int[][] expectedIdIndexes, List ids, int idIndex, int[][] fetchedIdIndexes, int[] putIdIndexes, Func cacheBeforeLoadFn = null) { var persister = Sfi.GetCollectionPersister($"{typeof(ReadOnly).FullName}.Items"); - var cache = (BatchableCache) persister.Cache.Cache; + var cache = (BatchableCache) persister.GetCache(null).Cache; cache.Clear(); if (cacheBeforeLoadFn != null) diff --git a/src/NHibernate.Test/CacheTest/BatchableCacheSubclassFixture.cs b/src/NHibernate.Test/CacheTest/BatchableCacheSubclassFixture.cs index f816113d5c2..360addc62d9 100644 --- a/src/NHibernate.Test/CacheTest/BatchableCacheSubclassFixture.cs +++ b/src/NHibernate.Test/CacheTest/BatchableCacheSubclassFixture.cs @@ -1,10 +1,9 @@ -using System; -using System.Collections; -using System.Collections.Generic; +using System.Collections.Generic; using System.Linq; using NHibernate.Cache; using NHibernate.Cfg; using NHibernate.DomainModel; +using NHibernate.Persister.Entity; using NHibernate.Test.CacheTest.Caches; using NUnit.Framework; @@ -80,14 +79,14 @@ protected override void OnTearDown() public void BatchableRootEntityTest() { var persister = Sfi.GetEntityPersister(typeof(Foo).FullName); - Assert.That(persister.Cache.Cache, Is.Not.Null); - Assert.That(persister.Cache.Cache, Is.TypeOf()); - var fooCache = (BatchableCache) persister.Cache.Cache; + Assert.That(persister.GetCache(null).Cache, Is.Not.Null); + Assert.That(persister.GetCache(null).Cache, Is.TypeOf()); + var fooCache = (BatchableCache) persister.GetCache(null).Cache; persister = Sfi.GetEntityPersister(typeof(Bar).FullName); - Assert.That(persister.Cache.Cache, Is.Not.Null); - Assert.That(persister.Cache.Cache, Is.TypeOf()); - var barCache = (BatchableCache) persister.Cache.Cache; + Assert.That(persister.GetCache(null).Cache, Is.Not.Null); + Assert.That(persister.GetCache(null).Cache, Is.TypeOf()); + var barCache = (BatchableCache) persister.GetCache(null).Cache; Assert.That(barCache, Is.EqualTo(fooCache)); diff --git a/src/NHibernate.Test/CacheTest/BuildCacheFixture.cs b/src/NHibernate.Test/CacheTest/BuildCacheFixture.cs index f0feea553c7..2068de52feb 100644 --- a/src/NHibernate.Test/CacheTest/BuildCacheFixture.cs +++ b/src/NHibernate.Test/CacheTest/BuildCacheFixture.cs @@ -5,6 +5,8 @@ using NHibernate.Cache; using NHibernate.Cfg; using NHibernate.Engine; +using NHibernate.Persister.Collection; +using NHibernate.Persister.Entity; using NHibernate.Util; using NUnit.Framework; using Environment = NHibernate.Cfg.Environment; @@ -41,16 +43,16 @@ public void CommonRegionHasOneUniqueCacheAndExpectedConcurrency(bool withPrefix) sfi = withPrefix ? BuildSessionFactory() : Sfi; var commonRegionCache = sfi.GetSecondLevelCacheRegion(fullRegion); var entityAName = typeof(EntityA).FullName; - var entityAConcurrencyCache = sfi.GetEntityPersister(entityAName).Cache; + var entityAConcurrencyCache = sfi.GetEntityPersister(entityAName).GetCache(null); var entityACache = entityAConcurrencyCache.Cache; var entityBName = typeof(EntityB).FullName; - var entityBConcurrencyCache = sfi.GetEntityPersister(entityBName).Cache; + var entityBConcurrencyCache = sfi.GetEntityPersister(entityBName).GetCache(null); var entityBCache = entityBConcurrencyCache.Cache; var relatedAConcurrencyCache = - sfi.GetCollectionPersister(StringHelper.Qualify(entityAName, nameof(EntityA.Related))).Cache; + sfi.GetCollectionPersister(StringHelper.Qualify(entityAName, nameof(EntityA.Related))).GetCache(null); var relatedACache = relatedAConcurrencyCache.Cache; var relatedBConcurrencyCache = - sfi.GetCollectionPersister(StringHelper.Qualify(entityBName, nameof(EntityB.Related))).Cache; + sfi.GetCollectionPersister(StringHelper.Qualify(entityBName, nameof(EntityB.Related))).GetCache(null); var relatedBCache = relatedBConcurrencyCache.Cache; var queryCache = sfi.GetQueryCache(region).Cache; Assert.Multiple( diff --git a/src/NHibernate.Test/FilterTest/DynamicFilterTest.cs b/src/NHibernate.Test/FilterTest/DynamicFilterTest.cs index 5e247ef72e1..1e04c03b6ac 100644 --- a/src/NHibernate.Test/FilterTest/DynamicFilterTest.cs +++ b/src/NHibernate.Test/FilterTest/DynamicFilterTest.cs @@ -5,6 +5,7 @@ using NHibernate.Cache; using NHibernate.Cache.Entry; using NHibernate.Criterion; +using NHibernate.Persister.Collection; using NHibernate.Transform; using NUnit.Framework; @@ -42,7 +43,7 @@ public void SecondLevelCachedCollectionsFiltering() var sp = (Salesperson) session.Load(typeof(Salesperson), testData.steveId); NHibernateUtil.Initialize(sp.Orders); Assert.IsTrue(persister.HasCache, "No cache for collection"); - cachedData = (CollectionCacheEntry) persister.Cache.Cache.Get(cacheKey); + cachedData = (CollectionCacheEntry) persister.GetCache(null).Cache.Get(cacheKey); Assert.IsNotNull(cachedData, "collection was not in cache"); } @@ -54,7 +55,7 @@ public void SecondLevelCachedCollectionsFiltering() .UniqueResult(); Assert.AreEqual(1, sp.Orders.Count, "Filtered-collection not bypassing 2L-cache"); - CollectionCacheEntry cachedData2 = (CollectionCacheEntry) persister.Cache.Cache.Get(cacheKey); + CollectionCacheEntry cachedData2 = (CollectionCacheEntry) persister.GetCache(null).Cache.Get(cacheKey); Assert.IsNotNull(cachedData2, "collection no longer in cache!"); Assert.AreSame(cachedData, cachedData2, "Different cache values!"); } diff --git a/src/NHibernate.Test/LazyGroup/LazyGroupFixture.cs b/src/NHibernate.Test/LazyGroup/LazyGroupFixture.cs index 82b51aa0c4a..022f83eb3c1 100644 --- a/src/NHibernate.Test/LazyGroup/LazyGroupFixture.cs +++ b/src/NHibernate.Test/LazyGroup/LazyGroupFixture.cs @@ -4,6 +4,7 @@ using System.Threading.Tasks; using NHibernate.Cache; using NHibernate.Cfg; +using NHibernate.Persister.Entity; using NUnit.Framework; namespace NHibernate.Test.LazyGroup @@ -124,7 +125,7 @@ public void TestUpdate(bool fetchBeforeUpdate) public void TestCache() { var persister = Sfi.GetEntityPersister(typeof(Person).FullName); - var cache = (HashtableCache) persister.Cache.Cache; + var cache = (HashtableCache) persister.GetCache(null).Cache; cache.Clear(); using (var s = OpenSession()) @@ -160,7 +161,7 @@ public void TestCache() public void TestInitializeFromCache() { var persister = Sfi.GetEntityPersister(typeof(Person).FullName); - var cache = (HashtableCache) persister.Cache.Cache; + var cache = (HashtableCache) persister.GetCache(null).Cache; cache.Clear(); Sfi.Statistics.Clear(); diff --git a/src/NHibernate.Test/NHSpecificTest/NH2568/UsageOfCustomCollectionPersisterTests.cs b/src/NHibernate.Test/NHSpecificTest/NH2568/UsageOfCustomCollectionPersisterTests.cs index de9e72cf3c7..720701d1fb5 100644 --- a/src/NHibernate.Test/NHSpecificTest/NH2568/UsageOfCustomCollectionPersisterTests.cs +++ b/src/NHibernate.Test/NHSpecificTest/NH2568/UsageOfCustomCollectionPersisterTests.cs @@ -47,6 +47,6 @@ public void BuildingSessionFactoryShouldNotThrows() public class MyCollectionPersister: OneToManyPersister { - public MyCollectionPersister(Mapping.Collection collection, ICacheConcurrencyStrategy cache, ISessionFactoryImplementor factory) : base(collection, cache, factory) {} + public MyCollectionPersister(Mapping.Collection collection, Func cacheByTenant, ISessionFactoryImplementor factory) : base(collection, cacheByTenant, factory) {} } } diff --git a/src/NHibernate/Action/BulkOperationCleanupAction.cs b/src/NHibernate/Action/BulkOperationCleanupAction.cs index fa52f0f878c..1e52aab5cc8 100644 --- a/src/NHibernate/Action/BulkOperationCleanupAction.cs +++ b/src/NHibernate/Action/BulkOperationCleanupAction.cs @@ -135,7 +135,7 @@ private void EvictCollectionRegions() { if (affectedCollectionRoles != null && affectedCollectionRoles.Any()) { - session.Factory.EvictCollection(affectedCollectionRoles); + session.Factory.EvictCollection(affectedCollectionRoles, session.GetTenantIdentifier()); } } @@ -143,7 +143,7 @@ private void EvictEntityRegions() { if (affectedEntityNames != null && affectedEntityNames.Any()) { - session.Factory.EvictEntity(affectedEntityNames); + session.Factory.EvictEntity(affectedEntityNames, session.GetTenantIdentifier()); } } diff --git a/src/NHibernate/Action/CollectionAction.cs b/src/NHibernate/Action/CollectionAction.cs index ca5be713bf6..bdbba4ad6aa 100644 --- a/src/NHibernate/Action/CollectionAction.cs +++ b/src/NHibernate/Action/CollectionAction.cs @@ -96,11 +96,8 @@ public virtual void BeforeExecutions() // earlier entity actions which actually updates // the database (this action is responsible for // second-level cache invalidation only) - if (persister.HasCache) - { - CacheKey ck = session.GenerateCacheKey(key, persister.KeyType, persister.Role); - softLock = persister.Cache.Lock(ck, null); - } + var ck = session.GetCacheAndKey(key, persister, out var cache); + softLock = cache?.Lock(ck, null); } /// Execute this action @@ -124,8 +121,8 @@ public virtual void BeforeExecutions() public virtual void ExecuteAfterTransactionCompletion(bool success) { - var ck = new CacheKey(key, persister.KeyType, persister.Role, Session.Factory); - persister.Cache.Release(ck, softLock); + var ck = Session.GetCacheAndKey(key, persister, out var cache); + cache.Release(ck, softLock); } #endregion @@ -137,11 +134,8 @@ public ISoftLock Lock protected internal void Evict() { - if (persister.HasCache) - { - CacheKey ck = session.GenerateCacheKey(key, persister.KeyType, persister.Role); - persister.Cache.Evict(ck); - } + CacheKey ck = session.GetCacheAndKey(key, persister, out var cache); + cache?.Evict(ck); } #region IComparable Members diff --git a/src/NHibernate/Action/CollectionUpdateAction.cs b/src/NHibernate/Action/CollectionUpdateAction.cs index 5b364554f9f..d5d4f5e5359 100644 --- a/src/NHibernate/Action/CollectionUpdateAction.cs +++ b/src/NHibernate/Action/CollectionUpdateAction.cs @@ -118,8 +118,7 @@ public override void ExecuteAfterTransactionCompletion(bool success) { // NH Different behavior: to support unlocking collections from the cache.(r3260) - CacheKey ck = Session.GenerateCacheKey(GetKey(), Persister.KeyType, Persister.Role); - + CacheKey ck = Session.GetCacheAndKey(GetKey(), Persister, out var cache); if (success) { // we can't disassemble a collection if it was uninitialized @@ -127,17 +126,17 @@ public override void ExecuteAfterTransactionCompletion(bool success) if (Collection.WasInitialized && Session.PersistenceContext.ContainsCollection(Collection)) { CollectionCacheEntry entry = CollectionCacheEntry.Create(Collection, Persister); - bool put = Persister.Cache.AfterUpdate(ck, entry, null, Lock); + bool put = cache.AfterUpdate(ck, entry, null, Lock); if (put && Session.Factory.Statistics.IsStatisticsEnabled) { - Session.Factory.StatisticsImplementor.SecondLevelCachePut(Persister.Cache.RegionName); + Session.Factory.StatisticsImplementor.SecondLevelCachePut(cache.RegionName); } } } else { - Persister.Cache.Release(ck, Lock); + cache.Release(ck, Lock); } } } diff --git a/src/NHibernate/Action/EntityDeleteAction.cs b/src/NHibernate/Action/EntityDeleteAction.cs index 10e1694e706..8f837da7652 100644 --- a/src/NHibernate/Action/EntityDeleteAction.cs +++ b/src/NHibernate/Action/EntityDeleteAction.cs @@ -54,16 +54,8 @@ public override void Execute() tmpVersion = persister.GetVersion(instance); } - CacheKey ck; - if (persister.HasCache) - { - ck = session.GenerateCacheKey(id, persister.IdentifierType, persister.RootEntityName); - sLock = persister.Cache.Lock(ck, version); - } - else - { - ck = null; - } + CacheKey ck = session.GetCacheAndKey(id, persister, out var cache); + sLock = cache?.Lock(ck, version); if (!isCascadeDeleteEnabled && !veto) { @@ -87,8 +79,7 @@ public override void Execute() persistenceContext.RemoveEntity(key); persistenceContext.RemoveProxy(key); - if (persister.HasCache) - persister.Cache.Evict(ck); + cache?.Evict(ck); PostDelete(); @@ -129,11 +120,8 @@ private bool PreDelete() protected override void AfterTransactionCompletionProcessImpl(bool success) { - if (Persister.HasCache) - { - CacheKey ck = Session.GenerateCacheKey(Id, Persister.IdentifierType, Persister.RootEntityName); - Persister.Cache.Release(ck, sLock); - } + var ck = Session.GetCacheAndKey(Id, Persister, out var cache); + cache?.Release(ck, sLock); if (success) { PostCommitDelete(); diff --git a/src/NHibernate/Action/EntityInsertAction.cs b/src/NHibernate/Action/EntityInsertAction.cs index 71250f48373..ce8d6e192a6 100644 --- a/src/NHibernate/Action/EntityInsertAction.cs +++ b/src/NHibernate/Action/EntityInsertAction.cs @@ -77,12 +77,13 @@ public override void Execute() CacheEntry ce = CacheEntry.Create(State, persister, version, session, instance); cacheEntry = persister.CacheEntryStructure.Structure(ce); - CacheKey ck = Session.GenerateCacheKey(id, persister.IdentifierType, persister.RootEntityName); - bool put = persister.Cache.Insert(ck, cacheEntry, version); + + CacheKey ck = Session.GetCacheAndKey(id, persister, out var cache); + bool put = cache.Insert(ck, cacheEntry, version); if (put && factory.Statistics.IsStatisticsEnabled) { - factory.StatisticsImplementor.SecondLevelCachePut(Persister.Cache.RegionName); + factory.StatisticsImplementor.SecondLevelCachePut(cache.RegionName); } } @@ -101,12 +102,13 @@ protected override void AfterTransactionCompletionProcessImpl(bool success) IEntityPersister persister = Persister; if (success && IsCachePutEnabled(persister)) { - CacheKey ck = Session.GenerateCacheKey(Id, persister.IdentifierType, persister.RootEntityName); - bool put = persister.Cache.AfterInsert(ck, cacheEntry, version); + + CacheKey ck = Session.GetCacheAndKey(Id, persister, out var cache); + bool put = cache.AfterInsert(ck, cacheEntry, version); if (put && Session.Factory.Statistics.IsStatisticsEnabled) { - Session.Factory.StatisticsImplementor.SecondLevelCachePut(Persister.Cache.RegionName); + Session.Factory.StatisticsImplementor.SecondLevelCachePut(cache.RegionName); } } if (success) diff --git a/src/NHibernate/Action/EntityUpdateAction.cs b/src/NHibernate/Action/EntityUpdateAction.cs index 8b3ded4b24d..6414b35aa87 100644 --- a/src/NHibernate/Action/EntityUpdateAction.cs +++ b/src/NHibernate/Action/EntityUpdateAction.cs @@ -67,12 +67,8 @@ public override void Execute() previousVersion = persister.GetVersion(instance); } - CacheKey ck = null; - if (persister.HasCache) - { - ck = session.GenerateCacheKey(id, persister.IdentifierType, persister.RootEntityName); - slock = persister.Cache.Lock(ck, previousVersion); - } + CacheKey ck = session.GetCacheAndKey(id, persister, out var cache); + slock = cache?.Lock(ck, previousVersion); if (!veto) { @@ -106,22 +102,22 @@ public override void Execute() entry.PostUpdate(instance, state, nextVersion); } - if (persister.HasCache) + if (cache != null) { if (persister.IsCacheInvalidationRequired || entry.Status != Status.Loaded) { - persister.Cache.Evict(ck); + cache.Evict(ck); } else { CacheEntry ce = CacheEntry.Create(state, persister, nextVersion, Session, instance); cacheEntry = persister.CacheEntryStructure.Structure(ce); - bool put = persister.Cache.Update(ck, cacheEntry, nextVersion, previousVersion); + bool put = cache.Update(ck, cacheEntry, nextVersion, previousVersion); if (put && factory.Statistics.IsStatisticsEnabled) { - factory.StatisticsImplementor.SecondLevelCachePut(Persister.Cache.RegionName); + factory.StatisticsImplementor.SecondLevelCachePut(cache.RegionName); } } } @@ -138,22 +134,22 @@ public override void Execute() protected override void AfterTransactionCompletionProcessImpl(bool success) { IEntityPersister persister = Persister; - if (persister.HasCache) + + CacheKey ck = Session.GetCacheAndKey(Id, persister, out var cache); + if (cache != null) { - CacheKey ck = Session.GenerateCacheKey(Id, persister.IdentifierType, persister.RootEntityName); - if (success && cacheEntry != null) { - bool put = persister.Cache.AfterUpdate(ck, cacheEntry, nextVersion, slock); + bool put = cache.AfterUpdate(ck, cacheEntry, nextVersion, slock); if (put && Session.Factory.Statistics.IsStatisticsEnabled) { - Session.Factory.StatisticsImplementor.SecondLevelCachePut(Persister.Cache.RegionName); + Session.Factory.StatisticsImplementor.SecondLevelCachePut(cache.RegionName); } } else { - persister.Cache.Release(ck, slock); + cache.Release(ck, slock); } } if (success) diff --git a/src/NHibernate/Async/Action/BulkOperationCleanupAction.cs b/src/NHibernate/Async/Action/BulkOperationCleanupAction.cs index 18d4553f24f..09a40b52999 100644 --- a/src/NHibernate/Async/Action/BulkOperationCleanupAction.cs +++ b/src/NHibernate/Async/Action/BulkOperationCleanupAction.cs @@ -76,7 +76,7 @@ private Task EvictCollectionRegionsAsync(CancellationToken cancellationToken) { if (affectedCollectionRoles != null && affectedCollectionRoles.Any()) { - return session.Factory.EvictCollectionAsync(affectedCollectionRoles, cancellationToken); + return session.Factory.EvictCollectionAsync(affectedCollectionRoles, session.GetTenantIdentifier(), cancellationToken); } return Task.CompletedTask; } @@ -96,7 +96,7 @@ private Task EvictEntityRegionsAsync(CancellationToken cancellationToken) { if (affectedEntityNames != null && affectedEntityNames.Any()) { - return session.Factory.EvictEntityAsync(affectedEntityNames, cancellationToken); + return session.Factory.EvictEntityAsync(affectedEntityNames, session.GetTenantIdentifier(), cancellationToken); } return Task.CompletedTask; } diff --git a/src/NHibernate/Async/Action/CollectionAction.cs b/src/NHibernate/Async/Action/CollectionAction.cs index 16a2a27460e..1ecdc0726b8 100644 --- a/src/NHibernate/Async/Action/CollectionAction.cs +++ b/src/NHibernate/Async/Action/CollectionAction.cs @@ -57,11 +57,8 @@ public virtual async Task BeforeExecutionsAsync(CancellationToken cancellationTo // earlier entity actions which actually updates // the database (this action is responsible for // second-level cache invalidation only) - if (persister.HasCache) - { - CacheKey ck = session.GenerateCacheKey(key, persister.KeyType, persister.Role); - softLock = await (persister.Cache.LockAsync(ck, null, cancellationToken)).ConfigureAwait(false); - } + var ck = session.GetCacheAndKey(key, persister, out var cache); + softLock = (cache == null ? null : await (cache.LockAsync(ck, null, cancellationToken)).ConfigureAwait(false)); } /// Execute this action @@ -69,18 +66,6 @@ public virtual async Task BeforeExecutionsAsync(CancellationToken cancellationTo public abstract Task ExecuteAsync(CancellationToken cancellationToken); public virtual Task ExecuteAfterTransactionCompletionAsync(bool success, CancellationToken cancellationToken) - { - if (cancellationToken.IsCancellationRequested) - { - return Task.FromCanceled(cancellationToken); - } - var ck = new CacheKey(key, persister.KeyType, persister.Role, Session.Factory); - return persister.Cache.ReleaseAsync(ck, softLock, cancellationToken); - } - - #endregion - - protected internal Task EvictAsync(CancellationToken cancellationToken) { if (cancellationToken.IsCancellationRequested) { @@ -88,17 +73,26 @@ protected internal Task EvictAsync(CancellationToken cancellationToken) } try { - if (persister.HasCache) - { - CacheKey ck = session.GenerateCacheKey(key, persister.KeyType, persister.Role); - return persister.Cache.EvictAsync(ck, cancellationToken); - } - return Task.CompletedTask; + var ck = Session.GetCacheAndKey(key, persister, out var cache); + return cache.ReleaseAsync(ck, softLock, cancellationToken); } catch (Exception ex) { return Task.FromException(ex); } } + + #endregion + + protected internal async Task EvictAsync(CancellationToken cancellationToken) + { + cancellationToken.ThrowIfCancellationRequested(); + CacheKey ck = session.GetCacheAndKey(key, persister, out var cache); + var evictTask = cache?.EvictAsync(ck, cancellationToken); + if (evictTask != null) + { + await (evictTask).ConfigureAwait(false); + } + } } } diff --git a/src/NHibernate/Async/Action/CollectionUpdateAction.cs b/src/NHibernate/Async/Action/CollectionUpdateAction.cs index 7a6fb0ab228..170d193f879 100644 --- a/src/NHibernate/Async/Action/CollectionUpdateAction.cs +++ b/src/NHibernate/Async/Action/CollectionUpdateAction.cs @@ -125,8 +125,7 @@ public override async Task ExecuteAfterTransactionCompletionAsync(bool success, cancellationToken.ThrowIfCancellationRequested(); // NH Different behavior: to support unlocking collections from the cache.(r3260) - CacheKey ck = Session.GenerateCacheKey(await (GetKeyAsync(cancellationToken)).ConfigureAwait(false), Persister.KeyType, Persister.Role); - + CacheKey ck = Session.GetCacheAndKey(await (GetKeyAsync(cancellationToken)).ConfigureAwait(false), Persister, out var cache); if (success) { // we can't disassemble a collection if it was uninitialized @@ -134,17 +133,17 @@ public override async Task ExecuteAfterTransactionCompletionAsync(bool success, if (Collection.WasInitialized && Session.PersistenceContext.ContainsCollection(Collection)) { CollectionCacheEntry entry = await (CollectionCacheEntry.CreateAsync(Collection, Persister, cancellationToken)).ConfigureAwait(false); - bool put = await (Persister.Cache.AfterUpdateAsync(ck, entry, null, Lock, cancellationToken)).ConfigureAwait(false); + bool put = await (cache.AfterUpdateAsync(ck, entry, null, Lock, cancellationToken)).ConfigureAwait(false); if (put && Session.Factory.Statistics.IsStatisticsEnabled) { - Session.Factory.StatisticsImplementor.SecondLevelCachePut(Persister.Cache.RegionName); + Session.Factory.StatisticsImplementor.SecondLevelCachePut(cache.RegionName); } } } else { - await (Persister.Cache.ReleaseAsync(ck, Lock, cancellationToken)).ConfigureAwait(false); + await (cache.ReleaseAsync(ck, Lock, cancellationToken)).ConfigureAwait(false); } } } diff --git a/src/NHibernate/Async/Action/EntityDeleteAction.cs b/src/NHibernate/Async/Action/EntityDeleteAction.cs index b177616afc3..89e603b48a9 100644 --- a/src/NHibernate/Async/Action/EntityDeleteAction.cs +++ b/src/NHibernate/Async/Action/EntityDeleteAction.cs @@ -49,16 +49,8 @@ public override async Task ExecuteAsync(CancellationToken cancellationToken) tmpVersion = persister.GetVersion(instance); } - CacheKey ck; - if (persister.HasCache) - { - ck = session.GenerateCacheKey(id, persister.IdentifierType, persister.RootEntityName); - sLock = await (persister.Cache.LockAsync(ck, version, cancellationToken)).ConfigureAwait(false); - } - else - { - ck = null; - } + CacheKey ck = session.GetCacheAndKey(id, persister, out var cache); + sLock = (cache == null ? null : await (cache.LockAsync(ck, version, cancellationToken)).ConfigureAwait(false)); if (!isCascadeDeleteEnabled && !veto) { @@ -82,8 +74,15 @@ public override async Task ExecuteAsync(CancellationToken cancellationToken) persistenceContext.RemoveEntity(key); persistenceContext.RemoveProxy(key); - if (persister.HasCache) - await (persister.Cache.EvictAsync(ck, cancellationToken)).ConfigureAwait(false); + var evictTask = cache?.EvictAsync(ck, cancellationToken); + + if (evictTask != null) + + { + + await (evictTask).ConfigureAwait(false); + + } await (PostDeleteAsync(cancellationToken)).ConfigureAwait(false); @@ -127,10 +126,11 @@ private async Task PreDeleteAsync(CancellationToken cancellationToken) protected override async Task AfterTransactionCompletionProcessImplAsync(bool success, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); - if (Persister.HasCache) + var ck = Session.GetCacheAndKey(Id, Persister, out var cache); + var releaseTask = cache?.ReleaseAsync(ck, sLock, cancellationToken); + if (releaseTask != null) { - CacheKey ck = Session.GenerateCacheKey(Id, Persister.IdentifierType, Persister.RootEntityName); - await (Persister.Cache.ReleaseAsync(ck, sLock, cancellationToken)).ConfigureAwait(false); + await (releaseTask).ConfigureAwait(false); } if (success) { diff --git a/src/NHibernate/Async/Action/EntityInsertAction.cs b/src/NHibernate/Async/Action/EntityInsertAction.cs index dced95776c3..c2966df0ada 100644 --- a/src/NHibernate/Async/Action/EntityInsertAction.cs +++ b/src/NHibernate/Async/Action/EntityInsertAction.cs @@ -73,12 +73,13 @@ public override async Task ExecuteAsync(CancellationToken cancellationToken) CacheEntry ce = await (CacheEntry.CreateAsync(State, persister, version, session, instance, cancellationToken)).ConfigureAwait(false); cacheEntry = persister.CacheEntryStructure.Structure(ce); - CacheKey ck = Session.GenerateCacheKey(id, persister.IdentifierType, persister.RootEntityName); - bool put = persister.Cache.Insert(ck, cacheEntry, version); + + CacheKey ck = Session.GetCacheAndKey(id, persister, out var cache); + bool put = cache.Insert(ck, cacheEntry, version); if (put && factory.Statistics.IsStatisticsEnabled) { - factory.StatisticsImplementor.SecondLevelCachePut(Persister.Cache.RegionName); + factory.StatisticsImplementor.SecondLevelCachePut(cache.RegionName); } } @@ -98,12 +99,13 @@ protected override async Task AfterTransactionCompletionProcessImplAsync(bool su IEntityPersister persister = Persister; if (success && IsCachePutEnabled(persister)) { - CacheKey ck = Session.GenerateCacheKey(Id, persister.IdentifierType, persister.RootEntityName); - bool put = await (persister.Cache.AfterInsertAsync(ck, cacheEntry, version, cancellationToken)).ConfigureAwait(false); + + CacheKey ck = Session.GetCacheAndKey(Id, persister, out var cache); + bool put = await (cache.AfterInsertAsync(ck, cacheEntry, version, cancellationToken)).ConfigureAwait(false); if (put && Session.Factory.Statistics.IsStatisticsEnabled) { - Session.Factory.StatisticsImplementor.SecondLevelCachePut(Persister.Cache.RegionName); + Session.Factory.StatisticsImplementor.SecondLevelCachePut(cache.RegionName); } } if (success) diff --git a/src/NHibernate/Async/Action/EntityUpdateAction.cs b/src/NHibernate/Async/Action/EntityUpdateAction.cs index 89b065f40aa..b9234c75d76 100644 --- a/src/NHibernate/Async/Action/EntityUpdateAction.cs +++ b/src/NHibernate/Async/Action/EntityUpdateAction.cs @@ -52,12 +52,8 @@ public override async Task ExecuteAsync(CancellationToken cancellationToken) previousVersion = persister.GetVersion(instance); } - CacheKey ck = null; - if (persister.HasCache) - { - ck = session.GenerateCacheKey(id, persister.IdentifierType, persister.RootEntityName); - slock = await (persister.Cache.LockAsync(ck, previousVersion, cancellationToken)).ConfigureAwait(false); - } + CacheKey ck = session.GetCacheAndKey(id, persister, out var cache); + slock = (cache == null ? null : await (cache.LockAsync(ck, previousVersion, cancellationToken)).ConfigureAwait(false)); if (!veto) { @@ -91,22 +87,22 @@ public override async Task ExecuteAsync(CancellationToken cancellationToken) entry.PostUpdate(instance, state, nextVersion); } - if (persister.HasCache) + if (cache != null) { if (persister.IsCacheInvalidationRequired || entry.Status != Status.Loaded) { - await (persister.Cache.EvictAsync(ck, cancellationToken)).ConfigureAwait(false); + await (cache.EvictAsync(ck, cancellationToken)).ConfigureAwait(false); } else { CacheEntry ce = await (CacheEntry.CreateAsync(state, persister, nextVersion, Session, instance, cancellationToken)).ConfigureAwait(false); cacheEntry = persister.CacheEntryStructure.Structure(ce); - bool put = await (persister.Cache.UpdateAsync(ck, cacheEntry, nextVersion, previousVersion, cancellationToken)).ConfigureAwait(false); + bool put = await (cache.UpdateAsync(ck, cacheEntry, nextVersion, previousVersion, cancellationToken)).ConfigureAwait(false); if (put && factory.Statistics.IsStatisticsEnabled) { - factory.StatisticsImplementor.SecondLevelCachePut(Persister.Cache.RegionName); + factory.StatisticsImplementor.SecondLevelCachePut(cache.RegionName); } } } @@ -124,22 +120,22 @@ protected override async Task AfterTransactionCompletionProcessImplAsync(bool su { cancellationToken.ThrowIfCancellationRequested(); IEntityPersister persister = Persister; - if (persister.HasCache) + + CacheKey ck = Session.GetCacheAndKey(Id, persister, out var cache); + if (cache != null) { - CacheKey ck = Session.GenerateCacheKey(Id, persister.IdentifierType, persister.RootEntityName); - if (success && cacheEntry != null) { - bool put = await (persister.Cache.AfterUpdateAsync(ck, cacheEntry, nextVersion, slock, cancellationToken)).ConfigureAwait(false); + bool put = await (cache.AfterUpdateAsync(ck, cacheEntry, nextVersion, slock, cancellationToken)).ConfigureAwait(false); if (put && Session.Factory.Statistics.IsStatisticsEnabled) { - Session.Factory.StatisticsImplementor.SecondLevelCachePut(Persister.Cache.RegionName); + Session.Factory.StatisticsImplementor.SecondLevelCachePut(cache.RegionName); } } else { - await (persister.Cache.ReleaseAsync(ck, slock, cancellationToken)).ConfigureAwait(false); + await (cache.ReleaseAsync(ck, slock, cancellationToken)).ConfigureAwait(false); } } if (success) diff --git a/src/NHibernate/Async/Engine/BatchFetchQueue.cs b/src/NHibernate/Async/Engine/BatchFetchQueue.cs index 4500f7eca55..5d3670458a2 100644 --- a/src/NHibernate/Async/Engine/BatchFetchQueue.cs +++ b/src/NHibernate/Async/Engine/BatchFetchQueue.cs @@ -39,7 +39,14 @@ public Task GetCollectionBatchAsync(ICollectionPersister collectionPer { return Task.FromCanceled(cancellationToken); } - return GetCollectionBatchAsync(collectionPersister, id, batchSize, true, null, cancellationToken); + try + { + return GetCollectionBatchAsync(collectionPersister, id, batchSize, null, collectionPersister.GetCache(null), cancellationToken); + } + catch (Exception ex) + { + return Task.FromException(ex); + } } /// @@ -48,12 +55,12 @@ public Task GetCollectionBatchAsync(ICollectionPersister collectionPer /// The persister for the collection role. /// A key that must be included in the batch fetch /// the maximum number of keys to return - /// Whether to check the cache for uninitialized collection keys. /// An array that will be filled with collection entries if set. + /// Will check cache if not null is provided /// A cancellation token that can be used to cancel the work /// An array of collection keys, of length (padded with nulls) - internal async Task GetCollectionBatchAsync(ICollectionPersister collectionPersister, object key, int batchSize, bool checkCache, - CollectionEntry[] collectionEntries, CancellationToken cancellationToken) + internal async Task GetCollectionBatchAsync(ICollectionPersister collectionPersister, object key, int batchSize, + CollectionEntry[] collectionEntries, ICacheConcurrencyStrategy cache, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); var keys = new object[batchSize]; @@ -65,8 +72,9 @@ internal async Task GetCollectionBatchAsync(ICollectionPersister colle // List of collection entries that haven't been checked for their existance in the cache. Besides the collection entry, // the index where the entry was found is also stored in order to correctly order the returning keys. var collectionKeys = new List, int>>(batchSize); - var batchableCache = collectionPersister.Cache?.GetCacheBase(); - + var batchableCache = cache?.GetCacheBase(); + bool checkCache = batchableCache != null; + if (!batchLoadableCollections.TryGetValue(collectionPersister.Role, out var map)) { return keys; @@ -165,7 +173,7 @@ async Task CheckCacheAndProcessResultAsync() } keyIndex = index; } - else if (!checkCache || batchableCache == null) + else if (!checkCache) { if (index < map.Count && (!keyIndex.HasValue || index < keyIndex.Value)) { @@ -230,7 +238,14 @@ public Task GetEntityBatchAsync(IEntityPersister persister, object id, { return Task.FromCanceled(cancellationToken); } - return GetEntityBatchAsync(persister, id, batchSize, true, cancellationToken); + try + { + return GetEntityBatchAsync(persister, id, batchSize, persister.GetCache(null), cancellationToken); + } + catch (Exception ex) + { + return Task.FromException(ex); + } } /// @@ -241,10 +256,10 @@ public Task GetEntityBatchAsync(IEntityPersister persister, object id, /// The persister for the entities being loaded. /// The identifier of the entity currently demanding load. /// The maximum number of keys to return - /// Whether to check the cache for uninitialized keys. + /// Cache to check or null to skip cache check /// A cancellation token that can be used to cancel the work /// An array of identifiers, of length (possibly padded with nulls) - internal async Task GetEntityBatchAsync(IEntityPersister persister, object id, int batchSize, bool checkCache, CancellationToken cancellationToken) + internal async Task GetEntityBatchAsync(IEntityPersister persister, object id, int batchSize, ICacheConcurrencyStrategy cache, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); var ids = new object[batchSize]; @@ -257,7 +272,8 @@ internal async Task GetEntityBatchAsync(IEntityPersister persister, ob // the index where the key was found is also stored in order to correctly order the returning keys. var entityKeys = new List>(batchSize); // If there is a cache, obsolete or not, batchableCache will not be null. - var batchableCache = persister.Cache?.GetCacheBase(); + var batchableCache = cache?.GetCacheBase(); + var checkCache = batchableCache != null; if (!batchLoadableEntityKeys.TryGetValue(persister.EntityName, out var set)) { @@ -336,7 +352,7 @@ async Task CheckCacheAndProcessResultAsync() { idIndex = index; } - else if (!checkCache || batchableCache == null) + else if (!checkCache) { if (index < set.Count && (!idIndex.HasValue || index < idIndex.Value)) { diff --git a/src/NHibernate/Async/Engine/ISessionImplementor.cs b/src/NHibernate/Async/Engine/ISessionImplementor.cs index d5108c3e8f8..2e517cc277e 100644 --- a/src/NHibernate/Async/Engine/ISessionImplementor.cs +++ b/src/NHibernate/Async/Engine/ISessionImplementor.cs @@ -21,6 +21,7 @@ using NHibernate.Impl; using NHibernate.Loader.Custom; using NHibernate.Multi; +using NHibernate.Persister.Collection; using NHibernate.Persister.Entity; using NHibernate.Transaction; using NHibernate.Type; diff --git a/src/NHibernate/Async/Engine/Loading/CollectionLoadContext.cs b/src/NHibernate/Async/Engine/Loading/CollectionLoadContext.cs index 1dede203731..c2e30e2c231 100644 --- a/src/NHibernate/Async/Engine/Loading/CollectionLoadContext.cs +++ b/src/NHibernate/Async/Engine/Loading/CollectionLoadContext.cs @@ -280,7 +280,7 @@ private async Task AddCollectionToCacheAsync(LoadingCollectionEntry lce, ICollec } CollectionCacheEntry entry = await (CollectionCacheEntry.CreateAsync(lce.Collection, persister, cancellationToken)).ConfigureAwait(false); - CacheKey cacheKey = session.GenerateCacheKey(lce.Key, persister.KeyType, persister.Role); + var cacheKey = session.GetCacheAndKey(lce.Key, persister, out var cache); if (persister.GetBatchSize() > 1) { @@ -294,13 +294,13 @@ private async Task AddCollectionToCacheAsync(LoadingCollectionEntry lce, ICollec } else { - bool put = await (persister.Cache.PutAsync(cacheKey, persister.CacheEntryStructure.Structure(entry), + bool put = await (cache.PutAsync(cacheKey, persister.CacheEntryStructure.Structure(entry), session.Timestamp, version, versionComparator, factory.Settings.IsMinimalPutsEnabled && session.CacheMode != CacheMode.Refresh, cancellationToken)).ConfigureAwait(false); if (put && factory.Statistics.IsStatisticsEnabled) { - factory.StatisticsImplementor.SecondLevelCachePut(persister.Cache.RegionName); + factory.StatisticsImplementor.SecondLevelCachePut(cache.RegionName); } } } diff --git a/src/NHibernate/Async/Engine/TwoPhaseLoad.cs b/src/NHibernate/Async/Engine/TwoPhaseLoad.cs index 65342639cea..63f33594d34 100644 --- a/src/NHibernate/Async/Engine/TwoPhaseLoad.cs +++ b/src/NHibernate/Async/Engine/TwoPhaseLoad.cs @@ -124,7 +124,8 @@ internal static async Task InitializeEntityAsync(object entity, bool readOnly, I object version = Versioning.GetVersion(hydratedState, persister); CacheEntry entry = await (CacheEntry.CreateAsync(hydratedState, persister, version, session, entity, cancellationToken)).ConfigureAwait(false); - CacheKey cacheKey = session.GenerateCacheKey(id, persister.IdentifierType, persister.RootEntityName); + + CacheKey cacheKey = session.GetCacheAndKey(id, persister, out var cache); if (cacheBatchingHandler != null && persister.IsBatchLoadable) { @@ -140,13 +141,13 @@ internal static async Task InitializeEntityAsync(object entity, bool readOnly, I else { bool put = - await (persister.Cache.PutAsync(cacheKey, persister.CacheEntryStructure.Structure(entry), session.Timestamp, version, + await (cache.PutAsync(cacheKey, persister.CacheEntryStructure.Structure(entry), session.Timestamp, version, persister.IsVersioned ? persister.VersionType.Comparator : null, UseMinimalPuts(session, entityEntry), cancellationToken)).ConfigureAwait(false); if (put && factory.Statistics.IsStatisticsEnabled) { - factory.StatisticsImplementor.SecondLevelCachePut(persister.Cache.RegionName); + factory.StatisticsImplementor.SecondLevelCachePut(cache.RegionName); } } } diff --git a/src/NHibernate/Async/Event/Default/AbstractLockUpgradeEventListener.cs b/src/NHibernate/Async/Event/Default/AbstractLockUpgradeEventListener.cs index 9688ab10663..a833468a390 100644 --- a/src/NHibernate/Async/Event/Default/AbstractLockUpgradeEventListener.cs +++ b/src/NHibernate/Async/Event/Default/AbstractLockUpgradeEventListener.cs @@ -51,18 +51,8 @@ protected virtual async Task UpgradeLockAsync(object entity, EntityEntry entry, log.Debug("locking {0} in mode: {1}", MessageHelper.InfoString(persister, entry.Id, source.Factory), requestedLockMode); } - ISoftLock slock; - CacheKey ck; - if (persister.HasCache) - { - ck = source.GenerateCacheKey(entry.Id, persister.IdentifierType, persister.RootEntityName); - slock = await (persister.Cache.LockAsync(ck, entry.Version, cancellationToken)).ConfigureAwait(false); - } - else - { - ck = null; - slock = null; - } + CacheKey ck = source.GetCacheAndKey(entry.Id, persister, out var cache); + var slock = (cache == null ? null : await (cache.LockAsync(ck, entry.Version, cancellationToken)).ConfigureAwait(false)); try { @@ -82,9 +72,18 @@ protected virtual async Task UpgradeLockAsync(object entity, EntityEntry entry, { // the database now holds a lock + the object is flushed from the cache, // so release the soft lock - if (persister.HasCache) + var releaseTask = cache?.ReleaseAsync(ck, slock, cancellationToken); + // the database now holds a lock + the object is flushed from the cache, + // so release the soft lock + if (releaseTask != null) + // the database now holds a lock + the object is flushed from the cache, + // so release the soft lock { - await (persister.Cache.ReleaseAsync(ck, slock, cancellationToken)).ConfigureAwait(false); + // the database now holds a lock + the object is flushed from the cache, + // so release the soft lock + await (releaseTask).ConfigureAwait(false); + // the database now holds a lock + the object is flushed from the cache, + // so release the soft lock } } } diff --git a/src/NHibernate/Async/Event/Default/DefaultInitializeCollectionEventListener.cs b/src/NHibernate/Async/Event/Default/DefaultInitializeCollectionEventListener.cs index 98f40782994..e4070abad00 100644 --- a/src/NHibernate/Async/Event/Default/DefaultInitializeCollectionEventListener.cs +++ b/src/NHibernate/Async/Event/Default/DefaultInitializeCollectionEventListener.cs @@ -94,7 +94,8 @@ private async Task InitializeCollectionFromCacheAsync( CollectionEntry[] collectionEntries = null; var collectionBatch = source.PersistenceContext.BatchFetchQueue.QueryCacheQueue ?.GetCollectionBatch(persister, collectionKey, out collectionEntries); - if (collectionBatch != null || batchSize > 1 && persister.Cache.PreferMultipleGet()) + var cache = persister.GetCache(source.GetTenantIdentifier()); + if (collectionBatch != null || batchSize > 1 && cache.PreferMultipleGet()) { // The first item in the array is the item that we want to load if (collectionBatch != null) @@ -110,8 +111,9 @@ private async Task InitializeCollectionFromCacheAsync( if (collectionBatch == null) { collectionEntries = new CollectionEntry[batchSize]; + //Do not check cache, so provide null collectionBatch = await (source.PersistenceContext.BatchFetchQueue - .GetCollectionBatchAsync(persister, collectionKey, batchSize, false, collectionEntries, cancellationToken)).ConfigureAwait(false); + .GetCollectionBatchAsync(persister, collectionKey, batchSize, collectionEntries, null, cancellationToken)).ConfigureAwait(false); } // Ignore null values as the retrieved batch may contains them when there are not enough @@ -126,23 +128,23 @@ private async Task InitializeCollectionFromCacheAsync( } keys.Add(source.GenerateCacheKey(key, persister.KeyType, persister.Role)); } - var cachedObjects = await (persister.Cache.GetManyAsync(keys.ToArray(), source.Timestamp, cancellationToken)).ConfigureAwait(false); + var cachedObjects = await (cache.GetManyAsync(keys.ToArray(), source.Timestamp, cancellationToken)).ConfigureAwait(false); for (var i = 1; i < cachedObjects.Length; i++) { var coll = source.PersistenceContext.BatchFetchQueue.GetBatchLoadableCollection(persister, collectionEntries[i]); - await (AssembleAsync(keys[i], cachedObjects[i], persister, source, coll, collectionBatch[i], false, cancellationToken)).ConfigureAwait(false); + await (AssembleAsync(keys[i], cachedObjects[i], persister, source, coll, collectionBatch[i], false, cache.RegionName, cancellationToken)).ConfigureAwait(false); } - return await (AssembleAsync(keys[0], cachedObjects[0], persister, source, collection, collectionKey, true, cancellationToken)).ConfigureAwait(false); + return await (AssembleAsync(keys[0], cachedObjects[0], persister, source, collection, collectionKey, true, cache.RegionName, cancellationToken)).ConfigureAwait(false); } var cacheKey = source.GenerateCacheKey(collectionKey, persister.KeyType, persister.Role); - var cachedObject = await (persister.Cache.GetAsync(cacheKey, source.Timestamp, cancellationToken)).ConfigureAwait(false); - return await (AssembleAsync(cacheKey, cachedObject, persister, source, collection, collectionKey, true, cancellationToken)).ConfigureAwait(false); + var cachedObject = await (cache.GetAsync(cacheKey, source.Timestamp, cancellationToken)).ConfigureAwait(false); + return await (AssembleAsync(cacheKey, cachedObject, persister, source, collection, collectionKey, true, cache.RegionName, cancellationToken)).ConfigureAwait(false); } private async Task AssembleAsync( CacheKey ck, object ce, ICollectionPersister persister, ISessionImplementor source, - IPersistentCollection collection, object collectionKey, bool alterStatistics, CancellationToken cancellationToken) + IPersistentCollection collection, object collectionKey, bool alterStatistics, string regionName, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); ISessionFactoryImplementor factory = source.Factory; @@ -150,11 +152,11 @@ private async Task AssembleAsync( { if (ce == null) { - factory.StatisticsImplementor.SecondLevelCacheMiss(persister.Cache.RegionName); + factory.StatisticsImplementor.SecondLevelCacheMiss(regionName); } else { - factory.StatisticsImplementor.SecondLevelCacheHit(persister.Cache.RegionName); + factory.StatisticsImplementor.SecondLevelCacheHit(regionName); } } diff --git a/src/NHibernate/Async/Event/Default/DefaultLoadEventListener.cs b/src/NHibernate/Async/Event/Default/DefaultLoadEventListener.cs index a351b6f0437..1b99f279065 100644 --- a/src/NHibernate/Async/Event/Default/DefaultLoadEventListener.cs +++ b/src/NHibernate/Async/Event/Default/DefaultLoadEventListener.cs @@ -228,17 +228,8 @@ private async Task ReturnNarrowedProxyAsync(LoadEvent @event, IEntityPer protected virtual async Task LockAndLoadAsync(LoadEvent @event, IEntityPersister persister, EntityKey keyToLoad, LoadType options, ISessionImplementor source, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); - ISoftLock sLock = null; - CacheKey ck; - if (persister.HasCache) - { - ck = source.GenerateCacheKey(@event.EntityId, persister.IdentifierType, persister.RootEntityName); - sLock = await (persister.Cache.LockAsync(ck, null, cancellationToken)).ConfigureAwait(false); - } - else - { - ck = null; - } + CacheKey ck = source.GetCacheAndKey(@event.EntityId, persister, out var cache); + var sLock = (cache == null ? null : await (cache.LockAsync(ck, null, cancellationToken)).ConfigureAwait(false)); object entity; try @@ -247,9 +238,10 @@ protected virtual async Task LockAndLoadAsync(LoadEvent @event, IEntityP } finally { - if (persister.HasCache) + var releaseTask = cache?.ReleaseAsync(ck, sLock, cancellationToken); + if (releaseTask != null) { - await (persister.Cache.ReleaseAsync(ck, sLock, cancellationToken)).ConfigureAwait(false); + await (releaseTask).ConfigureAwait(false); } } @@ -415,7 +407,8 @@ protected virtual async Task LoadFromSecondLevelCacheAsync(LoadEvent @ev var batchSize = persister.GetBatchSize(); var entityBatch = source.PersistenceContext.BatchFetchQueue.QueryCacheQueue ?.GetEntityBatch(persister, @event.EntityId); - if (entityBatch != null || batchSize > 1 && persister.Cache.PreferMultipleGet()) + var cache = persister.GetCache(source.GetTenantIdentifier()); + if (entityBatch != null || batchSize > 1 && cache.PreferMultipleGet()) { // The first item in the array is the item that we want to load if (entityBatch != null) @@ -430,7 +423,7 @@ protected virtual async Task LoadFromSecondLevelCacheAsync(LoadEvent @ev if (entityBatch == null) { - entityBatch = await (source.PersistenceContext.BatchFetchQueue.GetEntityBatchAsync(persister, @event.EntityId, batchSize, false, cancellationToken)).ConfigureAwait(false); + entityBatch = await (source.PersistenceContext.BatchFetchQueue.GetEntityBatchAsync(persister, @event.EntityId, batchSize, null, cancellationToken)).ConfigureAwait(false); } // Ignore null values as the retrieved batch may contains them when there are not enough @@ -445,7 +438,7 @@ protected virtual async Task LoadFromSecondLevelCacheAsync(LoadEvent @ev } keys.Add(source.GenerateCacheKey(key, persister.IdentifierType, persister.RootEntityName)); } - var cachedObjects = await (persister.Cache.GetManyAsync(keys.ToArray(), source.Timestamp, cancellationToken)).ConfigureAwait(false); + var cachedObjects = await (cache.GetManyAsync(keys.ToArray(), source.Timestamp, cancellationToken)).ConfigureAwait(false); for (var i = 1; i < cachedObjects.Length; i++) { cancellationToken.ThrowIfCancellationRequested(); @@ -459,7 +452,7 @@ protected virtual async Task LoadFromSecondLevelCacheAsync(LoadEvent @ev return await (AssembleAsync(keys[0], cachedObjects[0], @event, true)).ConfigureAwait(false); } var cacheKey = source.GenerateCacheKey(@event.EntityId, persister.IdentifierType, persister.RootEntityName); - var cachedObject = await (persister.Cache.GetAsync(cacheKey, source.Timestamp, cancellationToken)).ConfigureAwait(false); + var cachedObject = await (cache.GetAsync(cacheKey, source.Timestamp, cancellationToken)).ConfigureAwait(false); cancellationToken.ThrowIfCancellationRequested(); return await (AssembleAsync(cacheKey, cachedObject, @event, true)).ConfigureAwait(false); @@ -471,12 +464,12 @@ Task AssembleAsync(CacheKey ck, object ce, LoadEvent evt, bool alterStat { if (ce == null) { - factory.StatisticsImplementor.SecondLevelCacheMiss(persister.Cache.RegionName); + factory.StatisticsImplementor.SecondLevelCacheMiss(cache.RegionName); log.Debug("Entity cache miss: {0}", ck); } else { - factory.StatisticsImplementor.SecondLevelCacheHit(persister.Cache.RegionName); + factory.StatisticsImplementor.SecondLevelCacheHit(cache.RegionName); log.Debug("Entity cache hit: {0}", ck); } } diff --git a/src/NHibernate/Async/Event/Default/DefaultRefreshEventListener.cs b/src/NHibernate/Async/Event/Default/DefaultRefreshEventListener.cs index 780ebf7dd73..c940527a30d 100644 --- a/src/NHibernate/Async/Event/Default/DefaultRefreshEventListener.cs +++ b/src/NHibernate/Async/Event/Default/DefaultRefreshEventListener.cs @@ -108,10 +108,11 @@ public virtual async Task OnRefreshAsync(RefreshEvent @event, IDictionary refres await (new EvictVisitor(source).ProcessAsync(obj, persister, cancellationToken)).ConfigureAwait(false); } - if (persister.HasCache) + var ck = source.GetCacheAndKey(id, persister, out var cache); + var removeTask = cache?.RemoveAsync(ck, cancellationToken); + if (removeTask != null) { - CacheKey ck = source.GenerateCacheKey(id, persister.IdentifierType, persister.RootEntityName); - await (persister.Cache.RemoveAsync(ck, cancellationToken)).ConfigureAwait(false); + await (removeTask).ConfigureAwait(false); } await (EvictCachedCollectionsAsync(persister, id, source.Factory, cancellationToken)).ConfigureAwait(false); diff --git a/src/NHibernate/Async/ISessionFactory.cs b/src/NHibernate/Async/ISessionFactory.cs index 744c59b3bde..ac9404ea3e0 100644 --- a/src/NHibernate/Async/ISessionFactory.cs +++ b/src/NHibernate/Async/ISessionFactory.cs @@ -17,6 +17,7 @@ using NHibernate.Impl; using NHibernate.Metadata; using NHibernate.Stat; +using NHibernate.Util; namespace NHibernate { @@ -101,6 +102,39 @@ public static partial class SessionFactoryExtension } } } + + public static async Task EvictEntityAsync(this ISessionFactory factory, string entityName, object id, string tenantIdentifier, CancellationToken cancellationToken = default(CancellationToken)) + { + cancellationToken.ThrowIfCancellationRequested(); + if (tenantIdentifier == null) + await (factory.EvictEntityAsync(entityName, id, cancellationToken)).ConfigureAwait(false); + + await (ReflectHelper.CastOrThrow(factory, "multi-tenancy").EvictEntityAsync(entityName, id, tenantIdentifier, cancellationToken)).ConfigureAwait(false); + } + + public static async Task EvictCollectionAsync(this ISessionFactory factory, IEnumerable roleNames, string tenantIdentifier, CancellationToken cancellationToken = default(CancellationToken)) + { + cancellationToken.ThrowIfCancellationRequested(); + if (tenantIdentifier == null) + { + await (EvictCollectionAsync(factory, roleNames, cancellationToken)).ConfigureAwait(false); + return; + } + + await (ReflectHelper.CastOrThrow(factory, "multi-tenancy").EvictCollectionAsync(roleNames, tenantIdentifier, cancellationToken)).ConfigureAwait(false); + } + + public static async Task EvictEntityAsync(this ISessionFactory factory, IEnumerable entityNames, string tenantIdentifier, CancellationToken cancellationToken = default(CancellationToken)) + { + cancellationToken.ThrowIfCancellationRequested(); + if (tenantIdentifier == null) + { + await (EvictEntityAsync(factory, entityNames, cancellationToken)).ConfigureAwait(false); + return; + } + + await (ReflectHelper.CastOrThrow(factory, "multi-tenancy").EvictEntityAsync(entityNames, tenantIdentifier, cancellationToken)).ConfigureAwait(false); + } } public partial interface ISessionFactory : IDisposable diff --git a/src/NHibernate/Async/Impl/SessionFactoryImpl.cs b/src/NHibernate/Async/Impl/SessionFactoryImpl.cs index af52013f08b..f29aa2d7227 100644 --- a/src/NHibernate/Async/Impl/SessionFactoryImpl.cs +++ b/src/NHibernate/Async/Impl/SessionFactoryImpl.cs @@ -67,22 +67,6 @@ public sealed partial class SessionFactoryImpl : ISessionFactoryImplementor, IOb isClosed = true; - foreach (IEntityPersister p in entityPersisters.Values) - { - if (p.HasCache) - { - p.Cache.Destroy(); - } - } - - foreach (ICollectionPersister p in collectionPersisters.Values) - { - if (p.HasCache) - { - p.Cache.Destroy(); - } - } - if (settings.IsQueryCacheEnabled) { foreach (var cache in queryCaches.Values) @@ -121,24 +105,7 @@ public sealed partial class SessionFactoryImpl : ISessionFactoryImplementor, IOb { return Task.FromCanceled(cancellationToken); } - try - { - IEntityPersister p = GetEntityPersister(persistentClass.FullName); - if (p.HasCache) - { - if (log.IsDebugEnabled()) - { - log.Debug("evicting second-level cache: {0}", MessageHelper.InfoString(p, id)); - } - CacheKey ck = GenerateCacheKeyForEvict(id, p.IdentifierType, p.RootEntityName); - return p.Cache.RemoveAsync(ck, cancellationToken); - } - return Task.CompletedTask; - } - catch (Exception ex) - { - return Task.FromException(ex); - } + return EvictEntityAsync(persistentClass.FullName, id, null, cancellationToken); } public Task EvictAsync(System.Type persistentClass, CancellationToken cancellationToken = default(CancellationToken)) @@ -147,23 +114,7 @@ public sealed partial class SessionFactoryImpl : ISessionFactoryImplementor, IOb { return Task.FromCanceled(cancellationToken); } - try - { - IEntityPersister p = GetEntityPersister(persistentClass.FullName); - if (p.HasCache) - { - if (log.IsDebugEnabled()) - { - log.Debug("evicting second-level cache: {0}", p.EntityName); - } - return p.Cache.ClearAsync(cancellationToken); - } - return Task.CompletedTask; - } - catch (Exception ex) - { - return Task.FromException(ex); - } + return EvictEntityAsync(persistentClass.FullName, null, null, cancellationToken); } public Task EvictAsync(IEnumerable persistentClasses, CancellationToken cancellationToken) @@ -190,26 +141,37 @@ public Task EvictAsync(IEnumerable persistentClasses, CancellationT { return Task.FromCanceled(cancellationToken); } - try + return EvictEntityAsync(entityName, null, null, cancellationToken); + } + + public Task EvictEntityAsync(IEnumerable entityNames, CancellationToken cancellationToken) + { + if (cancellationToken.IsCancellationRequested) { - IEntityPersister p = GetEntityPersister(entityName); - if (p.HasCache) - { - if (log.IsDebugEnabled()) - { - log.Debug("evicting second-level cache: {0}", p.EntityName); - } - return p.Cache.ClearAsync(cancellationToken); - } - return Task.CompletedTask; + return Task.FromCanceled(cancellationToken); } - catch (Exception ex) + return EvictEntityAsync(entityNames, null, cancellationToken); + } + + public Task EvictEntityAsync(string entityName, object id, CancellationToken cancellationToken = default(CancellationToken)) + { + if (cancellationToken.IsCancellationRequested) { - return Task.FromException(ex); + return Task.FromCanceled(cancellationToken); } + return EvictEntityAsync(entityName, id, null, cancellationToken); } - public Task EvictEntityAsync(IEnumerable entityNames, CancellationToken cancellationToken) + public Task EvictEntityAsync(string entityName, object id, string tenantIdentifier, CancellationToken cancellationToken) + { + if (cancellationToken.IsCancellationRequested) + { + return Task.FromCanceled(cancellationToken); + } + return EvictEntityAsync(entityName, id, tenantIdentifier, null, cancellationToken); + } + + public Task EvictEntityAsync(IEnumerable entityNames, string tenantIdentifier, CancellationToken cancellationToken) { if (entityNames == null) throw new ArgumentNullException(nameof(entityNames)); @@ -221,42 +183,45 @@ public Task EvictEntityAsync(IEnumerable entityNames, CancellationToken async Task InternalEvictEntityAsync() { - foreach (var cacheGroup in entityNames.Select(GetEntityPersister).Where(x => x.HasCache).GroupBy(x => x.Cache)) + var processedCaches = new HashSet(); + foreach (var entityName in entityNames) { - if (log.IsDebugEnabled()) - { - log.Debug("evicting second-level cache for: {0}", - string.Join(", ", cacheGroup.Select(p => p.EntityName))); - } - await (cacheGroup.Key.ClearAsync(cancellationToken)).ConfigureAwait(false); + await (EvictEntityAsync(entityName, null, tenantIdentifier, processedCaches, cancellationToken)).ConfigureAwait(false); } } } - public Task EvictEntityAsync(string entityName, object id, CancellationToken cancellationToken = default(CancellationToken)) + private async Task EvictEntityAsync(string entityName, object id, string tenantIdentifier, HashSet processedCaches, CancellationToken cancellationToken) { - if (cancellationToken.IsCancellationRequested) - { - return Task.FromCanceled(cancellationToken); - } - try + cancellationToken.ThrowIfCancellationRequested(); + var p = GetEntityPersister(entityName); + if (!p.HasCache) + return; + + var cache = p.GetCache(CurrentSessionContext?.CurrentSession().GetSessionImplementation().GetTenantIdentifier() ?? tenantIdentifier); + if (id == null) { - IEntityPersister p = GetEntityPersister(entityName); - if (p.HasCache) + if (log.IsDebugEnabled()) { - if (log.IsDebugEnabled()) - { - log.Debug("evicting second-level cache: {0}", MessageHelper.InfoString(p, id, this)); - } - CacheKey cacheKey = GenerateCacheKeyForEvict(id, p.IdentifierType, p.RootEntityName); - return p.Cache.RemoveAsync(cacheKey, cancellationToken); + log.Debug("evicting second-level cache: {0}", entityName); } - return Task.CompletedTask; + + if (processedCaches == null || processedCaches.Add(cache)) + { + await (cache.ClearAsync(cancellationToken)).ConfigureAwait(false); + } + + return; } - catch (Exception ex) + + var ck = GenerateCacheKeyForEvict(id, p.IdentifierType, p.RootEntityName); + + if (log.IsDebugEnabled()) { - return Task.FromException(ex); + log.Debug("evicting second-level cache: {0}", MessageHelper.InfoString(p, id)); } + + await (cache.RemoveAsync(ck, cancellationToken)).ConfigureAwait(false); } public Task EvictCollectionAsync(string roleName, object id, CancellationToken cancellationToken = default(CancellationToken)) @@ -265,73 +230,86 @@ async Task InternalEvictEntityAsync() { return Task.FromCanceled(cancellationToken); } - try + return EvictCollectionAsync(roleName, id, null, cancellationToken); + } + + public Task EvictCollectionAsync(string roleName, CancellationToken cancellationToken = default(CancellationToken)) + { + if (cancellationToken.IsCancellationRequested) { - ICollectionPersister p = GetCollectionPersister(roleName); - if (p.HasCache) - { - if (log.IsDebugEnabled()) - { - log.Debug("evicting second-level cache: {0}", MessageHelper.CollectionInfoString(p, id)); - } - CacheKey ck = GenerateCacheKeyForEvict(id, p.KeyType, p.Role); - return p.Cache.RemoveAsync(ck, cancellationToken); - } - return Task.CompletedTask; + return Task.FromCanceled(cancellationToken); } - catch (Exception ex) + return EvictCollectionAsync(roleName, null, cancellationToken); + } + + public Task EvictCollectionAsync(IEnumerable roleNames, CancellationToken cancellationToken) + { + if (cancellationToken.IsCancellationRequested) { - return Task.FromException(ex); + return Task.FromCanceled(cancellationToken); } + return EvictCollectionAsync(roleNames, null, cancellationToken); } - public Task EvictCollectionAsync(string roleName, CancellationToken cancellationToken = default(CancellationToken)) + public Task EvictCollectionAsync(IEnumerable roleNames, string tenantIdentifier, CancellationToken cancellationToken) { + if (roleNames == null) + throw new ArgumentNullException(nameof(roleNames)); if (cancellationToken.IsCancellationRequested) { return Task.FromCanceled(cancellationToken); } - try + return InternalEvictCollectionAsync(); + async Task InternalEvictCollectionAsync() { - ICollectionPersister p = GetCollectionPersister(roleName); - if (p.HasCache) + + HashSet processedCaches = new HashSet(); + foreach (var roleName in roleNames) { - if (log.IsDebugEnabled()) - { - log.Debug("evicting second-level cache: {0}", p.Role); - } - return p.Cache.ClearAsync(cancellationToken); + await (EvictCollectionAsync(roleName, null, tenantIdentifier, processedCaches, cancellationToken)).ConfigureAwait(false); } - return Task.CompletedTask; - } - catch (Exception ex) - { - return Task.FromException(ex); } } - public Task EvictCollectionAsync(IEnumerable roleNames, CancellationToken cancellationToken) + public Task EvictCollectionAsync(string roleName, object id, string tenantIdentifier, CancellationToken cancellationToken) { - if (roleNames == null) - throw new ArgumentNullException(nameof(roleNames)); if (cancellationToken.IsCancellationRequested) { return Task.FromCanceled(cancellationToken); } - return InternalEvictCollectionAsync(); - async Task InternalEvictCollectionAsync() + return EvictCollectionAsync(roleName, id, tenantIdentifier, null, cancellationToken); + } + + private async Task EvictCollectionAsync(string roleName, object id, string tenantIdentifier, HashSet processedCaches, CancellationToken cancellationToken) + { + cancellationToken.ThrowIfCancellationRequested(); + ICollectionPersister p = GetCollectionPersister(roleName); + if (!p.HasCache) + return; + + var cache = p.GetCache(CurrentSessionContext?.CurrentSession().GetSessionImplementation().GetTenantIdentifier() ?? tenantIdentifier); + if (id == null) { + if (log.IsDebugEnabled()) + { + log.Debug("evicting second-level cache: {0}", p.Role); + } - foreach (var cacheGroup in roleNames.Select(GetCollectionPersister).Where(x => x.HasCache).GroupBy(x => x.Cache)) + if (processedCaches == null || processedCaches.Add(cache)) { - if (log.IsDebugEnabled()) - { - log.Debug("evicting second-level cache for: {0}", - string.Join(", ", cacheGroup.Select(p => p.Role))); - } - await (cacheGroup.Key.ClearAsync(cancellationToken)).ConfigureAwait(false); + await (cache.ClearAsync(cancellationToken)).ConfigureAwait(false); } + + return; } + + CacheKey ck = GenerateCacheKeyForEvict(id, p.KeyType, p.Role); + if (log.IsDebugEnabled()) + { + log.Debug("evicting second-level cache: {0}", MessageHelper.CollectionInfoString(p, id)); + } + + await (cache.RemoveAsync(ck, cancellationToken)).ConfigureAwait(false); } public async Task EvictQueriesAsync(CancellationToken cancellationToken = default(CancellationToken)) diff --git a/src/NHibernate/Async/Impl/StatelessSessionImpl.cs b/src/NHibernate/Async/Impl/StatelessSessionImpl.cs index f1ba521b612..e6642aa7c47 100644 --- a/src/NHibernate/Async/Impl/StatelessSessionImpl.cs +++ b/src/NHibernate/Async/Impl/StatelessSessionImpl.cs @@ -571,10 +571,11 @@ private Task GetAsync(System.Type persistentClass, object id, Cancellati // ); // } - if (persister.HasCache) + CacheKey ck = this.GetCacheAndKey(id, persister, out var cache); + var removeTask = cache?.RemoveAsync(ck, cancellationToken); + if (removeTask != null) { - CacheKey ck = GenerateCacheKey(id, persister.IdentifierType, persister.RootEntityName); - await (persister.Cache.RemoveAsync(ck, cancellationToken)).ConfigureAwait(false); + await (removeTask).ConfigureAwait(false); } string previousFetchProfile = FetchProfile; diff --git a/src/NHibernate/Async/Loader/Collection/BatchingCollectionInitializer.cs b/src/NHibernate/Async/Loader/Collection/BatchingCollectionInitializer.cs index 5300a05df53..491952e96c8 100644 --- a/src/NHibernate/Async/Loader/Collection/BatchingCollectionInitializer.cs +++ b/src/NHibernate/Async/Loader/Collection/BatchingCollectionInitializer.cs @@ -25,7 +25,7 @@ public async Task InitializeAsync(object id, ISessionImplementor session, Cancel { cancellationToken.ThrowIfCancellationRequested(); object[] batch = - await (session.PersistenceContext.BatchFetchQueue.GetCollectionBatchAsync(collectionPersister, id, batchSizes[0], cancellationToken)).ConfigureAwait(false); + await (session.PersistenceContext.BatchFetchQueue.GetCollectionBatchAsync(collectionPersister, id, batchSizes[0], null, collectionPersister.GetCache(session.GetTenantIdentifier()), cancellationToken)).ConfigureAwait(false); for (int i = 0; i < batchSizes.Length; i++) { @@ -42,4 +42,4 @@ public async Task InitializeAsync(object id, ISessionImplementor session, Cancel await (loaders[batchSizes.Length - 1].LoadCollectionAsync(session, id, collectionPersister.KeyType, cancellationToken)).ConfigureAwait(false); } } -} \ No newline at end of file +} diff --git a/src/NHibernate/Async/Loader/Loader.cs b/src/NHibernate/Async/Loader/Loader.cs index 58c6f066d45..e1b1d975de0 100644 --- a/src/NHibernate/Async/Loader/Loader.cs +++ b/src/NHibernate/Async/Loader/Loader.cs @@ -845,7 +845,7 @@ internal static async Task UpdateCacheForEntityAsync( var state = persister.GetPropertyValues(obj); var version = Versioning.GetVersion(state, persister); var cacheEntry = await (CacheEntry.CreateAsync(state, persister, version, session, obj, cancellationToken)).ConfigureAwait(false); - var cacheKey = session.GenerateCacheKey(id, persister.IdentifierType, persister.RootEntityName); + var cacheKey = session.GetCacheAndKey(id, persister, out var cache); if (cacheBatchingHandler != null && persister.IsBatchLoadable) { @@ -861,13 +861,13 @@ internal static async Task UpdateCacheForEntityAsync( else { var put = - await (persister.Cache.PutAsync(cacheKey, persister.CacheEntryStructure.Structure(cacheEntry), session.Timestamp, version, + await (cache.PutAsync(cacheKey, persister.CacheEntryStructure.Structure(cacheEntry), session.Timestamp, version, persister.IsVersioned ? persister.VersionType.Comparator : null, false, cancellationToken)).ConfigureAwait(false); if (put && factory.Statistics.IsStatisticsEnabled) { - factory.StatisticsImplementor.SecondLevelCachePut(persister.Cache.RegionName); + factory.StatisticsImplementor.SecondLevelCachePut(cache.RegionName); } } } diff --git a/src/NHibernate/Async/Persister/Collection/AbstractCollectionPersister.cs b/src/NHibernate/Async/Persister/Collection/AbstractCollectionPersister.cs index eb15d667603..b8714def6f3 100644 --- a/src/NHibernate/Async/Persister/Collection/AbstractCollectionPersister.cs +++ b/src/NHibernate/Async/Persister/Collection/AbstractCollectionPersister.cs @@ -40,7 +40,7 @@ namespace NHibernate.Persister.Collection using System.Threading.Tasks; using System.Threading; public abstract partial class AbstractCollectionPersister : ICollectionMetadata, ISqlLoadableCollection, - IPostInsertIdentityPersister, ISupportSelectModeJoinable, ICompositeKeyPostInsertIdentityPersister + IPostInsertIdentityPersister, ISupportSelectModeJoinable, ICompositeKeyPostInsertIdentityPersister, ICacheablePersister { public Task InitializeAsync(object key, ISessionImplementor session, CancellationToken cancellationToken) diff --git a/src/NHibernate/Async/Persister/Collection/ICollectionPersister.cs b/src/NHibernate/Async/Persister/Collection/ICollectionPersister.cs index ee58fe1ec30..2744e288567 100644 --- a/src/NHibernate/Async/Persister/Collection/ICollectionPersister.cs +++ b/src/NHibernate/Async/Persister/Collection/ICollectionPersister.cs @@ -19,12 +19,15 @@ using NHibernate.Metadata; using NHibernate.Persister.Entity; using NHibernate.Type; +using NHibernate.Util; namespace NHibernate.Persister.Collection { using System.Threading.Tasks; using System.Threading; public partial interface ICollectionPersister + //TODO 6.0: Uncomment + //,ICacheablePersister { /// diff --git a/src/NHibernate/Async/Persister/Entity/AbstractEntityPersister.cs b/src/NHibernate/Async/Persister/Entity/AbstractEntityPersister.cs index ff5580194f5..6e685193a00 100644 --- a/src/NHibernate/Async/Persister/Entity/AbstractEntityPersister.cs +++ b/src/NHibernate/Async/Persister/Entity/AbstractEntityPersister.cs @@ -45,7 +45,7 @@ namespace NHibernate.Persister.Entity using System.Threading; public abstract partial class AbstractEntityPersister : IOuterJoinLoadable, IQueryable, IClassMetadata, IUniqueKeyLoadable, ISqlLoadable, ILazyPropertyInitializer, IPostInsertIdentityPersister, ILockable, - ISupportSelectModeJoinable, ICompositeKeyPostInsertIdentityPersister + ISupportSelectModeJoinable, ICompositeKeyPostInsertIdentityPersister, ICacheablePersister { private partial class GeneratedIdentifierBinder : IBinder @@ -1201,8 +1201,8 @@ public virtual async Task FindModifiedAsync(object[] old, object[] curren // check to see if it is in the second-level cache if (HasCache && session.CacheMode.HasFlag(CacheMode.Get)) { - CacheKey ck = session.GenerateCacheKey(id, IdentifierType, RootEntityName); - if (await (Cache.GetAsync(ck, session.Timestamp, cancellationToken)).ConfigureAwait(false) != null) + CacheKey ck = session.GetCacheAndKey(id, this, out var cache); + if (await (cache.GetAsync(ck, session.Timestamp, cancellationToken)).ConfigureAwait(false) != null) return false; } diff --git a/src/NHibernate/Async/Persister/Entity/IEntityPersister.cs b/src/NHibernate/Async/Persister/Entity/IEntityPersister.cs index db1e4a16c1c..6d8c7776325 100644 --- a/src/NHibernate/Async/Persister/Entity/IEntityPersister.cs +++ b/src/NHibernate/Async/Persister/Entity/IEntityPersister.cs @@ -27,6 +27,8 @@ namespace NHibernate.Persister.Entity using System.Threading; public partial interface IEntityPersister : IOptimisticCacheSource + //TODO 6.0: Uncomment + //,ICacheablePersister { #region stuff that is persister-centric and/or EntityInfo-centric diff --git a/src/NHibernate/Cache/CacheBatcher.cs b/src/NHibernate/Cache/CacheBatcher.cs index f94a2377e5b..349853ac8b7 100644 --- a/src/NHibernate/Cache/CacheBatcher.cs +++ b/src/NHibernate/Cache/CacheBatcher.cs @@ -34,7 +34,7 @@ internal void AddToBatch(IEntityPersister persister, CachePutData data) { Log.Debug("Adding a put operation to batch for entity {0} and key {1}", persister.EntityName, data.Key); } - AddToBatch(persister.Cache, data); + AddToBatch(persister.GetCache(_session.GetTenantIdentifier()), data); } /// @@ -48,7 +48,7 @@ internal void AddToBatch(ICollectionPersister persister, CachePutData data) { Log.Debug("Adding a put operation to batch for collection role {0} and key {1}", persister.Role, data.Key); } - AddToBatch(persister.Cache, data); + AddToBatch(persister.GetCache(_session.GetTenantIdentifier()), data); } private void AddToBatch(ICacheConcurrencyStrategy cache, CachePutData data) diff --git a/src/NHibernate/Cfg/Settings.cs b/src/NHibernate/Cfg/Settings.cs index 878b9b60605..10d8184ecfa 100644 --- a/src/NHibernate/Cfg/Settings.cs +++ b/src/NHibernate/Cfg/Settings.cs @@ -135,7 +135,10 @@ public Settings() public ILinqToHqlGeneratorsRegistry LinqToHqlGeneratorsRegistry { get; internal set; } public IQueryModelRewriterFactory QueryModelRewriterFactory { get; internal set; } - + + //TODO: Implement + public bool IsMultiTenancyEnabled => false; + #endregion internal string GetFullCacheRegionName(string name) @@ -146,4 +149,4 @@ internal string GetFullCacheRegionName(string name) return name; } } -} \ No newline at end of file +} diff --git a/src/NHibernate/Engine/BatchFetchQueue.cs b/src/NHibernate/Engine/BatchFetchQueue.cs index f07c26e293e..173c5cbb196 100644 --- a/src/NHibernate/Engine/BatchFetchQueue.cs +++ b/src/NHibernate/Engine/BatchFetchQueue.cs @@ -214,7 +214,7 @@ public void RemoveBatchLoadableCollection(CollectionEntry ce) /// an array of collection keys, of length batchSize (padded with nulls) public object[] GetCollectionBatch(ICollectionPersister collectionPersister, object id, int batchSize) { - return GetCollectionBatch(collectionPersister, id, batchSize, true, null); + return GetCollectionBatch(collectionPersister, id, batchSize, null, collectionPersister.GetCache(null)); } /// @@ -223,11 +223,11 @@ public object[] GetCollectionBatch(ICollectionPersister collectionPersister, obj /// The persister for the collection role. /// A key that must be included in the batch fetch /// the maximum number of keys to return - /// Whether to check the cache for uninitialized collection keys. /// An array that will be filled with collection entries if set. + /// Will check cache if not null is provided /// An array of collection keys, of length (padded with nulls) - internal object[] GetCollectionBatch(ICollectionPersister collectionPersister, object key, int batchSize, bool checkCache, - CollectionEntry[] collectionEntries) + internal object[] GetCollectionBatch(ICollectionPersister collectionPersister, object key, int batchSize, + CollectionEntry[] collectionEntries, ICacheConcurrencyStrategy cache) { var keys = new object[batchSize]; keys[0] = key; // The first element of array is reserved for the actual instance we are loading @@ -238,8 +238,9 @@ internal object[] GetCollectionBatch(ICollectionPersister collectionPersister, o // List of collection entries that haven't been checked for their existance in the cache. Besides the collection entry, // the index where the entry was found is also stored in order to correctly order the returning keys. var collectionKeys = new List, int>>(batchSize); - var batchableCache = collectionPersister.Cache?.GetCacheBase(); - + var batchableCache = cache?.GetCacheBase(); + bool checkCache = batchableCache != null; + if (!batchLoadableCollections.TryGetValue(collectionPersister.Role, out var map)) { return keys; @@ -336,7 +337,7 @@ bool CheckCacheAndProcessResult() } keyIndex = index; } - else if (!checkCache || batchableCache == null) + else if (!checkCache) { if (index < map.Count && (!keyIndex.HasValue || index < keyIndex.Value)) { @@ -396,7 +397,7 @@ bool CheckCacheAndProcessResult() /// an array of identifiers, of length batchSize (possibly padded with nulls) public object[] GetEntityBatch(IEntityPersister persister, object id, int batchSize) { - return GetEntityBatch(persister, id, batchSize, true); + return GetEntityBatch(persister, id, batchSize, persister.GetCache(null)); } /// @@ -407,9 +408,9 @@ public object[] GetEntityBatch(IEntityPersister persister, object id, int batchS /// The persister for the entities being loaded. /// The identifier of the entity currently demanding load. /// The maximum number of keys to return - /// Whether to check the cache for uninitialized keys. + /// Cache to check or null to skip cache check /// An array of identifiers, of length (possibly padded with nulls) - internal object[] GetEntityBatch(IEntityPersister persister, object id, int batchSize, bool checkCache) + internal object[] GetEntityBatch(IEntityPersister persister, object id, int batchSize, ICacheConcurrencyStrategy cache) { var ids = new object[batchSize]; ids[0] = id; // The first element of array is reserved for the actual instance we are loading @@ -421,7 +422,8 @@ internal object[] GetEntityBatch(IEntityPersister persister, object id, int batc // the index where the key was found is also stored in order to correctly order the returning keys. var entityKeys = new List>(batchSize); // If there is a cache, obsolete or not, batchableCache will not be null. - var batchableCache = persister.Cache?.GetCacheBase(); + var batchableCache = cache?.GetCacheBase(); + var checkCache = batchableCache != null; if (!batchLoadableEntityKeys.TryGetValue(persister.EntityName, out var set)) { @@ -498,7 +500,7 @@ bool CheckCacheAndProcessResult() { idIndex = index; } - else if (!checkCache || batchableCache == null) + else if (!checkCache) { if (index < set.Count && (!idIndex.HasValue || index < idIndex.Value)) { diff --git a/src/NHibernate/Engine/ISessionImplementor.cs b/src/NHibernate/Engine/ISessionImplementor.cs index 84b30da88ae..31dc666fd55 100644 --- a/src/NHibernate/Engine/ISessionImplementor.cs +++ b/src/NHibernate/Engine/ISessionImplementor.cs @@ -11,6 +11,7 @@ using NHibernate.Impl; using NHibernate.Loader.Custom; using NHibernate.Multi; +using NHibernate.Persister.Collection; using NHibernate.Persister.Entity; using NHibernate.Transaction; using NHibernate.Type; @@ -21,6 +22,32 @@ namespace NHibernate.Engine // 6.0 TODO: Convert to interface methods, excepted SwitchCacheMode internal static partial class SessionImplementorExtensions { + //TODO: REMOVE + internal static string GetTenantIdentifier(this ISessionImplementor session) + { + return null; + } + + //NOTE: DO NOT merge (keep as internal extension) + internal static CacheKey GetCacheAndKey(this ISessionImplementor session, object id, IEntityPersister persister, out ICacheConcurrencyStrategy cache) + { + cache = null; + if (!persister.HasCache) + return null; + cache = persister.GetCache(session.GetTenantIdentifier()); + return session.GenerateCacheKey(id, persister.IdentifierType, persister.RootEntityName); + } + + //NOTE: DO NOT merge (keep as internal extension) + internal static CacheKey GetCacheAndKey(this ISessionImplementor session, object id, ICollectionPersister persister, out ICacheConcurrencyStrategy cache) + { + cache = null; + if (!persister.HasCache) + return null; + cache = persister.GetCache(session.GetTenantIdentifier()); + return session.GenerateCacheKey(id, persister.KeyType, persister.Role); + } + /// /// Instantiate the entity class, initializing with the given identifier /// diff --git a/src/NHibernate/Engine/Loading/CollectionLoadContext.cs b/src/NHibernate/Engine/Loading/CollectionLoadContext.cs index 5aae44f1862..ea9f117d944 100644 --- a/src/NHibernate/Engine/Loading/CollectionLoadContext.cs +++ b/src/NHibernate/Engine/Loading/CollectionLoadContext.cs @@ -376,7 +376,7 @@ private void AddCollectionToCache(LoadingCollectionEntry lce, ICollectionPersist } CollectionCacheEntry entry = CollectionCacheEntry.Create(lce.Collection, persister); - CacheKey cacheKey = session.GenerateCacheKey(lce.Key, persister.KeyType, persister.Role); + var cacheKey = session.GetCacheAndKey(lce.Key, persister, out var cache); if (persister.GetBatchSize() > 1) { @@ -390,13 +390,13 @@ private void AddCollectionToCache(LoadingCollectionEntry lce, ICollectionPersist } else { - bool put = persister.Cache.Put(cacheKey, persister.CacheEntryStructure.Structure(entry), + bool put = cache.Put(cacheKey, persister.CacheEntryStructure.Structure(entry), session.Timestamp, version, versionComparator, factory.Settings.IsMinimalPutsEnabled && session.CacheMode != CacheMode.Refresh); if (put && factory.Statistics.IsStatisticsEnabled) { - factory.StatisticsImplementor.SecondLevelCachePut(persister.Cache.RegionName); + factory.StatisticsImplementor.SecondLevelCachePut(cache.RegionName); } } } diff --git a/src/NHibernate/Engine/TwoPhaseLoad.cs b/src/NHibernate/Engine/TwoPhaseLoad.cs index 8ba9038b620..707a9a72c51 100644 --- a/src/NHibernate/Engine/TwoPhaseLoad.cs +++ b/src/NHibernate/Engine/TwoPhaseLoad.cs @@ -153,7 +153,8 @@ internal static void InitializeEntity(object entity, bool readOnly, ISessionImpl object version = Versioning.GetVersion(hydratedState, persister); CacheEntry entry = CacheEntry.Create(hydratedState, persister, version, session, entity); - CacheKey cacheKey = session.GenerateCacheKey(id, persister.IdentifierType, persister.RootEntityName); + + CacheKey cacheKey = session.GetCacheAndKey(id, persister, out var cache); if (cacheBatchingHandler != null && persister.IsBatchLoadable) { @@ -169,13 +170,13 @@ internal static void InitializeEntity(object entity, bool readOnly, ISessionImpl else { bool put = - persister.Cache.Put(cacheKey, persister.CacheEntryStructure.Structure(entry), session.Timestamp, version, + cache.Put(cacheKey, persister.CacheEntryStructure.Structure(entry), session.Timestamp, version, persister.IsVersioned ? persister.VersionType.Comparator : null, UseMinimalPuts(session, entityEntry)); if (put && factory.Statistics.IsStatisticsEnabled) { - factory.StatisticsImplementor.SecondLevelCachePut(persister.Cache.RegionName); + factory.StatisticsImplementor.SecondLevelCachePut(cache.RegionName); } } } diff --git a/src/NHibernate/Event/Default/AbstractLockUpgradeEventListener.cs b/src/NHibernate/Event/Default/AbstractLockUpgradeEventListener.cs index f30c88d4162..c781b0f65af 100644 --- a/src/NHibernate/Event/Default/AbstractLockUpgradeEventListener.cs +++ b/src/NHibernate/Event/Default/AbstractLockUpgradeEventListener.cs @@ -43,18 +43,8 @@ protected virtual void UpgradeLock(object entity, EntityEntry entry, LockMode re log.Debug("locking {0} in mode: {1}", MessageHelper.InfoString(persister, entry.Id, source.Factory), requestedLockMode); } - ISoftLock slock; - CacheKey ck; - if (persister.HasCache) - { - ck = source.GenerateCacheKey(entry.Id, persister.IdentifierType, persister.RootEntityName); - slock = persister.Cache.Lock(ck, entry.Version); - } - else - { - ck = null; - slock = null; - } + CacheKey ck = source.GetCacheAndKey(entry.Id, persister, out var cache); + var slock = cache?.Lock(ck, entry.Version); try { @@ -74,10 +64,7 @@ protected virtual void UpgradeLock(object entity, EntityEntry entry, LockMode re { // the database now holds a lock + the object is flushed from the cache, // so release the soft lock - if (persister.HasCache) - { - persister.Cache.Release(ck, slock); - } + cache?.Release(ck, slock); } } } diff --git a/src/NHibernate/Event/Default/DefaultInitializeCollectionEventListener.cs b/src/NHibernate/Event/Default/DefaultInitializeCollectionEventListener.cs index 6e72aee7c2d..d0be72f02c3 100644 --- a/src/NHibernate/Event/Default/DefaultInitializeCollectionEventListener.cs +++ b/src/NHibernate/Event/Default/DefaultInitializeCollectionEventListener.cs @@ -82,7 +82,8 @@ private bool InitializeCollectionFromCache( CollectionEntry[] collectionEntries = null; var collectionBatch = source.PersistenceContext.BatchFetchQueue.QueryCacheQueue ?.GetCollectionBatch(persister, collectionKey, out collectionEntries); - if (collectionBatch != null || batchSize > 1 && persister.Cache.PreferMultipleGet()) + var cache = persister.GetCache(source.GetTenantIdentifier()); + if (collectionBatch != null || batchSize > 1 && cache.PreferMultipleGet()) { // The first item in the array is the item that we want to load if (collectionBatch != null) @@ -98,8 +99,9 @@ private bool InitializeCollectionFromCache( if (collectionBatch == null) { collectionEntries = new CollectionEntry[batchSize]; + //Do not check cache, so provide null collectionBatch = source.PersistenceContext.BatchFetchQueue - .GetCollectionBatch(persister, collectionKey, batchSize, false, collectionEntries); + .GetCollectionBatch(persister, collectionKey, batchSize, collectionEntries, null); } // Ignore null values as the retrieved batch may contains them when there are not enough @@ -114,34 +116,34 @@ private bool InitializeCollectionFromCache( } keys.Add(source.GenerateCacheKey(key, persister.KeyType, persister.Role)); } - var cachedObjects = persister.Cache.GetMany(keys.ToArray(), source.Timestamp); + var cachedObjects = cache.GetMany(keys.ToArray(), source.Timestamp); for (var i = 1; i < cachedObjects.Length; i++) { var coll = source.PersistenceContext.BatchFetchQueue.GetBatchLoadableCollection(persister, collectionEntries[i]); - Assemble(keys[i], cachedObjects[i], persister, source, coll, collectionBatch[i], false); + Assemble(keys[i], cachedObjects[i], persister, source, coll, collectionBatch[i], false, cache.RegionName); } - return Assemble(keys[0], cachedObjects[0], persister, source, collection, collectionKey, true); + return Assemble(keys[0], cachedObjects[0], persister, source, collection, collectionKey, true, cache.RegionName); } var cacheKey = source.GenerateCacheKey(collectionKey, persister.KeyType, persister.Role); - var cachedObject = persister.Cache.Get(cacheKey, source.Timestamp); - return Assemble(cacheKey, cachedObject, persister, source, collection, collectionKey, true); + var cachedObject = cache.Get(cacheKey, source.Timestamp); + return Assemble(cacheKey, cachedObject, persister, source, collection, collectionKey, true, cache.RegionName); } private bool Assemble( CacheKey ck, object ce, ICollectionPersister persister, ISessionImplementor source, - IPersistentCollection collection, object collectionKey, bool alterStatistics) + IPersistentCollection collection, object collectionKey, bool alterStatistics, string regionName) { ISessionFactoryImplementor factory = source.Factory; if (factory.Statistics.IsStatisticsEnabled && alterStatistics) { if (ce == null) { - factory.StatisticsImplementor.SecondLevelCacheMiss(persister.Cache.RegionName); + factory.StatisticsImplementor.SecondLevelCacheMiss(regionName); } else { - factory.StatisticsImplementor.SecondLevelCacheHit(persister.Cache.RegionName); + factory.StatisticsImplementor.SecondLevelCacheHit(regionName); } } diff --git a/src/NHibernate/Event/Default/DefaultLoadEventListener.cs b/src/NHibernate/Event/Default/DefaultLoadEventListener.cs index c3db8bf4f0f..b37c9717507 100644 --- a/src/NHibernate/Event/Default/DefaultLoadEventListener.cs +++ b/src/NHibernate/Event/Default/DefaultLoadEventListener.cs @@ -247,17 +247,8 @@ private object CreateProxyIfNecessary(LoadEvent @event, IEntityPersister persist /// The loaded entity protected virtual object LockAndLoad(LoadEvent @event, IEntityPersister persister, EntityKey keyToLoad, LoadType options, ISessionImplementor source) { - ISoftLock sLock = null; - CacheKey ck; - if (persister.HasCache) - { - ck = source.GenerateCacheKey(@event.EntityId, persister.IdentifierType, persister.RootEntityName); - sLock = persister.Cache.Lock(ck, null); - } - else - { - ck = null; - } + CacheKey ck = source.GetCacheAndKey(@event.EntityId, persister, out var cache); + var sLock = cache?.Lock(ck, null); object entity; try @@ -266,10 +257,7 @@ protected virtual object LockAndLoad(LoadEvent @event, IEntityPersister persiste } finally { - if (persister.HasCache) - { - persister.Cache.Release(ck, sLock); - } + cache?.Release(ck, sLock); } object proxy = @event.Session.PersistenceContext.ProxyFor(persister, keyToLoad, entity); @@ -426,7 +414,8 @@ protected virtual object LoadFromSecondLevelCache(LoadEvent @event, IEntityPersi var batchSize = persister.GetBatchSize(); var entityBatch = source.PersistenceContext.BatchFetchQueue.QueryCacheQueue ?.GetEntityBatch(persister, @event.EntityId); - if (entityBatch != null || batchSize > 1 && persister.Cache.PreferMultipleGet()) + var cache = persister.GetCache(source.GetTenantIdentifier()); + if (entityBatch != null || batchSize > 1 && cache.PreferMultipleGet()) { // The first item in the array is the item that we want to load if (entityBatch != null) @@ -441,7 +430,7 @@ protected virtual object LoadFromSecondLevelCache(LoadEvent @event, IEntityPersi if (entityBatch == null) { - entityBatch = source.PersistenceContext.BatchFetchQueue.GetEntityBatch(persister, @event.EntityId, batchSize, false); + entityBatch = source.PersistenceContext.BatchFetchQueue.GetEntityBatch(persister, @event.EntityId, batchSize, null); } // Ignore null values as the retrieved batch may contains them when there are not enough @@ -456,7 +445,7 @@ protected virtual object LoadFromSecondLevelCache(LoadEvent @event, IEntityPersi } keys.Add(source.GenerateCacheKey(key, persister.IdentifierType, persister.RootEntityName)); } - var cachedObjects = persister.Cache.GetMany(keys.ToArray(), source.Timestamp); + var cachedObjects = cache.GetMany(keys.ToArray(), source.Timestamp); for (var i = 1; i < cachedObjects.Length; i++) { Assemble( @@ -468,7 +457,7 @@ protected virtual object LoadFromSecondLevelCache(LoadEvent @event, IEntityPersi return Assemble(keys[0], cachedObjects[0], @event, true); } var cacheKey = source.GenerateCacheKey(@event.EntityId, persister.IdentifierType, persister.RootEntityName); - var cachedObject = persister.Cache.Get(cacheKey, source.Timestamp); + var cachedObject = cache.Get(cacheKey, source.Timestamp); return Assemble(cacheKey, cachedObject, @event, true); object Assemble(CacheKey ck, object ce, LoadEvent evt, bool alterStatistics) @@ -477,12 +466,12 @@ object Assemble(CacheKey ck, object ce, LoadEvent evt, bool alterStatistics) { if (ce == null) { - factory.StatisticsImplementor.SecondLevelCacheMiss(persister.Cache.RegionName); + factory.StatisticsImplementor.SecondLevelCacheMiss(cache.RegionName); log.Debug("Entity cache miss: {0}", ck); } else { - factory.StatisticsImplementor.SecondLevelCacheHit(persister.Cache.RegionName); + factory.StatisticsImplementor.SecondLevelCacheHit(cache.RegionName); log.Debug("Entity cache hit: {0}", ck); } } diff --git a/src/NHibernate/Event/Default/DefaultRefreshEventListener.cs b/src/NHibernate/Event/Default/DefaultRefreshEventListener.cs index 08c4f40da61..841643fdc95 100644 --- a/src/NHibernate/Event/Default/DefaultRefreshEventListener.cs +++ b/src/NHibernate/Event/Default/DefaultRefreshEventListener.cs @@ -90,11 +90,8 @@ public virtual void OnRefresh(RefreshEvent @event, IDictionary refreshedAlready) new EvictVisitor(source).Process(obj, persister); } - if (persister.HasCache) - { - CacheKey ck = source.GenerateCacheKey(id, persister.IdentifierType, persister.RootEntityName); - persister.Cache.Remove(ck); - } + var ck = source.GetCacheAndKey(id, persister, out var cache); + cache?.Remove(ck); EvictCachedCollections(persister, id, source.Factory); diff --git a/src/NHibernate/ISessionFactory.cs b/src/NHibernate/ISessionFactory.cs index 544f79d1a57..c970bf0ebb2 100644 --- a/src/NHibernate/ISessionFactory.cs +++ b/src/NHibernate/ISessionFactory.cs @@ -7,6 +7,7 @@ using NHibernate.Impl; using NHibernate.Metadata; using NHibernate.Stat; +using NHibernate.Util; namespace NHibernate { @@ -84,6 +85,36 @@ public static void EvictCollection(this ISessionFactory factory, IEnumerable(factory, "multi-tenancy").EvictEntity(entityName, id, tenantIdentifier); + } + + public static void EvictCollection(this ISessionFactory factory, IEnumerable roleNames, string tenantIdentifier) + { + if (tenantIdentifier == null) + { + EvictCollection(factory, roleNames); + return; + } + + ReflectHelper.CastOrThrow(factory, "multi-tenancy").EvictCollection(roleNames, tenantIdentifier); + } + + public static void EvictEntity(this ISessionFactory factory, IEnumerable entityNames, string tenantIdentifier) + { + if (tenantIdentifier == null) + { + EvictEntity(factory, entityNames); + return; + } + + ReflectHelper.CastOrThrow(factory, "multi-tenancy").EvictEntity(entityNames, tenantIdentifier); + } } /// diff --git a/src/NHibernate/Impl/SessionFactoryImpl.cs b/src/NHibernate/Impl/SessionFactoryImpl.cs index ae70fe57ad2..aec7fa39df8 100644 --- a/src/NHibernate/Impl/SessionFactoryImpl.cs +++ b/src/NHibernate/Impl/SessionFactoryImpl.cs @@ -232,7 +232,7 @@ public SessionFactoryImpl(Configuration cfg, IMapping mapping, Settings settings #region Persisters - var caches = new Dictionary, ICacheConcurrencyStrategy>(); + var caches = new ConcurrentDictionary, ICacheConcurrencyStrategy>(); entityPersisters = new Dictionary(); implementorToEntityName = new Dictionary(); @@ -426,25 +426,41 @@ private IQueryCache BuildQueryCache(string queryCacheName) properties); } - private ICacheConcurrencyStrategy GetCacheConcurrencyStrategy( + private Func GetCacheConcurrencyStrategy( string cacheRegion, string strategy, bool isMutable, - Dictionary, ICacheConcurrencyStrategy> caches) + ConcurrentDictionary, ICacheConcurrencyStrategy> caches) { if (strategy == null || !settings.IsSecondLevelCacheEnabled) return null; - var cacheKey = new Tuple(cacheRegion, strategy); - if (caches.TryGetValue(cacheKey, out var cache)) - return cache; + //TODO: This immediate cache creation is added to avoid breaking change when user expects that caches are created right after session factory + //so cache can be retrieved via GetSecondLevelCacheRegion + //Consider to remove it in 6.0 + if (!Settings.IsMultiTenancyEnabled) + { + CreateCache(null); + } - cache = CacheFactory.CreateCache(strategy, GetCache(cacheRegion)); - caches.Add(cacheKey, cache); - if (isMutable && strategy == CacheFactory.ReadOnly) - log.Warn("read-only cache configured for mutable: {0}", name); + return CreateCache; + ICacheConcurrencyStrategy CreateCache(string tenantIdentifier) => + caches.GetOrAdd( + new Tuple(GetCacheRegion(cacheRegion, tenantIdentifier), strategy), + cacheKey => + { + if (isMutable && strategy == CacheFactory.ReadOnly) + log.Warn("read-only cache configured for mutable: {0}", name); + return + CacheFactory.CreateCache(strategy, GetCache(cacheKey.Item1)); + }); + } - return cache; + private static string GetCacheRegion(string cacheRegion, string tenantIdentifier) + { + if (tenantIdentifier == null) + return cacheRegion; + return tenantIdentifier + "|" + cacheRegion; } public EventListeners EventListeners @@ -856,22 +872,6 @@ public void Close() isClosed = true; - foreach (IEntityPersister p in entityPersisters.Values) - { - if (p.HasCache) - { - p.Cache.Destroy(); - } - } - - foreach (ICollectionPersister p in collectionPersisters.Values) - { - if (p.HasCache) - { - p.Cache.Destroy(); - } - } - if (settings.IsQueryCacheEnabled) { foreach (var cache in queryCaches.Values) @@ -906,29 +906,12 @@ public void Close() public void Evict(System.Type persistentClass, object id) { - IEntityPersister p = GetEntityPersister(persistentClass.FullName); - if (p.HasCache) - { - if (log.IsDebugEnabled()) - { - log.Debug("evicting second-level cache: {0}", MessageHelper.InfoString(p, id)); - } - CacheKey ck = GenerateCacheKeyForEvict(id, p.IdentifierType, p.RootEntityName); - p.Cache.Remove(ck); - } + EvictEntity(persistentClass.FullName, id, null); } public void Evict(System.Type persistentClass) { - IEntityPersister p = GetEntityPersister(persistentClass.FullName); - if (p.HasCache) - { - if (log.IsDebugEnabled()) - { - log.Debug("evicting second-level cache: {0}", p.EntityName); - } - p.Cache.Clear(); - } + EvictEntity(persistentClass.FullName, null, null); } public void Evict(IEnumerable persistentClasses) @@ -940,59 +923,70 @@ public void Evict(IEnumerable persistentClasses) public void EvictEntity(string entityName) { - IEntityPersister p = GetEntityPersister(entityName); - if (p.HasCache) - { - if (log.IsDebugEnabled()) - { - log.Debug("evicting second-level cache: {0}", p.EntityName); - } - p.Cache.Clear(); - } + EvictEntity(entityName, null, null); } public void EvictEntity(IEnumerable entityNames) + { + EvictEntity(entityNames, null); + } + + public void EvictEntity(string entityName, object id) + { + EvictEntity(entityName, id, null); + } + + public void EvictEntity(string entityName, object id, string tenantIdentifier) + { + EvictEntity(entityName, id, tenantIdentifier, null); + } + + public void EvictEntity(IEnumerable entityNames, string tenantIdentifier) { if (entityNames == null) throw new ArgumentNullException(nameof(entityNames)); - foreach (var cacheGroup in entityNames.Select(GetEntityPersister).Where(x => x.HasCache).GroupBy(x => x.Cache)) + var processedCaches = new HashSet(); + foreach (var entityName in entityNames) { - if (log.IsDebugEnabled()) - { - log.Debug("evicting second-level cache for: {0}", - string.Join(", ", cacheGroup.Select(p => p.EntityName))); - } - cacheGroup.Key.Clear(); + EvictEntity(entityName, null, tenantIdentifier, processedCaches); } } - public void EvictEntity(string entityName, object id) + private void EvictEntity(string entityName, object id, string tenantIdentifier, HashSet processedCaches) { - IEntityPersister p = GetEntityPersister(entityName); - if (p.HasCache) + var p = GetEntityPersister(entityName); + if (!p.HasCache) + return; + + var cache = p.GetCache(CurrentSessionContext?.CurrentSession().GetSessionImplementation().GetTenantIdentifier() ?? tenantIdentifier); + if (id == null) { + if (processedCaches?.Add(cache) == false) + return; + if (log.IsDebugEnabled()) { - log.Debug("evicting second-level cache: {0}", MessageHelper.InfoString(p, id, this)); + log.Debug("evicting second-level cache: {0}", entityName); } - CacheKey cacheKey = GenerateCacheKeyForEvict(id, p.IdentifierType, p.RootEntityName); - p.Cache.Remove(cacheKey); + + cache.Clear(); + return; + } + + var ck = GenerateCacheKeyForEvict(id, p.IdentifierType, p.RootEntityName); + + if (log.IsDebugEnabled()) + { + log.Debug("evicting second-level cache: {0}", MessageHelper.InfoString(p, id)); } + + cache.Remove(ck); } public void EvictCollection(string roleName, object id) { - ICollectionPersister p = GetCollectionPersister(roleName); - if (p.HasCache) - { - if (log.IsDebugEnabled()) - { - log.Debug("evicting second-level cache: {0}", MessageHelper.CollectionInfoString(p, id)); - } - CacheKey ck = GenerateCacheKeyForEvict(id, p.KeyType, p.Role); - p.Cache.Remove(ck); - } + EvictCollection(roleName, id, null); } private CacheKey GenerateCacheKeyForEvict(object id, IType type, string entityOrRoleName) @@ -1001,9 +995,9 @@ private CacheKey GenerateCacheKeyForEvict(object id, IType type, string entityOr if (CurrentSessionContext != null) { return CurrentSessionContext - .CurrentSession() - .GetSessionImplementation() - .GenerateCacheKey(id, type, entityOrRoleName); + .CurrentSession() + .GetSessionImplementation() + .GenerateCacheKey(id, type, entityOrRoleName); } return new CacheKey(id, type, entityOrRoleName, this); @@ -1011,31 +1005,59 @@ private CacheKey GenerateCacheKeyForEvict(object id, IType type, string entityOr public void EvictCollection(string roleName) { - ICollectionPersister p = GetCollectionPersister(roleName); - if (p.HasCache) - { - if (log.IsDebugEnabled()) - { - log.Debug("evicting second-level cache: {0}", p.Role); - } - p.Cache.Clear(); - } + EvictCollection(roleName, null); } public void EvictCollection(IEnumerable roleNames) + { + EvictCollection(roleNames, null); + } + + public void EvictCollection(IEnumerable roleNames, string tenantIdentifier) { if (roleNames == null) throw new ArgumentNullException(nameof(roleNames)); - foreach (var cacheGroup in roleNames.Select(GetCollectionPersister).Where(x => x.HasCache).GroupBy(x => x.Cache)) + HashSet processedCaches = new HashSet(); + foreach (var roleName in roleNames) + { + EvictCollection(roleName, null, tenantIdentifier, processedCaches); + } + } + + public void EvictCollection(string roleName, object id, string tenantIdentifier) + { + EvictCollection(roleName, id, tenantIdentifier, null); + } + + private void EvictCollection(string roleName, object id, string tenantIdentifier, HashSet processedCaches) + { + ICollectionPersister p = GetCollectionPersister(roleName); + if (!p.HasCache) + return; + + var cache = p.GetCache(CurrentSessionContext?.CurrentSession().GetSessionImplementation().GetTenantIdentifier() ?? tenantIdentifier); + if (id == null) { + if (processedCaches?.Add(cache) == false) + return; + if (log.IsDebugEnabled()) { - log.Debug("evicting second-level cache for: {0}", - string.Join(", ", cacheGroup.Select(p => p.Role))); + log.Debug("evicting second-level cache: {0}", p.Role); } - cacheGroup.Key.Clear(); + + cache.Clear(); + return; } + + CacheKey ck = GenerateCacheKeyForEvict(id, p.KeyType, p.Role); + if (log.IsDebugEnabled()) + { + log.Debug("evicting second-level cache: {0}", MessageHelper.CollectionInfoString(p, id)); + } + + cache.Remove(ck); } public IType GetReferencedPropertyType(string className, string propertyName) @@ -1063,11 +1085,13 @@ public UpdateTimestampsCache UpdateTimestampsCache get { return updateTimestampsCache; } } - // 6.0 TODO: type as CacheBase instead + // 6.0 TODO: return as ICollection instead #pragma warning disable 618 public IDictionary GetAllSecondLevelCacheRegions() #pragma warning restore 618 { + //TODO 6.0: uncomment + //return _allCacheRegions.Values; return _allCacheRegions // ToArray creates a moment in time snapshot diff --git a/src/NHibernate/Impl/StatelessSessionImpl.cs b/src/NHibernate/Impl/StatelessSessionImpl.cs index 4eb3630391e..994ad804d36 100644 --- a/src/NHibernate/Impl/StatelessSessionImpl.cs +++ b/src/NHibernate/Impl/StatelessSessionImpl.cs @@ -650,11 +650,8 @@ public void Refresh(string entityName, object entity, LockMode lockMode) // ); // } - if (persister.HasCache) - { - CacheKey ck = GenerateCacheKey(id, persister.IdentifierType, persister.RootEntityName); - persister.Cache.Remove(ck); - } + CacheKey ck = this.GetCacheAndKey(id, persister, out var cache); + cache?.Remove(ck); string previousFetchProfile = FetchProfile; object result; diff --git a/src/NHibernate/Loader/Collection/BatchingCollectionInitializer.cs b/src/NHibernate/Loader/Collection/BatchingCollectionInitializer.cs index 0df933e660a..d51971849eb 100644 --- a/src/NHibernate/Loader/Collection/BatchingCollectionInitializer.cs +++ b/src/NHibernate/Loader/Collection/BatchingCollectionInitializer.cs @@ -27,7 +27,7 @@ public BatchingCollectionInitializer(ICollectionPersister collectionPersister, i public void Initialize(object id, ISessionImplementor session) { object[] batch = - session.PersistenceContext.BatchFetchQueue.GetCollectionBatch(collectionPersister, id, batchSizes[0]); + session.PersistenceContext.BatchFetchQueue.GetCollectionBatch(collectionPersister, id, batchSizes[0], null, collectionPersister.GetCache(session.GetTenantIdentifier())); for (int i = 0; i < batchSizes.Length; i++) { @@ -86,4 +86,4 @@ public static ICollectionInitializer CreateBatchingCollectionInitializer(IQuerya } } } -} \ No newline at end of file +} diff --git a/src/NHibernate/Loader/Loader.cs b/src/NHibernate/Loader/Loader.cs index 58b4f45d570..65d389774ed 100644 --- a/src/NHibernate/Loader/Loader.cs +++ b/src/NHibernate/Loader/Loader.cs @@ -1221,7 +1221,7 @@ internal static void UpdateCacheForEntity( var state = persister.GetPropertyValues(obj); var version = Versioning.GetVersion(state, persister); var cacheEntry = CacheEntry.Create(state, persister, version, session, obj); - var cacheKey = session.GenerateCacheKey(id, persister.IdentifierType, persister.RootEntityName); + var cacheKey = session.GetCacheAndKey(id, persister, out var cache); if (cacheBatchingHandler != null && persister.IsBatchLoadable) { @@ -1237,13 +1237,13 @@ internal static void UpdateCacheForEntity( else { var put = - persister.Cache.Put(cacheKey, persister.CacheEntryStructure.Structure(cacheEntry), session.Timestamp, version, + cache.Put(cacheKey, persister.CacheEntryStructure.Structure(cacheEntry), session.Timestamp, version, persister.IsVersioned ? persister.VersionType.Comparator : null, false); if (put && factory.Statistics.IsStatisticsEnabled) { - factory.StatisticsImplementor.SecondLevelCachePut(persister.Cache.RegionName); + factory.StatisticsImplementor.SecondLevelCachePut(cache.RegionName); } } } diff --git a/src/NHibernate/Persister/Collection/AbstractCollectionPersister.cs b/src/NHibernate/Persister/Collection/AbstractCollectionPersister.cs index 4489945d400..e05f4df5a22 100644 --- a/src/NHibernate/Persister/Collection/AbstractCollectionPersister.cs +++ b/src/NHibernate/Persister/Collection/AbstractCollectionPersister.cs @@ -31,7 +31,7 @@ namespace NHibernate.Persister.Collection /// Summary description for AbstractCollectionPersister. /// public abstract partial class AbstractCollectionPersister : ICollectionMetadata, ISqlLoadableCollection, - IPostInsertIdentityPersister, ISupportSelectModeJoinable, ICompositeKeyPostInsertIdentityPersister + IPostInsertIdentityPersister, ISupportSelectModeJoinable, ICompositeKeyPostInsertIdentityPersister, ICacheablePersister { protected static readonly object NotFoundPlaceHolder = new object(); private readonly string role; @@ -124,7 +124,7 @@ public abstract partial class AbstractCollectionPersister : ICollectionMetadata, private readonly IIdentifierGenerator identifierGenerator; private readonly IPropertyMapping elementPropertyMapping; private readonly IEntityPersister elementPersister; - private readonly ICacheConcurrencyStrategy cache; + private readonly Func _cacheByTenant; private readonly CollectionType collectionType; private ICollectionInitializer initializer; @@ -163,10 +163,17 @@ public abstract partial class AbstractCollectionPersister : ICollectionMetadata, private static readonly INHibernateLogger log = NHibernateLogger.For(typeof (ICollectionPersister)); - public AbstractCollectionPersister(Mapping.Collection collection, ICacheConcurrencyStrategy cache, ISessionFactoryImplementor factory) + //Since 5.3 + [Obsolete("Use constructor with cacheByTenant delegate")] + public AbstractCollectionPersister(Mapping.Collection collection, ICacheConcurrencyStrategy cache, ISessionFactoryImplementor factory):this(collection, tenantId => cache, factory) + { + //TODO: Throw if multitenancy is enabled + } + + public AbstractCollectionPersister(Mapping.Collection collection, Func cacheByTenant, ISessionFactoryImplementor factory) { this.factory = factory; - this.cache = cache; + _cacheByTenant = cacheByTenant; if (factory.Settings.IsStructuredCacheEntriesEnabled) { cacheEntryStructure = collection.IsMap @@ -633,7 +640,7 @@ protected abstract ICollectionInitializer CreateSubselectInitializer(SubselectFe public bool HasCache { - get { return cache != null; } + get { return _cacheByTenant != null; } } public string GetSQLWhereString(string alias) @@ -1771,9 +1778,10 @@ private void CheckColumnDuplication(HashSet distinctColumns, IEnumerable } } + public ICacheConcurrencyStrategy Cache { - get { return cache; } + get { return _cacheByTenant?.Invoke(null); } } public CollectionType CollectionType @@ -2021,6 +2029,11 @@ protected virtual ISQLExceptionConverter SQLExceptionConverter get { return sqlExceptionConverter; } } + public ICacheConcurrencyStrategy GetCache(string tenantIdentifier) + { + return _cacheByTenant?.Invoke(tenantIdentifier); + } + public ICacheEntryStructure CacheEntryStructure { get { return cacheEntryStructure; } diff --git a/src/NHibernate/Persister/Collection/BasicCollectionPersister.cs b/src/NHibernate/Persister/Collection/BasicCollectionPersister.cs index 276ab028874..02775226311 100644 --- a/src/NHibernate/Persister/Collection/BasicCollectionPersister.cs +++ b/src/NHibernate/Persister/Collection/BasicCollectionPersister.cs @@ -22,8 +22,13 @@ namespace NHibernate.Persister.Collection /// public partial class BasicCollectionPersister : AbstractCollectionPersister { + //Since 5.3 + [Obsolete("Use constructor with cacheByTenant delegate")] public BasicCollectionPersister(Mapping.Collection collection, ICacheConcurrencyStrategy cache, ISessionFactoryImplementor factory) - : base(collection, cache, factory) { } + : this(collection, tenantId => cache, factory) { } + + public BasicCollectionPersister(Mapping.Collection collection, Func cacheByTenant, ISessionFactoryImplementor factory) + : base(collection, cacheByTenant, factory) { } public override bool CascadeDeleteEnabled { diff --git a/src/NHibernate/Persister/Collection/ICollectionPersister.cs b/src/NHibernate/Persister/Collection/ICollectionPersister.cs index 4d87ea158b3..b6606938b67 100644 --- a/src/NHibernate/Persister/Collection/ICollectionPersister.cs +++ b/src/NHibernate/Persister/Collection/ICollectionPersister.cs @@ -9,6 +9,7 @@ using NHibernate.Metadata; using NHibernate.Persister.Entity; using NHibernate.Type; +using NHibernate.Util; namespace NHibernate.Persister.Collection { @@ -29,10 +30,14 @@ namespace NHibernate.Persister.Collection /// May be considered an immutable view of the mapping object /// public partial interface ICollectionPersister + //TODO 6.0: Uncomment + //,ICacheablePersister { + //Since 5.3 /// /// Get the cache /// + [Obsolete("Use ICacheablePersister.GetCache instead")] ICacheConcurrencyStrategy Cache { get; } /// Get the cache structure @@ -285,6 +290,7 @@ public partial interface ICollectionPersister object NotFoundObject { get; } } + //6.0 TODO: Merge into ICollectionPersister public static class CollectionPersisterExtensions { /// @@ -304,5 +310,15 @@ public static int GetBatchSize(this ICollectionPersister persister) return 1; } + + public static ICacheConcurrencyStrategy GetCache(this ICollectionPersister persister, string tenantIdentifier) + { +#pragma warning disable 618 + if (tenantIdentifier == null) + return persister.Cache; +#pragma warning restore 618 + + return ReflectHelper.CastOrThrow(persister, "multi-tenancy").GetCache(tenantIdentifier); + } } } diff --git a/src/NHibernate/Persister/Collection/OneToManyPersister.cs b/src/NHibernate/Persister/Collection/OneToManyPersister.cs index 51626e9f619..c23a9c3a7b6 100644 --- a/src/NHibernate/Persister/Collection/OneToManyPersister.cs +++ b/src/NHibernate/Persister/Collection/OneToManyPersister.cs @@ -23,8 +23,15 @@ public partial class OneToManyPersister : AbstractCollectionPersister, ISupportS private readonly bool _keyIsNullable; private readonly bool _keyIsUpdateable; + //Since 5.3 + [Obsolete("Use constructor with cacheByTenant delegate")] public OneToManyPersister(Mapping.Collection collection, ICacheConcurrencyStrategy cache, ISessionFactoryImplementor factory) - : base(collection, cache, factory) + : base(collection, tenantId => cache, factory) + { + } + + public OneToManyPersister(Mapping.Collection collection, Func cacheByTenant, ISessionFactoryImplementor factory) + : base(collection, cacheByTenant, factory) { _cascadeDeleteEnabled = collection.Key.IsCascadeDeleteEnabled && factory.Dialect.SupportsCascadeDelete; _keyIsNullable = collection.Key.IsNullable; diff --git a/src/NHibernate/Persister/Entity/AbstractEntityPersister.cs b/src/NHibernate/Persister/Entity/AbstractEntityPersister.cs index 9901611ba10..a48c1b4d0c5 100644 --- a/src/NHibernate/Persister/Entity/AbstractEntityPersister.cs +++ b/src/NHibernate/Persister/Entity/AbstractEntityPersister.cs @@ -40,7 +40,7 @@ namespace NHibernate.Persister.Entity /// public abstract partial class AbstractEntityPersister : IOuterJoinLoadable, IQueryable, IClassMetadata, IUniqueKeyLoadable, ISqlLoadable, ILazyPropertyInitializer, IPostInsertIdentityPersister, ILockable, - ISupportSelectModeJoinable, ICompositeKeyPostInsertIdentityPersister + ISupportSelectModeJoinable, ICompositeKeyPostInsertIdentityPersister, ICacheablePersister { #region InclusionChecker @@ -118,7 +118,7 @@ public virtual void BindValues(DbCommand ps) private readonly ISessionFactoryImplementor factory; - private readonly ICacheConcurrencyStrategy cache; + private readonly Func _cacheByTenant; private readonly bool isLazyPropertiesCacheable; private readonly ICacheEntryStructure cacheEntryStructure; private readonly EntityMetamodel entityMetamodel; @@ -262,11 +262,20 @@ public virtual void BindValues(DbCommand ps) private readonly Lazy defaultUniqueKeyPropertyNamesForSelectId; private readonly Dictionary propertyTableNumbersByNameAndSubclass = new Dictionary(); - protected AbstractEntityPersister(PersistentClass persistentClass, ICacheConcurrencyStrategy cache, + [Obsolete("Use constructor with cacheByTenant delegate")] + protected AbstractEntityPersister( + PersistentClass persistentClass, + ICacheConcurrencyStrategy cache, + ISessionFactoryImplementor factory) : this(persistentClass, tenantIdentifier => cache, factory) + { + //TODO: Throw if multitenancy is enabled + } + + protected AbstractEntityPersister(PersistentClass persistentClass, Func cacheByTenant, ISessionFactoryImplementor factory) { this.factory = factory; - this.cache = cache; + _cacheByTenant = cacheByTenant; isLazyPropertiesCacheable = persistentClass.IsLazyPropertiesCacheable; cacheEntryStructure = factory.Settings.IsStructuredCacheEntriesEnabled ? (ICacheEntryStructure)new StructuredCacheEntry(this) @@ -825,10 +834,10 @@ public EntityMetamodel EntityMetamodel get { return entityMetamodel; } } - public ICacheConcurrencyStrategy Cache - { - get { return cache; } - } + [Obsolete("Use GetCache mehtod instead")] + public ICacheConcurrencyStrategy Cache => _cacheByTenant(null); + + public ICacheConcurrencyStrategy GetCache(string tenantIdentifier) => _cacheByTenant?.Invoke(tenantIdentifier); public ICacheEntryStructure CacheEntryStructure { @@ -1373,8 +1382,9 @@ public virtual object InitializeLazyProperty(string fieldName, object entity, IS var uninitializedLazyProperties = InstrumentationMetadata.GetUninitializedLazyProperties(entity); if (HasCache && session.CacheMode.HasFlag(CacheMode.Get) && IsLazyPropertiesCacheable) { - CacheKey cacheKey = session.GenerateCacheKey(id, IdentifierType, EntityName); - object ce = Cache.Get(cacheKey, session.Timestamp); + + CacheKey cacheKey = session.GetCacheAndKey(id, this, out var cache); + object ce = cache.Get(cacheKey, session.Timestamp); if (ce != null) { CacheEntry cacheEntry = (CacheEntry)CacheEntryStructure.Destructure(ce, factory); @@ -4146,7 +4156,7 @@ protected internal IEntityTuplizer GetTuplizer(ISessionImplementor session) public virtual bool HasCache { - get { return cache != null; } + get { return _cacheByTenant != null; } } private string GetSubclassEntityName(System.Type clazz) @@ -4233,8 +4243,8 @@ public virtual void AfterReassociate(object entity, ISessionImplementor session) // check to see if it is in the second-level cache if (HasCache && session.CacheMode.HasFlag(CacheMode.Get)) { - CacheKey ck = session.GenerateCacheKey(id, IdentifierType, RootEntityName); - if (Cache.Get(ck, session.Timestamp) != null) + CacheKey ck = session.GetCacheAndKey(id, this, out var cache); + if (cache.Get(ck, session.Timestamp) != null) return false; } diff --git a/src/NHibernate/Persister/Entity/IEntityPersister.cs b/src/NHibernate/Persister/Entity/IEntityPersister.cs index 495e6147ddd..5abad9e4cac 100644 --- a/src/NHibernate/Persister/Entity/IEntityPersister.cs +++ b/src/NHibernate/Persister/Entity/IEntityPersister.cs @@ -13,6 +13,29 @@ namespace NHibernate.Persister.Entity { + + //TODO 6.0: Make base for ICollectionPersister and IPersister + /// + /// Base interface for cacheable persisters + /// + public interface ICacheablePersister + { + /// + /// Get the cache + /// + /// tenantIdentifier or null if multi-tenancy is disabled + /// + ICacheConcurrencyStrategy GetCache(string tenantIdentifier); + + /// Get the cache structure + ICacheEntryStructure CacheEntryStructure { get;} + + /// + /// Is this persister cacheable + /// + bool HasCache { get; } + } + public struct EntityPersister { /// The property name of the "special" identifier property in HQL @@ -27,6 +50,8 @@ public struct EntityPersister /// matching the signature of: (PersistentClass, SessionFactoryImplementor) /// public partial interface IEntityPersister : IOptimisticCacheSource + //TODO 6.0: Uncomment + //,ICacheablePersister { /// /// The ISessionFactory to which this persister "belongs". @@ -195,9 +220,11 @@ public partial interface IEntityPersister : IOptimisticCacheSource /// bool IsLazyPropertiesCacheable { get; } + //Since 5.3 /// /// Get the cache (optional operation) /// + [Obsolete("Implement and use ICacheablePersister.GetCache instead")] ICacheConcurrencyStrategy Cache { get; } /// Get the cache structure @@ -606,13 +633,14 @@ void Update( IEntityTuplizer EntityTuplizer { get; } } - internal static class EntityPersisterExtensions + //6.0 TODO: Merge into IEntityPersister. + public static class EntityPersisterExtensions { /// /// Get the batch size of a entity persister. /// //6.0 TODO: Merge into IEntityPersister. - public static int GetBatchSize(this IEntityPersister persister) + internal static int GetBatchSize(this IEntityPersister persister) { if (persister is AbstractEntityPersister acp) { @@ -628,7 +656,7 @@ public static int GetBatchSize(this IEntityPersister persister) /// Called just after the entities properties have been initialized //6.0 TODO: Merge into IEntityPersister. - public static void AfterInitialize(this IEntityPersister persister, object entity, ISessionImplementor session) + internal static void AfterInitialize(this IEntityPersister persister, object entity, ISessionImplementor session) { if (persister is AbstractEntityPersister abstractEntityPersister) { @@ -640,5 +668,15 @@ public static void AfterInitialize(this IEntityPersister persister, object entit persister.AfterInitialize(entity, true, session); #pragma warning restore 618 } + + public static ICacheConcurrencyStrategy GetCache(this IEntityPersister persister, string tenantIdentifier) + { +#pragma warning disable 618 + if (tenantIdentifier == null) + return persister.Cache; +#pragma warning restore 618 + + return ReflectHelper.CastOrThrow(persister, "multi-tenancy").GetCache(tenantIdentifier); + } } } diff --git a/src/NHibernate/Persister/Entity/JoinedSubclassEntityPersister.cs b/src/NHibernate/Persister/Entity/JoinedSubclassEntityPersister.cs index 47b0a7c19a7..957caa8a9b3 100644 --- a/src/NHibernate/Persister/Entity/JoinedSubclassEntityPersister.cs +++ b/src/NHibernate/Persister/Entity/JoinedSubclassEntityPersister.cs @@ -88,6 +88,14 @@ public class JoinedSubclassEntityPersister : AbstractEntityPersister private readonly string discriminatorSQLString; private readonly object discriminatorValue; + public JoinedSubclassEntityPersister( + PersistentClass persistentClass, + ICacheConcurrencyStrategy cache, + ISessionFactoryImplementor factory, + IMapping mapping): this(persistentClass, tenantId => cache, factory, mapping) + { + } + /// /// Constructs the NormalizedEntityPerister for the PersistentClass. /// @@ -95,7 +103,7 @@ public class JoinedSubclassEntityPersister : AbstractEntityPersister /// The configured . /// The SessionFactory that this EntityPersister will be stored in. /// The mapping used to retrieve type information. - public JoinedSubclassEntityPersister(PersistentClass persistentClass, ICacheConcurrencyStrategy cache, + public JoinedSubclassEntityPersister(PersistentClass persistentClass, Func cache, ISessionFactoryImplementor factory, IMapping mapping) : base(persistentClass, cache, factory) { diff --git a/src/NHibernate/Persister/Entity/SingleTableEntityPersister.cs b/src/NHibernate/Persister/Entity/SingleTableEntityPersister.cs index 9aa8a71a3e4..f4849ddc920 100644 --- a/src/NHibernate/Persister/Entity/SingleTableEntityPersister.cs +++ b/src/NHibernate/Persister/Entity/SingleTableEntityPersister.cs @@ -76,7 +76,17 @@ public class SingleTableEntityPersister : AbstractEntityPersister, IQueryable //provided so we can join to keys other than the primary key private readonly Dictionary joinToKeyColumns; - public SingleTableEntityPersister(PersistentClass persistentClass, ICacheConcurrencyStrategy cache, + [Obsolete("Use constructor with cacheByTenant delegate")] + public SingleTableEntityPersister( + PersistentClass persistentClass, + ICacheConcurrencyStrategy cache, + ISessionFactoryImplementor factory, + IMapping mapping) + : this(persistentClass, tenantId => cache, factory, mapping) + { + } + + public SingleTableEntityPersister(PersistentClass persistentClass, Func cache, ISessionFactoryImplementor factory, IMapping mapping) : base(persistentClass, cache, factory) { diff --git a/src/NHibernate/Persister/Entity/UnionSubclassEntityPersister.cs b/src/NHibernate/Persister/Entity/UnionSubclassEntityPersister.cs index 346d0dfad2a..e52a9284a7e 100644 --- a/src/NHibernate/Persister/Entity/UnionSubclassEntityPersister.cs +++ b/src/NHibernate/Persister/Entity/UnionSubclassEntityPersister.cs @@ -1,3 +1,4 @@ +using System; using System.Collections.Generic; using System.Text; using NHibernate.Cache; @@ -25,8 +26,18 @@ public class UnionSubclassEntityPersister : AbstractEntityPersister private readonly string[] constraintOrderedTableNames; private readonly string[][] constraintOrderedKeyColumnNames; - public UnionSubclassEntityPersister(PersistentClass persistentClass, ICacheConcurrencyStrategy cache, - ISessionFactoryImplementor factory, IMapping mapping):base(persistentClass, cache, factory) + [Obsolete("Use constructor with cacheByTenant delegate")] + public UnionSubclassEntityPersister( + PersistentClass persistentClass, + ICacheConcurrencyStrategy cache, + ISessionFactoryImplementor factory, + IMapping mapping) : this(persistentClass, tenantId => cache, factory, mapping) + { + } + + public UnionSubclassEntityPersister(PersistentClass persistentClass, + Func cacheByTenant, + ISessionFactoryImplementor factory, IMapping mapping):base(persistentClass, cacheByTenant, factory) { if (IdentifierGenerator is IdentityGenerator) { @@ -393,4 +404,4 @@ public override string GetPropertyTableName(string propertyName) return TableName; } } -} \ No newline at end of file +} diff --git a/src/NHibernate/Persister/PersisterFactory.cs b/src/NHibernate/Persister/PersisterFactory.cs index 79e0cdf08a9..6db65c7cec6 100644 --- a/src/NHibernate/Persister/PersisterFactory.cs +++ b/src/NHibernate/Persister/PersisterFactory.cs @@ -3,6 +3,7 @@ using System.Text; using NHibernate.Cache; using NHibernate.Engine; +using NHibernate.Impl; using NHibernate.Mapping; using NHibernate.Persister.Collection; using NHibernate.Persister.Entity; @@ -18,23 +19,10 @@ public static class PersisterFactory //TODO: make ClassPersisters *not* depend on ISessionFactoryImplementor // interface, if possible - static readonly System.Type[] PersisterConstructorArgs = { - typeof(PersistentClass), - typeof(ICacheConcurrencyStrategy), - typeof(ISessionFactoryImplementor), - typeof(IMapping) - }; - - static readonly System.Type[] CollectionPersisterConstructorArgs = - { - typeof(Mapping.Collection), - typeof(ICacheConcurrencyStrategy), - typeof(ISessionFactoryImplementor) - }; - /// /// Creates a built in Entity Persister or a custom Persister. /// + [Obsolete("Use overload with cacheByTenant delegate")] public static IEntityPersister CreateClassPersister(PersistentClass model, ICacheConcurrencyStrategy cache, ISessionFactoryImplementor factory, IMapping cfg) { @@ -58,6 +46,30 @@ public static IEntityPersister CreateClassPersister(PersistentClass model, ICach } } + public static IEntityPersister CreateClassPersister(PersistentClass model, Func cacheByTenant, SessionFactoryImpl factory, IMapping cfg) + { + System.Type persisterClass = model.EntityPersisterClass; + + if (persisterClass == null || persisterClass == typeof(SingleTableEntityPersister)) + { + return new SingleTableEntityPersister(model, cacheByTenant, factory, cfg); + } + else if (persisterClass == typeof(JoinedSubclassEntityPersister)) + { + return new JoinedSubclassEntityPersister(model, cacheByTenant, factory, cfg); + } + else if (persisterClass == typeof(UnionSubclassEntityPersister)) + { + return new UnionSubclassEntityPersister(model, cacheByTenant, factory, cfg); + } + else + { + return Create(persisterClass, model, cacheByTenant, factory, cfg); + } + } + + //Since 5.3 + [Obsolete("Use overload with cacheByTenant delegate")] public static ICollectionPersister CreateCollectionPersister(Mapping.Collection model, ICacheConcurrencyStrategy cache, ISessionFactoryImplementor factory) { @@ -76,9 +88,73 @@ public static ICollectionPersister CreateCollectionPersister(Mapping.Collection } } + public static ICollectionPersister CreateCollectionPersister(Mapping.Collection model, Func cacheByTenant, SessionFactoryImpl factory) + { + System.Type persisterClass = model.CollectionPersisterClass; + if (persisterClass == null) + { + // default behaviour + if (model.IsOneToMany) + return new OneToManyPersister(model, cacheByTenant, factory); + return new BasicCollectionPersister(model, cacheByTenant, factory); + } + + return Create(persisterClass, model, cacheByTenant, factory); + } + /// /// Creates a specific Persister - could be a built in or custom persister. /// + public static IEntityPersister Create(System.Type persisterClass, PersistentClass model, + Func cache, ISessionFactoryImplementor factory, + IMapping cfg) + { + ConstructorInfo pc; + try + { + pc = persisterClass.GetConstructor(new[] { + TypeOf(model), + TypeOf(cache), + TypeOf(factory), + TypeOf(cfg) + }); + +#pragma warning disable CS0618 // Type or member is obsolete + //To support legacy custom persisters + if (pc == null) + { + return Create(persisterClass, model, cache(null), factory, cfg); + } +#pragma warning restore CS0618 // Type or member is obsolete + } + catch (Exception e) + { + throw new MappingException("Could not get constructor for " + persisterClass.Name, e); + } + + try + { + return (IEntityPersister) pc.Invoke(new object[] {model, cache, factory, cfg}); + } + catch (TargetInvocationException tie) + { + Exception e = tie.InnerException; + if (e is HibernateException) + { + throw ReflectHelper.UnwrapTargetInvocationException(tie); + } + else + { + throw new MappingException("Could not instantiate persister " + persisterClass.Name, e); + } + } + catch (Exception e) + { + throw new MappingException("Could not instantiate persister " + persisterClass.Name, e); + } + } + + [Obsolete("Please use overload with cacheByTenant delegate")] public static IEntityPersister Create(System.Type persisterClass, PersistentClass model, ICacheConcurrencyStrategy cache, ISessionFactoryImplementor factory, IMapping cfg) @@ -86,7 +162,12 @@ public static IEntityPersister Create(System.Type persisterClass, PersistentClas ConstructorInfo pc; try { - pc = persisterClass.GetConstructor(PersisterConstructorArgs); + pc = persisterClass.GetConstructor(new[] { + TypeOf(model), + TypeOf(cache), + TypeOf(factory), + TypeOf(cfg) + }); } catch (Exception e) { @@ -115,24 +196,27 @@ public static IEntityPersister Create(System.Type persisterClass, PersistentClas } } + //Since 5.3 + [Obsolete("Use overload with cacheByTenant delegate")] public static ICollectionPersister Create(System.Type persisterClass, Mapping.Collection model, ICacheConcurrencyStrategy cache, ISessionFactoryImplementor factory) { ConstructorInfo pc; + var ctorArgs = new[] {TypeOf(model), TypeOf(cache), TypeOf(factory)}; try { - pc = persisterClass.GetConstructor(CollectionPersisterConstructorArgs); + pc = persisterClass.GetConstructor(ctorArgs); } catch (Exception e) { - throw new MappingException("Could not get constructor for " + persisterClass.Name, e); + throw new MappingException("Could not get constructor for " + persisterClass.Name, e); } if(pc == null) { var messageBuilder = new StringBuilder(); messageBuilder.Append("Could not find a public constructor for ").Append(persisterClass.Name).AppendLine(";"); - messageBuilder.Append("- The ctor may have ").Append(CollectionPersisterConstructorArgs.Length).AppendLine(" parameters of types (in order):"); - System.Array.ForEach(CollectionPersisterConstructorArgs, t=> messageBuilder.AppendLine(t.FullName)); + messageBuilder.Append("- The ctor may have ").Append(ctorArgs.Length).AppendLine(" parameters of types (in order):"); + System.Array.ForEach(ctorArgs, t=> messageBuilder.AppendLine(t.FullName)); throw new MappingException(messageBuilder.ToString()); } try @@ -156,5 +240,66 @@ public static ICollectionPersister Create(System.Type persisterClass, Mapping.Co throw new MappingException("Could not instantiate collection persister " + persisterClass.Name, e); } } + + public static ICollectionPersister Create(System.Type persisterClass, Mapping.Collection model, + Func cacheByTenant, ISessionFactoryImplementor factory) + { + ConstructorInfo pc; + var ctorArgs = new[] + { + TypeOf(model), + TypeOf(cacheByTenant), + TypeOf(factory) + }; + try + { + pc = persisterClass.GetConstructor(ctorArgs); + +#pragma warning disable CS0618 // Type or member is obsolete + //To support legacy custom persisters + if (pc == null) + { + return Create(persisterClass, model, cacheByTenant(null), factory); + } +#pragma warning restore CS0618 // Type or member is obsolete + } + catch (Exception e) + { + throw new MappingException("Could not get constructor for " + persisterClass.Name, e); + } + if(pc == null) + { + var messageBuilder = new StringBuilder(); + messageBuilder.Append("Could not find a public constructor for ").Append(persisterClass.Name).AppendLine(";"); + messageBuilder.Append("- The ctor may have ").Append(ctorArgs.Length).AppendLine(" parameters of types (in order):"); + System.Array.ForEach(ctorArgs, t=> messageBuilder.AppendLine(t.FullName)); + throw new MappingException(messageBuilder.ToString()); + } + try + { + return (ICollectionPersister) pc.Invoke(new object[] {model, cacheByTenant, factory}); + } + catch (TargetInvocationException tie) + { + Exception e = tie.InnerException; + if (e is HibernateException) + { + throw ReflectHelper.UnwrapTargetInvocationException(tie); + } + else + { + throw new MappingException("Could not instantiate collection persister " + persisterClass.Name, e); + } + } + catch (Exception e) + { + throw new MappingException("Could not instantiate collection persister " + persisterClass.Name, e); + } + } + + private static System.Type TypeOf(T param) + { + return typeof(T); + } } }