diff --git a/src/NHibernate.Test/Async/NHSpecificTest/GH2559/FixtureByCode.cs b/src/NHibernate.Test/Async/NHSpecificTest/GH2559/FixtureByCode.cs
new file mode 100644
index 00000000000..8dea23c6d76
--- /dev/null
+++ b/src/NHibernate.Test/Async/NHSpecificTest/GH2559/FixtureByCode.cs
@@ -0,0 +1,156 @@
+//------------------------------------------------------------------------------
+//
+// 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.Linq;
+using NHibernate.Cfg.MappingSchema;
+using NHibernate.Linq;
+using NHibernate.Mapping.ByCode;
+using NUnit.Framework;
+
+namespace NHibernate.Test.NHSpecificTest.GH2559
+{
+ using System.Threading.Tasks;
+ [TestFixture]
+ public class ByCodeFixtureAsync : TestCaseMappingByCode
+ {
+ protected override HbmMapping GetMappings()
+ {
+ var mapper = new ModelMapper();
+ mapper.Class(rc =>
+ {
+ rc.Id(x => x.Id, m => m.Generator(Generators.Guid));
+ rc.Property(x => x.Name);
+ rc.Property(x => x.Age);
+ rc.Set(
+ x => x.Children,
+ colMap =>
+ {
+ colMap.Inverse(true);
+ colMap.Cascade(Mapping.ByCode.Cascade.DeleteOrphans);
+ colMap.Cache(c => c.Usage(CacheUsage.ReadWrite));
+ },
+ rel => rel.OneToMany());
+ rc.Set(
+ x => x.Cars,
+ colMap =>
+ {
+ colMap.Inverse(true);
+ colMap.Cascade(Mapping.ByCode.Cascade.DeleteOrphans);
+ colMap.Cache(c => c.Usage(CacheUsage.ReadWrite));
+ },
+ rel => rel.OneToMany());
+ rc.Cache(c => c.Usage(CacheUsage.ReadWrite));
+ });
+ mapper.Class(ch =>
+ {
+ ch.Id(x => x.Id, m => m.Generator(Generators.Guid));
+ ch.Property(x => x.Name);
+ ch.ManyToOne(c => c.Parent);
+
+ ch.Set(
+ x => x.Pets,
+ colMap =>
+ {
+ colMap.Inverse(true);
+ colMap.Cascade(Mapping.ByCode.Cascade.DeleteOrphans);
+ colMap.Cache(c => c.Usage(CacheUsage.ReadWrite));
+ },
+ rel => rel.OneToMany());
+
+ ch.Cache(c => c.Usage(CacheUsage.ReadWrite));
+ });
+ mapper.Class(ch =>
+ {
+ ch.Id(x => x.Id, m => m.Generator(Generators.Guid));
+ ch.Property(x => x.Name);
+ ch.ManyToOne(c => c.Owner);
+
+ ch.Cache(c => c.Usage(CacheUsage.ReadWrite));
+ });
+ mapper.Class(ch =>
+ {
+ ch.Id(x => x.Id, m => m.Generator(Generators.Guid));
+ ch.Property(x => x.Name);
+ ch.ManyToOne(c => c.Owner);
+
+ ch.Cache(c => c.Usage(CacheUsage.ReadWrite));
+ });
+
+ return mapper.CompileMappingForAllExplicitlyAddedEntities();
+ }
+
+ protected override void OnSetUp()
+ {
+ using (var session = OpenSession())
+ using (var transaction = session.BeginTransaction())
+ {
+ var person = new Person { Name = "Person 1", Age = 18 };
+
+ var car1 = new Car { Name = "Car1", Owner = person };
+ var car2 = new Car { Name = "Car2", Owner = person };
+ session.Save(car1);
+ session.Save(car2);
+
+ session.Save(person);
+ transaction.Commit();
+ }
+ }
+
+ protected override void OnTearDown()
+ {
+ using (var session = OpenSession())
+ using (var transaction = session.BeginTransaction())
+ {
+ session.CreateQuery("delete from Pet").ExecuteUpdate();
+ session.CreateQuery("delete from Child").ExecuteUpdate();
+ session.CreateQuery("delete from Car").ExecuteUpdate();
+ session.CreateQuery("delete from Person").ExecuteUpdate();
+ transaction.Commit();
+ }
+ }
+
+ [Test]
+ public async Task TestQueryCachingWithThenFetchManyAsync()
+ {
+ Person dbPerson;
+ Person cachePerson;
+ using (var session = OpenSession())
+ using (var transaction = session.BeginTransaction())
+ {
+ var query =
+ session
+ .Query()
+ .FetchMany(p => p.Children)
+ .ThenFetchMany(ch => ch.Pets)
+ .FetchMany(p => p.Cars) as IQueryable;
+
+ query = query.WithOptions(opt =>
+ opt.SetCacheable(true)
+ .SetCacheMode(CacheMode.Normal)
+ .SetCacheRegion("Long_Cache"));
+
+ dbPerson = (await (query.ToListAsync())).First(); // First time the result will be cached
+ cachePerson = (await (query.ToListAsync())).First();
+
+ await (transaction.CommitAsync());
+ }
+
+ Assert.That(NHibernateUtil.IsInitialized(dbPerson.Cars), Is.True);
+ Assert.That(NHibernateUtil.IsInitialized(cachePerson.Cars), Is.True);
+ Assert.That(dbPerson.Cars, Has.Count.EqualTo(2));
+ Assert.That(cachePerson.Cars, Has.Count.EqualTo(2));
+
+ Assert.That(NHibernateUtil.IsInitialized(dbPerson.Children), Is.True);
+ Assert.That(NHibernateUtil.IsInitialized(cachePerson.Children), Is.True);
+ Assert.That(dbPerson.Children, Has.Count.EqualTo(0));
+ Assert.That(cachePerson.Children, Has.Count.EqualTo(0));
+ }
+ }
+}
diff --git a/src/NHibernate.Test/NHSpecificTest/GH2559/Car.cs b/src/NHibernate.Test/NHSpecificTest/GH2559/Car.cs
new file mode 100644
index 00000000000..b93e028c8b6
--- /dev/null
+++ b/src/NHibernate.Test/NHSpecificTest/GH2559/Car.cs
@@ -0,0 +1,12 @@
+using System;
+
+namespace NHibernate.Test.NHSpecificTest.GH2559
+{
+ public class Car
+ {
+ public virtual Guid Id { get; set; }
+ public virtual string Name { get; set; }
+
+ public virtual Person Owner { get; set; }
+ }
+}
diff --git a/src/NHibernate.Test/NHSpecificTest/GH2559/Child.cs b/src/NHibernate.Test/NHSpecificTest/GH2559/Child.cs
new file mode 100644
index 00000000000..43f56d8b701
--- /dev/null
+++ b/src/NHibernate.Test/NHSpecificTest/GH2559/Child.cs
@@ -0,0 +1,20 @@
+using System;
+using System.Collections.Generic;
+
+namespace NHibernate.Test.NHSpecificTest.GH2559
+{
+ public class Child
+ {
+ public virtual Guid Id { get; set; }
+ public virtual string Name { get; set; }
+
+ public virtual Person Parent { get; set; }
+
+ public virtual ISet Pets
+ {
+ get => _pets ?? (_pets = new HashSet());
+ set => _pets = value;
+ }
+ private ISet _pets;
+ }
+}
diff --git a/src/NHibernate.Test/NHSpecificTest/GH2559/FixtureByCode.cs b/src/NHibernate.Test/NHSpecificTest/GH2559/FixtureByCode.cs
new file mode 100644
index 00000000000..33348db8bd9
--- /dev/null
+++ b/src/NHibernate.Test/NHSpecificTest/GH2559/FixtureByCode.cs
@@ -0,0 +1,145 @@
+using System.Linq;
+using NHibernate.Cfg.MappingSchema;
+using NHibernate.Linq;
+using NHibernate.Mapping.ByCode;
+using NUnit.Framework;
+
+namespace NHibernate.Test.NHSpecificTest.GH2559
+{
+ [TestFixture]
+ public class ByCodeFixture : TestCaseMappingByCode
+ {
+ protected override HbmMapping GetMappings()
+ {
+ var mapper = new ModelMapper();
+ mapper.Class(rc =>
+ {
+ rc.Id(x => x.Id, m => m.Generator(Generators.Guid));
+ rc.Property(x => x.Name);
+ rc.Property(x => x.Age);
+ rc.Set(
+ x => x.Children,
+ colMap =>
+ {
+ colMap.Inverse(true);
+ colMap.Cascade(Mapping.ByCode.Cascade.DeleteOrphans);
+ colMap.Cache(c => c.Usage(CacheUsage.ReadWrite));
+ },
+ rel => rel.OneToMany());
+ rc.Set(
+ x => x.Cars,
+ colMap =>
+ {
+ colMap.Inverse(true);
+ colMap.Cascade(Mapping.ByCode.Cascade.DeleteOrphans);
+ colMap.Cache(c => c.Usage(CacheUsage.ReadWrite));
+ },
+ rel => rel.OneToMany());
+ rc.Cache(c => c.Usage(CacheUsage.ReadWrite));
+ });
+ mapper.Class(ch =>
+ {
+ ch.Id(x => x.Id, m => m.Generator(Generators.Guid));
+ ch.Property(x => x.Name);
+ ch.ManyToOne(c => c.Parent);
+
+ ch.Set(
+ x => x.Pets,
+ colMap =>
+ {
+ colMap.Inverse(true);
+ colMap.Cascade(Mapping.ByCode.Cascade.DeleteOrphans);
+ colMap.Cache(c => c.Usage(CacheUsage.ReadWrite));
+ },
+ rel => rel.OneToMany());
+
+ ch.Cache(c => c.Usage(CacheUsage.ReadWrite));
+ });
+ mapper.Class(ch =>
+ {
+ ch.Id(x => x.Id, m => m.Generator(Generators.Guid));
+ ch.Property(x => x.Name);
+ ch.ManyToOne(c => c.Owner);
+
+ ch.Cache(c => c.Usage(CacheUsage.ReadWrite));
+ });
+ mapper.Class(ch =>
+ {
+ ch.Id(x => x.Id, m => m.Generator(Generators.Guid));
+ ch.Property(x => x.Name);
+ ch.ManyToOne(c => c.Owner);
+
+ ch.Cache(c => c.Usage(CacheUsage.ReadWrite));
+ });
+
+ return mapper.CompileMappingForAllExplicitlyAddedEntities();
+ }
+
+ protected override void OnSetUp()
+ {
+ using (var session = OpenSession())
+ using (var transaction = session.BeginTransaction())
+ {
+ var person = new Person { Name = "Person 1", Age = 18 };
+
+ var car1 = new Car { Name = "Car1", Owner = person };
+ var car2 = new Car { Name = "Car2", Owner = person };
+ session.Save(car1);
+ session.Save(car2);
+
+ session.Save(person);
+ transaction.Commit();
+ }
+ }
+
+ protected override void OnTearDown()
+ {
+ using (var session = OpenSession())
+ using (var transaction = session.BeginTransaction())
+ {
+ session.CreateQuery("delete from Pet").ExecuteUpdate();
+ session.CreateQuery("delete from Child").ExecuteUpdate();
+ session.CreateQuery("delete from Car").ExecuteUpdate();
+ session.CreateQuery("delete from Person").ExecuteUpdate();
+ transaction.Commit();
+ }
+ }
+
+ [Test]
+ public void TestQueryCachingWithThenFetchMany()
+ {
+ Person dbPerson;
+ Person cachePerson;
+ using (var session = OpenSession())
+ using (var transaction = session.BeginTransaction())
+ {
+ var query =
+ session
+ .Query()
+ .FetchMany(p => p.Children)
+ .ThenFetchMany(ch => ch.Pets)
+ .FetchMany(p => p.Cars) as IQueryable;
+
+ query = query.WithOptions(opt =>
+ opt.SetCacheable(true)
+ .SetCacheMode(CacheMode.Normal)
+ .SetCacheRegion("Long_Cache"));
+
+ dbPerson = query.ToList().First(); // First time the result will be cached
+ cachePerson = query.ToList().First();
+
+ transaction.Commit();
+ }
+
+ Assert.That(NHibernateUtil.IsInitialized(dbPerson.Cars), Is.True);
+ Assert.That(NHibernateUtil.IsInitialized(cachePerson.Cars), Is.True);
+ Assert.That(dbPerson.Cars, Has.Count.EqualTo(2));
+ Assert.That(cachePerson.Cars, Has.Count.EqualTo(2));
+
+ Assert.That(NHibernateUtil.IsInitialized(dbPerson.Children), Is.True);
+ Assert.That(NHibernateUtil.IsInitialized(cachePerson.Children), Is.True);
+ Assert.That(dbPerson.Children, Has.Count.EqualTo(0));
+ Assert.That(cachePerson.Children, Has.Count.EqualTo(0));
+ }
+ }
+}
diff --git a/src/NHibernate.Test/NHSpecificTest/GH2559/Person.cs b/src/NHibernate.Test/NHSpecificTest/GH2559/Person.cs
new file mode 100644
index 00000000000..8d125021ec9
--- /dev/null
+++ b/src/NHibernate.Test/NHSpecificTest/GH2559/Person.cs
@@ -0,0 +1,26 @@
+using System;
+using System.Collections.Generic;
+
+namespace NHibernate.Test.NHSpecificTest.GH2559
+{
+ public class Person
+ {
+ public virtual Guid Id { get; set; }
+ public virtual string Name { get; set; }
+ public virtual int Age { get; set; }
+
+ public virtual ISet Cars
+ {
+ get => _cars ?? (_cars = new HashSet());
+ set => _cars = value;
+ }
+ private ISet _cars;
+
+ public virtual ISet Children
+ {
+ get => _children ?? (_children = new HashSet());
+ set => _children = value;
+ }
+ private ISet _children;
+ }
+}
diff --git a/src/NHibernate.Test/NHSpecificTest/GH2559/Pet.cs b/src/NHibernate.Test/NHSpecificTest/GH2559/Pet.cs
new file mode 100644
index 00000000000..7b580d24bd1
--- /dev/null
+++ b/src/NHibernate.Test/NHSpecificTest/GH2559/Pet.cs
@@ -0,0 +1,12 @@
+using System;
+
+namespace NHibernate.Test.NHSpecificTest.GH2559
+{
+ public class Pet
+ {
+ public virtual Guid Id { get; set; }
+ public virtual string Name { get; set; }
+
+ public virtual Child Owner { get; set; }
+ }
+}
diff --git a/src/NHibernate/Async/Type/CollectionType.cs b/src/NHibernate/Async/Type/CollectionType.cs
index b103b445c22..432c11957a8 100644
--- a/src/NHibernate/Async/Type/CollectionType.cs
+++ b/src/NHibernate/Async/Type/CollectionType.cs
@@ -104,6 +104,11 @@ public override async Task