diff --git a/src/NHibernate.Test/Async/CacheTest/JsonSerializerCacheFixture.cs b/src/NHibernate.Test/Async/CacheTest/JsonSerializerCacheFixture.cs
new file mode 100644
index 00000000000..01403997a46
--- /dev/null
+++ b/src/NHibernate.Test/Async/CacheTest/JsonSerializerCacheFixture.cs
@@ -0,0 +1,536 @@
+//------------------------------------------------------------------------------
+//
+// 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;
+using System.Collections.Generic;
+using System.Linq;
+using NHibernate.Caches.CoreDistributedCache;
+using NHibernate.Caches.CoreDistributedCache.Memory;
+using NHibernate.Caches.Util.JsonSerializer;
+using NHibernate.Cfg;
+using NHibernate.Linq;
+using NHibernate.Multi;
+using NHibernate.Transform;
+using NUnit.Framework;
+using Environment = NHibernate.Cfg.Environment;
+
+namespace NHibernate.Test.CacheTest
+{
+ using System.Threading.Tasks;
+ [TestFixture]
+ public class JsonSerializerCacheFixtureAsync : TestCase
+ {
+ protected override string[] Mappings => new[]
+ {
+ "CacheTest.ReadOnly.hbm.xml",
+ "CacheTest.ReadWrite.hbm.xml"
+ };
+
+ protected override string MappingsAssembly => "NHibernate.Test";
+
+ protected override void Configure(Configuration configuration)
+ {
+ configuration.SetProperty(Environment.UseSecondLevelCache, "true");
+ configuration.SetProperty(Environment.UseQueryCache, "true");
+ configuration.SetProperty(Environment.GenerateStatistics, "true");
+ configuration.SetProperty(Environment.CacheProvider, typeof(CoreDistributedCacheProvider).AssemblyQualifiedName);
+ CoreDistributedCacheProvider.CacheFactory = new MemoryFactory();
+ var serializer = new JsonCacheSerializer();
+ serializer.RegisterType(typeof(Tuple), "tso");
+ CoreDistributedCacheProvider.DefaultSerializer = serializer;
+ }
+
+ protected override void OnSetUp()
+ {
+ using (var s = Sfi.OpenSession())
+ using (var tx = s.BeginTransaction())
+ {
+ var totalItems = 6;
+ for (var i = 1; i <= totalItems; i++)
+ {
+ var parent = new ReadOnly
+ {
+ Name = $"Name{i}"
+ };
+ for (var j = 1; j <= totalItems; j++)
+ {
+ var child = new ReadOnlyItem
+ {
+ Parent = parent
+ };
+ parent.Items.Add(child);
+ }
+ s.Save(parent);
+ }
+ for (var i = 1; i <= totalItems; i++)
+ {
+ var parent = new ReadWrite
+ {
+ Name = $"Name{i}"
+ };
+ for (var j = 1; j <= totalItems; j++)
+ {
+ var child = new ReadWriteItem
+ {
+ Parent = parent
+ };
+ parent.Items.Add(child);
+ }
+ s.Save(parent);
+ }
+ tx.Commit();
+ }
+ }
+
+ protected override void OnTearDown()
+ {
+ using (var s = OpenSession())
+ using (var tx = s.BeginTransaction())
+ {
+ s.CreateQuery("delete from ReadOnlyItem").ExecuteUpdate();
+ s.CreateQuery("delete from ReadWriteItem").ExecuteUpdate();
+ s.CreateQuery("delete from ReadOnly").ExecuteUpdate();
+ s.CreateQuery("delete from ReadWrite").ExecuteUpdate();
+ tx.Commit();
+ }
+ // Must rebuild the session factory, CoreDistribted cache being not clearable.
+ RebuildSessionFactory();
+ }
+
+ [Test]
+ public async Task CacheableScalarSqlQueryWithTransformerAsync()
+ {
+ async Task AssertQueryAsync(bool fromCache)
+ {
+ using (var s = OpenSession())
+ using (var t = s.BeginTransaction())
+ using (EnableStatisticsScope())
+ {
+ var l = await (s.CreateSQLQuery("select ro.Name as RegionCode from ReadOnly ro")
+ .AddScalar("regionCode", NHibernateUtil.String)
+ .SetResultTransformer(Transformers.AliasToBean())
+ .SetCacheable(true)
+ .ListAsync());
+ await (t.CommitAsync());
+
+ Assert.That(l.Count, Is.EqualTo(6));
+ var msg = "Results are expected from " + (fromCache ? "cache" : "DB");
+ Assert.That(Sfi.Statistics.QueryCacheMissCount, Is.EqualTo(fromCache ? 0 : 1), msg);
+ Assert.That(Sfi.Statistics.QueryCacheHitCount, Is.EqualTo(fromCache ? 1 : 0), msg);
+ }
+ }
+
+ await (AssertQueryAsync(false));
+ await (AssertQueryAsync(true));
+ }
+
+ [Test]
+ public async Task CacheableScalarSqlMultiQueryWithTransformerAsync()
+ {
+ async Task AssertQueryAsync(bool fromCache)
+ {
+ using (var s = OpenSession())
+ using (var t = s.BeginTransaction())
+ using (EnableStatisticsScope())
+ {
+ var q1 = s.CreateSQLQuery("select rw.Name as RegionCode from ReadWrite rw")
+ .AddScalar("regionCode", NHibernateUtil.String)
+ .SetResultTransformer(Transformers.AliasToBean())
+ .SetCacheable(true);
+ var q2 = s.CreateSQLQuery("select rw.Id as OrgId from ReadWrite rw")
+ .AddScalar("orgId", NHibernateUtil.Int64)
+ .SetResultTransformer(Transformers.AliasToBean())
+ .SetCacheable(true);
+
+ var batch = s.CreateQueryBatch();
+ batch.Add(q1);
+ batch.Add(q2);
+ await (batch.ExecuteAsync());
+
+ var l1 = await (batch.GetResultAsync(0));
+ var l2 = await (batch.GetResultAsync(1));
+
+ await (t.CommitAsync());
+
+ Assert.That(l1.Count, Is.EqualTo(6), "Unexpected results count for the first query.");
+ Assert.That(l2.Count, Is.EqualTo(6), "Unexpected results count for the second query.");
+ var msg = "Results are expected from " + (fromCache ? "cache" : "DB");
+ Assert.That(Sfi.Statistics.QueryCacheMissCount, Is.EqualTo(fromCache ? 0 : 2), msg);
+ Assert.That(Sfi.Statistics.QueryCacheHitCount, Is.EqualTo(fromCache ? 2 : 0), msg);
+ }
+ }
+
+ await (AssertQueryAsync(false));
+ await (AssertQueryAsync(true));
+ }
+
+ class ResultDto
+ {
+ public long OrgId { get; set; }
+ public string RegionCode { get; set; }
+ }
+
+ [Test]
+ public async Task QueryCacheTestAsync()
+ {
+ // QueryCache batching is used by QueryBatch.
+ if (!Sfi.ConnectionProvider.Driver.SupportsMultipleQueries)
+ Assert.Ignore($"{Sfi.ConnectionProvider.Driver} does not support multiple queries");
+
+ Sfi.Statistics.Clear();
+
+ using var s = OpenSession();
+
+ const string query = "from ReadOnly e where e.Name = :name";
+ const string name1 = "Name1";
+ const string name2 = "Name2";
+ const string name3 = "Name3";
+ const string name4 = "Name4";
+ const string name5 = "Name5";
+ var q1 =
+ s
+ .CreateQuery(query)
+ .SetString("name", name1)
+ .SetCacheable(true);
+ var q2 =
+ s
+ .CreateQuery(query)
+ .SetString("name", name2)
+ .SetCacheable(true);
+ var q3 =
+ s
+ .Query()
+ .Where(r => r.Name == name3)
+ .WithOptions(o => o.SetCacheable(true));
+ var q4 =
+ s
+ .QueryOver()
+ .Where(r => r.Name == name4)
+ .Cacheable();
+ var q5 =
+ s
+ .CreateSQLQuery("select * " + query)
+ .AddEntity(typeof(ReadOnly))
+ .SetString("name", name5)
+ .SetCacheable(true);
+
+ var queries =
+ s
+ .CreateQueryBatch()
+ .Add(q1)
+ .Add(q2)
+ .Add(q3)
+ .Add(q4)
+ .Add(q5);
+
+ using (var t = s.BeginTransaction())
+ {
+ await (queries.ExecuteAsync());
+ await (t.CommitAsync());
+ }
+
+ Assert.That(Sfi.Statistics.QueryExecutionCount, Is.EqualTo(1), "queries first execution count");
+ Assert.That(Sfi.Statistics.QueryCacheMissCount, Is.EqualTo(5), "cache misses first execution");
+ Assert.That(Sfi.Statistics.QueryCacheHitCount, Is.EqualTo(0), "cache hits first execution");
+ Assert.That(Sfi.Statistics.QueryCachePutCount, Is.EqualTo(5), "cache puts first execution");
+
+ // Run a second time, to test the query cache
+ using (var t = s.BeginTransaction())
+ {
+ await (queries.ExecuteAsync());
+ await (t.CommitAsync());
+ }
+
+ Assert.That(
+ await (queries.GetResultAsync(0)),
+ Has.Count.EqualTo(1).And.One.Property(nameof(ReadOnly.Name)).EqualTo(name1), "q1");
+ Assert.That(
+ await (queries.GetResultAsync(1)),
+ Has.Count.EqualTo(1).And.One.Property(nameof(ReadOnly.Name)).EqualTo(name2), "q2");
+ Assert.That(
+ await (queries.GetResultAsync(2)),
+ Has.Count.EqualTo(1).And.One.Property(nameof(ReadWrite.Name)).EqualTo(name3), "q3");
+ Assert.That(
+ await (queries.GetResultAsync(3)),
+ Has.Count.EqualTo(1).And.One.Property(nameof(ReadWrite.Name)).EqualTo(name4), "q4");
+ Assert.That(
+ await (queries.GetResultAsync(4)),
+ Has.Count.EqualTo(1).And.One.Property(nameof(ReadOnly.Name)).EqualTo(name5), "q5");
+
+ Assert.That(Sfi.Statistics.QueryExecutionCount, Is.EqualTo(1), "queries second execution count");
+ Assert.That(Sfi.Statistics.QueryCacheMissCount, Is.EqualTo(5), "cache misses second execution");
+ Assert.That(Sfi.Statistics.QueryCacheHitCount, Is.EqualTo(5), "cache hits second execution");
+ Assert.That(Sfi.Statistics.QueryCachePutCount, Is.EqualTo(5), "cache puts second execution");
+
+ // Update an entity to invalidate them
+ using (var t = s.BeginTransaction())
+ {
+ var readwrite1 = await (s.Query().SingleAsync(e => e.Name == name3));
+ readwrite1.Name = "NewName";
+ await (t.CommitAsync());
+ }
+
+ // Run a third time, to re-test the query cache
+ using (var t = s.BeginTransaction())
+ {
+ await (queries.ExecuteAsync());
+ await (t.CommitAsync());
+ }
+
+ Assert.That(
+ await (queries.GetResultAsync(0)),
+ Has.Count.EqualTo(1).And.One.Property(nameof(ReadOnly.Name)).EqualTo(name1), "q1 after update");
+ Assert.That(
+ await (queries.GetResultAsync(1)),
+ Has.Count.EqualTo(1).And.One.Property(nameof(ReadOnly.Name)).EqualTo(name2), "q2 after update");
+ Assert.That(
+ await (queries.GetResultAsync(2)),
+ Has.Count.EqualTo(0), "q3 after update");
+ Assert.That(
+ await (queries.GetResultAsync(3)),
+ Has.Count.EqualTo(1).And.One.Property(nameof(ReadWrite.Name)).EqualTo(name4), "q4 after update");
+ Assert.That(
+ await (queries.GetResultAsync(4)),
+ Has.Count.EqualTo(1).And.One.Property(nameof(ReadOnly.Name)).EqualTo(name5), "q5 after update");
+
+ // The two ReadWrite queries should have been re-executed, so count should have been incremented accordingly.
+ Assert.That(Sfi.Statistics.QueryExecutionCount, Is.EqualTo(3), "queries third execution count");
+ Assert.That(Sfi.Statistics.QueryCacheMissCount, Is.EqualTo(7), "cache misses third execution");
+ Assert.That(Sfi.Statistics.QueryCacheHitCount, Is.EqualTo(8), "cache hits third execution");
+ Assert.That(Sfi.Statistics.QueryCachePutCount, Is.EqualTo(7), "cache puts third execution");
+ }
+
+ [Test]
+ public async Task QueryEntityBatchCacheTestAsync()
+ {
+ Sfi.Statistics.Clear();
+
+ List items;
+
+ using (var s = OpenSession())
+ using (var tx = s.BeginTransaction())
+ {
+ items = await (s.Query()
+ .WithOptions(o => o.SetCacheable(true))
+ .ToListAsync());
+
+ await (tx.CommitAsync());
+ }
+
+ Assert.That(Sfi.Statistics.QueryExecutionCount, Is.EqualTo(1), "query first execution count");
+ Assert.That(Sfi.Statistics.QueryCacheMissCount, Is.EqualTo(1), "cache misses first execution");
+ Assert.That(Sfi.Statistics.QueryCacheHitCount, Is.EqualTo(0), "cache hits first execution");
+ Assert.That(Sfi.Statistics.QueryCachePutCount, Is.EqualTo(1), "cache puts first execution");
+
+ Sfi.Statistics.Clear();
+
+ using (var s = OpenSession())
+ using (var tx = s.BeginTransaction())
+ {
+ items = await (s.Query()
+ .WithOptions(o => o.SetCacheable(true))
+ .ToListAsync());
+
+ await (tx.CommitAsync());
+ }
+
+ Assert.That(items, Has.Count.EqualTo(36));
+
+ Assert.That(Sfi.Statistics.QueryExecutionCount, Is.EqualTo(0), "query second execution count");
+ Assert.That(Sfi.Statistics.QueryCacheMissCount, Is.EqualTo(0), "cache misses second execution");
+ Assert.That(Sfi.Statistics.QueryCacheHitCount, Is.EqualTo(1), "cache hits second execution");
+ Assert.That(Sfi.Statistics.QueryCachePutCount, Is.EqualTo(0), "cache puts second execution");
+ }
+
+ [TestCase(false)]
+ [TestCase(true)]
+ public async Task QueryFetchCollectionBatchCacheTestAsync(bool future)
+ {
+ if (future && !Sfi.ConnectionProvider.Driver.SupportsMultipleQueries)
+ {
+ Assert.Ignore($"{Sfi.ConnectionProvider.Driver} does not support multiple queries");
+ }
+
+ int middleId;
+
+ using (var s = OpenSession())
+ {
+ var ids = await (s.Query().Select(o => o.Id).OrderBy(o => o).ToListAsync());
+ middleId = ids[2];
+ }
+
+ Sfi.Statistics.Clear();
+
+ List items;
+ using (var s = OpenSession())
+ using (var tx = s.BeginTransaction())
+ {
+ if (future)
+ {
+ s.Query()
+ .WithOptions(o => o.SetCacheable(true))
+ .FetchMany(o => o.Items)
+ .Where(o => o.Id > middleId)
+ .ToFuture();
+
+ items = s.Query()
+ .WithOptions(o => o.SetCacheable(true))
+ .FetchMany(o => o.Items)
+ .Where(o => o.Id <= middleId)
+ .ToFuture()
+ .ToList();
+ }
+ else
+ {
+ items = await (s.Query()
+ .WithOptions(o => o.SetCacheable(true))
+ .FetchMany(o => o.Items)
+ .ToListAsync());
+ }
+
+ await (tx.CommitAsync());
+ }
+
+ Assert.That(items, Has.Count.EqualTo(future ? 3 : 6), "Unexpected items count");
+ Assert.That(Sfi.Statistics.QueryExecutionCount, Is.EqualTo(1), "Unexpected execution count");
+ Assert.That(Sfi.Statistics.QueryCachePutCount, Is.EqualTo(future ? 2 : 1), "Unexpected cache put count");
+ Assert.That(Sfi.Statistics.QueryCacheMissCount, Is.EqualTo(future ? 2 : 1), "Unexpected cache miss count");
+
+ Sfi.Statistics.Clear();
+
+ using (var s = OpenSession())
+ using (var tx = s.BeginTransaction())
+ {
+ if (future)
+ {
+ s.Query()
+ .WithOptions(o => o.SetCacheable(true))
+ .FetchMany(o => o.Items)
+ .Where(o => o.Id > middleId)
+ .ToFuture();
+
+ items = s.Query()
+ .WithOptions(o => o.SetCacheable(true))
+ .FetchMany(o => o.Items)
+ .Where(o => o.Id <= middleId)
+ .ToFuture()
+ .ToList();
+ }
+ else
+ {
+ items = await (s.Query()
+ .WithOptions(o => o.SetCacheable(true))
+ .FetchMany(o => o.Items)
+ .ToListAsync());
+ }
+
+ await (tx.CommitAsync());
+ }
+
+ Assert.That(items, Has.Count.EqualTo(future ? 3 : 6));
+ Assert.That(Sfi.Statistics.QueryExecutionCount, Is.EqualTo(0), "Unexpected execution count");
+ Assert.That(Sfi.Statistics.QueryCachePutCount, Is.EqualTo(0), "Unexpected cache put count");
+ Assert.That(Sfi.Statistics.QueryCacheMissCount, Is.EqualTo(0), "Unexpected cache miss count");
+ Assert.That(Sfi.Statistics.QueryCacheHitCount, Is.EqualTo(future ? 2 : 1), "Unexpected cache hit count");
+ }
+
+ [TestCase(false)]
+ [TestCase(true)]
+ public async Task QueryFetchEntityBatchCacheTestAsync(bool future)
+ {
+ if (future && !Sfi.ConnectionProvider.Driver.SupportsMultipleQueries)
+ {
+ Assert.Ignore($"{Sfi.ConnectionProvider.Driver} does not support multiple queries");
+ }
+
+ int middleId;
+
+ using (var s = OpenSession())
+ {
+ var ids = await (s.Query().Select(o => o.Id).OrderBy(o => o).ToListAsync());
+ middleId = ids[17];
+ }
+
+ Sfi.Statistics.Clear();
+
+ List items;
+ using (var s = OpenSession())
+ using (var tx = s.BeginTransaction())
+ {
+ if (future)
+ {
+ s.Query()
+ .WithOptions(o => o.SetCacheable(true))
+ .Fetch(o => o.Parent)
+ .Where(o => o.Id > middleId)
+ .ToFuture();
+
+ items = s.Query()
+ .WithOptions(o => o.SetCacheable(true))
+ .Fetch(o => o.Parent)
+ .Where(o => o.Id <= middleId)
+ .ToFuture()
+ .ToList();
+ }
+ else
+ {
+ items = await (s.Query()
+ .WithOptions(o => o.SetCacheable(true))
+ .Fetch(o => o.Parent)
+ .ToListAsync());
+ }
+
+ await (tx.CommitAsync());
+ }
+
+ Assert.That(items, Has.Count.EqualTo(future ? 18 : 36), "Unexpected items count");
+ Assert.That(Sfi.Statistics.QueryExecutionCount, Is.EqualTo(1), "Unexpected execution count");
+ Assert.That(Sfi.Statistics.QueryCachePutCount, Is.EqualTo(future ? 2 : 1), "Unexpected cache put count");
+ Assert.That(Sfi.Statistics.QueryCacheMissCount, Is.EqualTo(future ? 2 : 1), "Unexpected cache miss count");
+
+ Sfi.Statistics.Clear();
+
+ using (var s = OpenSession())
+ using (var tx = s.BeginTransaction())
+ {
+ if (future)
+ {
+ s.Query()
+ .WithOptions(o => o.SetCacheable(true))
+ .Fetch(o => o.Parent)
+ .Where(o => o.Id > middleId)
+ .ToFuture();
+
+ items = s.Query()
+ .WithOptions(o => o.SetCacheable(true))
+ .Fetch(o => o.Parent)
+ .Where(o => o.Id <= middleId)
+ .ToFuture()
+ .ToList();
+ }
+ else
+ {
+ items = await (s.Query()
+ .WithOptions(o => o.SetCacheable(true))
+ .Fetch(o => o.Parent)
+ .ToListAsync());
+ }
+
+ await (tx.CommitAsync());
+ }
+
+ Assert.That(items, Has.Count.EqualTo(future ? 18 : 36));
+ Assert.That(Sfi.Statistics.QueryExecutionCount, Is.EqualTo(0), "Unexpected execution count");
+ Assert.That(Sfi.Statistics.QueryCachePutCount, Is.EqualTo(0), "Unexpected cache put count");
+ Assert.That(Sfi.Statistics.QueryCacheMissCount, Is.EqualTo(0), "Unexpected cache miss count");
+ Assert.That(Sfi.Statistics.QueryCacheHitCount, Is.EqualTo(future ? 2 : 1), "Unexpected cache hit count");
+ }
+ }
+}
diff --git a/src/NHibernate.Test/Async/QueryTest/MultiCriteriaFixture.cs b/src/NHibernate.Test/Async/QueryTest/MultiCriteriaFixture.cs
index ff81250d911..848ead9056e 100644
--- a/src/NHibernate.Test/Async/QueryTest/MultiCriteriaFixture.cs
+++ b/src/NHibernate.Test/Async/QueryTest/MultiCriteriaFixture.cs
@@ -139,7 +139,7 @@ public async Task CanUseSecondLevelCacheWithPositionalParametersAsync()
await (CreateItemsAsync());
- await (DoMutiQueryAndAssertAsync());
+ await (DoMultiQueryAndAssertAsync());
Assert.AreEqual(1, cacheHashtable.Count);
}
@@ -148,19 +148,20 @@ public async Task CanUseSecondLevelCacheWithPositionalParametersAsync()
public async Task CanGetMultiQueryFromSecondLevelCacheAsync()
{
await (CreateItemsAsync());
- //set the query in the cache
- await (DoMutiQueryAndAssertAsync());
+ // Set the query in the cache.
+ await (DoMultiQueryAndAssertAsync());
var cacheHashtable = MultipleQueriesFixtureAsync.GetHashTableUsedAsQueryCache(Sfi);
- var cachedListEntry = (IList)new ArrayList(cacheHashtable.Values)[0];
- var cachedQuery = (IList)cachedListEntry[1];
+ var cachedListEntry = (IList) new ArrayList(cacheHashtable.Values)[0];
+ // The first element is a timestamp, then only we have the cached data.
+ var cachedQuery = (IList) cachedListEntry[1] ?? throw new InvalidOperationException("Cached data is null");
- var firstQueryResults = (IList)cachedQuery[0];
+ var firstQueryResults = (IList) cachedQuery[0];
firstQueryResults.Clear();
firstQueryResults.Add(3);
firstQueryResults.Add(4);
- var secondQueryResults = (IList)cachedQuery[1];
+ var secondQueryResults = (IList) cachedQuery[1];
secondQueryResults[0] = 2;
using (var s = Sfi.OpenSession())
@@ -172,9 +173,9 @@ public async Task CanGetMultiQueryFromSecondLevelCacheAsync()
.Add(CriteriaTransformer.Clone(criteria).SetProjection(Projections.RowCount()));
multiCriteria.SetCacheable(true);
var results = await (multiCriteria.ListAsync());
- var items = (IList)results[0];
+ var items = (IList) results[0];
Assert.AreEqual(2, items.Count);
- var count = (int)((IList)results[1])[0];
+ var count = (int) ((IList) results[1])[0];
Assert.AreEqual(2L, count);
}
}
@@ -184,12 +185,12 @@ public async Task CanUpdateStatisticsWhenGetMultiQueryFromSecondLevelCacheAsync(
{
await (CreateItemsAsync());
- await (DoMutiQueryAndAssertAsync());
+ await (DoMultiQueryAndAssertAsync());
Assert.AreEqual(0, Sfi.Statistics.QueryCacheHitCount);
Assert.AreEqual(1, Sfi.Statistics.QueryCacheMissCount);
Assert.AreEqual(1, Sfi.Statistics.QueryCachePutCount);
- await (DoMutiQueryAndAssertAsync());
+ await (DoMultiQueryAndAssertAsync());
Assert.AreEqual(1, Sfi.Statistics.QueryCacheHitCount);
Assert.AreEqual(1, Sfi.Statistics.QueryCacheMissCount);
Assert.AreEqual(1, Sfi.Statistics.QueryCachePutCount);
@@ -390,7 +391,7 @@ public async Task CanNotRetrieveDetachedCriteriaResultWithUnknownKeyAsync()
}
}
- private async Task DoMutiQueryAndAssertAsync(CancellationToken cancellationToken = default(CancellationToken))
+ private async Task DoMultiQueryAndAssertAsync(CancellationToken cancellationToken = default(CancellationToken))
{
using (var s = OpenSession())
{
@@ -401,9 +402,9 @@ public async Task CanNotRetrieveDetachedCriteriaResultWithUnknownKeyAsync()
.Add(CriteriaTransformer.Clone(criteria).SetProjection(Projections.RowCount()));
multiCriteria.SetCacheable(true);
var results = await (multiCriteria.ListAsync(cancellationToken));
- var items = (IList)results[0];
+ var items = (IList) results[0];
Assert.AreEqual(89, items.Count);
- var count = (int)((IList)results[1])[0];
+ var count = (int) ((IList) results[1])[0];
Assert.AreEqual(99L, count);
}
}
diff --git a/src/NHibernate.Test/Async/QueryTest/MultipleMixedQueriesFixture.cs b/src/NHibernate.Test/Async/QueryTest/MultipleMixedQueriesFixture.cs
index 5fc7d5f721a..9e4b39d77cf 100644
--- a/src/NHibernate.Test/Async/QueryTest/MultipleMixedQueriesFixture.cs
+++ b/src/NHibernate.Test/Async/QueryTest/MultipleMixedQueriesFixture.cs
@@ -78,19 +78,20 @@ public void NH_1085_WillGiveReasonableErrorIfBadParameterNameAsync()
public async Task CanGetMultiQueryFromSecondLevelCacheAsync()
{
await (CreateItemsAsync());
- //set the query in the cache
- await (DoMutiQueryAndAssertAsync());
+ // Set the query in the cache.
+ await (DoMultiQueryAndAssertAsync());
var cacheHashtable = MultipleQueriesFixtureAsync.GetHashTableUsedAsQueryCache(Sfi);
- var cachedListEntry = (IList)new ArrayList(cacheHashtable.Values)[0];
- var cachedQuery = (IList)cachedListEntry[1];
+ var cachedListEntry = (IList) new ArrayList(cacheHashtable.Values)[0];
+ // The first element is a timestamp, then only we have the cached data.
+ var cachedQuery = (IList) cachedListEntry[1] ?? throw new InvalidOperationException("Cached data is null");
- var firstQueryResults = (IList)cachedQuery[0];
+ var firstQueryResults = (IList) cachedQuery[0];
firstQueryResults.Clear();
firstQueryResults.Add(3);
firstQueryResults.Add(4);
- var secondQueryResults = (IList)cachedQuery[1];
+ var secondQueryResults = (IList) cachedQuery[1];
secondQueryResults[0] = 2L;
using (var s = Sfi.OpenSession())
@@ -103,9 +104,9 @@ public async Task CanGetMultiQueryFromSecondLevelCacheAsync()
.SetInt32(0, 50));
multiQuery.SetCacheable(true);
var results = await (multiQuery.ListAsync());
- var items = (IList)results[0];
+ var items = (IList) results[0];
Assert.AreEqual(2, items.Count);
- var count = (long)((IList)results[1])[0];
+ var count = (long) ((IList) results[1])[0];
Assert.AreEqual(2L, count);
}
}
@@ -185,12 +186,12 @@ public async Task CanUseSecondLevelCacheWithPositionalParametersAsync()
await (CreateItemsAsync());
- await (DoMutiQueryAndAssertAsync());
+ await (DoMultiQueryAndAssertAsync());
Assert.AreEqual(1, cacheHashtable.Count);
}
- private async Task DoMutiQueryAndAssertAsync(CancellationToken cancellationToken = default(CancellationToken))
+ private async Task DoMultiQueryAndAssertAsync(CancellationToken cancellationToken = default(CancellationToken))
{
using (var s = OpenSession())
{
@@ -202,9 +203,9 @@ public async Task CanUseSecondLevelCacheWithPositionalParametersAsync()
.SetInt32(0, 50));
multiQuery.SetCacheable(true);
var results = await (multiQuery.ListAsync(cancellationToken));
- var items = (IList)results[0];
+ var items = (IList) results[0];
Assert.AreEqual(89, items.Count);
- var count = (long)((IList)results[1])[0];
+ var count = (long) ((IList) results[1])[0];
Assert.AreEqual(99L, count);
}
}
diff --git a/src/NHibernate.Test/Async/QueryTest/MultipleQueriesFixture.cs b/src/NHibernate.Test/Async/QueryTest/MultipleQueriesFixture.cs
index 688bce5dc72..e8a84ffbe1a 100644
--- a/src/NHibernate.Test/Async/QueryTest/MultipleQueriesFixture.cs
+++ b/src/NHibernate.Test/Async/QueryTest/MultipleQueriesFixture.cs
@@ -81,19 +81,20 @@ public void NH_1085_WillGiveReasonableErrorIfBadParameterNameAsync()
public async Task CanGetMultiQueryFromSecondLevelCacheAsync()
{
await (CreateItemsAsync());
- //set the query in the cache
- await (DoMutiQueryAndAssertAsync());
+ // Set the query in the cache.
+ await (DoMultiQueryAndAssertAsync());
var cacheHashtable = GetHashTableUsedAsQueryCache(Sfi);
- var cachedListEntry = (IList)new ArrayList(cacheHashtable.Values)[0];
- var cachedQuery = (IList)cachedListEntry[1];
+ var cachedListEntry = (IList) new ArrayList(cacheHashtable.Values)[0];
+ // The first element is a timestamp, then only we have the cached data.
+ var cachedQuery = (IList) cachedListEntry[1] ?? throw new InvalidOperationException("Cached data is null");
- var firstQueryResults = (IList)cachedQuery[0];
+ var firstQueryResults = (IList) cachedQuery[0];
firstQueryResults.Clear();
firstQueryResults.Add(3);
firstQueryResults.Add(4);
- var secondQueryResults = (IList)cachedQuery[1];
+ var secondQueryResults = (IList) cachedQuery[1];
secondQueryResults[0] = 2L;
using (var s = Sfi.OpenSession())
@@ -106,9 +107,9 @@ public async Task CanGetMultiQueryFromSecondLevelCacheAsync()
.SetInt32(0, 50));
multiQuery.SetCacheable(true);
var results = await (multiQuery.ListAsync());
- var items = (IList)results[0];
+ var items = (IList) results[0];
Assert.AreEqual(2, items.Count);
- var count = (long)((IList)results[1])[0];
+ var count = (long) ((IList) results[1])[0];
Assert.AreEqual(2L, count);
}
}
@@ -187,12 +188,12 @@ public async Task CanUseSecondLevelCacheWithPositionalParametersAsync()
await (CreateItemsAsync());
- await (DoMutiQueryAndAssertAsync());
+ await (DoMultiQueryAndAssertAsync());
Assert.AreEqual(1, cacheHashtable.Count);
}
- private async Task DoMutiQueryAndAssertAsync(CancellationToken cancellationToken = default(CancellationToken))
+ private async Task DoMultiQueryAndAssertAsync(CancellationToken cancellationToken = default(CancellationToken))
{
using (var s = OpenSession())
{
@@ -204,9 +205,9 @@ public async Task CanUseSecondLevelCacheWithPositionalParametersAsync()
.SetInt32(0, 50));
multiQuery.SetCacheable(true);
var results = await (multiQuery.ListAsync(cancellationToken));
- var items = (IList)results[0];
+ var items = (IList) results[0];
Assert.AreEqual(89, items.Count);
- var count = (long)((IList)results[1])[0];
+ var count = (long) ((IList) results[1])[0];
Assert.AreEqual(99L, count);
}
}
diff --git a/src/NHibernate.Test/Async/SqlTest/Query/NativeSQLQueriesFixture.cs b/src/NHibernate.Test/Async/SqlTest/Query/NativeSQLQueriesFixture.cs
index 5ef4ebad9d8..6beefe2afa8 100644
--- a/src/NHibernate.Test/Async/SqlTest/Query/NativeSQLQueriesFixture.cs
+++ b/src/NHibernate.Test/Async/SqlTest/Query/NativeSQLQueriesFixture.cs
@@ -10,9 +10,10 @@
using System.Collections;
using System.Linq;
+using NHibernate.Criterion;
+using NHibernate.Multi;
using NHibernate.Transform;
using NUnit.Framework;
-using NHibernate.Criterion;
namespace NHibernate.Test.SqlTest.Query
{
@@ -62,6 +63,20 @@ protected override string MappingsAssembly
get { return "NHibernate.Test"; }
}
+ protected override void OnTearDown()
+ {
+ using (var session = OpenSession())
+ using (var transaction = session.BeginTransaction())
+ {
+ session.CreateQuery("delete from Employment").ExecuteUpdate();
+ session.CreateQuery("delete from System.Object").ExecuteUpdate();
+
+ transaction.Commit();
+ }
+
+ Sfi.QueryCache.Clear();
+ }
+
[Test]
public async Task FailOnNoAddEntityOrScalarAsync()
{
@@ -136,18 +151,6 @@ public async Task SQLQueryInterfaceAsync()
await (t.CommitAsync());
s.Close();
}
-
- 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());
- s.Close();
- }
}
[Test]
@@ -202,22 +205,10 @@ public async Task SQLQueryInterfaceCacheableAsync()
await (t.CommitAsync());
s.Close();
}
-
- 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());
- s.Close();
- }
}
[Test(Description = "GH-2904")]
- public async Task CacheableScalarSQLQueryAsync()
+ public async Task CacheableScalarSqlQueryAsync()
{
Organization ifa = new Organization("IFA");
Organization jboss = new Organization("JBoss");
@@ -270,25 +261,16 @@ Task GetCacheableSqlQueryResultsAsync()
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());
- }
}
class ResultDto
{
+ public long orgId { get; set; }
public string regionCode { get; set; }
}
[Test(Description = "GH-3169")]
- public async Task CacheableScalarSQLQueryWithTransformerAsync()
+ public async Task CacheableScalarSqlQueryWithTransformerAsync()
{
Organization ifa = new Organization("IFA");
@@ -312,23 +294,170 @@ async Task AssertQueryAsync(bool fromCache)
.ListAsync());
await (t.CommitAsync());
- Assert.AreEqual(1, l.Count);
- //TODO: Uncomment if we properly fix caching auto discovery type queries with transformers
- // var msg = "results are expected from " + (fromCache ? "cache" : "DB");
- // Assert.That(Sfi.Statistics.QueryCacheMissCount, Is.EqualTo(fromCache ? 0 : 1), msg);
- // Assert.That(Sfi.Statistics.QueryCacheHitCount, Is.EqualTo(fromCache ? 1 : 0), msg);
+ Assert.That(l.Count, Is.EqualTo(1));
+ var msg = "Results are expected from " + (fromCache ? "cache" : "DB");
+ Assert.That(Sfi.Statistics.QueryCacheMissCount, Is.EqualTo(fromCache ? 0 : 1), msg);
+ Assert.That(Sfi.Statistics.QueryCacheHitCount, Is.EqualTo(fromCache ? 1 : 0), msg);
}
}
await (AssertQueryAsync(false));
await (AssertQueryAsync(true));
+ }
- using (var s = OpenSession())
- using (var t = s.BeginTransaction())
+ [Test(Description = "GH-3169")]
+ public async Task CacheableScalarSqlEmptyQueryWithTransformerAsync()
+ {
+ async Task AssertQueryAsync(bool fromCache)
{
- await (s.DeleteAsync(ifa));
+ using (var s = OpenSession())
+ using (var t = s.BeginTransaction())
+ using (EnableStatisticsScope())
+ {
+ var l = await (s.CreateSQLQuery("select org.NAME as regionCode from ORGANIZATION org")
+ .AddScalar("regionCode", NHibernateUtil.String)
+ .SetResultTransformer(Transformers.AliasToBean())
+ .SetCacheable(true)
+ .ListAsync());
+ await (t.CommitAsync());
+
+ Assert.That(l.Count, Is.EqualTo(0));
+ var msg = "Results are expected from " + (fromCache ? "cache" : "DB");
+ Assert.That(Sfi.Statistics.QueryCacheMissCount, Is.EqualTo(fromCache ? 0 : 1), msg);
+ Assert.That(Sfi.Statistics.QueryCacheHitCount, Is.EqualTo(fromCache ? 1 : 0), msg);
+ }
+ }
+
+ await (AssertQueryAsync(false));
+ await (AssertQueryAsync(true));
+ }
+
+ [Test(Description = "GH-3169")]
+ public async Task CacheableScalarSqlMultiQueryWithTransformerAsync()
+ {
+ Organization ifa = new Organization("IFA");
+
+ using (ISession s = OpenSession())
+ using (ITransaction t = s.BeginTransaction())
+ {
+ await (s.SaveAsync(ifa));
await (t.CommitAsync());
}
+
+ async Task AssertQueryAsync(bool fromCache)
+ {
+ using (var s = OpenSession())
+ using (var t = s.BeginTransaction())
+ using (EnableStatisticsScope())
+ {
+ var q1 = s.CreateSQLQuery("select org.NAME as regionCode from ORGANIZATION org")
+ .AddScalar("regionCode", NHibernateUtil.String)
+ .SetResultTransformer(Transformers.AliasToBean())
+ .SetCacheable(true);
+ var q2 = s.CreateSQLQuery("select org.ORGID as orgId, org.NAME as regionCode from ORGANIZATION org")
+ .AddScalar("orgId", NHibernateUtil.Int64)
+ .AddScalar("regionCode", NHibernateUtil.String)
+ .SetResultTransformer(Transformers.AliasToBean())
+ .SetCacheable(true);
+
+ var batch = s.CreateQueryBatch();
+ batch.Add(q1);
+ batch.Add(q2);
+ await (batch.ExecuteAsync());
+
+ var l1 = await (batch.GetResultAsync(0));
+ var l2 = await (batch.GetResultAsync(1));
+
+ await (t.CommitAsync());
+
+ Assert.That(l1.Count, Is.EqualTo(1), "Unexpected results count for the first query.");
+ Assert.That(l2.Count, Is.EqualTo(1), "Unexpected results count for the second query.");
+ var msg = "Results are expected from " + (fromCache ? "cache" : "DB");
+ Assert.That(Sfi.Statistics.QueryCacheMissCount, Is.EqualTo(fromCache ? 0 : 2), msg);
+ Assert.That(Sfi.Statistics.QueryCacheHitCount, Is.EqualTo(fromCache ? 2 : 0), msg);
+ }
+ }
+
+ await (AssertQueryAsync(false));
+ await (AssertQueryAsync(true));
+ }
+
+ [Test(Description = "GH-3169")]
+ public async Task CacheableScalarSqlEmptyMultiQueryWithTransformerAsync()
+ {
+ async Task AssertQueryAsync(bool fromCache)
+ {
+ using (var s = OpenSession())
+ using (var t = s.BeginTransaction())
+ using (EnableStatisticsScope())
+ {
+ var q1 = s.CreateSQLQuery("select org.NAME as regionCode from ORGANIZATION org")
+ .AddScalar("regionCode", NHibernateUtil.String)
+ .SetResultTransformer(Transformers.AliasToBean())
+ .SetCacheable(true);
+ var q2 = s.CreateSQLQuery("select org.ORGID as orgId, org.NAME as regionCode from ORGANIZATION org")
+ .AddScalar("orgId", NHibernateUtil.Int64)
+ .AddScalar("regionCode", NHibernateUtil.String)
+ .SetResultTransformer(Transformers.AliasToBean())
+ .SetCacheable(true);
+
+ var batch = s.CreateQueryBatch();
+ batch.Add(q1);
+ batch.Add(q2);
+ await (batch.ExecuteAsync());
+
+ var l1 = await (batch.GetResultAsync(0));
+ var l2 = await (batch.GetResultAsync(1));
+
+ await (t.CommitAsync());
+
+ Assert.That(l1.Count, Is.EqualTo(0), "Unexpected results count for the first query.");
+ Assert.That(l2.Count, Is.EqualTo(0), "Unexpected results count for the second query.");
+ var msg = "Results are expected from " + (fromCache ? "cache" : "DB");
+ Assert.That(Sfi.Statistics.QueryCacheMissCount, Is.EqualTo(fromCache ? 0 : 2), msg);
+ Assert.That(Sfi.Statistics.QueryCacheHitCount, Is.EqualTo(fromCache ? 2 : 0), msg);
+ }
+ }
+
+ await (AssertQueryAsync(false));
+ await (AssertQueryAsync(true));
+ }
+
+ [Test(Description = "GH-3169")]
+ public async Task CacheableMultiScalarSqlQueryWithTransformerAsync()
+ {
+ Organization ifa = new Organization("IFA");
+
+ using (ISession s = OpenSession())
+ using (ITransaction t = s.BeginTransaction())
+ {
+ await (s.SaveAsync(ifa));
+ await (t.CommitAsync());
+ }
+
+ async Task AssertQueryAsync(bool fromCache)
+ {
+ using (var s = OpenSession())
+ using (var t = s.BeginTransaction())
+ using (EnableStatisticsScope())
+ {
+ var l = await (s.CreateSQLQuery("select org.ORGID as orgId, org.NAME as regionCode from ORGANIZATION org")
+ .AddScalar("orgId", NHibernateUtil.Int64)
+ .AddScalar("regionCode", NHibernateUtil.String)
+ .SetResultTransformer(Transformers.AliasToBean())
+ .SetCacheable(true)
+ .ListAsync());
+ await (t.CommitAsync());
+
+ Assert.That(l.Count, Is.EqualTo(1));
+ var msg = "Results are expected from " + (fromCache ? "cache" : "DB");
+ Assert.That(Sfi.Statistics.QueryCacheMissCount, Is.EqualTo(fromCache ? 0 : 1), msg);
+ Assert.That(Sfi.Statistics.QueryCacheHitCount, Is.EqualTo(fromCache ? 1 : 0), msg);
+ }
+ }
+
+ await (AssertQueryAsync(false));
+ await (AssertQueryAsync(true));
}
[Test]
@@ -356,11 +485,6 @@ public async Task ResultSetMappingDefinitionAsync()
.ListAsync());
Assert.AreEqual(l.Count, 1);
- await (s.DeleteAsync(emp));
- await (s.DeleteAsync(gavin));
- await (s.DeleteAsync(ifa));
- await (s.DeleteAsync(jboss));
-
await (t.CommitAsync());
s.Close();
}
@@ -443,8 +567,6 @@ public async Task ScalarValuesAsync()
Assert.AreEqual(o[1], "JBoss");
Assert.AreEqual(o[0], idJBoss);
- await (s.DeleteAsync(ifa));
- await (s.DeleteAsync(jboss));
await (t.CommitAsync());
s.Close();
}
diff --git a/src/NHibernate.Test/CacheTest/JsonSerializerCacheFixture.cs b/src/NHibernate.Test/CacheTest/JsonSerializerCacheFixture.cs
new file mode 100644
index 00000000000..bf1009c90d3
--- /dev/null
+++ b/src/NHibernate.Test/CacheTest/JsonSerializerCacheFixture.cs
@@ -0,0 +1,525 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using NHibernate.Caches.CoreDistributedCache;
+using NHibernate.Caches.CoreDistributedCache.Memory;
+using NHibernate.Caches.Util.JsonSerializer;
+using NHibernate.Cfg;
+using NHibernate.Linq;
+using NHibernate.Multi;
+using NHibernate.Transform;
+using NUnit.Framework;
+using Environment = NHibernate.Cfg.Environment;
+
+namespace NHibernate.Test.CacheTest
+{
+ [TestFixture]
+ public class JsonSerializerCacheFixture : TestCase
+ {
+ protected override string[] Mappings => new[]
+ {
+ "CacheTest.ReadOnly.hbm.xml",
+ "CacheTest.ReadWrite.hbm.xml"
+ };
+
+ protected override string MappingsAssembly => "NHibernate.Test";
+
+ protected override void Configure(Configuration configuration)
+ {
+ configuration.SetProperty(Environment.UseSecondLevelCache, "true");
+ configuration.SetProperty(Environment.UseQueryCache, "true");
+ configuration.SetProperty(Environment.GenerateStatistics, "true");
+ configuration.SetProperty(Environment.CacheProvider, typeof(CoreDistributedCacheProvider).AssemblyQualifiedName);
+ CoreDistributedCacheProvider.CacheFactory = new MemoryFactory();
+ var serializer = new JsonCacheSerializer();
+ serializer.RegisterType(typeof(Tuple), "tso");
+ CoreDistributedCacheProvider.DefaultSerializer = serializer;
+ }
+
+ protected override void OnSetUp()
+ {
+ using (var s = Sfi.OpenSession())
+ using (var tx = s.BeginTransaction())
+ {
+ var totalItems = 6;
+ for (var i = 1; i <= totalItems; i++)
+ {
+ var parent = new ReadOnly
+ {
+ Name = $"Name{i}"
+ };
+ for (var j = 1; j <= totalItems; j++)
+ {
+ var child = new ReadOnlyItem
+ {
+ Parent = parent
+ };
+ parent.Items.Add(child);
+ }
+ s.Save(parent);
+ }
+ for (var i = 1; i <= totalItems; i++)
+ {
+ var parent = new ReadWrite
+ {
+ Name = $"Name{i}"
+ };
+ for (var j = 1; j <= totalItems; j++)
+ {
+ var child = new ReadWriteItem
+ {
+ Parent = parent
+ };
+ parent.Items.Add(child);
+ }
+ s.Save(parent);
+ }
+ tx.Commit();
+ }
+ }
+
+ protected override void OnTearDown()
+ {
+ using (var s = OpenSession())
+ using (var tx = s.BeginTransaction())
+ {
+ s.CreateQuery("delete from ReadOnlyItem").ExecuteUpdate();
+ s.CreateQuery("delete from ReadWriteItem").ExecuteUpdate();
+ s.CreateQuery("delete from ReadOnly").ExecuteUpdate();
+ s.CreateQuery("delete from ReadWrite").ExecuteUpdate();
+ tx.Commit();
+ }
+ // Must rebuild the session factory, CoreDistribted cache being not clearable.
+ RebuildSessionFactory();
+ }
+
+ [Test]
+ public void CacheableScalarSqlQueryWithTransformer()
+ {
+ void AssertQuery(bool fromCache)
+ {
+ using (var s = OpenSession())
+ using (var t = s.BeginTransaction())
+ using (EnableStatisticsScope())
+ {
+ var l = s.CreateSQLQuery("select ro.Name as RegionCode from ReadOnly ro")
+ .AddScalar("regionCode", NHibernateUtil.String)
+ .SetResultTransformer(Transformers.AliasToBean())
+ .SetCacheable(true)
+ .List();
+ t.Commit();
+
+ Assert.That(l.Count, Is.EqualTo(6));
+ var msg = "Results are expected from " + (fromCache ? "cache" : "DB");
+ Assert.That(Sfi.Statistics.QueryCacheMissCount, Is.EqualTo(fromCache ? 0 : 1), msg);
+ Assert.That(Sfi.Statistics.QueryCacheHitCount, Is.EqualTo(fromCache ? 1 : 0), msg);
+ }
+ }
+
+ AssertQuery(false);
+ AssertQuery(true);
+ }
+
+ [Test]
+ public void CacheableScalarSqlMultiQueryWithTransformer()
+ {
+ void AssertQuery(bool fromCache)
+ {
+ using (var s = OpenSession())
+ using (var t = s.BeginTransaction())
+ using (EnableStatisticsScope())
+ {
+ var q1 = s.CreateSQLQuery("select rw.Name as RegionCode from ReadWrite rw")
+ .AddScalar("regionCode", NHibernateUtil.String)
+ .SetResultTransformer(Transformers.AliasToBean())
+ .SetCacheable(true);
+ var q2 = s.CreateSQLQuery("select rw.Id as OrgId from ReadWrite rw")
+ .AddScalar("orgId", NHibernateUtil.Int64)
+ .SetResultTransformer(Transformers.AliasToBean())
+ .SetCacheable(true);
+
+ var batch = s.CreateQueryBatch();
+ batch.Add(q1);
+ batch.Add(q2);
+ batch.Execute();
+
+ var l1 = batch.GetResult(0);
+ var l2 = batch.GetResult(1);
+
+ t.Commit();
+
+ Assert.That(l1.Count, Is.EqualTo(6), "Unexpected results count for the first query.");
+ Assert.That(l2.Count, Is.EqualTo(6), "Unexpected results count for the second query.");
+ var msg = "Results are expected from " + (fromCache ? "cache" : "DB");
+ Assert.That(Sfi.Statistics.QueryCacheMissCount, Is.EqualTo(fromCache ? 0 : 2), msg);
+ Assert.That(Sfi.Statistics.QueryCacheHitCount, Is.EqualTo(fromCache ? 2 : 0), msg);
+ }
+ }
+
+ AssertQuery(false);
+ AssertQuery(true);
+ }
+
+ class ResultDto
+ {
+ public long OrgId { get; set; }
+ public string RegionCode { get; set; }
+ }
+
+ [Test]
+ public void QueryCacheTest()
+ {
+ // QueryCache batching is used by QueryBatch.
+ if (!Sfi.ConnectionProvider.Driver.SupportsMultipleQueries)
+ Assert.Ignore($"{Sfi.ConnectionProvider.Driver} does not support multiple queries");
+
+ Sfi.Statistics.Clear();
+
+ using var s = OpenSession();
+
+ const string query = "from ReadOnly e where e.Name = :name";
+ const string name1 = "Name1";
+ const string name2 = "Name2";
+ const string name3 = "Name3";
+ const string name4 = "Name4";
+ const string name5 = "Name5";
+ var q1 =
+ s
+ .CreateQuery(query)
+ .SetString("name", name1)
+ .SetCacheable(true);
+ var q2 =
+ s
+ .CreateQuery(query)
+ .SetString("name", name2)
+ .SetCacheable(true);
+ var q3 =
+ s
+ .Query()
+ .Where(r => r.Name == name3)
+ .WithOptions(o => o.SetCacheable(true));
+ var q4 =
+ s
+ .QueryOver()
+ .Where(r => r.Name == name4)
+ .Cacheable();
+ var q5 =
+ s
+ .CreateSQLQuery("select * " + query)
+ .AddEntity(typeof(ReadOnly))
+ .SetString("name", name5)
+ .SetCacheable(true);
+
+ var queries =
+ s
+ .CreateQueryBatch()
+ .Add(q1)
+ .Add(q2)
+ .Add(q3)
+ .Add(q4)
+ .Add(q5);
+
+ using (var t = s.BeginTransaction())
+ {
+ queries.Execute();
+ t.Commit();
+ }
+
+ Assert.That(Sfi.Statistics.QueryExecutionCount, Is.EqualTo(1), "queries first execution count");
+ Assert.That(Sfi.Statistics.QueryCacheMissCount, Is.EqualTo(5), "cache misses first execution");
+ Assert.That(Sfi.Statistics.QueryCacheHitCount, Is.EqualTo(0), "cache hits first execution");
+ Assert.That(Sfi.Statistics.QueryCachePutCount, Is.EqualTo(5), "cache puts first execution");
+
+ // Run a second time, to test the query cache
+ using (var t = s.BeginTransaction())
+ {
+ queries.Execute();
+ t.Commit();
+ }
+
+ Assert.That(
+ queries.GetResult(0),
+ Has.Count.EqualTo(1).And.One.Property(nameof(ReadOnly.Name)).EqualTo(name1), "q1");
+ Assert.That(
+ queries.GetResult(1),
+ Has.Count.EqualTo(1).And.One.Property(nameof(ReadOnly.Name)).EqualTo(name2), "q2");
+ Assert.That(
+ queries.GetResult(2),
+ Has.Count.EqualTo(1).And.One.Property(nameof(ReadWrite.Name)).EqualTo(name3), "q3");
+ Assert.That(
+ queries.GetResult(3),
+ Has.Count.EqualTo(1).And.One.Property(nameof(ReadWrite.Name)).EqualTo(name4), "q4");
+ Assert.That(
+ queries.GetResult(4),
+ Has.Count.EqualTo(1).And.One.Property(nameof(ReadOnly.Name)).EqualTo(name5), "q5");
+
+ Assert.That(Sfi.Statistics.QueryExecutionCount, Is.EqualTo(1), "queries second execution count");
+ Assert.That(Sfi.Statistics.QueryCacheMissCount, Is.EqualTo(5), "cache misses second execution");
+ Assert.That(Sfi.Statistics.QueryCacheHitCount, Is.EqualTo(5), "cache hits second execution");
+ Assert.That(Sfi.Statistics.QueryCachePutCount, Is.EqualTo(5), "cache puts second execution");
+
+ // Update an entity to invalidate them
+ using (var t = s.BeginTransaction())
+ {
+ var readwrite1 = s.Query().Single(e => e.Name == name3);
+ readwrite1.Name = "NewName";
+ t.Commit();
+ }
+
+ // Run a third time, to re-test the query cache
+ using (var t = s.BeginTransaction())
+ {
+ queries.Execute();
+ t.Commit();
+ }
+
+ Assert.That(
+ queries.GetResult(0),
+ Has.Count.EqualTo(1).And.One.Property(nameof(ReadOnly.Name)).EqualTo(name1), "q1 after update");
+ Assert.That(
+ queries.GetResult(1),
+ Has.Count.EqualTo(1).And.One.Property(nameof(ReadOnly.Name)).EqualTo(name2), "q2 after update");
+ Assert.That(
+ queries.GetResult(2),
+ Has.Count.EqualTo(0), "q3 after update");
+ Assert.That(
+ queries.GetResult(3),
+ Has.Count.EqualTo(1).And.One.Property(nameof(ReadWrite.Name)).EqualTo(name4), "q4 after update");
+ Assert.That(
+ queries.GetResult(4),
+ Has.Count.EqualTo(1).And.One.Property(nameof(ReadOnly.Name)).EqualTo(name5), "q5 after update");
+
+ // The two ReadWrite queries should have been re-executed, so count should have been incremented accordingly.
+ Assert.That(Sfi.Statistics.QueryExecutionCount, Is.EqualTo(3), "queries third execution count");
+ Assert.That(Sfi.Statistics.QueryCacheMissCount, Is.EqualTo(7), "cache misses third execution");
+ Assert.That(Sfi.Statistics.QueryCacheHitCount, Is.EqualTo(8), "cache hits third execution");
+ Assert.That(Sfi.Statistics.QueryCachePutCount, Is.EqualTo(7), "cache puts third execution");
+ }
+
+ [Test]
+ public void QueryEntityBatchCacheTest()
+ {
+ Sfi.Statistics.Clear();
+
+ List items;
+
+ using (var s = OpenSession())
+ using (var tx = s.BeginTransaction())
+ {
+ items = s.Query()
+ .WithOptions(o => o.SetCacheable(true))
+ .ToList();
+
+ tx.Commit();
+ }
+
+ Assert.That(Sfi.Statistics.QueryExecutionCount, Is.EqualTo(1), "query first execution count");
+ Assert.That(Sfi.Statistics.QueryCacheMissCount, Is.EqualTo(1), "cache misses first execution");
+ Assert.That(Sfi.Statistics.QueryCacheHitCount, Is.EqualTo(0), "cache hits first execution");
+ Assert.That(Sfi.Statistics.QueryCachePutCount, Is.EqualTo(1), "cache puts first execution");
+
+ Sfi.Statistics.Clear();
+
+ using (var s = OpenSession())
+ using (var tx = s.BeginTransaction())
+ {
+ items = s.Query()
+ .WithOptions(o => o.SetCacheable(true))
+ .ToList();
+
+ tx.Commit();
+ }
+
+ Assert.That(items, Has.Count.EqualTo(36));
+
+ Assert.That(Sfi.Statistics.QueryExecutionCount, Is.EqualTo(0), "query second execution count");
+ Assert.That(Sfi.Statistics.QueryCacheMissCount, Is.EqualTo(0), "cache misses second execution");
+ Assert.That(Sfi.Statistics.QueryCacheHitCount, Is.EqualTo(1), "cache hits second execution");
+ Assert.That(Sfi.Statistics.QueryCachePutCount, Is.EqualTo(0), "cache puts second execution");
+ }
+
+ [TestCase(false)]
+ [TestCase(true)]
+ public void QueryFetchCollectionBatchCacheTest(bool future)
+ {
+ if (future && !Sfi.ConnectionProvider.Driver.SupportsMultipleQueries)
+ {
+ Assert.Ignore($"{Sfi.ConnectionProvider.Driver} does not support multiple queries");
+ }
+
+ int middleId;
+
+ using (var s = OpenSession())
+ {
+ var ids = s.Query().Select(o => o.Id).OrderBy(o => o).ToList();
+ middleId = ids[2];
+ }
+
+ Sfi.Statistics.Clear();
+
+ List items;
+ using (var s = OpenSession())
+ using (var tx = s.BeginTransaction())
+ {
+ if (future)
+ {
+ s.Query()
+ .WithOptions(o => o.SetCacheable(true))
+ .FetchMany(o => o.Items)
+ .Where(o => o.Id > middleId)
+ .ToFuture();
+
+ items = s.Query()
+ .WithOptions(o => o.SetCacheable(true))
+ .FetchMany(o => o.Items)
+ .Where(o => o.Id <= middleId)
+ .ToFuture()
+ .ToList();
+ }
+ else
+ {
+ items = s.Query()
+ .WithOptions(o => o.SetCacheable(true))
+ .FetchMany(o => o.Items)
+ .ToList();
+ }
+
+ tx.Commit();
+ }
+
+ Assert.That(items, Has.Count.EqualTo(future ? 3 : 6), "Unexpected items count");
+ Assert.That(Sfi.Statistics.QueryExecutionCount, Is.EqualTo(1), "Unexpected execution count");
+ Assert.That(Sfi.Statistics.QueryCachePutCount, Is.EqualTo(future ? 2 : 1), "Unexpected cache put count");
+ Assert.That(Sfi.Statistics.QueryCacheMissCount, Is.EqualTo(future ? 2 : 1), "Unexpected cache miss count");
+
+ Sfi.Statistics.Clear();
+
+ using (var s = OpenSession())
+ using (var tx = s.BeginTransaction())
+ {
+ if (future)
+ {
+ s.Query()
+ .WithOptions(o => o.SetCacheable(true))
+ .FetchMany(o => o.Items)
+ .Where(o => o.Id > middleId)
+ .ToFuture();
+
+ items = s.Query()
+ .WithOptions(o => o.SetCacheable(true))
+ .FetchMany(o => o.Items)
+ .Where(o => o.Id <= middleId)
+ .ToFuture()
+ .ToList();
+ }
+ else
+ {
+ items = s.Query()
+ .WithOptions(o => o.SetCacheable(true))
+ .FetchMany(o => o.Items)
+ .ToList();
+ }
+
+ tx.Commit();
+ }
+
+ Assert.That(items, Has.Count.EqualTo(future ? 3 : 6));
+ Assert.That(Sfi.Statistics.QueryExecutionCount, Is.EqualTo(0), "Unexpected execution count");
+ Assert.That(Sfi.Statistics.QueryCachePutCount, Is.EqualTo(0), "Unexpected cache put count");
+ Assert.That(Sfi.Statistics.QueryCacheMissCount, Is.EqualTo(0), "Unexpected cache miss count");
+ Assert.That(Sfi.Statistics.QueryCacheHitCount, Is.EqualTo(future ? 2 : 1), "Unexpected cache hit count");
+ }
+
+ [TestCase(false)]
+ [TestCase(true)]
+ public void QueryFetchEntityBatchCacheTest(bool future)
+ {
+ if (future && !Sfi.ConnectionProvider.Driver.SupportsMultipleQueries)
+ {
+ Assert.Ignore($"{Sfi.ConnectionProvider.Driver} does not support multiple queries");
+ }
+
+ int middleId;
+
+ using (var s = OpenSession())
+ {
+ var ids = s.Query().Select(o => o.Id).OrderBy(o => o).ToList();
+ middleId = ids[17];
+ }
+
+ Sfi.Statistics.Clear();
+
+ List items;
+ using (var s = OpenSession())
+ using (var tx = s.BeginTransaction())
+ {
+ if (future)
+ {
+ s.Query()
+ .WithOptions(o => o.SetCacheable(true))
+ .Fetch(o => o.Parent)
+ .Where(o => o.Id > middleId)
+ .ToFuture();
+
+ items = s.Query()
+ .WithOptions(o => o.SetCacheable(true))
+ .Fetch(o => o.Parent)
+ .Where(o => o.Id <= middleId)
+ .ToFuture()
+ .ToList();
+ }
+ else
+ {
+ items = s.Query()
+ .WithOptions(o => o.SetCacheable(true))
+ .Fetch(o => o.Parent)
+ .ToList();
+ }
+
+ tx.Commit();
+ }
+
+ Assert.That(items, Has.Count.EqualTo(future ? 18 : 36), "Unexpected items count");
+ Assert.That(Sfi.Statistics.QueryExecutionCount, Is.EqualTo(1), "Unexpected execution count");
+ Assert.That(Sfi.Statistics.QueryCachePutCount, Is.EqualTo(future ? 2 : 1), "Unexpected cache put count");
+ Assert.That(Sfi.Statistics.QueryCacheMissCount, Is.EqualTo(future ? 2 : 1), "Unexpected cache miss count");
+
+ Sfi.Statistics.Clear();
+
+ using (var s = OpenSession())
+ using (var tx = s.BeginTransaction())
+ {
+ if (future)
+ {
+ s.Query()
+ .WithOptions(o => o.SetCacheable(true))
+ .Fetch(o => o.Parent)
+ .Where(o => o.Id > middleId)
+ .ToFuture();
+
+ items = s.Query()
+ .WithOptions(o => o.SetCacheable(true))
+ .Fetch(o => o.Parent)
+ .Where(o => o.Id <= middleId)
+ .ToFuture()
+ .ToList();
+ }
+ else
+ {
+ items = s.Query()
+ .WithOptions(o => o.SetCacheable(true))
+ .Fetch(o => o.Parent)
+ .ToList();
+ }
+
+ tx.Commit();
+ }
+
+ Assert.That(items, Has.Count.EqualTo(future ? 18 : 36));
+ Assert.That(Sfi.Statistics.QueryExecutionCount, Is.EqualTo(0), "Unexpected execution count");
+ Assert.That(Sfi.Statistics.QueryCachePutCount, Is.EqualTo(0), "Unexpected cache put count");
+ Assert.That(Sfi.Statistics.QueryCacheMissCount, Is.EqualTo(0), "Unexpected cache miss count");
+ Assert.That(Sfi.Statistics.QueryCacheHitCount, Is.EqualTo(future ? 2 : 1), "Unexpected cache hit count");
+ }
+ }
+}
diff --git a/src/NHibernate.Test/NHibernate.Test.csproj b/src/NHibernate.Test/NHibernate.Test.csproj
index eb38a252a3a..c32dfd7d6e2 100644
--- a/src/NHibernate.Test/NHibernate.Test.csproj
+++ b/src/NHibernate.Test/NHibernate.Test.csproj
@@ -57,6 +57,8 @@
+
+
diff --git a/src/NHibernate.Test/QueryTest/MultiCriteriaFixture.cs b/src/NHibernate.Test/QueryTest/MultiCriteriaFixture.cs
index 2b170b95905..acebb19a929 100644
--- a/src/NHibernate.Test/QueryTest/MultiCriteriaFixture.cs
+++ b/src/NHibernate.Test/QueryTest/MultiCriteriaFixture.cs
@@ -127,7 +127,7 @@ public void CanUseSecondLevelCacheWithPositionalParameters()
CreateItems();
- DoMutiQueryAndAssert();
+ DoMultiQueryAndAssert();
Assert.AreEqual(1, cacheHashtable.Count);
}
@@ -136,19 +136,20 @@ public void CanUseSecondLevelCacheWithPositionalParameters()
public void CanGetMultiQueryFromSecondLevelCache()
{
CreateItems();
- //set the query in the cache
- DoMutiQueryAndAssert();
+ // Set the query in the cache.
+ DoMultiQueryAndAssert();
var cacheHashtable = MultipleQueriesFixture.GetHashTableUsedAsQueryCache(Sfi);
- var cachedListEntry = (IList)new ArrayList(cacheHashtable.Values)[0];
- var cachedQuery = (IList)cachedListEntry[1];
+ var cachedListEntry = (IList) new ArrayList(cacheHashtable.Values)[0];
+ // The first element is a timestamp, then only we have the cached data.
+ var cachedQuery = (IList) cachedListEntry[1] ?? throw new InvalidOperationException("Cached data is null");
- var firstQueryResults = (IList)cachedQuery[0];
+ var firstQueryResults = (IList) cachedQuery[0];
firstQueryResults.Clear();
firstQueryResults.Add(3);
firstQueryResults.Add(4);
- var secondQueryResults = (IList)cachedQuery[1];
+ var secondQueryResults = (IList) cachedQuery[1];
secondQueryResults[0] = 2;
using (var s = Sfi.OpenSession())
@@ -160,9 +161,9 @@ public void CanGetMultiQueryFromSecondLevelCache()
.Add(CriteriaTransformer.Clone(criteria).SetProjection(Projections.RowCount()));
multiCriteria.SetCacheable(true);
var results = multiCriteria.List();
- var items = (IList)results[0];
+ var items = (IList) results[0];
Assert.AreEqual(2, items.Count);
- var count = (int)((IList)results[1])[0];
+ var count = (int) ((IList) results[1])[0];
Assert.AreEqual(2L, count);
}
}
@@ -172,12 +173,12 @@ public void CanUpdateStatisticsWhenGetMultiQueryFromSecondLevelCache()
{
CreateItems();
- DoMutiQueryAndAssert();
+ DoMultiQueryAndAssert();
Assert.AreEqual(0, Sfi.Statistics.QueryCacheHitCount);
Assert.AreEqual(1, Sfi.Statistics.QueryCacheMissCount);
Assert.AreEqual(1, Sfi.Statistics.QueryCachePutCount);
- DoMutiQueryAndAssert();
+ DoMultiQueryAndAssert();
Assert.AreEqual(1, Sfi.Statistics.QueryCacheHitCount);
Assert.AreEqual(1, Sfi.Statistics.QueryCacheMissCount);
Assert.AreEqual(1, Sfi.Statistics.QueryCachePutCount);
@@ -436,7 +437,7 @@ public void CanNotRetrieveDetachedCriteriaResultWithUnknownKey()
}
}
- private void DoMutiQueryAndAssert()
+ private void DoMultiQueryAndAssert()
{
using (var s = OpenSession())
{
@@ -447,9 +448,9 @@ private void DoMutiQueryAndAssert()
.Add(CriteriaTransformer.Clone(criteria).SetProjection(Projections.RowCount()));
multiCriteria.SetCacheable(true);
var results = multiCriteria.List();
- var items = (IList)results[0];
+ var items = (IList) results[0];
Assert.AreEqual(89, items.Count);
- var count = (int)((IList)results[1])[0];
+ var count = (int) ((IList) results[1])[0];
Assert.AreEqual(99L, count);
}
}
diff --git a/src/NHibernate.Test/QueryTest/MultipleMixedQueriesFixture.cs b/src/NHibernate.Test/QueryTest/MultipleMixedQueriesFixture.cs
index 561d679a0a7..cfd26a9efac 100644
--- a/src/NHibernate.Test/QueryTest/MultipleMixedQueriesFixture.cs
+++ b/src/NHibernate.Test/QueryTest/MultipleMixedQueriesFixture.cs
@@ -66,19 +66,20 @@ public void NH_1085_WillGiveReasonableErrorIfBadParameterName()
public void CanGetMultiQueryFromSecondLevelCache()
{
CreateItems();
- //set the query in the cache
- DoMutiQueryAndAssert();
+ // Set the query in the cache.
+ DoMultiQueryAndAssert();
var cacheHashtable = MultipleQueriesFixture.GetHashTableUsedAsQueryCache(Sfi);
- var cachedListEntry = (IList)new ArrayList(cacheHashtable.Values)[0];
- var cachedQuery = (IList)cachedListEntry[1];
+ var cachedListEntry = (IList) new ArrayList(cacheHashtable.Values)[0];
+ // The first element is a timestamp, then only we have the cached data.
+ var cachedQuery = (IList) cachedListEntry[1] ?? throw new InvalidOperationException("Cached data is null");
- var firstQueryResults = (IList)cachedQuery[0];
+ var firstQueryResults = (IList) cachedQuery[0];
firstQueryResults.Clear();
firstQueryResults.Add(3);
firstQueryResults.Add(4);
- var secondQueryResults = (IList)cachedQuery[1];
+ var secondQueryResults = (IList) cachedQuery[1];
secondQueryResults[0] = 2L;
using (var s = Sfi.OpenSession())
@@ -91,9 +92,9 @@ public void CanGetMultiQueryFromSecondLevelCache()
.SetInt32(0, 50));
multiQuery.SetCacheable(true);
var results = multiQuery.List();
- var items = (IList)results[0];
+ var items = (IList) results[0];
Assert.AreEqual(2, items.Count);
- var count = (long)((IList)results[1])[0];
+ var count = (long) ((IList) results[1])[0];
Assert.AreEqual(2L, count);
}
}
@@ -173,12 +174,12 @@ public void CanUseSecondLevelCacheWithPositionalParameters()
CreateItems();
- DoMutiQueryAndAssert();
+ DoMultiQueryAndAssert();
Assert.AreEqual(1, cacheHashtable.Count);
}
- private void DoMutiQueryAndAssert()
+ private void DoMultiQueryAndAssert()
{
using (var s = OpenSession())
{
@@ -190,9 +191,9 @@ private void DoMutiQueryAndAssert()
.SetInt32(0, 50));
multiQuery.SetCacheable(true);
var results = multiQuery.List();
- var items = (IList)results[0];
+ var items = (IList) results[0];
Assert.AreEqual(89, items.Count);
- var count = (long)((IList)results[1])[0];
+ var count = (long) ((IList) results[1])[0];
Assert.AreEqual(99L, count);
}
}
diff --git a/src/NHibernate.Test/QueryTest/MultipleQueriesFixture.cs b/src/NHibernate.Test/QueryTest/MultipleQueriesFixture.cs
index 6c4bd1063b6..a4eb5e81b41 100644
--- a/src/NHibernate.Test/QueryTest/MultipleQueriesFixture.cs
+++ b/src/NHibernate.Test/QueryTest/MultipleQueriesFixture.cs
@@ -69,19 +69,20 @@ public void NH_1085_WillGiveReasonableErrorIfBadParameterName()
public void CanGetMultiQueryFromSecondLevelCache()
{
CreateItems();
- //set the query in the cache
- DoMutiQueryAndAssert();
+ // Set the query in the cache.
+ DoMultiQueryAndAssert();
var cacheHashtable = GetHashTableUsedAsQueryCache(Sfi);
- var cachedListEntry = (IList)new ArrayList(cacheHashtable.Values)[0];
- var cachedQuery = (IList)cachedListEntry[1];
+ var cachedListEntry = (IList) new ArrayList(cacheHashtable.Values)[0];
+ // The first element is a timestamp, then only we have the cached data.
+ var cachedQuery = (IList) cachedListEntry[1] ?? throw new InvalidOperationException("Cached data is null");
- var firstQueryResults = (IList)cachedQuery[0];
+ var firstQueryResults = (IList) cachedQuery[0];
firstQueryResults.Clear();
firstQueryResults.Add(3);
firstQueryResults.Add(4);
- var secondQueryResults = (IList)cachedQuery[1];
+ var secondQueryResults = (IList) cachedQuery[1];
secondQueryResults[0] = 2L;
using (var s = Sfi.OpenSession())
@@ -94,9 +95,9 @@ public void CanGetMultiQueryFromSecondLevelCache()
.SetInt32(0, 50));
multiQuery.SetCacheable(true);
var results = multiQuery.List();
- var items = (IList)results[0];
+ var items = (IList) results[0];
Assert.AreEqual(2, items.Count);
- var count = (long)((IList)results[1])[0];
+ var count = (long) ((IList) results[1])[0];
Assert.AreEqual(2L, count);
}
}
@@ -175,12 +176,12 @@ public void CanUseSecondLevelCacheWithPositionalParameters()
CreateItems();
- DoMutiQueryAndAssert();
+ DoMultiQueryAndAssert();
Assert.AreEqual(1, cacheHashtable.Count);
}
- private void DoMutiQueryAndAssert()
+ private void DoMultiQueryAndAssert()
{
using (var s = OpenSession())
{
@@ -192,9 +193,9 @@ private void DoMutiQueryAndAssert()
.SetInt32(0, 50));
multiQuery.SetCacheable(true);
var results = multiQuery.List();
- var items = (IList)results[0];
+ var items = (IList) results[0];
Assert.AreEqual(89, items.Count);
- var count = (long)((IList)results[1])[0];
+ var count = (long) ((IList) results[1])[0];
Assert.AreEqual(99L, count);
}
}
diff --git a/src/NHibernate.Test/SqlTest/Query/NativeSQLQueriesFixture.cs b/src/NHibernate.Test/SqlTest/Query/NativeSQLQueriesFixture.cs
index 80eeb3c4c73..38c395298df 100644
--- a/src/NHibernate.Test/SqlTest/Query/NativeSQLQueriesFixture.cs
+++ b/src/NHibernate.Test/SqlTest/Query/NativeSQLQueriesFixture.cs
@@ -1,8 +1,9 @@
using System.Collections;
using System.Linq;
+using NHibernate.Criterion;
+using NHibernate.Multi;
using NHibernate.Transform;
using NUnit.Framework;
-using NHibernate.Criterion;
namespace NHibernate.Test.SqlTest.Query
{
@@ -51,6 +52,20 @@ protected override string MappingsAssembly
get { return "NHibernate.Test"; }
}
+ protected override void OnTearDown()
+ {
+ using (var session = OpenSession())
+ using (var transaction = session.BeginTransaction())
+ {
+ session.CreateQuery("delete from Employment").ExecuteUpdate();
+ session.CreateQuery("delete from System.Object").ExecuteUpdate();
+
+ transaction.Commit();
+ }
+
+ Sfi.QueryCache.Clear();
+ }
+
[Test]
public void FailOnNoAddEntityOrScalar()
{
@@ -125,18 +140,6 @@ public void SQLQueryInterface()
t.Commit();
s.Close();
}
-
- using (var s = OpenSession())
- using (var t = s.BeginTransaction())
- {
- s.Delete(emp);
- s.Delete(gavin);
- s.Delete(ifa);
- s.Delete(jboss);
-
- t.Commit();
- s.Close();
- }
}
[Test]
@@ -191,22 +194,10 @@ public void SQLQueryInterfaceCacheable()
t.Commit();
s.Close();
}
-
- using (var s = OpenSession())
- using (var t = s.BeginTransaction())
- {
- s.Delete(emp);
- s.Delete(gavin);
- s.Delete(ifa);
- s.Delete(jboss);
-
- t.Commit();
- s.Close();
- }
}
[Test(Description = "GH-2904")]
- public void CacheableScalarSQLQuery()
+ public void CacheableScalarSqlQuery()
{
Organization ifa = new Organization("IFA");
Organization jboss = new Organization("JBoss");
@@ -252,25 +243,16 @@ IList GetCacheableSqlQueryResults()
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();
- }
}
class ResultDto
{
+ public long orgId { get; set; }
public string regionCode { get; set; }
}
[Test(Description = "GH-3169")]
- public void CacheableScalarSQLQueryWithTransformer()
+ public void CacheableScalarSqlQueryWithTransformer()
{
Organization ifa = new Organization("IFA");
@@ -294,23 +276,170 @@ void AssertQuery(bool fromCache)
.List();
t.Commit();
- Assert.AreEqual(1, l.Count);
- //TODO: Uncomment if we properly fix caching auto discovery type queries with transformers
- // var msg = "results are expected from " + (fromCache ? "cache" : "DB");
- // Assert.That(Sfi.Statistics.QueryCacheMissCount, Is.EqualTo(fromCache ? 0 : 1), msg);
- // Assert.That(Sfi.Statistics.QueryCacheHitCount, Is.EqualTo(fromCache ? 1 : 0), msg);
+ Assert.That(l.Count, Is.EqualTo(1));
+ var msg = "Results are expected from " + (fromCache ? "cache" : "DB");
+ Assert.That(Sfi.Statistics.QueryCacheMissCount, Is.EqualTo(fromCache ? 0 : 1), msg);
+ Assert.That(Sfi.Statistics.QueryCacheHitCount, Is.EqualTo(fromCache ? 1 : 0), msg);
}
}
AssertQuery(false);
AssertQuery(true);
+ }
- using (var s = OpenSession())
- using (var t = s.BeginTransaction())
+ [Test(Description = "GH-3169")]
+ public void CacheableScalarSqlEmptyQueryWithTransformer()
+ {
+ void AssertQuery(bool fromCache)
{
- s.Delete(ifa);
+ using (var s = OpenSession())
+ using (var t = s.BeginTransaction())
+ using (EnableStatisticsScope())
+ {
+ var l = s.CreateSQLQuery("select org.NAME as regionCode from ORGANIZATION org")
+ .AddScalar("regionCode", NHibernateUtil.String)
+ .SetResultTransformer(Transformers.AliasToBean())
+ .SetCacheable(true)
+ .List();
+ t.Commit();
+
+ Assert.That(l.Count, Is.EqualTo(0));
+ var msg = "Results are expected from " + (fromCache ? "cache" : "DB");
+ Assert.That(Sfi.Statistics.QueryCacheMissCount, Is.EqualTo(fromCache ? 0 : 1), msg);
+ Assert.That(Sfi.Statistics.QueryCacheHitCount, Is.EqualTo(fromCache ? 1 : 0), msg);
+ }
+ }
+
+ AssertQuery(false);
+ AssertQuery(true);
+ }
+
+ [Test(Description = "GH-3169")]
+ public void CacheableScalarSqlMultiQueryWithTransformer()
+ {
+ Organization ifa = new Organization("IFA");
+
+ using (ISession s = OpenSession())
+ using (ITransaction t = s.BeginTransaction())
+ {
+ s.Save(ifa);
t.Commit();
}
+
+ void AssertQuery(bool fromCache)
+ {
+ using (var s = OpenSession())
+ using (var t = s.BeginTransaction())
+ using (EnableStatisticsScope())
+ {
+ var q1 = s.CreateSQLQuery("select org.NAME as regionCode from ORGANIZATION org")
+ .AddScalar("regionCode", NHibernateUtil.String)
+ .SetResultTransformer(Transformers.AliasToBean())
+ .SetCacheable(true);
+ var q2 = s.CreateSQLQuery("select org.ORGID as orgId, org.NAME as regionCode from ORGANIZATION org")
+ .AddScalar("orgId", NHibernateUtil.Int64)
+ .AddScalar("regionCode", NHibernateUtil.String)
+ .SetResultTransformer(Transformers.AliasToBean())
+ .SetCacheable(true);
+
+ var batch = s.CreateQueryBatch();
+ batch.Add(q1);
+ batch.Add(q2);
+ batch.Execute();
+
+ var l1 = batch.GetResult(0);
+ var l2 = batch.GetResult(1);
+
+ t.Commit();
+
+ Assert.That(l1.Count, Is.EqualTo(1), "Unexpected results count for the first query.");
+ Assert.That(l2.Count, Is.EqualTo(1), "Unexpected results count for the second query.");
+ var msg = "Results are expected from " + (fromCache ? "cache" : "DB");
+ Assert.That(Sfi.Statistics.QueryCacheMissCount, Is.EqualTo(fromCache ? 0 : 2), msg);
+ Assert.That(Sfi.Statistics.QueryCacheHitCount, Is.EqualTo(fromCache ? 2 : 0), msg);
+ }
+ }
+
+ AssertQuery(false);
+ AssertQuery(true);
+ }
+
+ [Test(Description = "GH-3169")]
+ public void CacheableScalarSqlEmptyMultiQueryWithTransformer()
+ {
+ void AssertQuery(bool fromCache)
+ {
+ using (var s = OpenSession())
+ using (var t = s.BeginTransaction())
+ using (EnableStatisticsScope())
+ {
+ var q1 = s.CreateSQLQuery("select org.NAME as regionCode from ORGANIZATION org")
+ .AddScalar("regionCode", NHibernateUtil.String)
+ .SetResultTransformer(Transformers.AliasToBean())
+ .SetCacheable(true);
+ var q2 = s.CreateSQLQuery("select org.ORGID as orgId, org.NAME as regionCode from ORGANIZATION org")
+ .AddScalar("orgId", NHibernateUtil.Int64)
+ .AddScalar("regionCode", NHibernateUtil.String)
+ .SetResultTransformer(Transformers.AliasToBean())
+ .SetCacheable(true);
+
+ var batch = s.CreateQueryBatch();
+ batch.Add(q1);
+ batch.Add(q2);
+ batch.Execute();
+
+ var l1 = batch.GetResult(0);
+ var l2 = batch.GetResult(1);
+
+ t.Commit();
+
+ Assert.That(l1.Count, Is.EqualTo(0), "Unexpected results count for the first query.");
+ Assert.That(l2.Count, Is.EqualTo(0), "Unexpected results count for the second query.");
+ var msg = "Results are expected from " + (fromCache ? "cache" : "DB");
+ Assert.That(Sfi.Statistics.QueryCacheMissCount, Is.EqualTo(fromCache ? 0 : 2), msg);
+ Assert.That(Sfi.Statistics.QueryCacheHitCount, Is.EqualTo(fromCache ? 2 : 0), msg);
+ }
+ }
+
+ AssertQuery(false);
+ AssertQuery(true);
+ }
+
+ [Test(Description = "GH-3169")]
+ public void CacheableMultiScalarSqlQueryWithTransformer()
+ {
+ Organization ifa = new Organization("IFA");
+
+ using (ISession s = OpenSession())
+ using (ITransaction t = s.BeginTransaction())
+ {
+ s.Save(ifa);
+ t.Commit();
+ }
+
+ void AssertQuery(bool fromCache)
+ {
+ using (var s = OpenSession())
+ using (var t = s.BeginTransaction())
+ using (EnableStatisticsScope())
+ {
+ var l = s.CreateSQLQuery("select org.ORGID as orgId, org.NAME as regionCode from ORGANIZATION org")
+ .AddScalar("orgId", NHibernateUtil.Int64)
+ .AddScalar("regionCode", NHibernateUtil.String)
+ .SetResultTransformer(Transformers.AliasToBean())
+ .SetCacheable(true)
+ .List();
+ t.Commit();
+
+ Assert.That(l.Count, Is.EqualTo(1));
+ var msg = "Results are expected from " + (fromCache ? "cache" : "DB");
+ Assert.That(Sfi.Statistics.QueryCacheMissCount, Is.EqualTo(fromCache ? 0 : 1), msg);
+ Assert.That(Sfi.Statistics.QueryCacheHitCount, Is.EqualTo(fromCache ? 1 : 0), msg);
+ }
+ }
+
+ AssertQuery(false);
+ AssertQuery(true);
}
[Test]
@@ -338,11 +467,6 @@ public void ResultSetMappingDefinition()
.List();
Assert.AreEqual(l.Count, 1);
- s.Delete(emp);
- s.Delete(gavin);
- s.Delete(ifa);
- s.Delete(jboss);
-
t.Commit();
s.Close();
}
@@ -425,8 +549,6 @@ public void ScalarValues()
Assert.AreEqual(o[1], "JBoss");
Assert.AreEqual(o[0], idJBoss);
- s.Delete(ifa);
- s.Delete(jboss);
t.Commit();
s.Close();
}
diff --git a/src/NHibernate/Async/Cache/StandardQueryCache.cs b/src/NHibernate/Async/Cache/StandardQueryCache.cs
index ab7d4f4547e..d1a0ea7010b 100644
--- a/src/NHibernate/Async/Cache/StandardQueryCache.cs
+++ b/src/NHibernate/Async/Cache/StandardQueryCache.cs
@@ -13,7 +13,6 @@
using System.Collections.Generic;
using System.Linq;
using NHibernate.Cfg;
-using NHibernate.Collection;
using NHibernate.Engine;
using NHibernate.Persister.Collection;
using NHibernate.Type;
@@ -67,7 +66,7 @@ public async Task PutAsync(QueryKey key, ICacheAssembler[] returnTypes, IL
Log.Debug("caching query results in region: '{0}'; {1}", _regionName, key);
- await (Cache.PutAsync(key, await (GetCacheableResultAsync(returnTypes, session, result, ts, cancellationToken)).ConfigureAwait(false), cancellationToken)).ConfigureAwait(false);
+ await (Cache.PutAsync(key, await (GetCacheableResultAsync(returnTypes, session, result, ts, GetAutoDiscoveredAliases(key), cancellationToken)).ConfigureAwait(false), cancellationToken)).ConfigureAwait(false);
return true;
}
@@ -91,10 +90,35 @@ public async Task GetAsync(
try
{
- // 6.0 TODO: inline the call.
-#pragma warning disable 612
- return await (GetAsync(key, returnTypes, queryParameters.NaturalKeyLookup, spaces, session, cancellationToken)).ConfigureAwait(false);
-#pragma warning restore 612
+ if (Log.IsDebugEnabled())
+ Log.Debug("checking cached query results in region: '{0}'; {1}", _regionName, key);
+
+ var cacheable = (IList) await (Cache.GetAsync(key, cancellationToken)).ConfigureAwait(false);
+ if (cacheable == null)
+ {
+ Log.Debug("query results were not found in cache: {0}", key);
+ return null;
+ }
+
+ var timestamp = GetResultsMetadata(cacheable, out var aliases);
+
+ if (Log.IsDebugEnabled())
+ Log.Debug("Checking query spaces for up-to-dateness [{0}]", StringHelper.CollectionToString(spaces));
+
+ if (!queryParameters.NaturalKeyLookup && !await (IsUpToDateAsync(spaces, timestamp, cancellationToken)).ConfigureAwait(false))
+ {
+ Log.Debug("cached query results were not up to date for: {0}", key);
+ return null;
+ }
+
+ var result = await (GetResultFromCacheableAsync(key, returnTypes, queryParameters.NaturalKeyLookup, session, cacheable, cancellationToken)).ConfigureAwait(false);
+
+ if (result != null && key.ResultTransformer?.AutoDiscoverTypes == true && result.Count > 0)
+ {
+ key.ResultTransformer.SupplyAutoDiscoveredParameters(queryParameters.ResultTransformer, aliases);
+ }
+
+ return result;
}
finally
{
@@ -117,7 +141,7 @@ public async Task GetAsync(QueryKey key, ICacheAssembler[] returnTypes, b
return null;
}
- var timestamp = (long) cacheable[0];
+ var timestamp = GetResultsMetadata(cacheable, out _);
if (Log.IsDebugEnabled())
Log.Debug("Checking query spaces for up-to-dateness [{0}]", StringHelper.CollectionToString(spaces));
@@ -153,9 +177,10 @@ public async Task PutManyAsync(
if (queryParameters[i].NaturalKeyLookup && result.Count == 0)
continue;
+ var key = keys[i];
cached[i] = true;
- cachedKeys.Add(keys[i]);
- cachedResults.Add(await (GetCacheableResultAsync(returnTypes[i], session, result, ts, cancellationToken)).ConfigureAwait(false));
+ cachedKeys.Add(key);
+ cachedResults.Add(await (GetCacheableResultAsync(returnTypes[i], session, result, ts, GetAutoDiscoveredAliases(key), cancellationToken)).ConfigureAwait(false));
}
await (_cache.PutManyAsync(cachedKeys.ToArray(), cachedResults.ToArray(), cancellationToken)).ConfigureAwait(false);
@@ -189,14 +214,20 @@ public async Task GetManyAsync(
continue;
}
+ var timestamp = GetResultsMetadata(cacheable, out var aliases);
+ var key = keys[i];
+ if (key.ResultTransformer?.AutoDiscoverTypes == true && !IsEmpty(cacheable))
+ {
+ key.ResultTransformer.SupplyAutoDiscoveredParameters(queryParameters[i].ResultTransformer, aliases);
+ }
+
var querySpaces = spaces[i];
if (queryParameters[i].NaturalKeyLookup || querySpaces.Count == 0)
continue;
spacesToCheck.Add(querySpaces);
checkedSpacesIndexes.Add(i);
- // The timestamp is the first element of the cache result.
- checkedSpacesTimestamp.Add((long) cacheable[0]);
+ checkedSpacesTimestamp.Add(timestamp);
if (Log.IsDebugEnabled())
Log.Debug("Checking query spaces for up-to-dateness [{0}]", StringHelper.CollectionToString(querySpaces));
}
@@ -224,6 +255,7 @@ public async Task GetManyAsync(
if (checkedSpacesIndexes.Contains(i) && !upToDates[upToDatesIndex++])
{
Log.Debug("cached query results were not up to date for: {0}", key);
+ cacheables[i] = null;
continue;
}
@@ -241,7 +273,7 @@ public async Task GetManyAsync(
for (var i = 0; i < keys.Length; i++)
{
- if (finalReturnTypes[i] == null)
+ if (cacheables[i] == null)
{
continue;
}
@@ -264,7 +296,7 @@ public async Task GetManyAsync(
for (var i = 0; i < keys.Length; i++)
{
- if (finalReturnTypes[i] == null)
+ if (cacheables[i] == null)
{
continue;
}
@@ -299,10 +331,16 @@ private static async Task> GetCacheableResultAsync(
ICacheAssembler[] returnTypes,
ISessionImplementor session,
IList result,
- long ts, CancellationToken cancellationToken)
+ long ts,
+ string[] aliases, CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
- var cacheable = new List