From fa6170119f8901ae4dd380178ab2a9aeb1e7c026 Mon Sep 17 00:00:00 2001 From: delta-emil <9694906+delta-emil@users.noreply.github.com> Date: Wed, 29 Oct 2025 13:07:48 +0200 Subject: [PATCH 1/3] fix #3713 - query cache result transformer aliases bug --- .../Async/CacheTest/QueryCacheFixture.cs | 36 +++++++++++++++++++ .../CacheTest/QueryCacheFixture.cs | 36 +++++++++++++++++++ src/NHibernate/Async/Loader/Loader.cs | 1 + src/NHibernate/Loader/Custom/CustomLoader.cs | 8 +++++ src/NHibernate/Loader/Loader.cs | 5 +++ 5 files changed, 86 insertions(+) diff --git a/src/NHibernate.Test/Async/CacheTest/QueryCacheFixture.cs b/src/NHibernate.Test/Async/CacheTest/QueryCacheFixture.cs index 6209bb44853..6e9860e9884 100644 --- a/src/NHibernate.Test/Async/CacheTest/QueryCacheFixture.cs +++ b/src/NHibernate.Test/Async/CacheTest/QueryCacheFixture.cs @@ -11,6 +11,7 @@ using System.Collections; using NHibernate.Cfg; using NHibernate.DomainModel; +using NHibernate.Transform; using NUnit.Framework; using Environment = NHibernate.Cfg.Environment; @@ -101,5 +102,40 @@ public async Task QueryCacheWithScalarReturnAsync() Assert.That(result, Is.EqualTo(200012), "Unexpected cached result"); } } + + [Test] + public async Task QueryCacheWithAliasToBeanTransformerAsync() + { + using (var s = OpenSession()) + { + const string query = "select s.id_ as Id from Simple as s"; + var result1 = await (s + .CreateSQLQuery(query) + .SetCacheable(true) + .SetResultTransformer(Transformers.AliasToBean()) + .UniqueResultAsync()); + + Assert.That(result1, Is.InstanceOf()); + var dto1 = (SimpleDTO)result1; + Assert.That(dto1.Id, Is.EqualTo(1)); + + // Run a second time, just to test the query cache + var result2 = await (s + .CreateSQLQuery(query) + .SetCacheable(true) + .SetResultTransformer(Transformers.AliasToBean()) + .UniqueResultAsync()); + + Assert.That(result2, Is.InstanceOf()); + var dto2 = (SimpleDTO)result2; + Assert.That(dto2.Id, Is.EqualTo(1)); + } + } + + private class SimpleDTO + { + public long Id { get; set; } + public string Address { get; set; } + } } } diff --git a/src/NHibernate.Test/CacheTest/QueryCacheFixture.cs b/src/NHibernate.Test/CacheTest/QueryCacheFixture.cs index ddd4658ea89..d18ad430f52 100644 --- a/src/NHibernate.Test/CacheTest/QueryCacheFixture.cs +++ b/src/NHibernate.Test/CacheTest/QueryCacheFixture.cs @@ -1,6 +1,7 @@ using System.Collections; using NHibernate.Cfg; using NHibernate.DomainModel; +using NHibernate.Transform; using NUnit.Framework; using Environment = NHibernate.Cfg.Environment; @@ -90,5 +91,40 @@ public void QueryCacheWithScalarReturn() Assert.That(result, Is.EqualTo(200012), "Unexpected cached result"); } } + + [Test] + public void QueryCacheWithAliasToBeanTransformer() + { + using (var s = OpenSession()) + { + const string query = "select s.id_ as Id from Simple as s"; + var result1 = s + .CreateSQLQuery(query) + .SetCacheable(true) + .SetResultTransformer(Transformers.AliasToBean()) + .UniqueResult(); + + Assert.That(result1, Is.InstanceOf()); + var dto1 = (SimpleDTO)result1; + Assert.That(dto1.Id, Is.EqualTo(1)); + + // Run a second time, just to test the query cache + var result2 = s + .CreateSQLQuery(query) + .SetCacheable(true) + .SetResultTransformer(Transformers.AliasToBean()) + .UniqueResult(); + + Assert.That(result2, Is.InstanceOf()); + var dto2 = (SimpleDTO)result2; + Assert.That(dto2.Id, Is.EqualTo(1)); + } + } + + private class SimpleDTO + { + public long Id { get; set; } + public string Address { get; set; } + } } } diff --git a/src/NHibernate/Async/Loader/Loader.cs b/src/NHibernate/Async/Loader/Loader.cs index f797434cbe4..1fbcac0933d 100644 --- a/src/NHibernate/Async/Loader/Loader.cs +++ b/src/NHibernate/Async/Loader/Loader.cs @@ -1391,6 +1391,7 @@ private async Task ListUsingQueryCacheAsync(ISessionImplementor session, else { result = queryCacheBuilder.GetResultList(result); + ApplyCachedMetadata(key.ResultTransformer); } result = TransformCacheableResults(queryParameters, key.ResultTransformer, result); diff --git a/src/NHibernate/Loader/Custom/CustomLoader.cs b/src/NHibernate/Loader/Custom/CustomLoader.cs index fb5606a283d..3283bd1cef9 100644 --- a/src/NHibernate/Loader/Custom/CustomLoader.cs +++ b/src/NHibernate/Loader/Custom/CustomLoader.cs @@ -380,6 +380,14 @@ public override void AutoDiscoverTypes( queryParameters.ResultTransformer, transformerAliases); } + protected override void ApplyCachedMetadata(CacheableResultTransformer resultTransformer) + { + if (transformerAliases.Length == 0 && resultTransformer?.AutoDiscoveredAliases?.Length > 0) + { + transformerAliases = resultTransformer.AutoDiscoveredAliases; + } + } + protected override void ResetEffectiveExpectedType(IEnumerable parameterSpecs, QueryParameters queryParameters) { parameterSpecs.ResetEffectiveExpectedType(queryParameters); diff --git a/src/NHibernate/Loader/Loader.cs b/src/NHibernate/Loader/Loader.cs index ad3b5109021..01a036fd4da 100644 --- a/src/NHibernate/Loader/Loader.cs +++ b/src/NHibernate/Loader/Loader.cs @@ -1894,6 +1894,7 @@ private IList ListUsingQueryCache(ISessionImplementor session, QueryParameters q else { result = queryCacheBuilder.GetResultList(result); + ApplyCachedMetadata(key.ResultTransformer); } result = TransformCacheableResults(queryParameters, key.ResultTransformer, result); @@ -1901,6 +1902,10 @@ private IList ListUsingQueryCache(ISessionImplementor session, QueryParameters q return GetResultList(result, queryParameters.ResultTransformer); } + protected virtual void ApplyCachedMetadata(CacheableResultTransformer resultTransformer) + { + } + public IList TransformCacheableResults(QueryParameters queryParameters, CacheableResultTransformer transformer, IList result) { var resolvedTransformer = ResolveResultTransformer(queryParameters.ResultTransformer); From 56e4ebece5d8d215d9e49ff08f0135741de89e58 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Delaporte?= <12201973+fredericDelaporte@users.noreply.github.com> Date: Sun, 9 Nov 2025 20:19:53 +0100 Subject: [PATCH 2/3] Fix test --- src/NHibernate.Test/Async/CacheTest/QueryCacheFixture.cs | 2 +- src/NHibernate.Test/CacheTest/QueryCacheFixture.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/NHibernate.Test/Async/CacheTest/QueryCacheFixture.cs b/src/NHibernate.Test/Async/CacheTest/QueryCacheFixture.cs index 6e9860e9884..abde5f81228 100644 --- a/src/NHibernate.Test/Async/CacheTest/QueryCacheFixture.cs +++ b/src/NHibernate.Test/Async/CacheTest/QueryCacheFixture.cs @@ -108,7 +108,7 @@ public async Task QueryCacheWithAliasToBeanTransformerAsync() { using (var s = OpenSession()) { - const string query = "select s.id_ as Id from Simple as s"; + const string query = "select s.id_ as Id from Simple as s;"; var result1 = await (s .CreateSQLQuery(query) .SetCacheable(true) diff --git a/src/NHibernate.Test/CacheTest/QueryCacheFixture.cs b/src/NHibernate.Test/CacheTest/QueryCacheFixture.cs index d18ad430f52..2160fbc0fa5 100644 --- a/src/NHibernate.Test/CacheTest/QueryCacheFixture.cs +++ b/src/NHibernate.Test/CacheTest/QueryCacheFixture.cs @@ -97,7 +97,7 @@ public void QueryCacheWithAliasToBeanTransformer() { using (var s = OpenSession()) { - const string query = "select s.id_ as Id from Simple as s"; + const string query = "select s.id_ as Id from Simple as s;"; var result1 = s .CreateSQLQuery(query) .SetCacheable(true) From eb92dba8a2678a7a36f74704eaf0d3c728968a74 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Delaporte?= <12201973+fredericdelaporte@users.noreply.github.com> Date: Sun, 9 Nov 2025 20:37:45 +0100 Subject: [PATCH 3/3] Use transactions --- .../Async/CacheTest/QueryCacheFixture.cs | 16 ++++++++++++---- .../CacheTest/QueryCacheFixture.cs | 16 ++++++++++++---- 2 files changed, 24 insertions(+), 8 deletions(-) diff --git a/src/NHibernate.Test/Async/CacheTest/QueryCacheFixture.cs b/src/NHibernate.Test/Async/CacheTest/QueryCacheFixture.cs index abde5f81228..31fbe3454a5 100644 --- a/src/NHibernate.Test/Async/CacheTest/QueryCacheFixture.cs +++ b/src/NHibernate.Test/Async/CacheTest/QueryCacheFixture.cs @@ -106,9 +106,11 @@ public async Task QueryCacheWithScalarReturnAsync() [Test] public async Task QueryCacheWithAliasToBeanTransformerAsync() { + const string query = "select s.id_ as Id from Simple as s;"; + using (var s = OpenSession()) + using (var t = s.BeginTransaction()) { - const string query = "select s.id_ as Id from Simple as s;"; var result1 = await (s .CreateSQLQuery(query) .SetCacheable(true) @@ -116,10 +118,15 @@ public async Task QueryCacheWithAliasToBeanTransformerAsync() .UniqueResultAsync()); Assert.That(result1, Is.InstanceOf()); - var dto1 = (SimpleDTO)result1; + var dto1 = (SimpleDTO) result1; Assert.That(dto1.Id, Is.EqualTo(1)); + await (t.CommitAsync()); + } - // Run a second time, just to test the query cache + // Run a second time, just to test the query cache + using (var s = OpenSession()) + using (var t = s.BeginTransaction()) + { var result2 = await (s .CreateSQLQuery(query) .SetCacheable(true) @@ -127,8 +134,9 @@ public async Task QueryCacheWithAliasToBeanTransformerAsync() .UniqueResultAsync()); Assert.That(result2, Is.InstanceOf()); - var dto2 = (SimpleDTO)result2; + var dto2 = (SimpleDTO) result2; Assert.That(dto2.Id, Is.EqualTo(1)); + await (t.CommitAsync()); } } diff --git a/src/NHibernate.Test/CacheTest/QueryCacheFixture.cs b/src/NHibernate.Test/CacheTest/QueryCacheFixture.cs index 2160fbc0fa5..d8a7aa68e71 100644 --- a/src/NHibernate.Test/CacheTest/QueryCacheFixture.cs +++ b/src/NHibernate.Test/CacheTest/QueryCacheFixture.cs @@ -95,9 +95,11 @@ public void QueryCacheWithScalarReturn() [Test] public void QueryCacheWithAliasToBeanTransformer() { + const string query = "select s.id_ as Id from Simple as s;"; + using (var s = OpenSession()) + using (var t = s.BeginTransaction()) { - const string query = "select s.id_ as Id from Simple as s;"; var result1 = s .CreateSQLQuery(query) .SetCacheable(true) @@ -105,10 +107,15 @@ public void QueryCacheWithAliasToBeanTransformer() .UniqueResult(); Assert.That(result1, Is.InstanceOf()); - var dto1 = (SimpleDTO)result1; + var dto1 = (SimpleDTO) result1; Assert.That(dto1.Id, Is.EqualTo(1)); + t.Commit(); + } - // Run a second time, just to test the query cache + // Run a second time, just to test the query cache + using (var s = OpenSession()) + using (var t = s.BeginTransaction()) + { var result2 = s .CreateSQLQuery(query) .SetCacheable(true) @@ -116,8 +123,9 @@ public void QueryCacheWithAliasToBeanTransformer() .UniqueResult(); Assert.That(result2, Is.InstanceOf()); - var dto2 = (SimpleDTO)result2; + var dto2 = (SimpleDTO) result2; Assert.That(dto2.Id, Is.EqualTo(1)); + t.Commit(); } }