diff --git a/src/NHibernate.Test/Async/SqlTest/Query/NativeSQLQueriesFixture.cs b/src/NHibernate.Test/Async/SqlTest/Query/NativeSQLQueriesFixture.cs index b4111a4fe9e..e0bc9034324 100644 --- a/src/NHibernate.Test/Async/SqlTest/Query/NativeSQLQueriesFixture.cs +++ b/src/NHibernate.Test/Async/SqlTest/Query/NativeSQLQueriesFixture.cs @@ -216,6 +216,72 @@ public async Task SQLQueryInterfaceCacheableAsync() } } + [Test(Description = "GH-2904")] + public async Task CacheableScalarSQLQueryAsync() + { + Organization ifa = new Organization("IFA"); + Organization jboss = new Organization("JBoss"); + Person gavin = new Person("Gavin"); + Employment emp = new Employment(gavin, jboss, "AU"); + + using (ISession s = OpenSession()) + using (ITransaction t = s.BeginTransaction()) + { + await (s.SaveAsync(ifa)); + await (s.SaveAsync(jboss)); + await (s.SaveAsync(gavin)); + await (s.SaveAsync(emp)); + await (t.CommitAsync()); + } + + using (ISession s = OpenSession()) + { + Task GetCacheableSqlQueryResultsAsync() + { + try + { + return s.CreateSQLQuery( + "select emp.REGIONCODE " + + "from ORGANIZATION org " + + "left outer join EMPLOYMENT emp on org.ORGID = emp.EMPLOYER ") + .AddScalar("regionCode", NHibernateUtil.String) + .SetCacheable(true) + .ListAsync(); + } + catch (System.Exception ex) + { + return Task.FromException(ex); + } + } + + using (EnableStatisticsScope()) + { + IList l = await (GetCacheableSqlQueryResultsAsync()); + Assert.AreEqual(2, l.Count); + Assert.That(Sfi.Statistics.QueryCacheMissCount, Is.EqualTo(1), "results are expected from DB"); + Assert.That(Sfi.Statistics.QueryCacheHitCount, Is.EqualTo(0), "results are expected from DB"); + } + + using (EnableStatisticsScope()) + { + IList l2 = await (GetCacheableSqlQueryResultsAsync()); + Assert.AreEqual(2, l2.Count); + Assert.That(Sfi.Statistics.QueryCacheMissCount, Is.EqualTo(0), "results are expected from cache"); + Assert.That(Sfi.Statistics.QueryCacheHitCount, Is.EqualTo(1), "results are expected from cache"); + } + } + + using (var s = OpenSession()) + using (var t = s.BeginTransaction()) + { + await (s.DeleteAsync(emp)); + await (s.DeleteAsync(gavin)); + await (s.DeleteAsync(ifa)); + await (s.DeleteAsync(jboss)); + await (t.CommitAsync()); + } + } + [Test] public async Task ResultSetMappingDefinitionAsync() { diff --git a/src/NHibernate.Test/SqlTest/Query/NativeSQLQueriesFixture.cs b/src/NHibernate.Test/SqlTest/Query/NativeSQLQueriesFixture.cs index 934e3641f03..6cfe83845e3 100644 --- a/src/NHibernate.Test/SqlTest/Query/NativeSQLQueriesFixture.cs +++ b/src/NHibernate.Test/SqlTest/Query/NativeSQLQueriesFixture.cs @@ -205,6 +205,65 @@ public void SQLQueryInterfaceCacheable() } } + [Test(Description = "GH-2904")] + public void CacheableScalarSQLQuery() + { + Organization ifa = new Organization("IFA"); + Organization jboss = new Organization("JBoss"); + Person gavin = new Person("Gavin"); + Employment emp = new Employment(gavin, jboss, "AU"); + + using (ISession s = OpenSession()) + using (ITransaction t = s.BeginTransaction()) + { + s.Save(ifa); + s.Save(jboss); + s.Save(gavin); + s.Save(emp); + t.Commit(); + } + + using (ISession s = OpenSession()) + { + IList GetCacheableSqlQueryResults() + { + return s.CreateSQLQuery( + "select emp.REGIONCODE " + + "from ORGANIZATION org " + + "left outer join EMPLOYMENT emp on org.ORGID = emp.EMPLOYER ") + .AddScalar("regionCode", NHibernateUtil.String) + .SetCacheable(true) + .List(); + } + + using (EnableStatisticsScope()) + { + IList l = GetCacheableSqlQueryResults(); + Assert.AreEqual(2, l.Count); + Assert.That(Sfi.Statistics.QueryCacheMissCount, Is.EqualTo(1), "results are expected from DB"); + Assert.That(Sfi.Statistics.QueryCacheHitCount, Is.EqualTo(0), "results are expected from DB"); + } + + using (EnableStatisticsScope()) + { + IList l2 = GetCacheableSqlQueryResults(); + Assert.AreEqual(2, l2.Count); + Assert.That(Sfi.Statistics.QueryCacheMissCount, Is.EqualTo(0), "results are expected from cache"); + Assert.That(Sfi.Statistics.QueryCacheHitCount, Is.EqualTo(1), "results are expected from cache"); + } + } + + using (var s = OpenSession()) + using (var t = s.BeginTransaction()) + { + s.Delete(emp); + s.Delete(gavin); + s.Delete(ifa); + s.Delete(jboss); + t.Commit(); + } + } + [Test] public void ResultSetMappingDefinition() { diff --git a/src/NHibernate.Test/TestCase.cs b/src/NHibernate.Test/TestCase.cs index 894f6d2405b..eeab3bf2dc9 100644 --- a/src/NHibernate.Test/TestCase.cs +++ b/src/NHibernate.Test/TestCase.cs @@ -389,6 +389,11 @@ protected virtual ISession OpenSession() return Sfi.OpenSession(); } + protected virtual IDisposable EnableStatisticsScope() + { + return new StatisticsScope(Sfi); + } + protected virtual ISession OpenSession(IInterceptor sessionLocalInterceptor) { return Sfi.WithOptions().Interceptor(sessionLocalInterceptor).OpenSession(); @@ -574,6 +579,24 @@ public void Dispose() } } + protected class StatisticsScope : IDisposable + { + private readonly ISessionFactoryImplementor _factory; + + public StatisticsScope(ISessionFactoryImplementor factory) + { + _factory = factory; + _factory.Statistics.IsStatisticsEnabled = true; + _factory.Statistics.Clear(); + } + + public void Dispose() + { + _factory.Statistics.IsStatisticsEnabled = false; + _factory.Statistics.Clear(); + } + } + #endregion } } diff --git a/src/NHibernate/Transform/CacheableResultTransformer.cs b/src/NHibernate/Transform/CacheableResultTransformer.cs index caed8a31221..e3dba66d028 100644 --- a/src/NHibernate/Transform/CacheableResultTransformer.cs +++ b/src/NHibernate/Transform/CacheableResultTransformer.cs @@ -387,7 +387,7 @@ public override bool Equals(object o) // Auto-discovery does not allow distinguishing by transformer: just compare their queries if (AutoDiscoverTypes && that.AutoDiscoverTypes) - return _autoDiscoveredQuery == that._autoDiscoveredQuery; + return Equals(_autoDiscoveredQuery, that._autoDiscoveredQuery); return HasSameParameters(that); }