diff --git a/src/AsyncGenerator.yml b/src/AsyncGenerator.yml
index accaaeabe72..7022d322eb6 100644
--- a/src/AsyncGenerator.yml
+++ b/src/AsyncGenerator.yml
@@ -4,8 +4,6 @@
applyChanges: true
analyzation:
methodConversion:
- - conversion: Ignore
- hasAttributeName: ObsoleteAttribute
- conversion: Ignore
name: PostProcessInsert
containingTypeName: HqlSqlWalker
@@ -96,6 +94,9 @@
- conversion: Ignore
name: Exists
containingTypeName: AbstractCollectionPersister
+ - conversion: Ignore
+ name: QuoteTableAndColumns
+ containingTypeName: SchemaMetadataUpdater
- conversion: ToAsync
name: ExecuteReader
containingTypeName: IBatcher
@@ -119,6 +120,8 @@
- name: GetFieldValue
- name: IsDBNull
- name: WriteLine
+ ignoreAsyncCounterparts:
+ - rule: Obsolete
callForwarding: true
cancellationTokens:
guards: true
@@ -259,6 +262,9 @@ methodRules:
- containingType: NHibernate.Tool.hbm2ddl.SchemaValidator
- containingType: NHibernate.Tool.hbm2ddl.SchemaExport
name: PubliclyExposedType
+- filters:
+ - hasAttributeName: ObsoleteAttribute
+ name: Obsolete
typeRules:
- filters:
- containingAssemblyName: NHibernate
diff --git a/src/NHibernate.Test/Async/SecondLevelCacheTest/InvalidationTests.cs b/src/NHibernate.Test/Async/SecondLevelCacheTest/InvalidationTests.cs
new file mode 100644
index 00000000000..76e5b040cbb
--- /dev/null
+++ b/src/NHibernate.Test/Async/SecondLevelCacheTest/InvalidationTests.cs
@@ -0,0 +1,115 @@
+//------------------------------------------------------------------------------
+//
+// This code was generated by AsyncGenerator.
+//
+// Changes to this file may cause incorrect behavior and will be lost if
+// the code is regenerated.
+//
+//------------------------------------------------------------------------------
+
+
+using System.Collections;
+using System.Collections.Generic;
+using System.Linq;
+using System.Reflection;
+using NHibernate.Cache;
+using NHibernate.Cfg;
+using NHibernate.Engine;
+using NHibernate.Impl;
+using NHibernate.Test.SecondLevelCacheTests;
+using NSubstitute;
+using NUnit.Framework;
+
+namespace NHibernate.Test.SecondLevelCacheTest
+{
+ using System.Threading.Tasks;
+ using System.Threading;
+ [TestFixture]
+ public class InvalidationTestsAsync : TestCase
+ {
+ protected override string MappingsAssembly => "NHibernate.Test";
+
+ protected override IList Mappings => new[] { "SecondLevelCacheTest.Item.hbm.xml" };
+
+ protected override void Configure(Configuration configuration)
+ {
+ configuration.SetProperty(Environment.CacheProvider, typeof(HashtableCacheProvider).AssemblyQualifiedName);
+ configuration.SetProperty(Environment.UseQueryCache, "true");
+ }
+
+ [Test]
+ public async Task InvalidatesEntitiesAsync()
+ {
+ var debugSessionFactory = (DebugSessionFactory) Sfi;
+
+ var cache = Substitute.For(Sfi.Settings, new Dictionary());
+
+ var updateTimestampsCacheField = typeof(SessionFactoryImpl).GetField(
+ "updateTimestampsCache",
+ BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public);
+
+ updateTimestampsCacheField.SetValue(debugSessionFactory.ActualFactory, cache);
+
+ //"Received" assertions can not be used since the collection is reused and cleared between calls.
+ //The received args are cloned and stored
+ var preInvalidations = new List>();
+ var invalidations = new List>();
+
+ await (cache.PreInvalidateAsync(Arg.Do>(x => preInvalidations.Add(x.ToList())), CancellationToken.None));
+ await (cache.InvalidateAsync(Arg.Do>(x => invalidations.Add(x.ToList())), CancellationToken.None));
+
+ using (var session = OpenSession())
+ {
+ using (var tx = session.BeginTransaction())
+ {
+ foreach (var i in Enumerable.Range(1, 10))
+ {
+ var item = new Item {Id = i};
+ await (session.SaveAsync(item));
+ }
+
+ await (tx.CommitAsync());
+ }
+
+ using (var tx = session.BeginTransaction())
+ {
+ foreach (var i in Enumerable.Range(1, 10))
+ {
+ var item = await (session.GetAsync- (i));
+ item.Name = item.Id.ToString();
+ }
+
+ await (tx.CommitAsync());
+ }
+
+ using (var tx = session.BeginTransaction())
+ {
+ foreach (var i in Enumerable.Range(1, 10))
+ {
+ var item = await (session.GetAsync
- (i));
+ await (session.DeleteAsync(item));
+ }
+
+ await (tx.CommitAsync());
+ }
+ }
+
+ //Should receive one preinvalidation and one invalidation per commit
+ Assert.That(preInvalidations, Has.Count.EqualTo(3));
+ Assert.That(preInvalidations, Has.All.Count.EqualTo(1).And.Contains("Item"));
+
+ Assert.That(invalidations, Has.Count.EqualTo(3));
+ Assert.That(invalidations, Has.All.Count.EqualTo(1).And.Contains("Item"));
+ }
+
+ protected override void OnTearDown()
+ {
+ using (var s = OpenSession())
+ using (var tx = s.BeginTransaction())
+ {
+ s.Delete("from Item");
+ tx.Commit();
+ }
+ }
+ }
+}
diff --git a/src/NHibernate.Test/SecondLevelCacheTest/InvalidationTests.cs b/src/NHibernate.Test/SecondLevelCacheTest/InvalidationTests.cs
new file mode 100644
index 00000000000..050a488bc1e
--- /dev/null
+++ b/src/NHibernate.Test/SecondLevelCacheTest/InvalidationTests.cs
@@ -0,0 +1,102 @@
+using System.Collections;
+using System.Collections.Generic;
+using System.Linq;
+using System.Reflection;
+using NHibernate.Cache;
+using NHibernate.Cfg;
+using NHibernate.Impl;
+using NHibernate.Test.SecondLevelCacheTests;
+using NSubstitute;
+using NUnit.Framework;
+
+namespace NHibernate.Test.SecondLevelCacheTest
+{
+ [TestFixture]
+ public class InvalidationTests : TestCase
+ {
+ protected override string MappingsAssembly => "NHibernate.Test";
+
+ protected override IList Mappings => new[] { "SecondLevelCacheTest.Item.hbm.xml" };
+
+ protected override void Configure(Configuration configuration)
+ {
+ configuration.SetProperty(Environment.CacheProvider, typeof(HashtableCacheProvider).AssemblyQualifiedName);
+ configuration.SetProperty(Environment.UseQueryCache, "true");
+ }
+
+ [Test]
+ public void InvalidatesEntities()
+ {
+ var debugSessionFactory = (DebugSessionFactory) Sfi;
+
+ var cache = Substitute.For(Sfi.Settings, new Dictionary());
+
+ var updateTimestampsCacheField = typeof(SessionFactoryImpl).GetField(
+ "updateTimestampsCache",
+ BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public);
+
+ updateTimestampsCacheField.SetValue(debugSessionFactory.ActualFactory, cache);
+
+ //"Received" assertions can not be used since the collection is reused and cleared between calls.
+ //The received args are cloned and stored
+ var preInvalidations = new List>();
+ var invalidations = new List>();
+
+ cache.PreInvalidate(Arg.Do>(x => preInvalidations.Add(x.ToList())));
+ cache.Invalidate(Arg.Do>(x => invalidations.Add(x.ToList())));
+
+ using (var session = OpenSession())
+ {
+ using (var tx = session.BeginTransaction())
+ {
+ foreach (var i in Enumerable.Range(1, 10))
+ {
+ var item = new Item {Id = i};
+ session.Save(item);
+ }
+
+ tx.Commit();
+ }
+
+ using (var tx = session.BeginTransaction())
+ {
+ foreach (var i in Enumerable.Range(1, 10))
+ {
+ var item = session.Get
- (i);
+ item.Name = item.Id.ToString();
+ }
+
+ tx.Commit();
+ }
+
+ using (var tx = session.BeginTransaction())
+ {
+ foreach (var i in Enumerable.Range(1, 10))
+ {
+ var item = session.Get
- (i);
+ session.Delete(item);
+ }
+
+ tx.Commit();
+ }
+ }
+
+ //Should receive one preinvalidation and one invalidation per commit
+ Assert.That(preInvalidations, Has.Count.EqualTo(3));
+ Assert.That(preInvalidations, Has.All.Count.EqualTo(1).And.Contains("Item"));
+
+ Assert.That(invalidations, Has.Count.EqualTo(3));
+ Assert.That(invalidations, Has.All.Count.EqualTo(1).And.Contains("Item"));
+ }
+
+ protected override void OnTearDown()
+ {
+ using (var s = OpenSession())
+ using (var tx = s.BeginTransaction())
+ {
+ s.Delete("from Item");
+ tx.Commit();
+ }
+ }
+ }
+}
diff --git a/src/NHibernate/Async/Cache/UpdateTimestampsCache.cs b/src/NHibernate/Async/Cache/UpdateTimestampsCache.cs
index 5a1867189fd..9a086f16333 100644
--- a/src/NHibernate/Async/Cache/UpdateTimestampsCache.cs
+++ b/src/NHibernate/Async/Cache/UpdateTimestampsCache.cs
@@ -10,6 +10,7 @@
using System;
using System.Collections.Generic;
+using System.Linq;
using System.Runtime.CompilerServices;
using NHibernate.Cfg;
@@ -24,7 +25,7 @@ public partial class UpdateTimestampsCache
private readonly NHibernate.Util.AsyncLock _invalidate = new NHibernate.Util.AsyncLock();
private readonly NHibernate.Util.AsyncLock _isUpToDate = new NHibernate.Util.AsyncLock();
- public Task ClearAsync(CancellationToken cancellationToken)
+ public virtual Task ClearAsync(CancellationToken cancellationToken)
{
if (cancellationToken.IsCancellationRequested)
{
@@ -33,26 +34,65 @@ public Task ClearAsync(CancellationToken cancellationToken)
return updateTimestamps.ClearAsync(cancellationToken);
}
+ //Since v5.1
+ [Obsolete("Please use PreInvalidate(IReadOnlyCollection) instead.")]
+ public Task PreInvalidateAsync(object[] spaces, CancellationToken cancellationToken)
+ {
+ if (cancellationToken.IsCancellationRequested)
+ {
+ return Task.FromCanceled