From a251d326eee1b65a731c30d73b16ce44615d4227 Mon Sep 17 00:00:00 2001 From: ITMAGINATION Date: Tue, 23 Feb 2016 10:38:15 +0100 Subject: [PATCH 01/20] Tests using Criteria and QueryOver for NH-3848 --- .../NH3848/CriteriaTestFixture.cs | 70 +++++ .../NHSpecificTest/NH3848/Customer.cs | 23 ++ .../NHSpecificTest/NH3848/Order.cs | 11 + .../NH3848/QueryOverTestFixture.cs | 84 ++++++ .../NHSpecificTest/NH3848/TestFixture.cs | 266 ++++++++++++++++++ 5 files changed, 454 insertions(+) create mode 100644 src/NHibernate.Test/NHSpecificTest/NH3848/CriteriaTestFixture.cs create mode 100644 src/NHibernate.Test/NHSpecificTest/NH3848/Customer.cs create mode 100644 src/NHibernate.Test/NHSpecificTest/NH3848/Order.cs create mode 100644 src/NHibernate.Test/NHSpecificTest/NH3848/QueryOverTestFixture.cs create mode 100644 src/NHibernate.Test/NHSpecificTest/NH3848/TestFixture.cs diff --git a/src/NHibernate.Test/NHSpecificTest/NH3848/CriteriaTestFixture.cs b/src/NHibernate.Test/NHSpecificTest/NH3848/CriteriaTestFixture.cs new file mode 100644 index 00000000000..a53d3b9e03e --- /dev/null +++ b/src/NHibernate.Test/NHSpecificTest/NH3848/CriteriaTestFixture.cs @@ -0,0 +1,70 @@ +using System.Collections.Generic; +using NHibernate.Criterion; +using NHibernate.SqlCommand; +using NHibernate.Transform; + +namespace NHibernate.Test.NHSpecificTest.NH3848 +{ + public class CriteriaTestFixture : TestFixture + { + protected override IList GetCustomersWithFetchedOrdersWithoutRestrictions(ISession session) + { + return session.CreateCriteria() + .CreateAlias("Orders", "order", JoinType.LeftOuterJoin) + .SetResultTransformer(new DistinctRootEntityResultTransformer()) + .List(); + } + + protected override IList GetCustomersWithOrdersEagerLoaded(ISession session) + { + return session.CreateCriteria() + .SetFetchMode("Orders", FetchMode.Eager) + .SetResultTransformer(new DistinctRootEntityResultTransformer()) + .List(); + } + + protected override IList GetCustomersByOrderNumberUsingOnClause(ISession session, int orderNumber) + { + return session.CreateCriteria() + .CreateAlias("Orders", "order", JoinType.LeftOuterJoin, Restrictions.Eq("Number", orderNumber)) + .List(); + } + + protected override IList GetCustomersByOrderNumberUsingWhereClause(ISession session, int orderNumber) + { + return session.CreateCriteria() + .CreateCriteria("Orders", "Order", JoinType.LeftOuterJoin) + .Add(Restrictions.Eq("Number", orderNumber)) + .SetResultTransformer(new DistinctRootEntityResultTransformer()) + .List(); + } + + protected override IList GetCustomersByNameUsingWhereClause(ISession session, string customerName) + { + return session.CreateCriteria() + .CreateAlias("Orders", "order", JoinType.LeftOuterJoin) + .Add(Restrictions.Eq("Name", "First Customer")) + .SetResultTransformer(new DistinctRootEntityResultTransformer()) + .List(); + } + + protected override IList GetCustomersByOrderNumberUsingSubqueriesAndByNameUsingWhereClause(ISession session, int orderNumber, string customerName) + { + var detachedCriteria = DetachedCriteria.For() + .CreateAlias("Orders", "order", JoinType.LeftOuterJoin, Restrictions.Eq("Number", orderNumber)) + .SetProjection(Projections.Id()); + + return session.CreateCriteria() + .CreateAlias("Orders", "order1", JoinType.LeftOuterJoin) + .Add(Subqueries.PropertyIn("Id",detachedCriteria)) + .Add(Restrictions.Eq("Name", customerName)) + .SetResultTransformer(new DistinctRootEntityResultTransformer()) + .List(); + } + + protected override IList GetAllCustomers(ISession session) + { + return session.CreateCriteria().List(); + } + } +} diff --git a/src/NHibernate.Test/NHSpecificTest/NH3848/Customer.cs b/src/NHibernate.Test/NHSpecificTest/NH3848/Customer.cs new file mode 100644 index 00000000000..60d80e7d75b --- /dev/null +++ b/src/NHibernate.Test/NHSpecificTest/NH3848/Customer.cs @@ -0,0 +1,23 @@ +using System; +using System.Collections.Generic; + +namespace NHibernate.Test.NHSpecificTest.NH3848 +{ + public class Customer + { + public virtual Guid Id { get; set; } + public virtual ISet Orders { get; set; } + public virtual string Name { get; set; } + + public Customer() + { + Orders = new HashSet(); + } + + public virtual void AddOrder(Order order) + { + Orders.Add(order); + order.Customer = this; + } + } +} diff --git a/src/NHibernate.Test/NHSpecificTest/NH3848/Order.cs b/src/NHibernate.Test/NHSpecificTest/NH3848/Order.cs new file mode 100644 index 00000000000..a875ad79c1e --- /dev/null +++ b/src/NHibernate.Test/NHSpecificTest/NH3848/Order.cs @@ -0,0 +1,11 @@ +using System; + +namespace NHibernate.Test.NHSpecificTest.NH3848 +{ + public class Order + { + public virtual Guid Id { get; set; } + public virtual int Number { get; set; } + public virtual Customer Customer { get; set; } + } +} diff --git a/src/NHibernate.Test/NHSpecificTest/NH3848/QueryOverTestFixture.cs b/src/NHibernate.Test/NHSpecificTest/NH3848/QueryOverTestFixture.cs new file mode 100644 index 00000000000..3f09e783355 --- /dev/null +++ b/src/NHibernate.Test/NHSpecificTest/NH3848/QueryOverTestFixture.cs @@ -0,0 +1,84 @@ +using System.Collections.Generic; +using NHibernate.Criterion; +using NHibernate.SqlCommand; +using NHibernate.Transform; + +namespace NHibernate.Test.NHSpecificTest.NH3848 +{ + public class QueryOverTestFixture : TestFixture + { + protected override IList GetCustomersWithFetchedOrdersWithoutRestrictions(ISession session) + { + Order ordersAlias = null; + return session.QueryOver() + .JoinAlias(n => n.Orders, + () => ordersAlias, + JoinType.LeftOuterJoin) + .TransformUsing(new DistinctRootEntityResultTransformer()) + .List(); + } + + protected override IList GetCustomersWithOrdersEagerLoaded(ISession session) + { + return session.QueryOver() + .Fetch(n => n.Orders).Eager + .TransformUsing(new DistinctRootEntityResultTransformer()) + .List(); + } + + protected override IList GetCustomersByOrderNumberUsingOnClause(ISession session, int orderNumber) + { + Order ordersAlias = null; + return session.QueryOver() + .JoinAlias(n => n.Orders, + () => ordersAlias, + JoinType.LeftOuterJoin, + Restrictions.Eq("Number", orderNumber)) + .List(); + } + + protected override IList GetCustomersByOrderNumberUsingWhereClause(ISession session, int orderNumber) + { + Order ordersAlias = null; + return session.QueryOver() + .JoinQueryOver(n => n.Orders, () => ordersAlias, JoinType.LeftOuterJoin) + .Where(Restrictions.Eq("Number", orderNumber)) + .List(); + } + + protected override IList GetCustomersByNameUsingWhereClause(ISession session, string customerName) + { + Order ordersAlias = null; + return session.QueryOver() + .JoinAlias(n => n.Orders, + () => ordersAlias, + JoinType.LeftOuterJoin + ) + .Where(Restrictions.Eq("Name", customerName)) + .TransformUsing(new DistinctRootEntityResultTransformer()) + .List(); + + } + + protected override IList GetCustomersByOrderNumberUsingSubqueriesAndByNameUsingWhereClause(ISession session, int orderNumber, string customerName) + { + Order ordersAlias = null; + Order ordersAlias2 = null; + var subquery = QueryOver.Of() + .JoinAlias(n => n.Orders, () => ordersAlias, JoinType.LeftOuterJoin, Restrictions.Eq("Number", orderNumber)) + .Select(n => n.Id); + + return session.QueryOver() + .JoinAlias(n => n.Orders, () => ordersAlias2, JoinType.LeftOuterJoin) + .WithSubquery.WhereProperty(n => n.Id).In(subquery) + .Where(Restrictions.Eq("Name", customerName)) + .TransformUsing(new DistinctRootEntityResultTransformer()) + .List(); + } + + protected override IList GetAllCustomers(ISession session) + { + return session.QueryOver().List(); + } + } +} diff --git a/src/NHibernate.Test/NHSpecificTest/NH3848/TestFixture.cs b/src/NHibernate.Test/NHSpecificTest/NH3848/TestFixture.cs new file mode 100644 index 00000000000..7bc83c3a30d --- /dev/null +++ b/src/NHibernate.Test/NHSpecificTest/NH3848/TestFixture.cs @@ -0,0 +1,266 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Data; +using System.Linq; +using System.Linq.Expressions; +using NHibernate.Cache; +using NHibernate.Cfg; +using NHibernate.Cfg.MappingSchema; +using NHibernate.Mapping.ByCode; +using NUnit.Framework; + +namespace NHibernate.Test.NHSpecificTest.NH3848 +{ + public abstract class TestFixture : TestCaseMappingByCode + { + protected Customer Customer1; + protected Customer Customer2; + protected Customer Customer3; + protected const int OrderNumber = 2; + + protected override HbmMapping GetMappings() + { + var mapper = new ModelMapper(); + mapper.Class(rc => + { + rc.Table("Customers"); + rc.Id(x => x.Id, m => m.Generator(Generators.GuidComb)); + rc.Property(x => x.Name); + rc.Set(x => x.Orders, m => + { + m.Inverse(true); + m.Key(k => + { + k.Column("CustomerId"); + k.NotNullable(true); + }); + m.Cascade(Mapping.ByCode.Cascade.All.Include(Mapping.ByCode.Cascade.DeleteOrphans)); + m.Cache(c => c.Usage(CacheUsage.ReadWrite)); + }, m => m.OneToMany()); + }); + + mapper.Class(rc => + { + rc.Table("Orders"); + rc.Id(x => x.Id, m => m.Generator(Generators.GuidComb)); + rc.Property(x => x.Number); + rc.ManyToOne(x => x.Customer, m => m.Column("CustomerId")); + rc.Cache(c => c.Usage(CacheUsage.ReadWrite)); + }); + + return mapper.CompileMappingForAllExplicitlyAddedEntities(); + } + + protected override void Configure(Configuration configuration) + { + base.Configure(configuration); + configuration.Cache(c => + { + c.UseQueryCache = true; + c.Provider(); + }); + } + + protected override void OnSetUp() + { + using (var session = OpenSession()) + using (var transaction = session.BeginTransaction()) + { + Customer1 = new Customer { Name = "First Customer" }; + + Customer2 = new Customer { Name = "Second Customer" }; + + Customer3 = new Customer { Name = "Third Customer" }; + + var customer1Order1 = new Order { Number = 1 }; + Customer1.AddOrder(customer1Order1); + + var customer1Order2 = new Order { Number = 2 }; + Customer1.AddOrder(customer1Order2); + + var customer2Order1 = new Order { Number = 1 }; + Customer2.AddOrder(customer2Order1); + + var customer2Order2 = new Order { Number = 2 }; + Customer2.AddOrder(customer2Order2); + + var customer3Order1 = new Order { Number = 1 }; + Customer3.AddOrder(customer3Order1); + + session.Save(Customer1); + session.Save(Customer2); + session.Save(Customer3); + + transaction.Commit(); + session.Flush(); + } + } + + protected override void OnTearDown() + { + ClearSecondLevelCacheFor(typeof(Customer)); + ClearCollectionCache(n => n.Orders); + ClearSecondLevelCacheFor(typeof(Order)); + + using (var session = OpenSession()) + using (var transaction = session.BeginTransaction()) + { + session.Delete("from System.Object"); + + session.Flush(); + transaction.Commit(); + } + } + + [Test] + public virtual void ChildCollectionsFromLeftOuterJoinWithOnClauseRestrictionOnCollectionShouldNotBeInSecondLevelCache() + { + var firstSession = OpenSession(); + var customersWithOrderNumberEqualsTo2 = GetCustomersByOrderNumberUsingOnClause(firstSession, OrderNumber); + + var secondSession = OpenSession(); + var customers = GetAllCustomers(secondSession); + + Assert.That(customersWithOrderNumberEqualsTo2.Single(n => n.Id == Customer1.Id).Orders, Has.Count.EqualTo(Customer1.Orders.Count(n => n.Number == OrderNumber))); + Assert.That(customersWithOrderNumberEqualsTo2.Single(n => n.Id == Customer2.Id).Orders, Has.Count.EqualTo(Customer2.Orders.Count(n => n.Number == OrderNumber))); + Assert.That(customersWithOrderNumberEqualsTo2.Single(n => n.Id == Customer3.Id).Orders, Has.Count.EqualTo(Customer3.Orders.Count(n => n.Number == OrderNumber))); + + Assert.That(customers.Single(n => n.Id == Customer1.Id).Orders, Has.Count.EqualTo(Customer1.Orders.Count)); + Assert.That(customers.Single(n => n.Id == Customer2.Id).Orders, Has.Count.EqualTo(Customer2.Orders.Count)); + Assert.That(customers.Single(n => n.Id == Customer3.Id).Orders, Has.Count.EqualTo(Customer3.Orders.Count)); + + firstSession.Dispose(); + secondSession.Dispose(); + } + + [Test] + public void ChildCollectionsFromLeftOuterJoinWithWhereClauseRestrictionOnCollectionShouldNotBeInSecondLevelCache() + { + var firstSession = OpenSession(); + var customersWithOrderNumberEqualsTo2 = GetCustomersByOrderNumberUsingWhereClause(firstSession, OrderNumber); + + var secondSession = OpenSession(); + var customers = GetAllCustomers(secondSession); + + Assert.That(customersWithOrderNumberEqualsTo2.Single(n => n.Id == Customer1.Id).Orders, Has.Count.EqualTo(Customer1.Orders.Count(n => n.Number == OrderNumber))); + Assert.That(customersWithOrderNumberEqualsTo2.Single(n => n.Id == Customer2.Id).Orders, Has.Count.EqualTo(Customer2.Orders.Count(n => n.Number == OrderNumber))); + + Assert.That(customers.Single(n => n.Id == Customer3.Id).Orders, Has.Count.EqualTo(Customer3.Orders.Count)); + Assert.That(customers.Single(n => n.Id == Customer1.Id).Orders, Has.Count.EqualTo(Customer1.Orders.Count)); + Assert.That(customers.Single(n => n.Id == Customer2.Id).Orders, Has.Count.EqualTo(Customer2.Orders.Count)); + + firstSession.Dispose(); + secondSession.Dispose(); + } + + [Test] + public void ChildCollectionsEagerFetchedShouldBeInSecondLevelCache() + { + var firstSession = OpenSession(); + var customersWithOrderNumberEqualsTo2 = GetCustomersWithOrdersEagerLoaded(firstSession); + + using (IDbCommand cmd = OpenSession().Connection.CreateCommand()) + { + cmd.CommandText = "DELETE FROM Orders;"; + cmd.ExecuteNonQuery(); + cmd.Connection.Close(); + } + + var secondSession = OpenSession(); + var customers = GetAllCustomers(secondSession); + + + Assert.That(customersWithOrderNumberEqualsTo2.Single(n => n.Id == Customer1.Id).Orders, Has.Count.EqualTo(Customer1.Orders.Count)); + Assert.That(customersWithOrderNumberEqualsTo2.Single(n => n.Id == Customer2.Id).Orders, Has.Count.EqualTo(Customer2.Orders.Count)); + + Assert.That(customers.Single(n => n.Id == Customer3.Id).Orders, Has.Count.EqualTo(Customer3.Orders.Count)); + Assert.That(customers.Single(n => n.Id == Customer1.Id).Orders, Has.Count.EqualTo(Customer1.Orders.Count)); + Assert.That(customers.Single(n => n.Id == Customer2.Id).Orders, Has.Count.EqualTo(Customer2.Orders.Count)); + + firstSession.Dispose(); + secondSession.Dispose(); + } + + [Test] + public void ChildCollectionsFromLeftOuterJoinWithWhereClauseRestrictionOnRootShouldBeInSecondLevelCache() + { + var firstSession = OpenSession(); + var customersWithOrderNumberEqualsTo2 = GetCustomersByNameUsingWhereClause(firstSession, "First Customer"); + + + using (IDbCommand cmd = OpenSession().Connection.CreateCommand()) + { + cmd.CommandText = "DELETE FROM Orders;"; + cmd.ExecuteNonQuery(); + cmd.Connection.Close(); + } + + var secondSession = OpenSession(); + var customers = secondSession.Get(Customer1.Id); + + Assert.That(customersWithOrderNumberEqualsTo2.Single(n => n.Id == Customer1.Id).Orders, Has.Count.EqualTo(Customer1.Orders.Count)); + Assert.That(customers.Orders, Has.Count.EqualTo(Customer1.Orders.Count)); + + firstSession.Dispose(); + secondSession.Dispose(); + } + + [Test] + public void ChildCollectionsFromLeftOuterJoinShouldBeInSecondLevelCacheIfQueryContainsSubqueryWithRestrictionOnLeftOuterJoin() + { + var firstSession = OpenSession(); + var customersWithOrderNumberEqualsTo2 = GetCustomersByOrderNumberUsingSubqueriesAndByNameUsingWhereClause(firstSession, OrderNumber, Customer1.Name); + + var secondSession = OpenSession(); + var customers = GetAllCustomers(secondSession); + + Assert.That(customersWithOrderNumberEqualsTo2.Single(n => n.Id == Customer1.Id).Orders, Has.Count.EqualTo(Customer1.Orders.Count)); + + using (IDbCommand cmd = OpenSession().Connection.CreateCommand()) + { + cmd.CommandText = "DELETE FROM Orders;"; + cmd.ExecuteNonQuery(); + cmd.Connection.Close(); + } + + Assert.That(customers.Single(n => n.Id == Customer1.Id).Orders, Has.Count.EqualTo(Customer1.Orders.Count)); + Assert.That(customers.Single(n => n.Id == Customer2.Id).Orders, Has.Count.EqualTo(0)); + Assert.That(customers.Single(n => n.Id == Customer3.Id).Orders, Has.Count.EqualTo(0)); + + firstSession.Dispose(); + secondSession.Dispose(); + } + + protected void ClearSecondLevelCacheFor(System.Type entity) + { + var entityName = entity.FullName; + sessions.EvictEntity(entityName); + var entityPersister = sessions.GetEntityPersister(entityName); + if (!entityPersister.HasCache) + return; + + var querySpaces = entityPersister.QuerySpaces.Cast().ToArray(); + sessions.UpdateTimestampsCache.Invalidate(querySpaces); + } + + protected void ClearCollectionCache(Expression> pathToCollection) + { + var rootEntityTypeFullPath = typeof(T).FullName; + var memberExpression = pathToCollection.Body as MemberExpression; + if (memberExpression == null) + throw new ArgumentException("pathToCollection should be member expression"); + + var role = string.Format("{0}.{1}", rootEntityTypeFullPath, memberExpression.Member.Name); + sessions.EvictCollection(role); + } + + protected abstract IList GetCustomersWithFetchedOrdersWithoutRestrictions(ISession session); + protected abstract IList GetCustomersWithOrdersEagerLoaded(ISession session); + protected abstract IList GetCustomersByOrderNumberUsingOnClause(ISession session, int orderNumber); + protected abstract IList GetCustomersByOrderNumberUsingWhereClause(ISession session, int orderNumber); + protected abstract IList GetCustomersByNameUsingWhereClause(ISession session, string customerName); + protected abstract IList GetCustomersByOrderNumberUsingSubqueriesAndByNameUsingWhereClause(ISession session, int orderNumber, string customerName); + protected abstract IList GetAllCustomers(ISession session); + } +} From d0fe800c5a1d95636bec45d6a74aa5794a0e57a7 Mon Sep 17 00:00:00 2001 From: ITMAGINATION Date: Tue, 23 Feb 2016 10:44:52 +0100 Subject: [PATCH 02/20] Fix using Criteria and QuerOver for NH-3848 --- .../NH3848/CriteriaTestFixture.cs | 2 +- .../NH3848/QueryOverTestFixture.cs | 2 +- .../NHSpecificTest/NH3848/TestFixture.cs | 9 ++++----- .../Engine/Loading/CollectionLoadContext.cs | 13 +++++++------ src/NHibernate/Engine/QueryParameters.cs | 18 ++++++++++++------ src/NHibernate/Impl/CriteriaImpl.cs | 8 ++++++++ src/NHibernate/Impl/MultiCriteriaImpl.cs | 2 +- src/NHibernate/Impl/MultiQueryImpl.cs | 2 +- .../Loader/Criteria/CriteriaLoader.cs | 2 +- .../Loader/Criteria/CriteriaQueryTranslator.cs | 7 ++++++- src/NHibernate/Loader/Loader.cs | 14 +++++++------- 11 files changed, 49 insertions(+), 30 deletions(-) diff --git a/src/NHibernate.Test/NHSpecificTest/NH3848/CriteriaTestFixture.cs b/src/NHibernate.Test/NHSpecificTest/NH3848/CriteriaTestFixture.cs index a53d3b9e03e..944fa635efd 100644 --- a/src/NHibernate.Test/NHSpecificTest/NH3848/CriteriaTestFixture.cs +++ b/src/NHibernate.Test/NHSpecificTest/NH3848/CriteriaTestFixture.cs @@ -18,7 +18,7 @@ protected override IList GetCustomersWithFetchedOrdersWithoutRestricti protected override IList GetCustomersWithOrdersEagerLoaded(ISession session) { return session.CreateCriteria() - .SetFetchMode("Orders", FetchMode.Eager) + .Fetch(SelectMode.Fetch, "Orders") .SetResultTransformer(new DistinctRootEntityResultTransformer()) .List(); } diff --git a/src/NHibernate.Test/NHSpecificTest/NH3848/QueryOverTestFixture.cs b/src/NHibernate.Test/NHSpecificTest/NH3848/QueryOverTestFixture.cs index 3f09e783355..345e79ce7b6 100644 --- a/src/NHibernate.Test/NHSpecificTest/NH3848/QueryOverTestFixture.cs +++ b/src/NHibernate.Test/NHSpecificTest/NH3848/QueryOverTestFixture.cs @@ -21,7 +21,7 @@ protected override IList GetCustomersWithFetchedOrdersWithoutRestricti protected override IList GetCustomersWithOrdersEagerLoaded(ISession session) { return session.QueryOver() - .Fetch(n => n.Orders).Eager + .Fetch(SelectMode.Fetch, n => n.Orders) .TransformUsing(new DistinctRootEntityResultTransformer()) .List(); } diff --git a/src/NHibernate.Test/NHSpecificTest/NH3848/TestFixture.cs b/src/NHibernate.Test/NHSpecificTest/NH3848/TestFixture.cs index 7bc83c3a30d..8f8f93df8d5 100644 --- a/src/NHibernate.Test/NHSpecificTest/NH3848/TestFixture.cs +++ b/src/NHibernate.Test/NHSpecificTest/NH3848/TestFixture.cs @@ -235,13 +235,12 @@ public void ChildCollectionsFromLeftOuterJoinShouldBeInSecondLevelCacheIfQueryCo protected void ClearSecondLevelCacheFor(System.Type entity) { var entityName = entity.FullName; - sessions.EvictEntity(entityName); - var entityPersister = sessions.GetEntityPersister(entityName); + Sfi.EvictEntity(entityName); + var entityPersister = Sfi.GetEntityPersister(entityName); if (!entityPersister.HasCache) return; - var querySpaces = entityPersister.QuerySpaces.Cast().ToArray(); - sessions.UpdateTimestampsCache.Invalidate(querySpaces); + Sfi.UpdateTimestampsCache.PreInvalidate(entityPersister.QuerySpaces.ToList()); } protected void ClearCollectionCache(Expression> pathToCollection) @@ -252,7 +251,7 @@ protected void ClearCollectionCache(Expression> pathToCo throw new ArgumentException("pathToCollection should be member expression"); var role = string.Format("{0}.{1}", rootEntityTypeFullPath, memberExpression.Member.Name); - sessions.EvictCollection(role); + Sfi.EvictCollection(role); } protected abstract IList GetCustomersWithFetchedOrdersWithoutRestrictions(ISession session); diff --git a/src/NHibernate/Engine/Loading/CollectionLoadContext.cs b/src/NHibernate/Engine/Loading/CollectionLoadContext.cs index a0a9c46c134..40c136e1950 100644 --- a/src/NHibernate/Engine/Loading/CollectionLoadContext.cs +++ b/src/NHibernate/Engine/Loading/CollectionLoadContext.cs @@ -145,7 +145,8 @@ public IPersistentCollection GetLoadingCollection(ICollectionPersister persister /// complete. /// /// The persister for which to complete loading. - public void EndLoadingCollections(ICollectionPersister persister) + /// Indicates if collcetion can be put in cache + public void EndLoadingCollections(ICollectionPersister persister, bool canAddCollectionsToCache) { if (!loadContexts.HasLoadingCollectionEntries && (localLoadingCollectionKeys.Count == 0)) { @@ -190,7 +191,7 @@ public void EndLoadingCollections(ICollectionPersister persister) } localLoadingCollectionKeys.ExceptWith(toRemove); - EndLoadingCollections(persister, matches); + EndLoadingCollections(persister, matches, canAddCollectionsToCache); if ((localLoadingCollectionKeys.Count == 0)) { // todo : hack!!! @@ -203,7 +204,7 @@ public void EndLoadingCollections(ICollectionPersister persister) } } - private void EndLoadingCollections(ICollectionPersister persister, IList matchedCollectionEntries) + private void EndLoadingCollections(ICollectionPersister persister, IList matchedCollectionEntries, bool canAddCollectionsToCache) { if (matchedCollectionEntries == null || matchedCollectionEntries.Count == 0) { @@ -224,7 +225,7 @@ private void EndLoadingCollections(ICollectionPersister persister, IList cacheBatcher.AddToBatch(persister, data)); + data => cacheBatcher.AddToBatch(persister, data), canAddCollectionsToCache); } cacheBatcher.ExecuteBatch(); @@ -235,7 +236,7 @@ private void EndLoadingCollections(ICollectionPersister persister, IList cacheBatchingHandler) + Action cacheBatchingHandler, bool canAddCollectionsToCache) { if (log.IsDebugEnabled()) { @@ -269,7 +270,7 @@ private void EndLoadingCollection(LoadingCollectionEntry lce, ICollectionPersist ce.PostInitialize(lce.Collection, persistenceContext); } - bool addToCache = hasNoQueuedOperations && persister.HasCache && + bool addToCache = canAddCollectionsToCache && hasNoQueuedOperations && persister.HasCache && session.CacheMode.HasFlag(CacheMode.Put) && !ce.IsDoremove; // and this is not a forced initialization during flush if (addToCache) diff --git a/src/NHibernate/Engine/QueryParameters.cs b/src/NHibernate/Engine/QueryParameters.cs index 517e4770607..a041818f065 100644 --- a/src/NHibernate/Engine/QueryParameters.cs +++ b/src/NHibernate/Engine/QueryParameters.cs @@ -37,20 +37,20 @@ public QueryParameters(IType[] positionalParameterTypes, object[] postionalParam : this(positionalParameterTypes, postionalParameterValues, null, collectionKeys) {} public QueryParameters(IType[] positionalParameterTypes, object[] postionalParameterValues, IDictionary namedParameters, object[] collectionKeys) - : this(positionalParameterTypes, postionalParameterValues, namedParameters, null, null, false, false, false, null, null, collectionKeys, null) {} + : this(positionalParameterTypes, postionalParameterValues, namedParameters, null, null, false, false, false, null, null, collectionKeys, null, true) {} public QueryParameters(IType[] positionalParameterTypes, object[] positionalParameterValues, IDictionary lockModes, RowSelection rowSelection, bool isReadOnlyInitialized, bool readOnly, bool cacheable, string cacheRegion, string comment, bool isLookupByNaturalKey, IResultTransformer transformer) - : this(positionalParameterTypes, positionalParameterValues, null, lockModes, rowSelection, isReadOnlyInitialized, readOnly, cacheable, cacheRegion, comment, null, transformer) + : this(positionalParameterTypes, positionalParameterValues, null, lockModes, rowSelection, isReadOnlyInitialized, readOnly, cacheable, cacheRegion, comment, null, transformer, true) { NaturalKeyLookup = isLookupByNaturalKey; } public QueryParameters(IDictionary namedParameters, IDictionary lockModes, RowSelection rowSelection, bool isReadOnlyInitialized, - bool readOnly, bool cacheable, string cacheRegion, string comment, bool isLookupByNaturalKey, IResultTransformer transformer) + bool readOnly, bool cacheable, string cacheRegion, string comment, bool isLookupByNaturalKey, IResultTransformer transformer, bool canAddCollectionsToCache) : this( TypeHelper.EmptyTypeArray, Array.Empty(), namedParameters, lockModes, rowSelection, isReadOnlyInitialized, readOnly, cacheable, cacheRegion, comment, null, - transformer) + transformer, canAddCollectionsToCache) { // used by CriteriaTranslator NaturalKeyLookup = isLookupByNaturalKey; @@ -58,7 +58,7 @@ public QueryParameters(IDictionary namedParameters, IDiction public QueryParameters(IType[] positionalParameterTypes, object[] positionalParameterValues, IDictionary namedParameters, IDictionary lockModes, RowSelection rowSelection, bool isReadOnlyInitialized, bool readOnly, bool cacheable, string cacheRegion, - string comment, object[] collectionKeys, IResultTransformer transformer) + string comment, object[] collectionKeys, IResultTransformer transformer, bool canAddCollectionsToCache) { PositionalParameterTypes = positionalParameterTypes ?? Array.Empty(); PositionalParameterValues = positionalParameterValues ?? Array.Empty(); @@ -72,6 +72,7 @@ public QueryParameters(IType[] positionalParameterTypes, object[] positionalPara IsReadOnlyInitialized = isReadOnlyInitialized; this.readOnly = readOnly; ResultTransformer = transformer; + CanAddCollectionsToCache = canAddCollectionsToCache; } public QueryParameters(IType[] positionalParameterTypes, object[] positionalParameterValues, IDictionary namedParameters, @@ -79,7 +80,7 @@ public QueryParameters(IType[] positionalParameterTypes, object[] positionalPara string comment, object[] collectionKeys, object optionalObject, string optionalEntityName, object optionalId, IResultTransformer transformer) : this( positionalParameterTypes, positionalParameterValues, namedParameters, lockModes, rowSelection, isReadOnlyInitialized, readOnly, cacheable, cacheRegion, comment, collectionKeys, - transformer) + transformer, true) { OptionalEntityName = optionalEntityName; OptionalId = optionalId; @@ -139,6 +140,11 @@ public bool HasRowSelection public bool Callable { get; set; } + /// + /// Indicates if we can add loaded child collections to second lvl cache. + /// + public bool CanAddCollectionsToCache { get; set; } + public bool ReadOnly { get diff --git a/src/NHibernate/Impl/CriteriaImpl.cs b/src/NHibernate/Impl/CriteriaImpl.cs index 511ca3e651f..76a8c8efb2a 100644 --- a/src/NHibernate/Impl/CriteriaImpl.cs +++ b/src/NHibernate/Impl/CriteriaImpl.cs @@ -688,6 +688,7 @@ public sealed partial class Subcriteria : ICriteria, ISupportSelectModeCriteria private LockMode lockMode; private readonly JoinType joinType; private ICriterion withClause; + private bool hasRestrictions; internal Subcriteria(CriteriaImpl root, ICriteria parent, string path, string alias, JoinType joinType, ICriterion withClause, string joinEntityName = null) { @@ -698,6 +699,7 @@ internal Subcriteria(CriteriaImpl root, ICriteria parent, string path, string al this.joinType = joinType; this.withClause = withClause; JoinEntityName = joinEntityName; + hasRestrictions = withClause != null; root.subcriteriaList.Add(this); @@ -731,6 +733,11 @@ public string Path get { return path; } } + public bool HasRestrictions + { + get { return hasRestrictions; } + } + public ICriteria Parent { get { return parent; } @@ -770,6 +777,7 @@ public ICriteria SetLockMode(LockMode lockMode) public ICriteria Add(ICriterion expression) { + hasRestrictions = true; root.Add(this, expression); return this; } diff --git a/src/NHibernate/Impl/MultiCriteriaImpl.cs b/src/NHibernate/Impl/MultiCriteriaImpl.cs index 98f87bd9bb5..733cf174eb1 100644 --- a/src/NHibernate/Impl/MultiCriteriaImpl.cs +++ b/src/NHibernate/Impl/MultiCriteriaImpl.cs @@ -273,7 +273,7 @@ private void GetResultsFromDatabase(IList results) for (int i = 0; i < loaders.Count; i++) { CriteriaLoader loader = loaders[i]; - loader.InitializeEntitiesAndCollections(hydratedObjects[i], reader, session, session.DefaultReadOnly); + loader.InitializeEntitiesAndCollections(hydratedObjects[i], reader, session, session.DefaultReadOnly, null, parameters[i].CanAddCollectionsToCache); if (createSubselects[i]) { diff --git a/src/NHibernate/Impl/MultiQueryImpl.cs b/src/NHibernate/Impl/MultiQueryImpl.cs index 278c2822ff0..3b4e9026168 100644 --- a/src/NHibernate/Impl/MultiQueryImpl.cs +++ b/src/NHibernate/Impl/MultiQueryImpl.cs @@ -616,7 +616,7 @@ protected List DoList() ITranslator translator = translators[i]; QueryParameters parameter = parameters[i]; - translator.Loader.InitializeEntitiesAndCollections(hydratedObjects[i], reader, session, false); + translator.Loader.InitializeEntitiesAndCollections(hydratedObjects[i], reader, session, false, null, parameters[i].CanAddCollectionsToCache); if (createSubselects[i]) { diff --git a/src/NHibernate/Loader/Criteria/CriteriaLoader.cs b/src/NHibernate/Loader/Criteria/CriteriaLoader.cs index 658896a5096..8d2da586db5 100644 --- a/src/NHibernate/Loader/Criteria/CriteriaLoader.cs +++ b/src/NHibernate/Loader/Criteria/CriteriaLoader.cs @@ -94,7 +94,7 @@ protected override bool IsChildFetchEntity(int i) public IList List(ISessionImplementor session) { - return List(session, translator.GetQueryParameters(), querySpaces); + return List(session, translator.GetQueryParameters(), querySpaces); } protected override IResultTransformer ResolveResultTransformer(IResultTransformer resultTransformer) diff --git a/src/NHibernate/Loader/Criteria/CriteriaQueryTranslator.cs b/src/NHibernate/Loader/Criteria/CriteriaQueryTranslator.cs index c46efecfc8f..a54dbd18369 100644 --- a/src/NHibernate/Loader/Criteria/CriteriaQueryTranslator.cs +++ b/src/NHibernate/Loader/Criteria/CriteriaQueryTranslator.cs @@ -139,6 +139,7 @@ public QueryParameters GetQueryParameters() selection.FetchSize = rootCriteria.FetchSize; var lockModes = new Dictionary(); + var canAddCollectionsToCache = true; foreach (KeyValuePair me in rootCriteria.LockModes) { ICriteria subcriteria = GetAliasedCriteria(me.Key); @@ -152,6 +153,9 @@ public QueryParameters GetQueryParameters() { lockModes[GetSQLAlias(subcriteria)] = lm; } + + if (subcriteria.HasRestrictions && subcriteria.JoinType == JoinType.LeftOuterJoin) + canAddCollectionsToCache = false; } IDictionary queryNamedParameters = CollectedParameters.ToDictionary(np => np.Name, np => new TypedValue(np.Type, np.Value)); @@ -167,7 +171,8 @@ public QueryParameters GetQueryParameters() rootCriteria.CacheRegion, rootCriteria.Comment, rootCriteria.LookupByNaturalKey, - rootCriteria.ResultTransformer) + rootCriteria.ResultTransformer, + canAddCollectionsToCache) { CacheMode = rootCriteria.CacheMode }; diff --git a/src/NHibernate/Loader/Loader.cs b/src/NHibernate/Loader/Loader.cs index c138c22bfb5..6c15984d951 100644 --- a/src/NHibernate/Loader/Loader.cs +++ b/src/NHibernate/Loader/Loader.cs @@ -315,7 +315,7 @@ protected object LoadSingleRow(DbDataReader resultSet, ISessionImplementor sessi queryParameters.NamedParameters); } - InitializeEntitiesAndCollections(hydratedObjects, resultSet, session, queryParameters.IsReadOnly(session)); + InitializeEntitiesAndCollections(hydratedObjects, resultSet, session, queryParameters.IsReadOnly(session), null, queryParameters.CanAddCollectionsToCache); session.PersistenceContext.InitializeNonLazyCollections(); return result; } @@ -517,7 +517,7 @@ private IList DoQuery(ISessionImplementor session, QueryParameters queryParamete session.Batcher.CloseCommand(st, rs); } - InitializeEntitiesAndCollections(hydratedObjects, rs, session, queryParameters.IsReadOnly(session)); + InitializeEntitiesAndCollections(hydratedObjects, rs, session, queryParameters.IsReadOnly(session), null, queryParameters.CanAddCollectionsToCache); if (createSubselects) { @@ -601,7 +601,7 @@ private IEnumerable CreateSubselects(IList keys, Qu internal void InitializeEntitiesAndCollections( IList hydratedObjects, object resultSetId, ISessionImplementor session, bool readOnly, - CacheBatcher cacheBatcher = null) + CacheBatcher cacheBatcher = null, bool canAddCollectionsToCache = true) { ICollectionPersister[] collectionPersisters = CollectionPersisters; if (collectionPersisters != null) @@ -615,7 +615,7 @@ internal void InitializeEntitiesAndCollections( //during loading //TODO: or we could do this polymorphically, and have two // different operations implemented differently for arrays - EndCollectionLoad(resultSetId, session, collectionPersisters[i]); + EndCollectionLoad(resultSetId, session, collectionPersisters[i], canAddCollectionsToCache); } } } @@ -666,17 +666,17 @@ internal void InitializeEntitiesAndCollections( //the entities, since we might call hashCode() on the elements //TODO: or we could do this polymorphically, and have two // different operations implemented differently for arrays - EndCollectionLoad(resultSetId, session, collectionPersisters[i]); + EndCollectionLoad(resultSetId, session, collectionPersisters[i], canAddCollectionsToCache); } } } } - private static void EndCollectionLoad(object resultSetId, ISessionImplementor session, ICollectionPersister collectionPersister) + private static void EndCollectionLoad(object resultSetId, ISessionImplementor session, ICollectionPersister collectionPersister, bool canAddCollectionsToCache) { //this is a query and we are loading multiple instances of the same collection role session.PersistenceContext.LoadContexts.GetCollectionLoadContext((DbDataReader)resultSetId).EndLoadingCollections( - collectionPersister); + collectionPersister, canAddCollectionsToCache); } From d35233eeffd3d1ac026eb0716c3b9eb7947ca063 Mon Sep 17 00:00:00 2001 From: "Iwanow, Mihail (EXT)" Date: Wed, 14 Nov 2018 15:43:14 +0100 Subject: [PATCH 03/20] =?UTF-8?q?Child=20collection=20fetched=20using=20le?= =?UTF-8?q?ft=20join=20with=20restrictions=20shouldn=E2=80=99t=20be=20stor?= =?UTF-8?q?ed=20in=20second=20level=20cache?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../NHSpecificTest/NH3848/Company.cs | 11 + .../NH3848/CriteriaTestFixture.cs | 88 ++- .../NHSpecificTest/NH3848/Customer.cs | 10 +- .../NH3848/QueryOverTestFixture.cs | 115 ++- .../NHSpecificTest/NH3848/TestFixture.cs | 740 ++++++++++++------ .../Engine/Loading/CollectionLoadContext.cs | 15 +- src/NHibernate/Async/Loader/Loader.cs | 15 +- .../Async/Multi/QueryBatchItemBase.cs | 2 +- .../Engine/Loading/CollectionLoadContext.cs | 17 +- src/NHibernate/Engine/QueryParameters.cs | 16 +- src/NHibernate/Impl/MultiCriteriaImpl.cs | 2 +- src/NHibernate/Impl/MultiQueryImpl.cs | 2 +- .../Criteria/CriteriaQueryTranslator.cs | 14 +- src/NHibernate/Loader/Loader.cs | 14 +- 14 files changed, 749 insertions(+), 312 deletions(-) create mode 100644 src/NHibernate.Test/NHSpecificTest/NH3848/Company.cs diff --git a/src/NHibernate.Test/NHSpecificTest/NH3848/Company.cs b/src/NHibernate.Test/NHSpecificTest/NH3848/Company.cs new file mode 100644 index 00000000000..42214e07f8b --- /dev/null +++ b/src/NHibernate.Test/NHSpecificTest/NH3848/Company.cs @@ -0,0 +1,11 @@ +using System; + +namespace NHibernate.Test.NHSpecificTest.NH3848 +{ + public class Company + { + public virtual Guid Id { get; set; } + public virtual string Name { get; set; } + public virtual Customer Customer { get; set; } + } +} diff --git a/src/NHibernate.Test/NHSpecificTest/NH3848/CriteriaTestFixture.cs b/src/NHibernate.Test/NHSpecificTest/NH3848/CriteriaTestFixture.cs index 944fa635efd..0f025c0bb40 100644 --- a/src/NHibernate.Test/NHSpecificTest/NH3848/CriteriaTestFixture.cs +++ b/src/NHibernate.Test/NHSpecificTest/NH3848/CriteriaTestFixture.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using System.Threading.Tasks; using NHibernate.Criterion; using NHibernate.SqlCommand; using NHibernate.Transform; @@ -18,19 +19,50 @@ protected override IList GetCustomersWithFetchedOrdersWithoutRestricti protected override IList GetCustomersWithOrdersEagerLoaded(ISession session) { return session.CreateCriteria() - .Fetch(SelectMode.Fetch, "Orders") - .SetResultTransformer(new DistinctRootEntityResultTransformer()) + .Fetch("Orders") + .SetResultTransformer(new DistinctRootEntityResultTransformer()) .List(); } - protected override IList GetCustomersByOrderNumberUsingOnClause(ISession session, int orderNumber) + protected override async Task> GetCustomersWithOrdersEagerLoadedAsync(ISession session) + { + return await session.CreateCriteria() + .Fetch("Orders") + .SetResultTransformer(new DistinctRootEntityResultTransformer()) + .ListAsync().ConfigureAwait(false); + } + + protected override IList GetCustomersByOrderNumberUsingOnClause(ISession session, int orderNumber) { return session.CreateCriteria() .CreateAlias("Orders", "order", JoinType.LeftOuterJoin, Restrictions.Eq("Number", orderNumber)) .List(); } - protected override IList GetCustomersByOrderNumberUsingWhereClause(ISession session, int orderNumber) + protected override async Task> GetCustomersByOrderNumberUsingOnClauseAsync(ISession session, int orderNumber) + { + return await session.CreateCriteria() + .CreateAlias("Orders", "order", JoinType.LeftOuterJoin, Restrictions.Eq("Number", orderNumber)) + .ListAsync().ConfigureAwait(false); + } + + protected override IList GetCustomersWithCompaniesByOrderNumberUsingOnClause(ISession session, int orderNumber) + { + return session.CreateCriteria() + .CreateAlias("Orders", "order", JoinType.LeftOuterJoin, Restrictions.Eq("Number", orderNumber)) + .CreateAlias("Companies", "company", JoinType.LeftOuterJoin) + .List(); + } + + protected override async Task> GetCustomersWithCompaniesByOrderNumberUsingOnClauseAsync(ISession session, int orderNumber) + { + return await session.CreateCriteria() + .CreateAlias("Orders", "order", JoinType.LeftOuterJoin, Restrictions.Eq("Number", orderNumber)) + .CreateAlias("Companies", "company", JoinType.LeftOuterJoin) + .ListAsync().ConfigureAwait(false); + } + + protected override IList GetCustomersByOrderNumberUsingWhereClause(ISession session, int orderNumber) { return session.CreateCriteria() .CreateCriteria("Orders", "Order", JoinType.LeftOuterJoin) @@ -39,7 +71,16 @@ protected override IList GetCustomersByOrderNumberUsingWhereClause(ISe .List(); } - protected override IList GetCustomersByNameUsingWhereClause(ISession session, string customerName) + protected override async Task> GetCustomersByOrderNumberUsingWhereClauseAsync(ISession session, int orderNumber) + { + return await session.CreateCriteria() + .CreateCriteria("Orders", "Order", JoinType.LeftOuterJoin) + .Add(Restrictions.Eq("Number", orderNumber)) + .SetResultTransformer(new DistinctRootEntityResultTransformer()) + .ListAsync().ConfigureAwait(false); + } + + protected override IList GetCustomersByNameUsingWhereClause(ISession session, string customerName) { return session.CreateCriteria() .CreateAlias("Orders", "order", JoinType.LeftOuterJoin) @@ -48,7 +89,16 @@ protected override IList GetCustomersByNameUsingWhereClause(ISession s .List(); } - protected override IList GetCustomersByOrderNumberUsingSubqueriesAndByNameUsingWhereClause(ISession session, int orderNumber, string customerName) + protected override async Task> GetCustomersByNameUsingWhereClauseAsync(ISession session, string customerName) + { + return await session.CreateCriteria() + .CreateAlias("Orders", "order", JoinType.LeftOuterJoin) + .Add(Restrictions.Eq("Name", "First Customer")) + .SetResultTransformer(new DistinctRootEntityResultTransformer()) + .ListAsync().ConfigureAwait(false); + } + + protected override IList GetCustomersByOrderNumberUsingSubqueriesAndByNameUsingWhereClause(ISession session, int orderNumber, string customerName) { var detachedCriteria = DetachedCriteria.For() .CreateAlias("Orders", "order", JoinType.LeftOuterJoin, Restrictions.Eq("Number", orderNumber)) @@ -62,9 +112,29 @@ protected override IList GetCustomersByOrderNumberUsingSubqueriesAndBy .List(); } - protected override IList GetAllCustomers(ISession session) + + protected override async Task> GetCustomersByOrderNumberUsingSubqueriesAndByNameUsingWhereClauseAsync(ISession session, int orderNumber, string customerName) + { + var detachedCriteria = DetachedCriteria.For() + .CreateAlias("Orders", "order", JoinType.LeftOuterJoin, Restrictions.Eq("Number", orderNumber)) + .SetProjection(Projections.Id()); + + return await session.CreateCriteria() + .CreateAlias("Orders", "order1", JoinType.LeftOuterJoin) + .Add(Subqueries.PropertyIn("Id", detachedCriteria)) + .Add(Restrictions.Eq("Name", customerName)) + .SetResultTransformer(new DistinctRootEntityResultTransformer()) + .ListAsync().ConfigureAwait(false); + } + + protected override IList GetAllCustomers(ISession session) { return session.CreateCriteria().List(); - } - } + } + + protected override async Task> GetAllCustomersAsync(ISession session) + { + return await session.CreateCriteria().ListAsync().ConfigureAwait(false); + } + } } diff --git a/src/NHibernate.Test/NHSpecificTest/NH3848/Customer.cs b/src/NHibernate.Test/NHSpecificTest/NH3848/Customer.cs index 60d80e7d75b..dae8744391e 100644 --- a/src/NHibernate.Test/NHSpecificTest/NH3848/Customer.cs +++ b/src/NHibernate.Test/NHSpecificTest/NH3848/Customer.cs @@ -7,11 +7,13 @@ public class Customer { public virtual Guid Id { get; set; } public virtual ISet Orders { get; set; } + public virtual ISet Companies { get; set; } public virtual string Name { get; set; } public Customer() { Orders = new HashSet(); + Companies = new HashSet(); } public virtual void AddOrder(Order order) @@ -19,5 +21,11 @@ public virtual void AddOrder(Order order) Orders.Add(order); order.Customer = this; } - } + + public virtual void AddCompany(Company company) + { + Companies.Add(company); + company.Customer = this; + } + } } diff --git a/src/NHibernate.Test/NHSpecificTest/NH3848/QueryOverTestFixture.cs b/src/NHibernate.Test/NHSpecificTest/NH3848/QueryOverTestFixture.cs index 345e79ce7b6..4032a2e885b 100644 --- a/src/NHibernate.Test/NHSpecificTest/NH3848/QueryOverTestFixture.cs +++ b/src/NHibernate.Test/NHSpecificTest/NH3848/QueryOverTestFixture.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using System.Threading.Tasks; using NHibernate.Criterion; using NHibernate.SqlCommand; using NHibernate.Transform; @@ -21,12 +22,20 @@ protected override IList GetCustomersWithFetchedOrdersWithoutRestricti protected override IList GetCustomersWithOrdersEagerLoaded(ISession session) { return session.QueryOver() - .Fetch(SelectMode.Fetch, n => n.Orders) - .TransformUsing(new DistinctRootEntityResultTransformer()) + .Fetch(SelectMode.Fetch, n => n.Orders) + .TransformUsing(new DistinctRootEntityResultTransformer()) .List(); } - protected override IList GetCustomersByOrderNumberUsingOnClause(ISession session, int orderNumber) + protected override async Task> GetCustomersWithOrdersEagerLoadedAsync(ISession session) + { + return await session.QueryOver() + .Fetch(SelectMode.Fetch, n => n.Orders) + .TransformUsing(new DistinctRootEntityResultTransformer()) + .ListAsync().ConfigureAwait(false); + } + + protected override IList GetCustomersByOrderNumberUsingOnClause(ISession session, int orderNumber) { Order ordersAlias = null; return session.QueryOver() @@ -37,16 +46,69 @@ protected override IList GetCustomersByOrderNumberUsingOnClause(ISessi .List(); } - protected override IList GetCustomersByOrderNumberUsingWhereClause(ISession session, int orderNumber) + protected override async Task> GetCustomersByOrderNumberUsingOnClauseAsync(ISession session, int orderNumber) + { + Order ordersAlias = null; + + return await session.QueryOver() + .JoinAlias(n => n.Orders, + () => ordersAlias, + JoinType.LeftOuterJoin, + Restrictions.Eq("Number", orderNumber)) + .ListAsync().ConfigureAwait(false); + } + + protected override IList GetCustomersWithCompaniesByOrderNumberUsingOnClause(ISession session, int orderNumber) + { + Order ordersAlias = null; + Company companiesAlias = null; + + return session.QueryOver() + .JoinAlias(n => n.Orders, + () => ordersAlias, + JoinType.LeftOuterJoin, + Restrictions.Eq("Number", orderNumber)) + .JoinAlias(n => n.Companies, + () => companiesAlias, + JoinType.LeftOuterJoin) + .List(); + } + + protected override async Task> GetCustomersWithCompaniesByOrderNumberUsingOnClauseAsync(ISession session, int orderNumber) + { + Order ordersAlias = null; + Company companiesAlias = null; + + return await session.QueryOver() + .JoinAlias(n => n.Orders, + () => ordersAlias, + JoinType.LeftOuterJoin, + Restrictions.Eq("Number", orderNumber)) + .JoinAlias(n => n.Companies, + () => companiesAlias, + JoinType.LeftOuterJoin) + .ListAsync().ConfigureAwait(false); + } + + protected override IList GetCustomersByOrderNumberUsingWhereClause(ISession session, int orderNumber) { Order ordersAlias = null; return session.QueryOver() .JoinQueryOver(n => n.Orders, () => ordersAlias, JoinType.LeftOuterJoin) - .Where(Restrictions.Eq("Number", orderNumber)) + .Where(Restrictions.Eq("Number", orderNumber)) .List(); } - protected override IList GetCustomersByNameUsingWhereClause(ISession session, string customerName) + protected override async Task> GetCustomersByOrderNumberUsingWhereClauseAsync(ISession session, int orderNumber) + { + Order ordersAlias = null; + return await session.QueryOver() + .JoinQueryOver(n => n.Orders, () => ordersAlias, JoinType.LeftOuterJoin) + .Where(Restrictions.Eq("Number", orderNumber)) + .ListAsync().ConfigureAwait(false); + } + + protected override IList GetCustomersByNameUsingWhereClause(ISession session, string customerName) { Order ordersAlias = null; return session.QueryOver() @@ -60,7 +122,21 @@ protected override IList GetCustomersByNameUsingWhereClause(ISession s } - protected override IList GetCustomersByOrderNumberUsingSubqueriesAndByNameUsingWhereClause(ISession session, int orderNumber, string customerName) + protected override async Task> GetCustomersByNameUsingWhereClauseAsync(ISession session, string customerName) + { + Order ordersAlias = null; + + return await session.QueryOver() + .JoinAlias(n => n.Orders, + () => ordersAlias, + JoinType.LeftOuterJoin + ) + .Where(Restrictions.Eq("Name", customerName)) + .TransformUsing(new DistinctRootEntityResultTransformer()) + .ListAsync().ConfigureAwait(false); + } + + protected override IList GetCustomersByOrderNumberUsingSubqueriesAndByNameUsingWhereClause(ISession session, int orderNumber, string customerName) { Order ordersAlias = null; Order ordersAlias2 = null; @@ -76,9 +152,30 @@ protected override IList GetCustomersByOrderNumberUsingSubqueriesAndBy .List(); } - protected override IList GetAllCustomers(ISession session) + protected override async Task> GetCustomersByOrderNumberUsingSubqueriesAndByNameUsingWhereClauseAsync(ISession session, int orderNumber, string customerName) + { + Order ordersAlias = null; + Order ordersAlias2 = null; + var subquery = QueryOver.Of() + .JoinAlias(n => n.Orders, () => ordersAlias, JoinType.LeftOuterJoin, Restrictions.Eq("Number", orderNumber)) + .Select(n => n.Id); + + return await session.QueryOver() + .JoinAlias(n => n.Orders, () => ordersAlias2, JoinType.LeftOuterJoin) + .WithSubquery.WhereProperty(n => n.Id).In(subquery) + .Where(Restrictions.Eq("Name", customerName)) + .TransformUsing(new DistinctRootEntityResultTransformer()) + .ListAsync().ConfigureAwait(false); + } + + protected override IList GetAllCustomers(ISession session) { return session.QueryOver().List(); } - } + + protected override async Task> GetAllCustomersAsync(ISession session) + { + return await session.QueryOver().ListAsync().ConfigureAwait(false); + } + } } diff --git a/src/NHibernate.Test/NHSpecificTest/NH3848/TestFixture.cs b/src/NHibernate.Test/NHSpecificTest/NH3848/TestFixture.cs index 8f8f93df8d5..85c5c781d09 100644 --- a/src/NHibernate.Test/NHSpecificTest/NH3848/TestFixture.cs +++ b/src/NHibernate.Test/NHSpecificTest/NH3848/TestFixture.cs @@ -4,6 +4,7 @@ using System.Data; using System.Linq; using System.Linq.Expressions; +using System.Threading.Tasks; using NHibernate.Cache; using NHibernate.Cfg; using NHibernate.Cfg.MappingSchema; @@ -12,254 +13,493 @@ namespace NHibernate.Test.NHSpecificTest.NH3848 { - public abstract class TestFixture : TestCaseMappingByCode - { - protected Customer Customer1; - protected Customer Customer2; - protected Customer Customer3; - protected const int OrderNumber = 2; - - protected override HbmMapping GetMappings() - { - var mapper = new ModelMapper(); - mapper.Class(rc => - { - rc.Table("Customers"); - rc.Id(x => x.Id, m => m.Generator(Generators.GuidComb)); - rc.Property(x => x.Name); - rc.Set(x => x.Orders, m => - { - m.Inverse(true); - m.Key(k => - { - k.Column("CustomerId"); - k.NotNullable(true); - }); - m.Cascade(Mapping.ByCode.Cascade.All.Include(Mapping.ByCode.Cascade.DeleteOrphans)); - m.Cache(c => c.Usage(CacheUsage.ReadWrite)); - }, m => m.OneToMany()); - }); - - mapper.Class(rc => - { - rc.Table("Orders"); - rc.Id(x => x.Id, m => m.Generator(Generators.GuidComb)); - rc.Property(x => x.Number); - rc.ManyToOne(x => x.Customer, m => m.Column("CustomerId")); - rc.Cache(c => c.Usage(CacheUsage.ReadWrite)); - }); - - return mapper.CompileMappingForAllExplicitlyAddedEntities(); - } - - protected override void Configure(Configuration configuration) - { - base.Configure(configuration); - configuration.Cache(c => - { - c.UseQueryCache = true; - c.Provider(); - }); - } - - protected override void OnSetUp() - { - using (var session = OpenSession()) - using (var transaction = session.BeginTransaction()) - { - Customer1 = new Customer { Name = "First Customer" }; - - Customer2 = new Customer { Name = "Second Customer" }; - - Customer3 = new Customer { Name = "Third Customer" }; - - var customer1Order1 = new Order { Number = 1 }; - Customer1.AddOrder(customer1Order1); - - var customer1Order2 = new Order { Number = 2 }; - Customer1.AddOrder(customer1Order2); - - var customer2Order1 = new Order { Number = 1 }; - Customer2.AddOrder(customer2Order1); - - var customer2Order2 = new Order { Number = 2 }; - Customer2.AddOrder(customer2Order2); - - var customer3Order1 = new Order { Number = 1 }; - Customer3.AddOrder(customer3Order1); - - session.Save(Customer1); - session.Save(Customer2); - session.Save(Customer3); - - transaction.Commit(); - session.Flush(); - } - } - - protected override void OnTearDown() - { - ClearSecondLevelCacheFor(typeof(Customer)); - ClearCollectionCache(n => n.Orders); - ClearSecondLevelCacheFor(typeof(Order)); - - using (var session = OpenSession()) - using (var transaction = session.BeginTransaction()) - { - session.Delete("from System.Object"); - - session.Flush(); - transaction.Commit(); - } - } - - [Test] - public virtual void ChildCollectionsFromLeftOuterJoinWithOnClauseRestrictionOnCollectionShouldNotBeInSecondLevelCache() - { - var firstSession = OpenSession(); - var customersWithOrderNumberEqualsTo2 = GetCustomersByOrderNumberUsingOnClause(firstSession, OrderNumber); - - var secondSession = OpenSession(); - var customers = GetAllCustomers(secondSession); - - Assert.That(customersWithOrderNumberEqualsTo2.Single(n => n.Id == Customer1.Id).Orders, Has.Count.EqualTo(Customer1.Orders.Count(n => n.Number == OrderNumber))); - Assert.That(customersWithOrderNumberEqualsTo2.Single(n => n.Id == Customer2.Id).Orders, Has.Count.EqualTo(Customer2.Orders.Count(n => n.Number == OrderNumber))); - Assert.That(customersWithOrderNumberEqualsTo2.Single(n => n.Id == Customer3.Id).Orders, Has.Count.EqualTo(Customer3.Orders.Count(n => n.Number == OrderNumber))); - - Assert.That(customers.Single(n => n.Id == Customer1.Id).Orders, Has.Count.EqualTo(Customer1.Orders.Count)); - Assert.That(customers.Single(n => n.Id == Customer2.Id).Orders, Has.Count.EqualTo(Customer2.Orders.Count)); - Assert.That(customers.Single(n => n.Id == Customer3.Id).Orders, Has.Count.EqualTo(Customer3.Orders.Count)); - - firstSession.Dispose(); - secondSession.Dispose(); - } - - [Test] - public void ChildCollectionsFromLeftOuterJoinWithWhereClauseRestrictionOnCollectionShouldNotBeInSecondLevelCache() - { - var firstSession = OpenSession(); - var customersWithOrderNumberEqualsTo2 = GetCustomersByOrderNumberUsingWhereClause(firstSession, OrderNumber); - - var secondSession = OpenSession(); - var customers = GetAllCustomers(secondSession); - - Assert.That(customersWithOrderNumberEqualsTo2.Single(n => n.Id == Customer1.Id).Orders, Has.Count.EqualTo(Customer1.Orders.Count(n => n.Number == OrderNumber))); - Assert.That(customersWithOrderNumberEqualsTo2.Single(n => n.Id == Customer2.Id).Orders, Has.Count.EqualTo(Customer2.Orders.Count(n => n.Number == OrderNumber))); - - Assert.That(customers.Single(n => n.Id == Customer3.Id).Orders, Has.Count.EqualTo(Customer3.Orders.Count)); - Assert.That(customers.Single(n => n.Id == Customer1.Id).Orders, Has.Count.EqualTo(Customer1.Orders.Count)); - Assert.That(customers.Single(n => n.Id == Customer2.Id).Orders, Has.Count.EqualTo(Customer2.Orders.Count)); - - firstSession.Dispose(); - secondSession.Dispose(); - } - - [Test] - public void ChildCollectionsEagerFetchedShouldBeInSecondLevelCache() - { - var firstSession = OpenSession(); - var customersWithOrderNumberEqualsTo2 = GetCustomersWithOrdersEagerLoaded(firstSession); - - using (IDbCommand cmd = OpenSession().Connection.CreateCommand()) - { - cmd.CommandText = "DELETE FROM Orders;"; - cmd.ExecuteNonQuery(); - cmd.Connection.Close(); - } - - var secondSession = OpenSession(); - var customers = GetAllCustomers(secondSession); - - - Assert.That(customersWithOrderNumberEqualsTo2.Single(n => n.Id == Customer1.Id).Orders, Has.Count.EqualTo(Customer1.Orders.Count)); - Assert.That(customersWithOrderNumberEqualsTo2.Single(n => n.Id == Customer2.Id).Orders, Has.Count.EqualTo(Customer2.Orders.Count)); - - Assert.That(customers.Single(n => n.Id == Customer3.Id).Orders, Has.Count.EqualTo(Customer3.Orders.Count)); - Assert.That(customers.Single(n => n.Id == Customer1.Id).Orders, Has.Count.EqualTo(Customer1.Orders.Count)); - Assert.That(customers.Single(n => n.Id == Customer2.Id).Orders, Has.Count.EqualTo(Customer2.Orders.Count)); - - firstSession.Dispose(); - secondSession.Dispose(); - } - - [Test] - public void ChildCollectionsFromLeftOuterJoinWithWhereClauseRestrictionOnRootShouldBeInSecondLevelCache() - { - var firstSession = OpenSession(); - var customersWithOrderNumberEqualsTo2 = GetCustomersByNameUsingWhereClause(firstSession, "First Customer"); - - - using (IDbCommand cmd = OpenSession().Connection.CreateCommand()) - { - cmd.CommandText = "DELETE FROM Orders;"; - cmd.ExecuteNonQuery(); - cmd.Connection.Close(); - } - - var secondSession = OpenSession(); - var customers = secondSession.Get(Customer1.Id); - - Assert.That(customersWithOrderNumberEqualsTo2.Single(n => n.Id == Customer1.Id).Orders, Has.Count.EqualTo(Customer1.Orders.Count)); - Assert.That(customers.Orders, Has.Count.EqualTo(Customer1.Orders.Count)); - - firstSession.Dispose(); - secondSession.Dispose(); - } - - [Test] - public void ChildCollectionsFromLeftOuterJoinShouldBeInSecondLevelCacheIfQueryContainsSubqueryWithRestrictionOnLeftOuterJoin() - { - var firstSession = OpenSession(); - var customersWithOrderNumberEqualsTo2 = GetCustomersByOrderNumberUsingSubqueriesAndByNameUsingWhereClause(firstSession, OrderNumber, Customer1.Name); - - var secondSession = OpenSession(); - var customers = GetAllCustomers(secondSession); - - Assert.That(customersWithOrderNumberEqualsTo2.Single(n => n.Id == Customer1.Id).Orders, Has.Count.EqualTo(Customer1.Orders.Count)); - - using (IDbCommand cmd = OpenSession().Connection.CreateCommand()) - { - cmd.CommandText = "DELETE FROM Orders;"; - cmd.ExecuteNonQuery(); - cmd.Connection.Close(); - } - - Assert.That(customers.Single(n => n.Id == Customer1.Id).Orders, Has.Count.EqualTo(Customer1.Orders.Count)); - Assert.That(customers.Single(n => n.Id == Customer2.Id).Orders, Has.Count.EqualTo(0)); - Assert.That(customers.Single(n => n.Id == Customer3.Id).Orders, Has.Count.EqualTo(0)); - - firstSession.Dispose(); - secondSession.Dispose(); - } - - protected void ClearSecondLevelCacheFor(System.Type entity) - { - var entityName = entity.FullName; - Sfi.EvictEntity(entityName); - var entityPersister = Sfi.GetEntityPersister(entityName); - if (!entityPersister.HasCache) - return; - - Sfi.UpdateTimestampsCache.PreInvalidate(entityPersister.QuerySpaces.ToList()); - } - - protected void ClearCollectionCache(Expression> pathToCollection) - { - var rootEntityTypeFullPath = typeof(T).FullName; - var memberExpression = pathToCollection.Body as MemberExpression; - if (memberExpression == null) - throw new ArgumentException("pathToCollection should be member expression"); - - var role = string.Format("{0}.{1}", rootEntityTypeFullPath, memberExpression.Member.Name); - Sfi.EvictCollection(role); - } - - protected abstract IList GetCustomersWithFetchedOrdersWithoutRestrictions(ISession session); - protected abstract IList GetCustomersWithOrdersEagerLoaded(ISession session); - protected abstract IList GetCustomersByOrderNumberUsingOnClause(ISession session, int orderNumber); - protected abstract IList GetCustomersByOrderNumberUsingWhereClause(ISession session, int orderNumber); - protected abstract IList GetCustomersByNameUsingWhereClause(ISession session, string customerName); - protected abstract IList GetCustomersByOrderNumberUsingSubqueriesAndByNameUsingWhereClause(ISession session, int orderNumber, string customerName); - protected abstract IList GetAllCustomers(ISession session); - } + public abstract class TestFixture : TestCaseMappingByCode + { + protected Customer Customer1; + protected Customer Customer2; + protected Customer Customer3; + protected const int OrderNumber = 2; + + protected override HbmMapping GetMappings() + { + var mapper = new ModelMapper(); + mapper.Class(rc => + { + rc.Table("Customers"); + rc.Id(x => x.Id, m => m.Generator(Generators.GuidComb)); + rc.Property(x => x.Name); + rc.Set(x => x.Orders, m => + { + m.Inverse(true); + m.Key(k => + { + k.Column("CustomerId"); + k.NotNullable(true); + }); + m.Cascade(Mapping.ByCode.Cascade.All.Include(Mapping.ByCode.Cascade.DeleteOrphans)); + m.Cache(c => c.Usage(CacheUsage.ReadWrite)); + }, m => m.OneToMany()); + + rc.Set(x => x.Companies, m => + { + m.Inverse(true); + m.Key(k => + { + k.Column("CustomerId"); + k.NotNullable(true); + }); + m.Cascade(Mapping.ByCode.Cascade.All.Include(Mapping.ByCode.Cascade.DeleteOrphans)); + m.Cache(c => c.Usage(CacheUsage.ReadWrite)); + }, m => m.OneToMany()); + }); + + mapper.Class(rc => + { + rc.Table("Orders"); + rc.Id(x => x.Id, m => m.Generator(Generators.GuidComb)); + rc.Property(x => x.Number); + rc.ManyToOne(x => x.Customer, m => m.Column("CustomerId")); + rc.Cache(c => c.Usage(CacheUsage.ReadWrite)); + }); + + mapper.Class(rc => + { + rc.Table("Companies"); + rc.Id(x => x.Id, m => m.Generator(Generators.GuidComb)); + rc.Property(x => x.Name); + rc.ManyToOne(x => x.Customer, m => m.Column("CustomerId")); + rc.Cache(c => c.Usage(CacheUsage.ReadWrite)); + }); + + return mapper.CompileMappingForAllExplicitlyAddedEntities(); + } + + protected override void Configure(Configuration configuration) + { + base.Configure(configuration); + configuration.Cache(c => + { + c.UseQueryCache = true; + c.Provider(); + }); + } + + protected override void OnSetUp() + { + using (var session = OpenSession()) + using (var transaction = session.BeginTransaction()) + { + Customer1 = new Customer { Name = "First Customer" }; + + Customer2 = new Customer { Name = "Second Customer" }; + + Customer3 = new Customer { Name = "Third Customer" }; + + var customer1Order1 = new Order { Number = 1 }; + var customer1Company1 = new Company { Name = "First" }; + Customer1.AddOrder(customer1Order1); + Customer1.AddCompany(customer1Company1); + + var customer1Order2 = new Order { Number = 2 }; + var customer1Company2 = new Company { Name = "Second" }; + Customer1.AddOrder(customer1Order2); + Customer1.AddCompany(customer1Company2); + + var customer2Order1 = new Order { Number = 1 }; + var customer2Company1 = new Company { Name = "First" }; + Customer2.AddOrder(customer2Order1); + Customer2.AddCompany(customer2Company1); + + var customer2Order2 = new Order { Number = 2 }; + var customer2Company2 = new Company { Name = "Second" }; + Customer2.AddOrder(customer2Order2); + Customer2.AddCompany(customer2Company2); + + var customer3Company1 = new Company { Name = "First" }; + var customer3Order1 = new Order { Number = 1 }; + Customer3.AddOrder(customer3Order1); + Customer3.AddCompany(customer3Company1); + + session.Save(Customer1); + session.Save(Customer2); + session.Save(Customer3); + + transaction.Commit(); + session.Flush(); + } + } + + protected override void OnTearDown() + { + ClearSecondLevelCacheFor(typeof(Customer)); + ClearCollectionCache(n => n.Orders); + ClearCollectionCache(n => n.Companies); + ClearSecondLevelCacheFor(typeof(Order)); + + using (var session = OpenSession()) + using (var transaction = session.BeginTransaction()) + { + session.Delete("from System.Object"); + + session.Flush(); + transaction.Commit(); + } + } + + [Test] + public virtual void ChildCollectionsFromLeftOuterJoinWithOnClauseRestrictionOnCollectionShouldNotBeInSecondLevelCache() + { + var firstSession = OpenSession(); + var customersWithOrderNumberEqualsTo2 = GetCustomersByOrderNumberUsingOnClause(firstSession, OrderNumber); + + var secondSession = OpenSession(); + var customers = GetAllCustomers(secondSession); + + Assert.That(customersWithOrderNumberEqualsTo2.Single(n => n.Id == Customer1.Id).Orders, Has.Count.EqualTo(Customer1.Orders.Count(n => n.Number == OrderNumber))); + Assert.That(customersWithOrderNumberEqualsTo2.Single(n => n.Id == Customer2.Id).Orders, Has.Count.EqualTo(Customer2.Orders.Count(n => n.Number == OrderNumber))); + Assert.That(customersWithOrderNumberEqualsTo2.Single(n => n.Id == Customer3.Id).Orders, Has.Count.EqualTo(Customer3.Orders.Count(n => n.Number == OrderNumber))); + + Assert.That(customers.Single(n => n.Id == Customer1.Id).Orders, Has.Count.EqualTo(Customer1.Orders.Count)); + Assert.That(customers.Single(n => n.Id == Customer2.Id).Orders, Has.Count.EqualTo(Customer2.Orders.Count)); + Assert.That(customers.Single(n => n.Id == Customer3.Id).Orders, Has.Count.EqualTo(Customer3.Orders.Count)); + + firstSession.Dispose(); + secondSession.Dispose(); + } + + [Test] + public virtual async Task ChildCollectionsFromLeftOuterJoinWithOnClauseRestrictionOnCollectionShouldNotBeInSecondLevelCacheAsync() + { + var firstSession = OpenSession(); + var customersWithOrderNumberEqualsTo2 = await GetCustomersByOrderNumberUsingOnClauseAsync(firstSession, OrderNumber).ConfigureAwait(false); + + var secondSession = OpenSession(); + var customers = await GetAllCustomersAsync(secondSession).ConfigureAwait(false); + + Assert.That(customersWithOrderNumberEqualsTo2.Single(n => n.Id == Customer1.Id).Orders, Has.Count.EqualTo(Customer1.Orders.Count(n => n.Number == OrderNumber))); + Assert.That(customersWithOrderNumberEqualsTo2.Single(n => n.Id == Customer2.Id).Orders, Has.Count.EqualTo(Customer2.Orders.Count(n => n.Number == OrderNumber))); + Assert.That(customersWithOrderNumberEqualsTo2.Single(n => n.Id == Customer3.Id).Orders, Has.Count.EqualTo(Customer3.Orders.Count(n => n.Number == OrderNumber))); + + Assert.That(customers.Single(n => n.Id == Customer1.Id).Orders, Has.Count.EqualTo(Customer1.Orders.Count)); + Assert.That(customers.Single(n => n.Id == Customer2.Id).Orders, Has.Count.EqualTo(Customer2.Orders.Count)); + Assert.That(customers.Single(n => n.Id == Customer3.Id).Orders, Has.Count.EqualTo(Customer3.Orders.Count)); + + firstSession.Dispose(); + secondSession.Dispose(); + } + + [Test] + public void ChildCollectionsFromLeftOuterJoinWithWhereClauseRestrictionOnCollectionShouldNotBeInSecondLevelCache() + { + var firstSession = OpenSession(); + var customersWithOrderNumberEqualsTo2 = GetCustomersByOrderNumberUsingWhereClause(firstSession, OrderNumber); + + var secondSession = OpenSession(); + var customers = GetAllCustomers(secondSession); + + Assert.That(customersWithOrderNumberEqualsTo2.Single(n => n.Id == Customer1.Id).Orders, Has.Count.EqualTo(Customer1.Orders.Count(n => n.Number == OrderNumber))); + Assert.That(customersWithOrderNumberEqualsTo2.Single(n => n.Id == Customer2.Id).Orders, Has.Count.EqualTo(Customer2.Orders.Count(n => n.Number == OrderNumber))); + + Assert.That(customers.Single(n => n.Id == Customer3.Id).Orders, Has.Count.EqualTo(Customer3.Orders.Count)); + Assert.That(customers.Single(n => n.Id == Customer1.Id).Orders, Has.Count.EqualTo(Customer1.Orders.Count)); + Assert.That(customers.Single(n => n.Id == Customer2.Id).Orders, Has.Count.EqualTo(Customer2.Orders.Count)); + + firstSession.Dispose(); + secondSession.Dispose(); + } + + [Test] + public async Task ChildCollectionsFromLeftOuterJoinWithWhereClauseRestrictionOnCollectionShouldNotBeInSecondLevelCacheAsync() + { + var firstSession = OpenSession(); + var customersWithOrderNumberEqualsTo2 = await GetCustomersByOrderNumberUsingWhereClauseAsync(firstSession, OrderNumber).ConfigureAwait(false); + + var secondSession = OpenSession(); + var customers = await GetAllCustomersAsync(secondSession).ConfigureAwait(false); + + Assert.That(customersWithOrderNumberEqualsTo2.Single(n => n.Id == Customer1.Id).Orders, Has.Count.EqualTo(Customer1.Orders.Count(n => n.Number == OrderNumber))); + Assert.That(customersWithOrderNumberEqualsTo2.Single(n => n.Id == Customer2.Id).Orders, Has.Count.EqualTo(Customer2.Orders.Count(n => n.Number == OrderNumber))); + + Assert.That(customers.Single(n => n.Id == Customer3.Id).Orders, Has.Count.EqualTo(Customer3.Orders.Count)); + Assert.That(customers.Single(n => n.Id == Customer1.Id).Orders, Has.Count.EqualTo(Customer1.Orders.Count)); + Assert.That(customers.Single(n => n.Id == Customer2.Id).Orders, Has.Count.EqualTo(Customer2.Orders.Count)); + + firstSession.Dispose(); + secondSession.Dispose(); + } + + [Test] + public void ChildCollectionsEagerFetchedShouldBeInSecondLevelCache() + { + var firstSession = OpenSession(); + var customersWithOrderNumberEqualsTo2 = GetCustomersWithOrdersEagerLoaded(firstSession); + + using (var session = OpenSession()) + using (IDbCommand cmd = session.Connection.CreateCommand()) + { + cmd.CommandText = "DELETE FROM Orders;"; + cmd.ExecuteNonQuery(); + cmd.Connection.Close(); + } + + var secondSession = OpenSession(); + var customers = GetAllCustomers(secondSession); + + + Assert.That(customersWithOrderNumberEqualsTo2.Single(n => n.Id == Customer1.Id).Orders, Has.Count.EqualTo(Customer1.Orders.Count)); + Assert.That(customersWithOrderNumberEqualsTo2.Single(n => n.Id == Customer2.Id).Orders, Has.Count.EqualTo(Customer2.Orders.Count)); + + Assert.That(customers.Single(n => n.Id == Customer3.Id).Orders, Has.Count.EqualTo(Customer3.Orders.Count)); + Assert.That(customers.Single(n => n.Id == Customer1.Id).Orders, Has.Count.EqualTo(Customer1.Orders.Count)); + Assert.That(customers.Single(n => n.Id == Customer2.Id).Orders, Has.Count.EqualTo(Customer2.Orders.Count)); + + firstSession.Dispose(); + secondSession.Dispose(); + } + + [Test] + public async Task ChildCollectionsEagerFetchedShouldBeInSecondLevelCacheAsync() + { + var firstSession = OpenSession(); + var customersWithOrderNumberEqualsTo2 = await GetCustomersWithOrdersEagerLoadedAsync(firstSession).ConfigureAwait(false); + + using (var session = OpenSession()) + using (IDbCommand cmd = session.Connection.CreateCommand()) + { + cmd.CommandText = "DELETE FROM Orders;"; + cmd.ExecuteNonQuery(); + cmd.Connection.Close(); + } + + var secondSession = OpenSession(); + var customers = await GetAllCustomersAsync(secondSession).ConfigureAwait(false); + + + Assert.That(customersWithOrderNumberEqualsTo2.Single(n => n.Id == Customer1.Id).Orders, Has.Count.EqualTo(Customer1.Orders.Count)); + Assert.That(customersWithOrderNumberEqualsTo2.Single(n => n.Id == Customer2.Id).Orders, Has.Count.EqualTo(Customer2.Orders.Count)); + + Assert.That(customers.Single(n => n.Id == Customer3.Id).Orders, Has.Count.EqualTo(Customer3.Orders.Count)); + Assert.That(customers.Single(n => n.Id == Customer1.Id).Orders, Has.Count.EqualTo(Customer1.Orders.Count)); + Assert.That(customers.Single(n => n.Id == Customer2.Id).Orders, Has.Count.EqualTo(Customer2.Orders.Count)); + + firstSession.Dispose(); + secondSession.Dispose(); + } + + [Test] + public void ChildCollectionsFromLeftOuterJoinWithWhereClauseRestrictionOnRootShouldBeInSecondLevelCache() + { + var firstSession = OpenSession(); + var customersWithOrderNumberEqualsTo2 = GetCustomersByNameUsingWhereClause(firstSession, "First Customer"); + + using (var session = OpenSession()) + using (IDbCommand cmd = session.Connection.CreateCommand()) + { + cmd.CommandText = "DELETE FROM Orders;"; + cmd.ExecuteNonQuery(); + cmd.Connection.Close(); + } + + var secondSession = OpenSession(); + var customers = secondSession.Get(Customer1.Id); + + Assert.That(customersWithOrderNumberEqualsTo2.Single(n => n.Id == Customer1.Id).Orders, Has.Count.EqualTo(Customer1.Orders.Count)); + Assert.That(customers.Orders, Has.Count.EqualTo(Customer1.Orders.Count)); + + firstSession.Dispose(); + secondSession.Dispose(); + } + + [Test] + public async Task ChildCollectionsFromLeftOuterJoinWithWhereClauseRestrictionOnRootShouldBeInSecondLevelCacheAsync() + { + var firstSession = OpenSession(); + var customersWithOrderNumberEqualsTo2 = await GetCustomersByNameUsingWhereClauseAsync(firstSession, "First Customer").ConfigureAwait(false); + + using (var session = OpenSession()) + using (IDbCommand cmd = session.Connection.CreateCommand()) + { + cmd.CommandText = "DELETE FROM Orders;"; + cmd.ExecuteNonQuery(); + cmd.Connection.Close(); + } + + var secondSession = OpenSession(); + var customers = await secondSession.GetAsync(Customer1.Id).ConfigureAwait(false); + + Assert.That(customersWithOrderNumberEqualsTo2.Single(n => n.Id == Customer1.Id).Orders, Has.Count.EqualTo(Customer1.Orders.Count)); + Assert.That(customers.Orders, Has.Count.EqualTo(Customer1.Orders.Count)); + + firstSession.Dispose(); + secondSession.Dispose(); + } + + [Test] + public void ChildCollectionsFromLeftOuterJoinShouldBeInSecondLevelCacheIfQueryContainsSubqueryWithRestrictionOnLeftOuterJoin() + { + var firstSession = OpenSession(); + var customersWithOrderNumberEqualsTo2 = GetCustomersByOrderNumberUsingSubqueriesAndByNameUsingWhereClause(firstSession, OrderNumber, Customer1.Name); + + var secondSession = OpenSession(); + var customers = GetAllCustomers(secondSession); + + Assert.That(customersWithOrderNumberEqualsTo2.Single(n => n.Id == Customer1.Id).Orders, Has.Count.EqualTo(Customer1.Orders.Count)); + + using (var thirdSession = OpenSession()) + using (IDbCommand cmd = thirdSession.Connection.CreateCommand()) + { + cmd.CommandText = "DELETE FROM Orders;"; + cmd.ExecuteNonQuery(); + cmd.Connection.Close(); + } + + Assert.That(customers.Single(n => n.Id == Customer1.Id).Orders, Has.Count.EqualTo(Customer1.Orders.Count)); + Assert.That(customers.Single(n => n.Id == Customer2.Id).Orders, Has.Count.EqualTo(0)); + Assert.That(customers.Single(n => n.Id == Customer3.Id).Orders, Has.Count.EqualTo(0)); + + firstSession.Dispose(); + secondSession.Dispose(); + } + + [Test] + public async Task ChildCollectionsFromLeftOuterJoinShouldBeInSecondLevelCacheIfQueryContainsSubqueryWithRestrictionOnLeftOuterJoinAsync() + { + var firstSession = OpenSession(); + var customersWithOrderNumberEqualsTo2 = await GetCustomersByOrderNumberUsingSubqueriesAndByNameUsingWhereClauseAsync(firstSession, OrderNumber, Customer1.Name) + .ConfigureAwait(false); + + var secondSession = OpenSession(); + var customers = await GetAllCustomersAsync(secondSession).ConfigureAwait(false); + + Assert.That(customersWithOrderNumberEqualsTo2.Single(n => n.Id == Customer1.Id).Orders, Has.Count.EqualTo(Customer1.Orders.Count)); + + using (var thirdSession = OpenSession()) + using (IDbCommand cmd = thirdSession.Connection.CreateCommand()) + { + cmd.CommandText = "DELETE FROM Orders;"; + cmd.ExecuteNonQuery(); + cmd.Connection.Close(); + } + + Assert.That(customers.Single(n => n.Id == Customer1.Id).Orders, Has.Count.EqualTo(Customer1.Orders.Count)); + Assert.That(customers.Single(n => n.Id == Customer2.Id).Orders, Has.Count.EqualTo(0)); + Assert.That(customers.Single(n => n.Id == Customer3.Id).Orders, Has.Count.EqualTo(0)); + + firstSession.Dispose(); + secondSession.Dispose(); + } + + [Test] + public virtual void ChildCollectionsFromLeftOuterJoinOnlyWithRestrictionShouldNotBeIn2LvlCache() + { + var firstSession = OpenSession(); + var customersWithOrderNumberEqualsTo2AndCompanies = GetCustomersWithCompaniesByOrderNumberUsingOnClause(firstSession, OrderNumber); + + using (var session = OpenSession()) + using (IDbCommand cmd = session.Connection.CreateCommand()) + { + cmd.CommandText = "DELETE FROM Orders; DELETE FROM Companies;"; + cmd.ExecuteNonQuery(); + cmd.Connection.Close(); + } + + var secondSession = OpenSession(); + var customers = GetAllCustomers(secondSession); + + Assert.That(customersWithOrderNumberEqualsTo2AndCompanies.First(n => n.Id == Customer1.Id).Orders, Has.Count.EqualTo(Customer1.Orders.Count(n => n.Number == OrderNumber))); + Assert.That(customersWithOrderNumberEqualsTo2AndCompanies.First(n => n.Id == Customer2.Id).Orders, Has.Count.EqualTo(Customer2.Orders.Count(n => n.Number == OrderNumber))); + Assert.That(customersWithOrderNumberEqualsTo2AndCompanies.First(n => n.Id == Customer3.Id).Orders, Has.Count.EqualTo(Customer3.Orders.Count(n => n.Number == OrderNumber))); + + Assert.That(customersWithOrderNumberEqualsTo2AndCompanies.First(n => n.Id == Customer1.Id).Companies, Has.Count.EqualTo(Customer1.Companies.Count())); + Assert.That(customersWithOrderNumberEqualsTo2AndCompanies.First(n => n.Id == Customer2.Id).Companies, Has.Count.EqualTo(Customer2.Companies.Count())); + Assert.That(customersWithOrderNumberEqualsTo2AndCompanies.First(n => n.Id == Customer3.Id).Companies, Has.Count.EqualTo(Customer3.Companies.Count())); + + Assert.That(customers.Single(n => n.Id == Customer1.Id).Orders, Has.Count.EqualTo(0)); + Assert.That(customers.Single(n => n.Id == Customer2.Id).Orders, Has.Count.EqualTo(0)); + Assert.That(customers.Single(n => n.Id == Customer3.Id).Orders, Has.Count.EqualTo(0)); + + Assert.That(customers.Single(n => n.Id == Customer1.Id).Companies, Has.Count.EqualTo(Customer1.Companies.Count)); + Assert.That(customers.Single(n => n.Id == Customer2.Id).Companies, Has.Count.EqualTo(Customer2.Companies.Count)); + Assert.That(customers.Single(n => n.Id == Customer3.Id).Companies, Has.Count.EqualTo(Customer3.Companies.Count)); + + firstSession.Dispose(); + secondSession.Dispose(); + } + + [Test] + public virtual async Task ChildCollectionsFromLeftOuterJoinOnlyWithRestrictionShouldNotBeIn2LvlCacheAsync() + { + var firstSession = OpenSession(); + var customersWithOrderNumberEqualsTo2AndCompanies = await GetCustomersWithCompaniesByOrderNumberUsingOnClauseAsync(firstSession, OrderNumber).ConfigureAwait(false); + + using (var session = OpenSession()) + using (IDbCommand cmd = session.Connection.CreateCommand()) + { + cmd.CommandText = "DELETE FROM Orders; DELETE FROM Companies;"; + cmd.ExecuteNonQuery(); + cmd.Connection.Close(); + } + + var secondSession = OpenSession(); + var customers = await GetAllCustomersAsync(secondSession).ConfigureAwait(false); + + Assert.That(customersWithOrderNumberEqualsTo2AndCompanies.First(n => n.Id == Customer1.Id).Orders, Has.Count.EqualTo(Customer1.Orders.Count(n => n.Number == OrderNumber))); + Assert.That(customersWithOrderNumberEqualsTo2AndCompanies.First(n => n.Id == Customer2.Id).Orders, Has.Count.EqualTo(Customer2.Orders.Count(n => n.Number == OrderNumber))); + Assert.That(customersWithOrderNumberEqualsTo2AndCompanies.First(n => n.Id == Customer3.Id).Orders, Has.Count.EqualTo(Customer3.Orders.Count(n => n.Number == OrderNumber))); + + Assert.That(customersWithOrderNumberEqualsTo2AndCompanies.First(n => n.Id == Customer1.Id).Companies, Has.Count.EqualTo(Customer1.Companies.Count())); + Assert.That(customersWithOrderNumberEqualsTo2AndCompanies.First(n => n.Id == Customer2.Id).Companies, Has.Count.EqualTo(Customer2.Companies.Count())); + Assert.That(customersWithOrderNumberEqualsTo2AndCompanies.First(n => n.Id == Customer3.Id).Companies, Has.Count.EqualTo(Customer3.Companies.Count())); + + Assert.That(customers.Single(n => n.Id == Customer1.Id).Orders, Has.Count.EqualTo(0)); + Assert.That(customers.Single(n => n.Id == Customer2.Id).Orders, Has.Count.EqualTo(0)); + Assert.That(customers.Single(n => n.Id == Customer3.Id).Orders, Has.Count.EqualTo(0)); + + Assert.That(customers.Single(n => n.Id == Customer1.Id).Companies, Has.Count.EqualTo(Customer1.Companies.Count)); + Assert.That(customers.Single(n => n.Id == Customer2.Id).Companies, Has.Count.EqualTo(Customer2.Companies.Count)); + Assert.That(customers.Single(n => n.Id == Customer3.Id).Companies, Has.Count.EqualTo(Customer3.Companies.Count)); + + firstSession.Dispose(); + secondSession.Dispose(); + } + + protected void ClearSecondLevelCacheFor(System.Type entity) + { + var entityName = entity.FullName; + Sfi.EvictEntity(entityName); + var entityPersister = Sfi.GetEntityPersister(entityName); + if (!entityPersister.HasCache) + return; + + var querySpaces = entityPersister.QuerySpaces.Cast().ToList().AsReadOnly(); + Sfi.UpdateTimestampsCache.PreInvalidate(querySpaces); + } + + protected void ClearCollectionCache(Expression> pathToCollection) + { + var rootEntityTypeFullPath = typeof(T).FullName; + var memberExpression = pathToCollection.Body as MemberExpression; + if (memberExpression == null) + throw new ArgumentException("pathToCollection should be member expression"); + + var role = string.Format("{0}.{1}", rootEntityTypeFullPath, memberExpression.Member.Name); + Sfi.EvictCollection(role); + } + + protected abstract IList GetCustomersWithFetchedOrdersWithoutRestrictions(ISession session); + protected abstract IList GetCustomersWithOrdersEagerLoaded(ISession session); + protected abstract Task> GetCustomersWithOrdersEagerLoadedAsync(ISession session); + protected abstract IList GetCustomersByOrderNumberUsingOnClause(ISession session, int orderNumber); + protected abstract Task> GetCustomersByOrderNumberUsingOnClauseAsync(ISession session, int orderNumber); + protected abstract IList GetCustomersByOrderNumberUsingWhereClause(ISession session, int orderNumber); + protected abstract Task> GetCustomersByOrderNumberUsingWhereClauseAsync(ISession session, int orderNumber); + protected abstract IList GetCustomersByNameUsingWhereClause(ISession session, string customerName); + protected abstract Task> GetCustomersByNameUsingWhereClauseAsync(ISession session, string customerName); + protected abstract IList GetCustomersByOrderNumberUsingSubqueriesAndByNameUsingWhereClause(ISession session, int orderNumber, string customerName); + protected abstract Task> GetCustomersByOrderNumberUsingSubqueriesAndByNameUsingWhereClauseAsync(ISession session, int orderNumber, string customerName); + protected abstract IList GetCustomersWithCompaniesByOrderNumberUsingOnClause(ISession session, int orderNumber); + protected abstract Task> GetCustomersWithCompaniesByOrderNumberUsingOnClauseAsync(ISession session, int orderNumber); + protected abstract IList GetAllCustomers(ISession session); + protected abstract Task> GetAllCustomersAsync(ISession session); + } } diff --git a/src/NHibernate/Async/Engine/Loading/CollectionLoadContext.cs b/src/NHibernate/Async/Engine/Loading/CollectionLoadContext.cs index be9f1d105ee..66333b8ffbf 100644 --- a/src/NHibernate/Async/Engine/Loading/CollectionLoadContext.cs +++ b/src/NHibernate/Async/Engine/Loading/CollectionLoadContext.cs @@ -33,8 +33,9 @@ public partial class CollectionLoadContext /// complete. /// /// The persister for which to complete loading. + /// List of collections that won't be added to the cache. /// A cancellation token that can be used to cancel the work - public async Task EndLoadingCollectionsAsync(ICollectionPersister persister, CancellationToken cancellationToken) + public async Task EndLoadingCollectionsAsync(ICollectionPersister persister, HashSet uncacheableCollections, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); if (!loadContexts.HasLoadingCollectionEntries && (localLoadingCollectionKeys.Count == 0)) @@ -80,7 +81,7 @@ public async Task EndLoadingCollectionsAsync(ICollectionPersister persister, Can } localLoadingCollectionKeys.ExceptWith(toRemove); - await (EndLoadingCollectionsAsync(persister, matches, cancellationToken)).ConfigureAwait(false); + await (EndLoadingCollectionsAsync(persister, matches, uncacheableCollections, cancellationToken)).ConfigureAwait(false); if ((localLoadingCollectionKeys.Count == 0)) { // todo : hack!!! @@ -93,7 +94,8 @@ public async Task EndLoadingCollectionsAsync(ICollectionPersister persister, Can } } - private async Task EndLoadingCollectionsAsync(ICollectionPersister persister, IList matchedCollectionEntries, CancellationToken cancellationToken) + private async Task EndLoadingCollectionsAsync(ICollectionPersister persister, IList matchedCollectionEntries, + HashSet uncacheableCollections, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); if (matchedCollectionEntries == null || matchedCollectionEntries.Count == 0) @@ -115,7 +117,7 @@ private async Task EndLoadingCollectionsAsync(ICollectionPersister persister, IL for (int i = 0; i < count; i++) { await (EndLoadingCollectionAsync(matchedCollectionEntries[i], persister, - data => cacheBatcher.AddToBatch(persister, data), cancellationToken)).ConfigureAwait(false); + data => cacheBatcher.AddToBatch(persister, data), uncacheableCollections, cancellationToken)).ConfigureAwait(false); } await (cacheBatcher.ExecuteBatchAsync(cancellationToken)).ConfigureAwait(false); @@ -126,7 +128,7 @@ private async Task EndLoadingCollectionsAsync(ICollectionPersister persister, IL } private async Task EndLoadingCollectionAsync(LoadingCollectionEntry lce, ICollectionPersister persister, - Action cacheBatchingHandler, CancellationToken cancellationToken) + Action cacheBatchingHandler, HashSet uncacheableCollections, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); if (log.IsDebugEnabled()) @@ -161,7 +163,8 @@ private async Task EndLoadingCollectionAsync(LoadingCollectionEntry lce, ICollec ce.PostInitialize(lce.Collection, persistenceContext); } - bool addToCache = hasNoQueuedOperations && persister.HasCache && + bool addToCache = hasNoQueuedOperations && persister.HasCache && + (uncacheableCollections == null || !uncacheableCollections.Contains(lce.Persister.Role)) && session.CacheMode.HasFlag(CacheMode.Put) && !ce.IsDoremove; // and this is not a forced initialization during flush if (addToCache) diff --git a/src/NHibernate/Async/Loader/Loader.cs b/src/NHibernate/Async/Loader/Loader.cs index f511e4eebe7..63b700f354c 100644 --- a/src/NHibernate/Async/Loader/Loader.cs +++ b/src/NHibernate/Async/Loader/Loader.cs @@ -129,7 +129,7 @@ protected async Task LoadSingleRowAsync(DbDataReader resultSet, ISession queryParameters.NamedParameters); } - await (InitializeEntitiesAndCollectionsAsync(hydratedObjects, resultSet, session, queryParameters.IsReadOnly(session), cancellationToken: cancellationToken)).ConfigureAwait(false); + await (InitializeEntitiesAndCollectionsAsync(hydratedObjects, resultSet, session, queryParameters.IsReadOnly(session), null, queryParameters.UncacheableCollections,cancellationToken: cancellationToken)).ConfigureAwait(false); await (session.PersistenceContext.InitializeNonLazyCollectionsAsync(cancellationToken)).ConfigureAwait(false); return result; } @@ -321,7 +321,7 @@ private async Task DoQueryAsync(ISessionImplementor session, QueryParamet session.Batcher.CloseCommand(st, rs); } - await (InitializeEntitiesAndCollectionsAsync(hydratedObjects, rs, session, queryParameters.IsReadOnly(session), cancellationToken: cancellationToken)).ConfigureAwait(false); + await (InitializeEntitiesAndCollectionsAsync(hydratedObjects, rs, session, queryParameters.IsReadOnly(session), null, queryParameters.UncacheableCollections, cancellationToken: cancellationToken)).ConfigureAwait(false); if (createSubselects) { @@ -334,7 +334,7 @@ private async Task DoQueryAsync(ISessionImplementor session, QueryParamet internal async Task InitializeEntitiesAndCollectionsAsync( IList hydratedObjects, object resultSetId, ISessionImplementor session, bool readOnly, - CacheBatcher cacheBatcher = null, CancellationToken cancellationToken = default(CancellationToken)) + CacheBatcher cacheBatcher = null, HashSet uncacheableCollections = null, CancellationToken cancellationToken = default(CancellationToken)) { cancellationToken.ThrowIfCancellationRequested(); ICollectionPersister[] collectionPersisters = CollectionPersisters; @@ -349,7 +349,7 @@ internal async Task InitializeEntitiesAndCollectionsAsync( //during loading //TODO: or we could do this polymorphically, and have two // different operations implemented differently for arrays - await (EndCollectionLoadAsync(resultSetId, session, collectionPersisters[i], cancellationToken)).ConfigureAwait(false); + await (EndCollectionLoadAsync(resultSetId, session, collectionPersisters[i], uncacheableCollections, cancellationToken)).ConfigureAwait(false); } } } @@ -400,13 +400,14 @@ internal async Task InitializeEntitiesAndCollectionsAsync( //the entities, since we might call hashCode() on the elements //TODO: or we could do this polymorphically, and have two // different operations implemented differently for arrays - await (EndCollectionLoadAsync(resultSetId, session, collectionPersisters[i], cancellationToken)).ConfigureAwait(false); + await (EndCollectionLoadAsync(resultSetId, session, collectionPersisters[i], uncacheableCollections, cancellationToken)).ConfigureAwait(false); } } } } - private static Task EndCollectionLoadAsync(object resultSetId, ISessionImplementor session, ICollectionPersister collectionPersister, CancellationToken cancellationToken) + private static Task EndCollectionLoadAsync(object resultSetId, ISessionImplementor session, ICollectionPersister collectionPersister, + HashSet uncacheableCollections, CancellationToken cancellationToken) { if (cancellationToken.IsCancellationRequested) { @@ -416,7 +417,7 @@ private static Task EndCollectionLoadAsync(object resultSetId, ISessionImplement { //this is a query and we are loading multiple instances of the same collection role return session.PersistenceContext.LoadContexts.GetCollectionLoadContext((DbDataReader)resultSetId).EndLoadingCollectionsAsync( - collectionPersister, cancellationToken); + collectionPersister, uncacheableCollections, cancellationToken); } catch (Exception ex) { diff --git a/src/NHibernate/Async/Multi/QueryBatchItemBase.cs b/src/NHibernate/Async/Multi/QueryBatchItemBase.cs index 47e7bf0a952..7f0e8a4ca54 100644 --- a/src/NHibernate/Async/Multi/QueryBatchItemBase.cs +++ b/src/NHibernate/Async/Multi/QueryBatchItemBase.cs @@ -134,7 +134,7 @@ private async Task InitializeEntitiesAndCollectionsAsync(DbDataReader reader, Li continue; await (queryInfo.Loader.InitializeEntitiesAndCollectionsAsync( hydratedObjects[i], reader, Session, queryInfo.Parameters.IsReadOnly(Session), - queryInfo.CacheBatcher, cancellationToken)).ConfigureAwait(false); + queryInfo.CacheBatcher, queryInfo.Parameters.UncacheableCollections, cancellationToken)).ConfigureAwait(false); } } } diff --git a/src/NHibernate/Engine/Loading/CollectionLoadContext.cs b/src/NHibernate/Engine/Loading/CollectionLoadContext.cs index 40c136e1950..03e786e123c 100644 --- a/src/NHibernate/Engine/Loading/CollectionLoadContext.cs +++ b/src/NHibernate/Engine/Loading/CollectionLoadContext.cs @@ -145,8 +145,8 @@ public IPersistentCollection GetLoadingCollection(ICollectionPersister persister /// complete. /// /// The persister for which to complete loading. - /// Indicates if collcetion can be put in cache - public void EndLoadingCollections(ICollectionPersister persister, bool canAddCollectionsToCache) + /// Indicates if collcetions can be put in cache + public void EndLoadingCollections(ICollectionPersister persister, HashSet uncacheableCollections) { if (!loadContexts.HasLoadingCollectionEntries && (localLoadingCollectionKeys.Count == 0)) { @@ -191,7 +191,7 @@ public void EndLoadingCollections(ICollectionPersister persister, bool canAddCol } localLoadingCollectionKeys.ExceptWith(toRemove); - EndLoadingCollections(persister, matches, canAddCollectionsToCache); + EndLoadingCollections(persister, matches, uncacheableCollections); if ((localLoadingCollectionKeys.Count == 0)) { // todo : hack!!! @@ -204,7 +204,7 @@ public void EndLoadingCollections(ICollectionPersister persister, bool canAddCol } } - private void EndLoadingCollections(ICollectionPersister persister, IList matchedCollectionEntries, bool canAddCollectionsToCache) + private void EndLoadingCollections(ICollectionPersister persister, IList matchedCollectionEntries, HashSet uncacheableCollections) { if (matchedCollectionEntries == null || matchedCollectionEntries.Count == 0) { @@ -225,7 +225,7 @@ private void EndLoadingCollections(ICollectionPersister persister, IList cacheBatcher.AddToBatch(persister, data), canAddCollectionsToCache); + data => cacheBatcher.AddToBatch(persister, data), uncacheableCollections); } cacheBatcher.ExecuteBatch(); @@ -236,7 +236,7 @@ private void EndLoadingCollections(ICollectionPersister persister, IList cacheBatchingHandler, bool canAddCollectionsToCache) + Action cacheBatchingHandler, HashSet uncacheableCollections) { if (log.IsDebugEnabled()) { @@ -270,8 +270,9 @@ private void EndLoadingCollection(LoadingCollectionEntry lce, ICollectionPersist ce.PostInitialize(lce.Collection, persistenceContext); } - bool addToCache = canAddCollectionsToCache && hasNoQueuedOperations && persister.HasCache && - session.CacheMode.HasFlag(CacheMode.Put) && !ce.IsDoremove; // and this is not a forced initialization during flush + bool addToCache = hasNoQueuedOperations && persister.HasCache && + session.CacheMode.HasFlag(CacheMode.Put) && + (uncacheableCollections == null || !uncacheableCollections.Contains(lce.Persister.Role)) && !ce.IsDoremove; // and this is not a forced initialization during flush if (addToCache) { diff --git a/src/NHibernate/Engine/QueryParameters.cs b/src/NHibernate/Engine/QueryParameters.cs index a041818f065..10ed698e686 100644 --- a/src/NHibernate/Engine/QueryParameters.cs +++ b/src/NHibernate/Engine/QueryParameters.cs @@ -37,20 +37,20 @@ public QueryParameters(IType[] positionalParameterTypes, object[] postionalParam : this(positionalParameterTypes, postionalParameterValues, null, collectionKeys) {} public QueryParameters(IType[] positionalParameterTypes, object[] postionalParameterValues, IDictionary namedParameters, object[] collectionKeys) - : this(positionalParameterTypes, postionalParameterValues, namedParameters, null, null, false, false, false, null, null, collectionKeys, null, true) {} + : this(positionalParameterTypes, postionalParameterValues, namedParameters, null, null, false, false, false, null, null, collectionKeys, null, null) {} public QueryParameters(IType[] positionalParameterTypes, object[] positionalParameterValues, IDictionary lockModes, RowSelection rowSelection, bool isReadOnlyInitialized, bool readOnly, bool cacheable, string cacheRegion, string comment, bool isLookupByNaturalKey, IResultTransformer transformer) - : this(positionalParameterTypes, positionalParameterValues, null, lockModes, rowSelection, isReadOnlyInitialized, readOnly, cacheable, cacheRegion, comment, null, transformer, true) + : this(positionalParameterTypes, positionalParameterValues, null, lockModes, rowSelection, isReadOnlyInitialized, readOnly, cacheable, cacheRegion, comment, null, transformer, null) { NaturalKeyLookup = isLookupByNaturalKey; } public QueryParameters(IDictionary namedParameters, IDictionary lockModes, RowSelection rowSelection, bool isReadOnlyInitialized, - bool readOnly, bool cacheable, string cacheRegion, string comment, bool isLookupByNaturalKey, IResultTransformer transformer, bool canAddCollectionsToCache) + bool readOnly, bool cacheable, string cacheRegion, string comment, bool isLookupByNaturalKey, IResultTransformer transformer, HashSet uncacheableCollections) : this( TypeHelper.EmptyTypeArray, Array.Empty(), namedParameters, lockModes, rowSelection, isReadOnlyInitialized, readOnly, cacheable, cacheRegion, comment, null, - transformer, canAddCollectionsToCache) + transformer, uncacheableCollections) { // used by CriteriaTranslator NaturalKeyLookup = isLookupByNaturalKey; @@ -58,7 +58,7 @@ public QueryParameters(IDictionary namedParameters, IDiction public QueryParameters(IType[] positionalParameterTypes, object[] positionalParameterValues, IDictionary namedParameters, IDictionary lockModes, RowSelection rowSelection, bool isReadOnlyInitialized, bool readOnly, bool cacheable, string cacheRegion, - string comment, object[] collectionKeys, IResultTransformer transformer, bool canAddCollectionsToCache) + string comment, object[] collectionKeys, IResultTransformer transformer, HashSet uncacheableCollections) { PositionalParameterTypes = positionalParameterTypes ?? Array.Empty(); PositionalParameterValues = positionalParameterValues ?? Array.Empty(); @@ -72,7 +72,7 @@ public QueryParameters(IType[] positionalParameterTypes, object[] positionalPara IsReadOnlyInitialized = isReadOnlyInitialized; this.readOnly = readOnly; ResultTransformer = transformer; - CanAddCollectionsToCache = canAddCollectionsToCache; + UncacheableCollections = uncacheableCollections; } public QueryParameters(IType[] positionalParameterTypes, object[] positionalParameterValues, IDictionary namedParameters, @@ -80,7 +80,7 @@ public QueryParameters(IType[] positionalParameterTypes, object[] positionalPara string comment, object[] collectionKeys, object optionalObject, string optionalEntityName, object optionalId, IResultTransformer transformer) : this( positionalParameterTypes, positionalParameterValues, namedParameters, lockModes, rowSelection, isReadOnlyInitialized, readOnly, cacheable, cacheRegion, comment, collectionKeys, - transformer, true) + transformer, null) { OptionalEntityName = optionalEntityName; OptionalId = optionalId; @@ -143,7 +143,7 @@ public bool HasRowSelection /// /// Indicates if we can add loaded child collections to second lvl cache. /// - public bool CanAddCollectionsToCache { get; set; } + public HashSet UncacheableCollections { get; set; } public bool ReadOnly { diff --git a/src/NHibernate/Impl/MultiCriteriaImpl.cs b/src/NHibernate/Impl/MultiCriteriaImpl.cs index 733cf174eb1..f9bc6bb0e5d 100644 --- a/src/NHibernate/Impl/MultiCriteriaImpl.cs +++ b/src/NHibernate/Impl/MultiCriteriaImpl.cs @@ -273,7 +273,7 @@ private void GetResultsFromDatabase(IList results) for (int i = 0; i < loaders.Count; i++) { CriteriaLoader loader = loaders[i]; - loader.InitializeEntitiesAndCollections(hydratedObjects[i], reader, session, session.DefaultReadOnly, null, parameters[i].CanAddCollectionsToCache); + loader.InitializeEntitiesAndCollections(hydratedObjects[i], reader, session, session.DefaultReadOnly, null, parameters[i].UncacheableCollections); if (createSubselects[i]) { diff --git a/src/NHibernate/Impl/MultiQueryImpl.cs b/src/NHibernate/Impl/MultiQueryImpl.cs index 3b4e9026168..982c4b43f46 100644 --- a/src/NHibernate/Impl/MultiQueryImpl.cs +++ b/src/NHibernate/Impl/MultiQueryImpl.cs @@ -616,7 +616,7 @@ protected List DoList() ITranslator translator = translators[i]; QueryParameters parameter = parameters[i]; - translator.Loader.InitializeEntitiesAndCollections(hydratedObjects[i], reader, session, false, null, parameters[i].CanAddCollectionsToCache); + translator.Loader.InitializeEntitiesAndCollections(hydratedObjects[i], reader, session, false, null, parameters[i].UncacheableCollections); if (createSubselects[i]) { diff --git a/src/NHibernate/Loader/Criteria/CriteriaQueryTranslator.cs b/src/NHibernate/Loader/Criteria/CriteriaQueryTranslator.cs index a54dbd18369..8d52fbeca67 100644 --- a/src/NHibernate/Loader/Criteria/CriteriaQueryTranslator.cs +++ b/src/NHibernate/Loader/Criteria/CriteriaQueryTranslator.cs @@ -139,7 +139,8 @@ public QueryParameters GetQueryParameters() selection.FetchSize = rootCriteria.FetchSize; var lockModes = new Dictionary(); - var canAddCollectionsToCache = true; + var uncacheableCollections = new HashSet(); + foreach (KeyValuePair me in rootCriteria.LockModes) { ICriteria subcriteria = GetAliasedCriteria(me.Key); @@ -154,8 +155,13 @@ public QueryParameters GetQueryParameters() lockModes[GetSQLAlias(subcriteria)] = lm; } - if (subcriteria.HasRestrictions && subcriteria.JoinType == JoinType.LeftOuterJoin) - canAddCollectionsToCache = false; + if (subcriteria.HasRestrictions && subcriteria.JoinType == JoinType.LeftOuterJoin) + { + var collectionName = $"{rootCriteria.EntityOrClassName}.{subcriteria.Path}"; + + if (!uncacheableCollections.Contains(collectionName)) + uncacheableCollections.Add(collectionName); + } } IDictionary queryNamedParameters = CollectedParameters.ToDictionary(np => np.Name, np => new TypedValue(np.Type, np.Value)); @@ -172,7 +178,7 @@ public QueryParameters GetQueryParameters() rootCriteria.Comment, rootCriteria.LookupByNaturalKey, rootCriteria.ResultTransformer, - canAddCollectionsToCache) + uncacheableCollections) { CacheMode = rootCriteria.CacheMode }; diff --git a/src/NHibernate/Loader/Loader.cs b/src/NHibernate/Loader/Loader.cs index 6c15984d951..910364f3705 100644 --- a/src/NHibernate/Loader/Loader.cs +++ b/src/NHibernate/Loader/Loader.cs @@ -315,7 +315,7 @@ protected object LoadSingleRow(DbDataReader resultSet, ISessionImplementor sessi queryParameters.NamedParameters); } - InitializeEntitiesAndCollections(hydratedObjects, resultSet, session, queryParameters.IsReadOnly(session), null, queryParameters.CanAddCollectionsToCache); + InitializeEntitiesAndCollections(hydratedObjects, resultSet, session, queryParameters.IsReadOnly(session), null, queryParameters.UncacheableCollections); session.PersistenceContext.InitializeNonLazyCollections(); return result; } @@ -517,7 +517,7 @@ private IList DoQuery(ISessionImplementor session, QueryParameters queryParamete session.Batcher.CloseCommand(st, rs); } - InitializeEntitiesAndCollections(hydratedObjects, rs, session, queryParameters.IsReadOnly(session), null, queryParameters.CanAddCollectionsToCache); + InitializeEntitiesAndCollections(hydratedObjects, rs, session, queryParameters.IsReadOnly(session), null, queryParameters.UncacheableCollections); if (createSubselects) { @@ -601,7 +601,7 @@ private IEnumerable CreateSubselects(IList keys, Qu internal void InitializeEntitiesAndCollections( IList hydratedObjects, object resultSetId, ISessionImplementor session, bool readOnly, - CacheBatcher cacheBatcher = null, bool canAddCollectionsToCache = true) + CacheBatcher cacheBatcher = null, HashSet uncacheableCollections = null) { ICollectionPersister[] collectionPersisters = CollectionPersisters; if (collectionPersisters != null) @@ -615,7 +615,7 @@ internal void InitializeEntitiesAndCollections( //during loading //TODO: or we could do this polymorphically, and have two // different operations implemented differently for arrays - EndCollectionLoad(resultSetId, session, collectionPersisters[i], canAddCollectionsToCache); + EndCollectionLoad(resultSetId, session, collectionPersisters[i], uncacheableCollections); } } } @@ -666,17 +666,17 @@ internal void InitializeEntitiesAndCollections( //the entities, since we might call hashCode() on the elements //TODO: or we could do this polymorphically, and have two // different operations implemented differently for arrays - EndCollectionLoad(resultSetId, session, collectionPersisters[i], canAddCollectionsToCache); + EndCollectionLoad(resultSetId, session, collectionPersisters[i], uncacheableCollections); } } } } - private static void EndCollectionLoad(object resultSetId, ISessionImplementor session, ICollectionPersister collectionPersister, bool canAddCollectionsToCache) + private static void EndCollectionLoad(object resultSetId, ISessionImplementor session, ICollectionPersister collectionPersister, HashSet uncacheableCollections) { //this is a query and we are loading multiple instances of the same collection role session.PersistenceContext.LoadContexts.GetCollectionLoadContext((DbDataReader)resultSetId).EndLoadingCollections( - collectionPersister, canAddCollectionsToCache); + collectionPersister, uncacheableCollections); } From b66721f143e86e6d7967270c790702b7cb467245 Mon Sep 17 00:00:00 2001 From: "Iwanow, Mihail (EXT)" Date: Wed, 14 Nov 2018 16:31:06 +0100 Subject: [PATCH 04/20] Fix tests NH 3848 for Firebird --- .../NHSpecificTest/NH3848/TestFixture.cs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/NHibernate.Test/NHSpecificTest/NH3848/TestFixture.cs b/src/NHibernate.Test/NHSpecificTest/NH3848/TestFixture.cs index 85c5c781d09..78db0c498aa 100644 --- a/src/NHibernate.Test/NHSpecificTest/NH3848/TestFixture.cs +++ b/src/NHibernate.Test/NHSpecificTest/NH3848/TestFixture.cs @@ -398,7 +398,15 @@ public virtual void ChildCollectionsFromLeftOuterJoinOnlyWithRestrictionShouldNo using (var session = OpenSession()) using (IDbCommand cmd = session.Connection.CreateCommand()) { - cmd.CommandText = "DELETE FROM Orders; DELETE FROM Companies;"; + cmd.CommandText = "DELETE FROM Orders;"; + cmd.ExecuteNonQuery(); + cmd.Connection.Close(); + } + + using (var session = OpenSession()) + using (IDbCommand cmd = session.Connection.CreateCommand()) + { + cmd.CommandText = "DELETE FROM Companies;"; cmd.ExecuteNonQuery(); cmd.Connection.Close(); } From 34974e74a540a23daca71fcf81dfb50af263ffd2 Mon Sep 17 00:00:00 2001 From: "Iwanow, Mihail (EXT)" Date: Wed, 14 Nov 2018 16:54:55 +0100 Subject: [PATCH 05/20] Fix async tests Nh 3848 --- .../NHSpecificTest/NH3848/TestFixture.cs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/NHibernate.Test/NHSpecificTest/NH3848/TestFixture.cs b/src/NHibernate.Test/NHSpecificTest/NH3848/TestFixture.cs index 78db0c498aa..529a69d0104 100644 --- a/src/NHibernate.Test/NHSpecificTest/NH3848/TestFixture.cs +++ b/src/NHibernate.Test/NHSpecificTest/NH3848/TestFixture.cs @@ -443,7 +443,15 @@ public virtual async Task ChildCollectionsFromLeftOuterJoinOnlyWithRestrictionSh using (var session = OpenSession()) using (IDbCommand cmd = session.Connection.CreateCommand()) { - cmd.CommandText = "DELETE FROM Orders; DELETE FROM Companies;"; + cmd.CommandText = "DELETE FROM Companies;"; + cmd.ExecuteNonQuery(); + cmd.Connection.Close(); + } + + using (var session = OpenSession()) + using (IDbCommand cmd = session.Connection.CreateCommand()) + { + cmd.CommandText = "DELETE FROM Orders;"; cmd.ExecuteNonQuery(); cmd.Connection.Close(); } From b0fea9aaebaa3db4e3e9232440e7344430a7e6be Mon Sep 17 00:00:00 2001 From: "Iwanow, Mihail (EXT)" Date: Thu, 15 Nov 2018 11:21:57 +0100 Subject: [PATCH 06/20] Add autogenerated async code --- .../NH3848/CriteriaTestFixture.cs | 146 +++++++ .../Async/NHSpecificTest/NH3848/Fixture.cs | 388 ++++++++++++++++++ .../NH3848/QueryOverTestFixture.cs | 167 ++++++++ .../NH3848/CriteriaTestFixture.cs | 118 ++---- .../NHSpecificTest/NH3848/Customer.cs | 30 +- .../NH3848/{TestFixture.cs => Fixture.cs} | 179 +------- .../NHSpecificTest/NH3848/Order.cs | 12 +- .../NH3848/QueryOverTestFixture.cs | 130 ++---- .../Engine/Loading/CollectionLoadContext.cs | 21 +- .../Async/Impl/MultiCriteriaImpl.cs | 2 +- src/NHibernate/Async/Impl/MultiQueryImpl.cs | 2 +- .../Async/Loader/Criteria/CriteriaLoader.cs | 2 +- src/NHibernate/Async/Loader/Loader.cs | 7 +- .../Async/Multi/QueryBatchItemBase.cs | 2 +- .../Engine/Loading/CollectionLoadContext.cs | 18 +- src/NHibernate/Engine/QueryParameters.cs | 24 +- src/NHibernate/Impl/MultiCriteriaImpl.cs | 2 +- src/NHibernate/Impl/MultiQueryImpl.cs | 2 +- .../Criteria/CriteriaQueryTranslator.cs | 10 +- src/NHibernate/Loader/Loader.cs | 12 +- 20 files changed, 827 insertions(+), 447 deletions(-) create mode 100644 src/NHibernate.Test/Async/NHSpecificTest/NH3848/CriteriaTestFixture.cs create mode 100644 src/NHibernate.Test/Async/NHSpecificTest/NH3848/Fixture.cs create mode 100644 src/NHibernate.Test/Async/NHSpecificTest/NH3848/QueryOverTestFixture.cs rename src/NHibernate.Test/NHSpecificTest/NH3848/{TestFixture.cs => Fixture.cs} (59%) diff --git a/src/NHibernate.Test/Async/NHSpecificTest/NH3848/CriteriaTestFixture.cs b/src/NHibernate.Test/Async/NHSpecificTest/NH3848/CriteriaTestFixture.cs new file mode 100644 index 00000000000..6db16313fa4 --- /dev/null +++ b/src/NHibernate.Test/Async/NHSpecificTest/NH3848/CriteriaTestFixture.cs @@ -0,0 +1,146 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by AsyncGenerator. +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + + +using System.Collections.Generic; +using NHibernate.Criterion; +using NHibernate.SqlCommand; +using NHibernate.Transform; + +namespace NHibernate.Test.NHSpecificTest.NH3848 +{ + using System.Threading.Tasks; + using System.Threading; + public class CriteriaTestFixtureAsync : FixtureAsync + { + protected override Task> GetCustomersWithFetchedOrdersWithoutRestrictionsAsync(ISession session, CancellationToken cancellationToken = default(CancellationToken)) + { + try + { + return session.CreateCriteria() + .CreateAlias("Orders", "order", JoinType.LeftOuterJoin) + .SetResultTransformer(new DistinctRootEntityResultTransformer()) + .ListAsync(cancellationToken); + } + catch (System.Exception ex) + { + return Task.FromException>(ex); + } + } + + protected override Task> GetCustomersWithOrdersEagerLoadedAsync(ISession session, CancellationToken cancellationToken = default(CancellationToken)) + { + try + { + return session.CreateCriteria() + .Fetch("Orders") + .SetResultTransformer(new DistinctRootEntityResultTransformer()) + .ListAsync(cancellationToken); + } + catch (System.Exception ex) + { + return Task.FromException>(ex); + } + } + + protected override Task> GetCustomersByOrderNumberUsingOnClauseAsync(ISession session, int orderNumber, CancellationToken cancellationToken = default(CancellationToken)) + { + try + { + return session.CreateCriteria() + .CreateAlias("Orders", "order", JoinType.LeftOuterJoin, Restrictions.Eq("Number", orderNumber)) + .ListAsync(cancellationToken); + } + catch (System.Exception ex) + { + return Task.FromException>(ex); + } + } + + protected override Task> GetCustomersWithCompaniesByOrderNumberUsingOnClauseAsync(ISession session, int orderNumber, CancellationToken cancellationToken = default(CancellationToken)) + { + try + { + return session.CreateCriteria() + .CreateAlias("Orders", "order", JoinType.LeftOuterJoin, Restrictions.Eq("Number", orderNumber)) + .CreateAlias("Companies", "company", JoinType.LeftOuterJoin) + .ListAsync(cancellationToken); + } + catch (System.Exception ex) + { + return Task.FromException>(ex); + } + } + + protected override Task> GetCustomersByOrderNumberUsingWhereClauseAsync(ISession session, int orderNumber, CancellationToken cancellationToken = default(CancellationToken)) + { + try + { + return session.CreateCriteria() + .CreateCriteria("Orders", "Order", JoinType.LeftOuterJoin) + .Add(Restrictions.Eq("Number", orderNumber)) + .SetResultTransformer(new DistinctRootEntityResultTransformer()) + .ListAsync(cancellationToken); + } + catch (System.Exception ex) + { + return Task.FromException>(ex); + } + } + + protected override Task> GetCustomersByNameUsingWhereClauseAsync(ISession session, string customerName, CancellationToken cancellationToken = default(CancellationToken)) + { + try + { + return session.CreateCriteria() + .CreateAlias("Orders", "order", JoinType.LeftOuterJoin) + .Add(Restrictions.Eq("Name", "First Customer")) + .SetResultTransformer(new DistinctRootEntityResultTransformer()) + .ListAsync(cancellationToken); + } + catch (System.Exception ex) + { + return Task.FromException>(ex); + } + } + + protected override Task> GetCustomersByOrderNumberUsingSubqueriesAndByNameUsingWhereClauseAsync(ISession session, int orderNumber, string customerName, CancellationToken cancellationToken = default(CancellationToken)) + { + try + { + var detachedCriteria = DetachedCriteria.For() + .CreateAlias("Orders", "order", JoinType.LeftOuterJoin, Restrictions.Eq("Number", orderNumber)) + .SetProjection(Projections.Id()); + + return session.CreateCriteria() + .CreateAlias("Orders", "order1", JoinType.LeftOuterJoin) + .Add(Subqueries.PropertyIn("Id", detachedCriteria)) + .Add(Restrictions.Eq("Name", customerName)) + .SetResultTransformer(new DistinctRootEntityResultTransformer()) + .ListAsync(cancellationToken); + } + catch (System.Exception ex) + { + return Task.FromException>(ex); + } + } + + protected override Task> GetAllCustomersAsync(ISession session, CancellationToken cancellationToken = default(CancellationToken)) + { + try + { + return session.CreateCriteria().ListAsync(cancellationToken); + } + catch (System.Exception ex) + { + return Task.FromException>(ex); + } + } + } +} diff --git a/src/NHibernate.Test/Async/NHSpecificTest/NH3848/Fixture.cs b/src/NHibernate.Test/Async/NHSpecificTest/NH3848/Fixture.cs new file mode 100644 index 00000000000..c4e88457d77 --- /dev/null +++ b/src/NHibernate.Test/Async/NHSpecificTest/NH3848/Fixture.cs @@ -0,0 +1,388 @@ +//------------------------------------------------------------------------------ +// +// 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; +using System.Collections.Generic; +using System.Data; +using System.Linq; +using System.Linq.Expressions; +using NHibernate.Cache; +using NHibernate.Cfg; +using NHibernate.Cfg.MappingSchema; +using NHibernate.Mapping.ByCode; +using NUnit.Framework; + +namespace NHibernate.Test.NHSpecificTest.NH3848 +{ + using System.Threading.Tasks; + using System.Threading; + [TestFixture] + public abstract class FixtureAsync : TestCaseMappingByCode + { + protected Customer Customer1; + protected Customer Customer2; + protected Customer Customer3; + protected const int OrderNumber = 2; + + protected override HbmMapping GetMappings() + { + var mapper = new ModelMapper(); + mapper.Class(rc => + { + rc.Table("Customers"); + rc.Id(x => x.Id, m => m.Generator(Generators.GuidComb)); + rc.Property(x => x.Name); + rc.Set(x => x.Orders, m => + { + m.Inverse(true); + m.Key(k => + { + k.Column("CustomerId"); + k.NotNullable(true); + }); + m.Cascade(Mapping.ByCode.Cascade.All.Include(Mapping.ByCode.Cascade.DeleteOrphans)); + m.Cache(c => c.Usage(CacheUsage.ReadWrite)); + }, m => m.OneToMany()); + + rc.Set(x => x.Companies, m => + { + m.Inverse(true); + m.Key(k => + { + k.Column("CustomerId"); + k.NotNullable(true); + }); + m.Cascade(Mapping.ByCode.Cascade.All.Include(Mapping.ByCode.Cascade.DeleteOrphans)); + m.Cache(c => c.Usage(CacheUsage.ReadWrite)); + }, m => m.OneToMany()); + }); + + mapper.Class(rc => + { + rc.Table("Orders"); + rc.Id(x => x.Id, m => m.Generator(Generators.GuidComb)); + rc.Property(x => x.Number); + rc.ManyToOne(x => x.Customer, m => m.Column("CustomerId")); + rc.Cache(c => c.Usage(CacheUsage.ReadWrite)); + }); + + mapper.Class(rc => + { + rc.Table("Companies"); + rc.Id(x => x.Id, m => m.Generator(Generators.GuidComb)); + rc.Property(x => x.Name); + rc.ManyToOne(x => x.Customer, m => m.Column("CustomerId")); + rc.Cache(c => c.Usage(CacheUsage.ReadWrite)); + }); + + return mapper.CompileMappingForAllExplicitlyAddedEntities(); + } + + protected override void Configure(Configuration configuration) + { + base.Configure(configuration); + configuration.Cache(c => + { + c.UseQueryCache = true; + c.Provider(); + }); + } + + protected override void OnSetUp() + { + using (var session = OpenSession()) + using (var transaction = session.BeginTransaction()) + { + Customer1 = new Customer { Name = "First Customer" }; + + Customer2 = new Customer { Name = "Second Customer" }; + + Customer3 = new Customer { Name = "Third Customer" }; + + var customer1Order1 = new Order { Number = 1 }; + var customer1Company1 = new Company { Name = "First" }; + Customer1.AddOrder(customer1Order1); + Customer1.AddCompany(customer1Company1); + + var customer1Order2 = new Order { Number = 2 }; + var customer1Company2 = new Company { Name = "Second" }; + Customer1.AddOrder(customer1Order2); + Customer1.AddCompany(customer1Company2); + + var customer2Order1 = new Order { Number = 1 }; + var customer2Company1 = new Company { Name = "First" }; + Customer2.AddOrder(customer2Order1); + Customer2.AddCompany(customer2Company1); + + var customer2Order2 = new Order { Number = 2 }; + var customer2Company2 = new Company { Name = "Second" }; + Customer2.AddOrder(customer2Order2); + Customer2.AddCompany(customer2Company2); + + var customer3Company1 = new Company { Name = "First" }; + var customer3Order1 = new Order { Number = 1 }; + Customer3.AddOrder(customer3Order1); + Customer3.AddCompany(customer3Company1); + + session.Save(Customer1); + session.Save(Customer2); + session.Save(Customer3); + + transaction.Commit(); + session.Flush(); + } + } + + protected override void OnTearDown() + { + ClearSecondLevelCacheFor(typeof(Customer)); + ClearCollectionCache(n => n.Orders); + ClearCollectionCache(n => n.Companies); + ClearSecondLevelCacheFor(typeof(Order)); + + using (var session = OpenSession()) + using (var transaction = session.BeginTransaction()) + { + session.Delete("from System.Object"); + + session.Flush(); + transaction.Commit(); + } + } + + [Test] + public virtual async Task ChildCollectionsFromLeftOuterJoinWithOnClauseRestrictionOnCollectionShouldNotBeInSecondLevelCacheAsync() + { + var firstSession = OpenSession(); + var customersWithOrderNumberEqualsTo2 = await (GetCustomersByOrderNumberUsingOnClauseAsync(firstSession, OrderNumber)); + + var secondSession = OpenSession(); + var customers = await (GetAllCustomersAsync(secondSession)); + + Assert.That(customersWithOrderNumberEqualsTo2.Single(n => n.Id == Customer1.Id).Orders, Has.Count.EqualTo(Customer1.Orders.Count(n => n.Number == OrderNumber))); + Assert.That(customersWithOrderNumberEqualsTo2.Single(n => n.Id == Customer2.Id).Orders, Has.Count.EqualTo(Customer2.Orders.Count(n => n.Number == OrderNumber))); + Assert.That(customersWithOrderNumberEqualsTo2.Single(n => n.Id == Customer3.Id).Orders, Has.Count.EqualTo(Customer3.Orders.Count(n => n.Number == OrderNumber))); + + Assert.That(customers.Single(n => n.Id == Customer1.Id).Orders, Has.Count.EqualTo(Customer1.Orders.Count)); + Assert.That(customers.Single(n => n.Id == Customer2.Id).Orders, Has.Count.EqualTo(Customer2.Orders.Count)); + Assert.That(customers.Single(n => n.Id == Customer3.Id).Orders, Has.Count.EqualTo(Customer3.Orders.Count)); + + firstSession.Dispose(); + secondSession.Dispose(); + } + + [Test] + public async Task ChildCollectionsFromLeftOuterJoinWithWhereClauseRestrictionOnCollectionShouldNotBeInSecondLevelCacheAsync() + { + var firstSession = OpenSession(); + var customersWithOrderNumberEqualsTo2 = await (GetCustomersByOrderNumberUsingWhereClauseAsync(firstSession, OrderNumber)); + + var secondSession = OpenSession(); + var customers = await (GetAllCustomersAsync(secondSession)); + + Assert.That(customersWithOrderNumberEqualsTo2.Single(n => n.Id == Customer1.Id).Orders, Has.Count.EqualTo(Customer1.Orders.Count(n => n.Number == OrderNumber))); + Assert.That(customersWithOrderNumberEqualsTo2.Single(n => n.Id == Customer2.Id).Orders, Has.Count.EqualTo(Customer2.Orders.Count(n => n.Number == OrderNumber))); + + Assert.That(customers.Single(n => n.Id == Customer3.Id).Orders, Has.Count.EqualTo(Customer3.Orders.Count)); + Assert.That(customers.Single(n => n.Id == Customer1.Id).Orders, Has.Count.EqualTo(Customer1.Orders.Count)); + Assert.That(customers.Single(n => n.Id == Customer2.Id).Orders, Has.Count.EqualTo(Customer2.Orders.Count)); + + firstSession.Dispose(); + secondSession.Dispose(); + } + + [Test] + public async Task ChildCollectionsEagerFetchedShouldBeInSecondLevelCacheAsync() + { + var firstSession = OpenSession(); + var customersWithOrderNumberEqualsTo2 = await (GetCustomersWithOrdersEagerLoadedAsync(firstSession)); + + using (var session = OpenSession()) + using (IDbCommand cmd = session.Connection.CreateCommand()) + { + cmd.CommandText = "DELETE FROM Orders;"; + cmd.ExecuteNonQuery(); + cmd.Connection.Close(); + } + + var secondSession = OpenSession(); + var customers = await (GetAllCustomersAsync(secondSession)); + + Assert.That(customersWithOrderNumberEqualsTo2.Single(n => n.Id == Customer1.Id).Orders, Has.Count.EqualTo(Customer1.Orders.Count)); + Assert.That(customersWithOrderNumberEqualsTo2.Single(n => n.Id == Customer2.Id).Orders, Has.Count.EqualTo(Customer2.Orders.Count)); + + Assert.That(customers.Single(n => n.Id == Customer3.Id).Orders, Has.Count.EqualTo(Customer3.Orders.Count)); + Assert.That(customers.Single(n => n.Id == Customer1.Id).Orders, Has.Count.EqualTo(Customer1.Orders.Count)); + Assert.That(customers.Single(n => n.Id == Customer2.Id).Orders, Has.Count.EqualTo(Customer2.Orders.Count)); + + firstSession.Dispose(); + secondSession.Dispose(); + } + + [Test] + public async Task ChildCollectionsFromLeftOuterJoinWithWhereClauseRestrictionOnRootShouldBeInSecondLevelCacheAsync() + { + var firstSession = OpenSession(); + var customersWithOrderNumberEqualsTo2 = await (GetCustomersByNameUsingWhereClauseAsync(firstSession, "First Customer")); + + using (var session = OpenSession()) + using (IDbCommand cmd = session.Connection.CreateCommand()) + { + cmd.CommandText = "DELETE FROM Orders;"; + cmd.ExecuteNonQuery(); + cmd.Connection.Close(); + } + + var secondSession = OpenSession(); + var customers = await (secondSession.GetAsync(Customer1.Id)); + + Assert.That(customersWithOrderNumberEqualsTo2.Single(n => n.Id == Customer1.Id).Orders, Has.Count.EqualTo(Customer1.Orders.Count)); + Assert.That(customers.Orders, Has.Count.EqualTo(Customer1.Orders.Count)); + + firstSession.Dispose(); + secondSession.Dispose(); + } + + [Test] + public async Task ChildCollectionsFromLeftOuterJoinShouldBeInSecondLevelCacheIfQueryContainsSubqueryWithRestrictionOnLeftOuterJoinAsync() + { + var firstSession = OpenSession(); + var customersWithOrderNumberEqualsTo2 = await (GetCustomersByOrderNumberUsingSubqueriesAndByNameUsingWhereClauseAsync(firstSession, OrderNumber, Customer1.Name)); + + var secondSession = OpenSession(); + var customers = await (GetAllCustomersAsync(secondSession)); + + Assert.That(customersWithOrderNumberEqualsTo2.Single(n => n.Id == Customer1.Id).Orders, Has.Count.EqualTo(Customer1.Orders.Count)); + + using (var thirdSession = OpenSession()) + using (IDbCommand cmd = thirdSession.Connection.CreateCommand()) + { + cmd.CommandText = "DELETE FROM Orders;"; + cmd.ExecuteNonQuery(); + cmd.Connection.Close(); + } + + Assert.That(customers.Single(n => n.Id == Customer1.Id).Orders, Has.Count.EqualTo(Customer1.Orders.Count)); + Assert.That(customers.Single(n => n.Id == Customer2.Id).Orders, Has.Count.EqualTo(0)); + Assert.That(customers.Single(n => n.Id == Customer3.Id).Orders, Has.Count.EqualTo(0)); + + firstSession.Dispose(); + secondSession.Dispose(); + } + + [Test] + public virtual async Task ChildCollectionsFromLeftOuterJoinOnlyWithRestrictionShouldNotBeIn2LvlCacheAsync() + { + var firstSession = OpenSession(); + var customersWithOrderNumberEqualsTo2AndCompanies = await (GetCustomersWithCompaniesByOrderNumberUsingOnClauseAsync(firstSession, OrderNumber)); + + using (var session = OpenSession()) + using (IDbCommand cmd = session.Connection.CreateCommand()) + { + cmd.CommandText = "DELETE FROM Orders;"; + cmd.ExecuteNonQuery(); + cmd.Connection.Close(); + } + + using (var session = OpenSession()) + using (IDbCommand cmd = session.Connection.CreateCommand()) + { + cmd.CommandText = "DELETE FROM Companies;"; + cmd.ExecuteNonQuery(); + cmd.Connection.Close(); + } + + var secondSession = OpenSession(); + var customers = await (GetAllCustomersAsync(secondSession)); + + Assert.That(customersWithOrderNumberEqualsTo2AndCompanies.First(n => n.Id == Customer1.Id).Orders, Has.Count.EqualTo(Customer1.Orders.Count(n => n.Number == OrderNumber))); + Assert.That(customersWithOrderNumberEqualsTo2AndCompanies.First(n => n.Id == Customer2.Id).Orders, Has.Count.EqualTo(Customer2.Orders.Count(n => n.Number == OrderNumber))); + Assert.That(customersWithOrderNumberEqualsTo2AndCompanies.First(n => n.Id == Customer3.Id).Orders, Has.Count.EqualTo(Customer3.Orders.Count(n => n.Number == OrderNumber))); + + Assert.That(customersWithOrderNumberEqualsTo2AndCompanies.First(n => n.Id == Customer1.Id).Companies, Has.Count.EqualTo(Customer1.Companies.Count())); + Assert.That(customersWithOrderNumberEqualsTo2AndCompanies.First(n => n.Id == Customer2.Id).Companies, Has.Count.EqualTo(Customer2.Companies.Count())); + Assert.That(customersWithOrderNumberEqualsTo2AndCompanies.First(n => n.Id == Customer3.Id).Companies, Has.Count.EqualTo(Customer3.Companies.Count())); + + Assert.That(customers.Single(n => n.Id == Customer1.Id).Orders, Has.Count.EqualTo(0)); + Assert.That(customers.Single(n => n.Id == Customer2.Id).Orders, Has.Count.EqualTo(0)); + Assert.That(customers.Single(n => n.Id == Customer3.Id).Orders, Has.Count.EqualTo(0)); + + Assert.That(customers.Single(n => n.Id == Customer1.Id).Companies, Has.Count.EqualTo(Customer1.Companies.Count)); + Assert.That(customers.Single(n => n.Id == Customer2.Id).Companies, Has.Count.EqualTo(Customer2.Companies.Count)); + Assert.That(customers.Single(n => n.Id == Customer3.Id).Companies, Has.Count.EqualTo(Customer3.Companies.Count)); + + firstSession.Dispose(); + secondSession.Dispose(); + } + + protected async Task ClearSecondLevelCacheForAsync(System.Type entity, CancellationToken cancellationToken = default(CancellationToken)) + { + var entityName = entity.FullName; + await (Sfi.EvictEntityAsync(entityName, cancellationToken)); + var entityPersister = Sfi.GetEntityPersister(entityName); + if (!entityPersister.HasCache) + return; + + var querySpaces = entityPersister.QuerySpaces.Cast().ToList().AsReadOnly(); + await (Sfi.UpdateTimestampsCache.PreInvalidateAsync(querySpaces, cancellationToken)); + } + + protected void ClearSecondLevelCacheFor(System.Type entity) + { + var entityName = entity.FullName; + Sfi.EvictEntity(entityName); + var entityPersister = Sfi.GetEntityPersister(entityName); + if (!entityPersister.HasCache) + return; + + var querySpaces = entityPersister.QuerySpaces.Cast().ToList().AsReadOnly(); + Sfi.UpdateTimestampsCache.PreInvalidate(querySpaces); + } + + protected Task ClearCollectionCacheAsync(Expression> pathToCollection, CancellationToken cancellationToken = default(CancellationToken)) + { + try + { + var rootEntityTypeFullPath = typeof(T).FullName; + var memberExpression = pathToCollection.Body as MemberExpression; + if (memberExpression == null) + return Task.FromException(new ArgumentException("pathToCollection should be member expression")); + + var role = string.Format("{0}.{1}", rootEntityTypeFullPath, memberExpression.Member.Name); + return Sfi.EvictCollectionAsync(role, cancellationToken); + } + catch (Exception ex) + { + return Task.FromException(ex); + } + } + + protected void ClearCollectionCache(Expression> pathToCollection) + { + var rootEntityTypeFullPath = typeof(T).FullName; + var memberExpression = pathToCollection.Body as MemberExpression; + if (memberExpression == null) + throw new ArgumentException("pathToCollection should be member expression"); + + var role = string.Format("{0}.{1}", rootEntityTypeFullPath, memberExpression.Member.Name); + Sfi.EvictCollection(role); + } + + protected abstract Task> GetCustomersWithFetchedOrdersWithoutRestrictionsAsync(ISession session, CancellationToken cancellationToken = default(CancellationToken)); + protected abstract Task> GetCustomersWithOrdersEagerLoadedAsync(ISession session, CancellationToken cancellationToken = default(CancellationToken)); + protected abstract Task> GetCustomersByOrderNumberUsingOnClauseAsync(ISession session, int orderNumber, CancellationToken cancellationToken = default(CancellationToken)); + protected abstract Task> GetCustomersByOrderNumberUsingWhereClauseAsync(ISession session, int orderNumber, CancellationToken cancellationToken = default(CancellationToken)); + protected abstract Task> GetCustomersByNameUsingWhereClauseAsync(ISession session, string customerName, CancellationToken cancellationToken = default(CancellationToken)); + protected abstract Task> GetCustomersByOrderNumberUsingSubqueriesAndByNameUsingWhereClauseAsync(ISession session, int orderNumber, string customerName, CancellationToken cancellationToken = default(CancellationToken)); + protected abstract Task> GetCustomersWithCompaniesByOrderNumberUsingOnClauseAsync(ISession session, int orderNumber, CancellationToken cancellationToken = default(CancellationToken)); + protected abstract Task> GetAllCustomersAsync(ISession session, CancellationToken cancellationToken = default(CancellationToken)); + } +} diff --git a/src/NHibernate.Test/Async/NHSpecificTest/NH3848/QueryOverTestFixture.cs b/src/NHibernate.Test/Async/NHSpecificTest/NH3848/QueryOverTestFixture.cs new file mode 100644 index 00000000000..f56a4ddc9a8 --- /dev/null +++ b/src/NHibernate.Test/Async/NHSpecificTest/NH3848/QueryOverTestFixture.cs @@ -0,0 +1,167 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by AsyncGenerator. +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + + +using System.Collections.Generic; +using NHibernate.Criterion; +using NHibernate.SqlCommand; +using NHibernate.Transform; + +namespace NHibernate.Test.NHSpecificTest.NH3848 +{ + using System.Threading.Tasks; + using System.Threading; + public class QueryOverTestFixtureAsync : FixtureAsync + { + protected override Task> GetCustomersWithFetchedOrdersWithoutRestrictionsAsync(ISession session, CancellationToken cancellationToken = default(CancellationToken)) + { + try + { + Order ordersAlias = null; + return session.QueryOver() + .JoinAlias(n => n.Orders, + () => ordersAlias, + JoinType.LeftOuterJoin) + .TransformUsing(new DistinctRootEntityResultTransformer()) + .ListAsync(cancellationToken); + } + catch (System.Exception ex) + { + return Task.FromException>(ex); + } + } + + protected override Task> GetCustomersWithOrdersEagerLoadedAsync(ISession session, CancellationToken cancellationToken = default(CancellationToken)) + { + try + { + return session.QueryOver() + .Fetch(SelectMode.Fetch, n => n.Orders) + .TransformUsing(new DistinctRootEntityResultTransformer()) + .ListAsync(cancellationToken); + } + catch (System.Exception ex) + { + return Task.FromException>(ex); + } + } + + protected override Task> GetCustomersByOrderNumberUsingOnClauseAsync(ISession session, int orderNumber, CancellationToken cancellationToken = default(CancellationToken)) + { + try + { + Order ordersAlias = null; + return session.QueryOver() + .JoinAlias(n => n.Orders, + () => ordersAlias, + JoinType.LeftOuterJoin, + Restrictions.Eq("Number", orderNumber)) + .ListAsync(cancellationToken); + } + catch (System.Exception ex) + { + return Task.FromException>(ex); + } + } + + protected override Task> GetCustomersWithCompaniesByOrderNumberUsingOnClauseAsync(ISession session, int orderNumber, CancellationToken cancellationToken = default(CancellationToken)) + { + try + { + Order ordersAlias = null; + Company companiesAlias = null; + + return session.QueryOver() + .JoinAlias(n => n.Orders, + () => ordersAlias, + JoinType.LeftOuterJoin, + Restrictions.Eq("Number", orderNumber)) + .JoinAlias(n => n.Companies, + () => companiesAlias, + JoinType.LeftOuterJoin) + .ListAsync(cancellationToken); + } + catch (System.Exception ex) + { + return Task.FromException>(ex); + } + } + + protected override Task> GetCustomersByOrderNumberUsingWhereClauseAsync(ISession session, int orderNumber, CancellationToken cancellationToken = default(CancellationToken)) + { + try + { + Order ordersAlias = null; + return session.QueryOver() + .JoinQueryOver(n => n.Orders, () => ordersAlias, JoinType.LeftOuterJoin) + .Where(Restrictions.Eq("Number", orderNumber)) + .ListAsync(cancellationToken); + } + catch (System.Exception ex) + { + return Task.FromException>(ex); + } + } + + protected override Task> GetCustomersByNameUsingWhereClauseAsync(ISession session, string customerName, CancellationToken cancellationToken = default(CancellationToken)) + { + try + { + Order ordersAlias = null; + return session.QueryOver() + .JoinAlias(n => n.Orders, + () => ordersAlias, + JoinType.LeftOuterJoin + ) + .Where(Restrictions.Eq("Name", customerName)) + .TransformUsing(new DistinctRootEntityResultTransformer()) + .ListAsync(cancellationToken); + } + catch (System.Exception ex) + { + return Task.FromException>(ex); + } + } + + protected override Task> GetCustomersByOrderNumberUsingSubqueriesAndByNameUsingWhereClauseAsync(ISession session, int orderNumber, string customerName, CancellationToken cancellationToken = default(CancellationToken)) + { + try + { + Order ordersAlias = null; + Order ordersAlias2 = null; + var subquery = QueryOver.Of() + .JoinAlias(n => n.Orders, () => ordersAlias, JoinType.LeftOuterJoin, Restrictions.Eq("Number", orderNumber)) + .Select(n => n.Id); + + return session.QueryOver() + .JoinAlias(n => n.Orders, () => ordersAlias2, JoinType.LeftOuterJoin) + .WithSubquery.WhereProperty(n => n.Id).In(subquery) + .Where(Restrictions.Eq("Name", customerName)) + .TransformUsing(new DistinctRootEntityResultTransformer()) + .ListAsync(cancellationToken); + } + catch (System.Exception ex) + { + return Task.FromException>(ex); + } + } + + protected override Task> GetAllCustomersAsync(ISession session, CancellationToken cancellationToken = default(CancellationToken)) + { + try + { + return session.QueryOver().ListAsync(cancellationToken); + } + catch (System.Exception ex) + { + return Task.FromException>(ex); + } + } + } +} diff --git a/src/NHibernate.Test/NHSpecificTest/NH3848/CriteriaTestFixture.cs b/src/NHibernate.Test/NHSpecificTest/NH3848/CriteriaTestFixture.cs index 0f025c0bb40..c3f90b196ad 100644 --- a/src/NHibernate.Test/NHSpecificTest/NH3848/CriteriaTestFixture.cs +++ b/src/NHibernate.Test/NHSpecificTest/NH3848/CriteriaTestFixture.cs @@ -1,140 +1,78 @@ using System.Collections.Generic; -using System.Threading.Tasks; using NHibernate.Criterion; using NHibernate.SqlCommand; using NHibernate.Transform; namespace NHibernate.Test.NHSpecificTest.NH3848 { - public class CriteriaTestFixture : TestFixture - { - protected override IList GetCustomersWithFetchedOrdersWithoutRestrictions(ISession session) - { - return session.CreateCriteria() - .CreateAlias("Orders", "order", JoinType.LeftOuterJoin) - .SetResultTransformer(new DistinctRootEntityResultTransformer()) - .List(); - } - - protected override IList GetCustomersWithOrdersEagerLoaded(ISession session) - { - return session.CreateCriteria() - .Fetch("Orders") - .SetResultTransformer(new DistinctRootEntityResultTransformer()) - .List(); - } - - protected override async Task> GetCustomersWithOrdersEagerLoadedAsync(ISession session) + public class CriteriaTestFixture : Fixture + { + protected override IList GetCustomersWithFetchedOrdersWithoutRestrictions(ISession session) { - return await session.CreateCriteria() - .Fetch("Orders") - .SetResultTransformer(new DistinctRootEntityResultTransformer()) - .ListAsync().ConfigureAwait(false); + return session.CreateCriteria() + .CreateAlias("Orders", "order", JoinType.LeftOuterJoin) + .SetResultTransformer(new DistinctRootEntityResultTransformer()) + .List(); } - protected override IList GetCustomersByOrderNumberUsingOnClause(ISession session, int orderNumber) - { - return session.CreateCriteria() - .CreateAlias("Orders", "order", JoinType.LeftOuterJoin, Restrictions.Eq("Number", orderNumber)) - .List(); - } - - protected override async Task> GetCustomersByOrderNumberUsingOnClauseAsync(ISession session, int orderNumber) + protected override IList GetCustomersWithOrdersEagerLoaded(ISession session) { - return await session.CreateCriteria() - .CreateAlias("Orders", "order", JoinType.LeftOuterJoin, Restrictions.Eq("Number", orderNumber)) - .ListAsync().ConfigureAwait(false); + return session.CreateCriteria() + .Fetch("Orders") + .SetResultTransformer(new DistinctRootEntityResultTransformer()) + .List(); } - protected override IList GetCustomersWithCompaniesByOrderNumberUsingOnClause(ISession session, int orderNumber) + protected override IList GetCustomersByOrderNumberUsingOnClause(ISession session, int orderNumber) { return session.CreateCriteria() .CreateAlias("Orders", "order", JoinType.LeftOuterJoin, Restrictions.Eq("Number", orderNumber)) - .CreateAlias("Companies", "company", JoinType.LeftOuterJoin) .List(); } - protected override async Task> GetCustomersWithCompaniesByOrderNumberUsingOnClauseAsync(ISession session, int orderNumber) + protected override IList GetCustomersWithCompaniesByOrderNumberUsingOnClause(ISession session, int orderNumber) { - return await session.CreateCriteria() + return session.CreateCriteria() .CreateAlias("Orders", "order", JoinType.LeftOuterJoin, Restrictions.Eq("Number", orderNumber)) .CreateAlias("Companies", "company", JoinType.LeftOuterJoin) - .ListAsync().ConfigureAwait(false); + .List(); } protected override IList GetCustomersByOrderNumberUsingWhereClause(ISession session, int orderNumber) - { - return session.CreateCriteria() - .CreateCriteria("Orders", "Order", JoinType.LeftOuterJoin) - .Add(Restrictions.Eq("Number", orderNumber)) - .SetResultTransformer(new DistinctRootEntityResultTransformer()) - .List(); - } - - protected override async Task> GetCustomersByOrderNumberUsingWhereClauseAsync(ISession session, int orderNumber) { - return await session.CreateCriteria() + return session.CreateCriteria() .CreateCriteria("Orders", "Order", JoinType.LeftOuterJoin) - .Add(Restrictions.Eq("Number", orderNumber)) - .SetResultTransformer(new DistinctRootEntityResultTransformer()) - .ListAsync().ConfigureAwait(false); + .Add(Restrictions.Eq("Number", orderNumber)) + .SetResultTransformer(new DistinctRootEntityResultTransformer()) + .List(); } protected override IList GetCustomersByNameUsingWhereClause(ISession session, string customerName) - { - return session.CreateCriteria() - .CreateAlias("Orders", "order", JoinType.LeftOuterJoin) - .Add(Restrictions.Eq("Name", "First Customer")) - .SetResultTransformer(new DistinctRootEntityResultTransformer()) - .List(); - } - - protected override async Task> GetCustomersByNameUsingWhereClauseAsync(ISession session, string customerName) { - return await session.CreateCriteria() + return session.CreateCriteria() .CreateAlias("Orders", "order", JoinType.LeftOuterJoin) - .Add(Restrictions.Eq("Name", "First Customer")) + .Add(Restrictions.Eq("Name", "First Customer")) .SetResultTransformer(new DistinctRootEntityResultTransformer()) - .ListAsync().ConfigureAwait(false); + .List(); } protected override IList GetCustomersByOrderNumberUsingSubqueriesAndByNameUsingWhereClause(ISession session, int orderNumber, string customerName) - { - var detachedCriteria = DetachedCriteria.For() - .CreateAlias("Orders", "order", JoinType.LeftOuterJoin, Restrictions.Eq("Number", orderNumber)) - .SetProjection(Projections.Id()); - - return session.CreateCriteria() - .CreateAlias("Orders", "order1", JoinType.LeftOuterJoin) - .Add(Subqueries.PropertyIn("Id",detachedCriteria)) - .Add(Restrictions.Eq("Name", customerName)) - .SetResultTransformer(new DistinctRootEntityResultTransformer()) - .List(); - } - - - protected override async Task> GetCustomersByOrderNumberUsingSubqueriesAndByNameUsingWhereClauseAsync(ISession session, int orderNumber, string customerName) { var detachedCriteria = DetachedCriteria.For() - .CreateAlias("Orders", "order", JoinType.LeftOuterJoin, Restrictions.Eq("Number", orderNumber)) - .SetProjection(Projections.Id()); + .CreateAlias("Orders", "order", JoinType.LeftOuterJoin, Restrictions.Eq("Number", orderNumber)) + .SetProjection(Projections.Id()); - return await session.CreateCriteria() + return session.CreateCriteria() .CreateAlias("Orders", "order1", JoinType.LeftOuterJoin) .Add(Subqueries.PropertyIn("Id", detachedCriteria)) .Add(Restrictions.Eq("Name", customerName)) .SetResultTransformer(new DistinctRootEntityResultTransformer()) - .ListAsync().ConfigureAwait(false); + .List(); } protected override IList GetAllCustomers(ISession session) - { - return session.CreateCriteria().List(); - } - - protected override async Task> GetAllCustomersAsync(ISession session) { - return await session.CreateCriteria().ListAsync().ConfigureAwait(false); + return session.CreateCriteria().List(); } } } diff --git a/src/NHibernate.Test/NHSpecificTest/NH3848/Customer.cs b/src/NHibernate.Test/NHSpecificTest/NH3848/Customer.cs index dae8744391e..e3520fcd1d3 100644 --- a/src/NHibernate.Test/NHSpecificTest/NH3848/Customer.cs +++ b/src/NHibernate.Test/NHSpecificTest/NH3848/Customer.cs @@ -3,24 +3,24 @@ namespace NHibernate.Test.NHSpecificTest.NH3848 { - public class Customer - { - public virtual Guid Id { get; set; } - public virtual ISet Orders { get; set; } - public virtual ISet Companies { get; set; } - public virtual string Name { get; set; } + public class Customer + { + public virtual Guid Id { get; set; } + public virtual ISet Orders { get; set; } + public virtual ISet Companies { get; set; } + public virtual string Name { get; set; } - public Customer() - { - Orders = new HashSet(); + public Customer() + { + Orders = new HashSet(); Companies = new HashSet(); - } + } - public virtual void AddOrder(Order order) - { - Orders.Add(order); - order.Customer = this; - } + public virtual void AddOrder(Order order) + { + Orders.Add(order); + order.Customer = this; + } public virtual void AddCompany(Company company) { diff --git a/src/NHibernate.Test/NHSpecificTest/NH3848/TestFixture.cs b/src/NHibernate.Test/NHSpecificTest/NH3848/Fixture.cs similarity index 59% rename from src/NHibernate.Test/NHSpecificTest/NH3848/TestFixture.cs rename to src/NHibernate.Test/NHSpecificTest/NH3848/Fixture.cs index 529a69d0104..51ad8e0ed1c 100644 --- a/src/NHibernate.Test/NHSpecificTest/NH3848/TestFixture.cs +++ b/src/NHibernate.Test/NHSpecificTest/NH3848/Fixture.cs @@ -4,7 +4,6 @@ using System.Data; using System.Linq; using System.Linq.Expressions; -using System.Threading.Tasks; using NHibernate.Cache; using NHibernate.Cfg; using NHibernate.Cfg.MappingSchema; @@ -13,7 +12,8 @@ namespace NHibernate.Test.NHSpecificTest.NH3848 { - public abstract class TestFixture : TestCaseMappingByCode + [TestFixture] + public abstract class Fixture : TestCaseMappingByCode { protected Customer Customer1; protected Customer Customer2; @@ -167,27 +167,6 @@ public virtual void ChildCollectionsFromLeftOuterJoinWithOnClauseRestrictionOnCo secondSession.Dispose(); } - [Test] - public virtual async Task ChildCollectionsFromLeftOuterJoinWithOnClauseRestrictionOnCollectionShouldNotBeInSecondLevelCacheAsync() - { - var firstSession = OpenSession(); - var customersWithOrderNumberEqualsTo2 = await GetCustomersByOrderNumberUsingOnClauseAsync(firstSession, OrderNumber).ConfigureAwait(false); - - var secondSession = OpenSession(); - var customers = await GetAllCustomersAsync(secondSession).ConfigureAwait(false); - - Assert.That(customersWithOrderNumberEqualsTo2.Single(n => n.Id == Customer1.Id).Orders, Has.Count.EqualTo(Customer1.Orders.Count(n => n.Number == OrderNumber))); - Assert.That(customersWithOrderNumberEqualsTo2.Single(n => n.Id == Customer2.Id).Orders, Has.Count.EqualTo(Customer2.Orders.Count(n => n.Number == OrderNumber))); - Assert.That(customersWithOrderNumberEqualsTo2.Single(n => n.Id == Customer3.Id).Orders, Has.Count.EqualTo(Customer3.Orders.Count(n => n.Number == OrderNumber))); - - Assert.That(customers.Single(n => n.Id == Customer1.Id).Orders, Has.Count.EqualTo(Customer1.Orders.Count)); - Assert.That(customers.Single(n => n.Id == Customer2.Id).Orders, Has.Count.EqualTo(Customer2.Orders.Count)); - Assert.That(customers.Single(n => n.Id == Customer3.Id).Orders, Has.Count.EqualTo(Customer3.Orders.Count)); - - firstSession.Dispose(); - secondSession.Dispose(); - } - [Test] public void ChildCollectionsFromLeftOuterJoinWithWhereClauseRestrictionOnCollectionShouldNotBeInSecondLevelCache() { @@ -208,26 +187,6 @@ public void ChildCollectionsFromLeftOuterJoinWithWhereClauseRestrictionOnCollect secondSession.Dispose(); } - [Test] - public async Task ChildCollectionsFromLeftOuterJoinWithWhereClauseRestrictionOnCollectionShouldNotBeInSecondLevelCacheAsync() - { - var firstSession = OpenSession(); - var customersWithOrderNumberEqualsTo2 = await GetCustomersByOrderNumberUsingWhereClauseAsync(firstSession, OrderNumber).ConfigureAwait(false); - - var secondSession = OpenSession(); - var customers = await GetAllCustomersAsync(secondSession).ConfigureAwait(false); - - Assert.That(customersWithOrderNumberEqualsTo2.Single(n => n.Id == Customer1.Id).Orders, Has.Count.EqualTo(Customer1.Orders.Count(n => n.Number == OrderNumber))); - Assert.That(customersWithOrderNumberEqualsTo2.Single(n => n.Id == Customer2.Id).Orders, Has.Count.EqualTo(Customer2.Orders.Count(n => n.Number == OrderNumber))); - - Assert.That(customers.Single(n => n.Id == Customer3.Id).Orders, Has.Count.EqualTo(Customer3.Orders.Count)); - Assert.That(customers.Single(n => n.Id == Customer1.Id).Orders, Has.Count.EqualTo(Customer1.Orders.Count)); - Assert.That(customers.Single(n => n.Id == Customer2.Id).Orders, Has.Count.EqualTo(Customer2.Orders.Count)); - - firstSession.Dispose(); - secondSession.Dispose(); - } - [Test] public void ChildCollectionsEagerFetchedShouldBeInSecondLevelCache() { @@ -245,36 +204,6 @@ public void ChildCollectionsEagerFetchedShouldBeInSecondLevelCache() var secondSession = OpenSession(); var customers = GetAllCustomers(secondSession); - - Assert.That(customersWithOrderNumberEqualsTo2.Single(n => n.Id == Customer1.Id).Orders, Has.Count.EqualTo(Customer1.Orders.Count)); - Assert.That(customersWithOrderNumberEqualsTo2.Single(n => n.Id == Customer2.Id).Orders, Has.Count.EqualTo(Customer2.Orders.Count)); - - Assert.That(customers.Single(n => n.Id == Customer3.Id).Orders, Has.Count.EqualTo(Customer3.Orders.Count)); - Assert.That(customers.Single(n => n.Id == Customer1.Id).Orders, Has.Count.EqualTo(Customer1.Orders.Count)); - Assert.That(customers.Single(n => n.Id == Customer2.Id).Orders, Has.Count.EqualTo(Customer2.Orders.Count)); - - firstSession.Dispose(); - secondSession.Dispose(); - } - - [Test] - public async Task ChildCollectionsEagerFetchedShouldBeInSecondLevelCacheAsync() - { - var firstSession = OpenSession(); - var customersWithOrderNumberEqualsTo2 = await GetCustomersWithOrdersEagerLoadedAsync(firstSession).ConfigureAwait(false); - - using (var session = OpenSession()) - using (IDbCommand cmd = session.Connection.CreateCommand()) - { - cmd.CommandText = "DELETE FROM Orders;"; - cmd.ExecuteNonQuery(); - cmd.Connection.Close(); - } - - var secondSession = OpenSession(); - var customers = await GetAllCustomersAsync(secondSession).ConfigureAwait(false); - - Assert.That(customersWithOrderNumberEqualsTo2.Single(n => n.Id == Customer1.Id).Orders, Has.Count.EqualTo(Customer1.Orders.Count)); Assert.That(customersWithOrderNumberEqualsTo2.Single(n => n.Id == Customer2.Id).Orders, Has.Count.EqualTo(Customer2.Orders.Count)); @@ -310,30 +239,6 @@ public void ChildCollectionsFromLeftOuterJoinWithWhereClauseRestrictionOnRootSho secondSession.Dispose(); } - [Test] - public async Task ChildCollectionsFromLeftOuterJoinWithWhereClauseRestrictionOnRootShouldBeInSecondLevelCacheAsync() - { - var firstSession = OpenSession(); - var customersWithOrderNumberEqualsTo2 = await GetCustomersByNameUsingWhereClauseAsync(firstSession, "First Customer").ConfigureAwait(false); - - using (var session = OpenSession()) - using (IDbCommand cmd = session.Connection.CreateCommand()) - { - cmd.CommandText = "DELETE FROM Orders;"; - cmd.ExecuteNonQuery(); - cmd.Connection.Close(); - } - - var secondSession = OpenSession(); - var customers = await secondSession.GetAsync(Customer1.Id).ConfigureAwait(false); - - Assert.That(customersWithOrderNumberEqualsTo2.Single(n => n.Id == Customer1.Id).Orders, Has.Count.EqualTo(Customer1.Orders.Count)); - Assert.That(customers.Orders, Has.Count.EqualTo(Customer1.Orders.Count)); - - firstSession.Dispose(); - secondSession.Dispose(); - } - [Test] public void ChildCollectionsFromLeftOuterJoinShouldBeInSecondLevelCacheIfQueryContainsSubqueryWithRestrictionOnLeftOuterJoin() { @@ -361,34 +266,6 @@ public void ChildCollectionsFromLeftOuterJoinShouldBeInSecondLevelCacheIfQueryCo secondSession.Dispose(); } - [Test] - public async Task ChildCollectionsFromLeftOuterJoinShouldBeInSecondLevelCacheIfQueryContainsSubqueryWithRestrictionOnLeftOuterJoinAsync() - { - var firstSession = OpenSession(); - var customersWithOrderNumberEqualsTo2 = await GetCustomersByOrderNumberUsingSubqueriesAndByNameUsingWhereClauseAsync(firstSession, OrderNumber, Customer1.Name) - .ConfigureAwait(false); - - var secondSession = OpenSession(); - var customers = await GetAllCustomersAsync(secondSession).ConfigureAwait(false); - - Assert.That(customersWithOrderNumberEqualsTo2.Single(n => n.Id == Customer1.Id).Orders, Has.Count.EqualTo(Customer1.Orders.Count)); - - using (var thirdSession = OpenSession()) - using (IDbCommand cmd = thirdSession.Connection.CreateCommand()) - { - cmd.CommandText = "DELETE FROM Orders;"; - cmd.ExecuteNonQuery(); - cmd.Connection.Close(); - } - - Assert.That(customers.Single(n => n.Id == Customer1.Id).Orders, Has.Count.EqualTo(Customer1.Orders.Count)); - Assert.That(customers.Single(n => n.Id == Customer2.Id).Orders, Has.Count.EqualTo(0)); - Assert.That(customers.Single(n => n.Id == Customer3.Id).Orders, Has.Count.EqualTo(0)); - - firstSession.Dispose(); - secondSession.Dispose(); - } - [Test] public virtual void ChildCollectionsFromLeftOuterJoinOnlyWithRestrictionShouldNotBeIn2LvlCache() { @@ -434,51 +311,6 @@ public virtual void ChildCollectionsFromLeftOuterJoinOnlyWithRestrictionShouldNo secondSession.Dispose(); } - [Test] - public virtual async Task ChildCollectionsFromLeftOuterJoinOnlyWithRestrictionShouldNotBeIn2LvlCacheAsync() - { - var firstSession = OpenSession(); - var customersWithOrderNumberEqualsTo2AndCompanies = await GetCustomersWithCompaniesByOrderNumberUsingOnClauseAsync(firstSession, OrderNumber).ConfigureAwait(false); - - using (var session = OpenSession()) - using (IDbCommand cmd = session.Connection.CreateCommand()) - { - cmd.CommandText = "DELETE FROM Companies;"; - cmd.ExecuteNonQuery(); - cmd.Connection.Close(); - } - - using (var session = OpenSession()) - using (IDbCommand cmd = session.Connection.CreateCommand()) - { - cmd.CommandText = "DELETE FROM Orders;"; - cmd.ExecuteNonQuery(); - cmd.Connection.Close(); - } - - var secondSession = OpenSession(); - var customers = await GetAllCustomersAsync(secondSession).ConfigureAwait(false); - - Assert.That(customersWithOrderNumberEqualsTo2AndCompanies.First(n => n.Id == Customer1.Id).Orders, Has.Count.EqualTo(Customer1.Orders.Count(n => n.Number == OrderNumber))); - Assert.That(customersWithOrderNumberEqualsTo2AndCompanies.First(n => n.Id == Customer2.Id).Orders, Has.Count.EqualTo(Customer2.Orders.Count(n => n.Number == OrderNumber))); - Assert.That(customersWithOrderNumberEqualsTo2AndCompanies.First(n => n.Id == Customer3.Id).Orders, Has.Count.EqualTo(Customer3.Orders.Count(n => n.Number == OrderNumber))); - - Assert.That(customersWithOrderNumberEqualsTo2AndCompanies.First(n => n.Id == Customer1.Id).Companies, Has.Count.EqualTo(Customer1.Companies.Count())); - Assert.That(customersWithOrderNumberEqualsTo2AndCompanies.First(n => n.Id == Customer2.Id).Companies, Has.Count.EqualTo(Customer2.Companies.Count())); - Assert.That(customersWithOrderNumberEqualsTo2AndCompanies.First(n => n.Id == Customer3.Id).Companies, Has.Count.EqualTo(Customer3.Companies.Count())); - - Assert.That(customers.Single(n => n.Id == Customer1.Id).Orders, Has.Count.EqualTo(0)); - Assert.That(customers.Single(n => n.Id == Customer2.Id).Orders, Has.Count.EqualTo(0)); - Assert.That(customers.Single(n => n.Id == Customer3.Id).Orders, Has.Count.EqualTo(0)); - - Assert.That(customers.Single(n => n.Id == Customer1.Id).Companies, Has.Count.EqualTo(Customer1.Companies.Count)); - Assert.That(customers.Single(n => n.Id == Customer2.Id).Companies, Has.Count.EqualTo(Customer2.Companies.Count)); - Assert.That(customers.Single(n => n.Id == Customer3.Id).Companies, Has.Count.EqualTo(Customer3.Companies.Count)); - - firstSession.Dispose(); - secondSession.Dispose(); - } - protected void ClearSecondLevelCacheFor(System.Type entity) { var entityName = entity.FullName; @@ -504,18 +336,11 @@ protected void ClearCollectionCache(Expression> pathToCo protected abstract IList GetCustomersWithFetchedOrdersWithoutRestrictions(ISession session); protected abstract IList GetCustomersWithOrdersEagerLoaded(ISession session); - protected abstract Task> GetCustomersWithOrdersEagerLoadedAsync(ISession session); protected abstract IList GetCustomersByOrderNumberUsingOnClause(ISession session, int orderNumber); - protected abstract Task> GetCustomersByOrderNumberUsingOnClauseAsync(ISession session, int orderNumber); protected abstract IList GetCustomersByOrderNumberUsingWhereClause(ISession session, int orderNumber); - protected abstract Task> GetCustomersByOrderNumberUsingWhereClauseAsync(ISession session, int orderNumber); protected abstract IList GetCustomersByNameUsingWhereClause(ISession session, string customerName); - protected abstract Task> GetCustomersByNameUsingWhereClauseAsync(ISession session, string customerName); protected abstract IList GetCustomersByOrderNumberUsingSubqueriesAndByNameUsingWhereClause(ISession session, int orderNumber, string customerName); - protected abstract Task> GetCustomersByOrderNumberUsingSubqueriesAndByNameUsingWhereClauseAsync(ISession session, int orderNumber, string customerName); protected abstract IList GetCustomersWithCompaniesByOrderNumberUsingOnClause(ISession session, int orderNumber); - protected abstract Task> GetCustomersWithCompaniesByOrderNumberUsingOnClauseAsync(ISession session, int orderNumber); protected abstract IList GetAllCustomers(ISession session); - protected abstract Task> GetAllCustomersAsync(ISession session); } } diff --git a/src/NHibernate.Test/NHSpecificTest/NH3848/Order.cs b/src/NHibernate.Test/NHSpecificTest/NH3848/Order.cs index a875ad79c1e..946e98bdeba 100644 --- a/src/NHibernate.Test/NHSpecificTest/NH3848/Order.cs +++ b/src/NHibernate.Test/NHSpecificTest/NH3848/Order.cs @@ -2,10 +2,10 @@ namespace NHibernate.Test.NHSpecificTest.NH3848 { - public class Order - { - public virtual Guid Id { get; set; } - public virtual int Number { get; set; } - public virtual Customer Customer { get; set; } - } + public class Order + { + public virtual Guid Id { get; set; } + public virtual int Number { get; set; } + public virtual Customer Customer { get; set; } + } } diff --git a/src/NHibernate.Test/NHSpecificTest/NH3848/QueryOverTestFixture.cs b/src/NHibernate.Test/NHSpecificTest/NH3848/QueryOverTestFixture.cs index 4032a2e885b..f7b1ad1ba1a 100644 --- a/src/NHibernate.Test/NHSpecificTest/NH3848/QueryOverTestFixture.cs +++ b/src/NHibernate.Test/NHSpecificTest/NH3848/QueryOverTestFixture.cs @@ -1,61 +1,40 @@ using System.Collections.Generic; -using System.Threading.Tasks; using NHibernate.Criterion; using NHibernate.SqlCommand; using NHibernate.Transform; namespace NHibernate.Test.NHSpecificTest.NH3848 { - public class QueryOverTestFixture : TestFixture - { - protected override IList GetCustomersWithFetchedOrdersWithoutRestrictions(ISession session) - { - Order ordersAlias = null; - return session.QueryOver() - .JoinAlias(n => n.Orders, - () => ordersAlias, - JoinType.LeftOuterJoin) - .TransformUsing(new DistinctRootEntityResultTransformer()) - .List(); - } - - protected override IList GetCustomersWithOrdersEagerLoaded(ISession session) - { - return session.QueryOver() - .Fetch(SelectMode.Fetch, n => n.Orders) + public class QueryOverTestFixture : Fixture + { + protected override IList GetCustomersWithFetchedOrdersWithoutRestrictions(ISession session) + { + Order ordersAlias = null; + return session.QueryOver() + .JoinAlias(n => n.Orders, + () => ordersAlias, + JoinType.LeftOuterJoin) .TransformUsing(new DistinctRootEntityResultTransformer()) - .List(); - } + .List(); + } - protected override async Task> GetCustomersWithOrdersEagerLoadedAsync(ISession session) + protected override IList GetCustomersWithOrdersEagerLoaded(ISession session) { - return await session.QueryOver() + return session.QueryOver() .Fetch(SelectMode.Fetch, n => n.Orders) .TransformUsing(new DistinctRootEntityResultTransformer()) - .ListAsync().ConfigureAwait(false); + .List(); } protected override IList GetCustomersByOrderNumberUsingOnClause(ISession session, int orderNumber) - { - Order ordersAlias = null; - return session.QueryOver() - .JoinAlias(n => n.Orders, - () => ordersAlias, - JoinType.LeftOuterJoin, - Restrictions.Eq("Number", orderNumber)) - .List(); - } - - protected override async Task> GetCustomersByOrderNumberUsingOnClauseAsync(ISession session, int orderNumber) { Order ordersAlias = null; - - return await session.QueryOver() + return session.QueryOver() .JoinAlias(n => n.Orders, () => ordersAlias, JoinType.LeftOuterJoin, Restrictions.Eq("Number", orderNumber)) - .ListAsync().ConfigureAwait(false); + .List(); } protected override IList GetCustomersWithCompaniesByOrderNumberUsingOnClause(ISession session, int orderNumber) @@ -74,85 +53,29 @@ protected override IList GetCustomersWithCompaniesByOrderNumberUsingOn .List(); } - protected override async Task> GetCustomersWithCompaniesByOrderNumberUsingOnClauseAsync(ISession session, int orderNumber) - { - Order ordersAlias = null; - Company companiesAlias = null; - - return await session.QueryOver() - .JoinAlias(n => n.Orders, - () => ordersAlias, - JoinType.LeftOuterJoin, - Restrictions.Eq("Number", orderNumber)) - .JoinAlias(n => n.Companies, - () => companiesAlias, - JoinType.LeftOuterJoin) - .ListAsync().ConfigureAwait(false); - } - protected override IList GetCustomersByOrderNumberUsingWhereClause(ISession session, int orderNumber) - { - Order ordersAlias = null; - return session.QueryOver() - .JoinQueryOver(n => n.Orders, () => ordersAlias, JoinType.LeftOuterJoin) - .Where(Restrictions.Eq("Number", orderNumber)) - .List(); - } - - protected override async Task> GetCustomersByOrderNumberUsingWhereClauseAsync(ISession session, int orderNumber) { Order ordersAlias = null; - return await session.QueryOver() + return session.QueryOver() .JoinQueryOver(n => n.Orders, () => ordersAlias, JoinType.LeftOuterJoin) - .Where(Restrictions.Eq("Number", orderNumber)) - .ListAsync().ConfigureAwait(false); + .Where(Restrictions.Eq("Number", orderNumber)) + .List(); } protected override IList GetCustomersByNameUsingWhereClause(ISession session, string customerName) - { - Order ordersAlias = null; - return session.QueryOver() - .JoinAlias(n => n.Orders, - () => ordersAlias, - JoinType.LeftOuterJoin - ) - .Where(Restrictions.Eq("Name", customerName)) - .TransformUsing(new DistinctRootEntityResultTransformer()) - .List(); - - } - - protected override async Task> GetCustomersByNameUsingWhereClauseAsync(ISession session, string customerName) { Order ordersAlias = null; - - return await session.QueryOver() + return session.QueryOver() .JoinAlias(n => n.Orders, () => ordersAlias, JoinType.LeftOuterJoin ) .Where(Restrictions.Eq("Name", customerName)) .TransformUsing(new DistinctRootEntityResultTransformer()) - .ListAsync().ConfigureAwait(false); + .List(); } protected override IList GetCustomersByOrderNumberUsingSubqueriesAndByNameUsingWhereClause(ISession session, int orderNumber, string customerName) - { - Order ordersAlias = null; - Order ordersAlias2 = null; - var subquery = QueryOver.Of() - .JoinAlias(n => n.Orders, () => ordersAlias, JoinType.LeftOuterJoin, Restrictions.Eq("Number", orderNumber)) - .Select(n => n.Id); - - return session.QueryOver() - .JoinAlias(n => n.Orders, () => ordersAlias2, JoinType.LeftOuterJoin) - .WithSubquery.WhereProperty(n => n.Id).In(subquery) - .Where(Restrictions.Eq("Name", customerName)) - .TransformUsing(new DistinctRootEntityResultTransformer()) - .List(); - } - - protected override async Task> GetCustomersByOrderNumberUsingSubqueriesAndByNameUsingWhereClauseAsync(ISession session, int orderNumber, string customerName) { Order ordersAlias = null; Order ordersAlias2 = null; @@ -160,22 +83,17 @@ protected override async Task> GetCustomersByOrderNumberUsingSub .JoinAlias(n => n.Orders, () => ordersAlias, JoinType.LeftOuterJoin, Restrictions.Eq("Number", orderNumber)) .Select(n => n.Id); - return await session.QueryOver() + return session.QueryOver() .JoinAlias(n => n.Orders, () => ordersAlias2, JoinType.LeftOuterJoin) .WithSubquery.WhereProperty(n => n.Id).In(subquery) .Where(Restrictions.Eq("Name", customerName)) .TransformUsing(new DistinctRootEntityResultTransformer()) - .ListAsync().ConfigureAwait(false); + .List(); } protected override IList GetAllCustomers(ISession session) - { - return session.QueryOver().List(); - } - - protected override async Task> GetAllCustomersAsync(ISession session) { - return await session.QueryOver().ListAsync().ConfigureAwait(false); + return session.QueryOver().List(); } } } diff --git a/src/NHibernate/Async/Engine/Loading/CollectionLoadContext.cs b/src/NHibernate/Async/Engine/Loading/CollectionLoadContext.cs index 66333b8ffbf..cdd1e673b48 100644 --- a/src/NHibernate/Async/Engine/Loading/CollectionLoadContext.cs +++ b/src/NHibernate/Async/Engine/Loading/CollectionLoadContext.cs @@ -33,7 +33,7 @@ public partial class CollectionLoadContext /// complete. /// /// The persister for which to complete loading. - /// List of collections that won't be added to the cache. + /// Indicates if collcetions can be put in cache /// A cancellation token that can be used to cancel the work public async Task EndLoadingCollectionsAsync(ICollectionPersister persister, HashSet uncacheableCollections, CancellationToken cancellationToken) { @@ -94,8 +94,7 @@ public async Task EndLoadingCollectionsAsync(ICollectionPersister persister, Has } } - private async Task EndLoadingCollectionsAsync(ICollectionPersister persister, IList matchedCollectionEntries, - HashSet uncacheableCollections, CancellationToken cancellationToken) + private async Task EndLoadingCollectionsAsync(ICollectionPersister persister, IList matchedCollectionEntries, HashSet uncacheableCollections, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); if (matchedCollectionEntries == null || matchedCollectionEntries.Count == 0) @@ -117,7 +116,7 @@ private async Task EndLoadingCollectionsAsync(ICollectionPersister persister, IL for (int i = 0; i < count; i++) { await (EndLoadingCollectionAsync(matchedCollectionEntries[i], persister, - data => cacheBatcher.AddToBatch(persister, data), uncacheableCollections, cancellationToken)).ConfigureAwait(false); + data => cacheBatcher.AddToBatch(persister, data), uncacheableCollections, cancellationToken)).ConfigureAwait(false); } await (cacheBatcher.ExecuteBatchAsync(cancellationToken)).ConfigureAwait(false); @@ -128,7 +127,7 @@ private async Task EndLoadingCollectionsAsync(ICollectionPersister persister, IL } private async Task EndLoadingCollectionAsync(LoadingCollectionEntry lce, ICollectionPersister persister, - Action cacheBatchingHandler, HashSet uncacheableCollections, CancellationToken cancellationToken) + Action cacheBatchingHandler, HashSet uncacheableCollections, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); if (log.IsDebugEnabled()) @@ -163,9 +162,9 @@ private async Task EndLoadingCollectionAsync(LoadingCollectionEntry lce, ICollec ce.PostInitialize(lce.Collection, persistenceContext); } - bool addToCache = hasNoQueuedOperations && persister.HasCache && - (uncacheableCollections == null || !uncacheableCollections.Contains(lce.Persister.Role)) && - session.CacheMode.HasFlag(CacheMode.Put) && !ce.IsDoremove; // and this is not a forced initialization during flush + bool addToCache = hasNoQueuedOperations && persister.HasCache && + session.CacheMode.HasFlag(CacheMode.Put) && + (uncacheableCollections == null || !uncacheableCollections.Contains(lce.Persister.Role)) && !ce.IsDoremove; // and this is not a forced initialization during flush if (addToCache) { @@ -193,7 +192,7 @@ private async Task EndLoadingCollectionAsync(LoadingCollectionEntry lce, ICollec /// The action for handling cache batching /// A cancellation token that can be used to cancel the work private async Task AddCollectionToCacheAsync(LoadingCollectionEntry lce, ICollectionPersister persister, - Action cacheBatchingHandler, CancellationToken cancellationToken) + Action cacheBatchingHandler, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); ISessionImplementor session = LoadContext.PersistenceContext.Session; @@ -248,8 +247,8 @@ private async Task AddCollectionToCacheAsync(LoadingCollectionEntry lce, ICollec else { bool put = await (persister.Cache.PutAsync(cacheKey, persister.CacheEntryStructure.Structure(entry), - session.Timestamp, version, versionComparator, - factory.Settings.IsMinimalPutsEnabled && session.CacheMode != CacheMode.Refresh, cancellationToken)).ConfigureAwait(false); + session.Timestamp, version, versionComparator, + factory.Settings.IsMinimalPutsEnabled && session.CacheMode != CacheMode.Refresh, cancellationToken)).ConfigureAwait(false); if (put && factory.Statistics.IsStatisticsEnabled) { diff --git a/src/NHibernate/Async/Impl/MultiCriteriaImpl.cs b/src/NHibernate/Async/Impl/MultiCriteriaImpl.cs index 66e293c3b08..0ac4d998f8f 100644 --- a/src/NHibernate/Async/Impl/MultiCriteriaImpl.cs +++ b/src/NHibernate/Async/Impl/MultiCriteriaImpl.cs @@ -200,7 +200,7 @@ private async Task GetResultsFromDatabaseAsync(IList results, CancellationToken for (int i = 0; i < loaders.Count; i++) { CriteriaLoader loader = loaders[i]; - await (loader.InitializeEntitiesAndCollectionsAsync(hydratedObjects[i], reader, session, session.DefaultReadOnly, cancellationToken: cancellationToken)).ConfigureAwait(false); + await (loader.InitializeEntitiesAndCollectionsAsync(hydratedObjects[i], reader, session, session.DefaultReadOnly, null, parameters[i].UncacheableCollections, cancellationToken)).ConfigureAwait(false); if (createSubselects[i]) { diff --git a/src/NHibernate/Async/Impl/MultiQueryImpl.cs b/src/NHibernate/Async/Impl/MultiQueryImpl.cs index 93cf48d59ad..8ac041b2919 100644 --- a/src/NHibernate/Async/Impl/MultiQueryImpl.cs +++ b/src/NHibernate/Async/Impl/MultiQueryImpl.cs @@ -172,7 +172,7 @@ protected async Task> DoListAsync(CancellationToken cancellationTok ITranslator translator = translators[i]; QueryParameters parameter = parameters[i]; - await (translator.Loader.InitializeEntitiesAndCollectionsAsync(hydratedObjects[i], reader, session, false, cancellationToken: cancellationToken)).ConfigureAwait(false); + await (translator.Loader.InitializeEntitiesAndCollectionsAsync(hydratedObjects[i], reader, session, false, null, parameters[i].UncacheableCollections, cancellationToken)).ConfigureAwait(false); if (createSubselects[i]) { diff --git a/src/NHibernate/Async/Loader/Criteria/CriteriaLoader.cs b/src/NHibernate/Async/Loader/Criteria/CriteriaLoader.cs index ab3d62dc218..4765a911a99 100644 --- a/src/NHibernate/Async/Loader/Criteria/CriteriaLoader.cs +++ b/src/NHibernate/Async/Loader/Criteria/CriteriaLoader.cs @@ -36,7 +36,7 @@ public Task ListAsync(ISessionImplementor session, CancellationToken canc } try { - return ListAsync(session, translator.GetQueryParameters(), querySpaces, cancellationToken); + return ListAsync(session, translator.GetQueryParameters(), querySpaces, cancellationToken); } catch (Exception ex) { diff --git a/src/NHibernate/Async/Loader/Loader.cs b/src/NHibernate/Async/Loader/Loader.cs index 63b700f354c..4cc8e2cecd2 100644 --- a/src/NHibernate/Async/Loader/Loader.cs +++ b/src/NHibernate/Async/Loader/Loader.cs @@ -129,7 +129,7 @@ protected async Task LoadSingleRowAsync(DbDataReader resultSet, ISession queryParameters.NamedParameters); } - await (InitializeEntitiesAndCollectionsAsync(hydratedObjects, resultSet, session, queryParameters.IsReadOnly(session), null, queryParameters.UncacheableCollections,cancellationToken: cancellationToken)).ConfigureAwait(false); + await (InitializeEntitiesAndCollectionsAsync(hydratedObjects, resultSet, session, queryParameters.IsReadOnly(session), null, queryParameters.UncacheableCollections, cancellationToken)).ConfigureAwait(false); await (session.PersistenceContext.InitializeNonLazyCollectionsAsync(cancellationToken)).ConfigureAwait(false); return result; } @@ -321,7 +321,7 @@ private async Task DoQueryAsync(ISessionImplementor session, QueryParamet session.Batcher.CloseCommand(st, rs); } - await (InitializeEntitiesAndCollectionsAsync(hydratedObjects, rs, session, queryParameters.IsReadOnly(session), null, queryParameters.UncacheableCollections, cancellationToken: cancellationToken)).ConfigureAwait(false); + await (InitializeEntitiesAndCollectionsAsync(hydratedObjects, rs, session, queryParameters.IsReadOnly(session), null, queryParameters.UncacheableCollections, cancellationToken)).ConfigureAwait(false); if (createSubselects) { @@ -406,8 +406,7 @@ internal async Task InitializeEntitiesAndCollectionsAsync( } } - private static Task EndCollectionLoadAsync(object resultSetId, ISessionImplementor session, ICollectionPersister collectionPersister, - HashSet uncacheableCollections, CancellationToken cancellationToken) + private static Task EndCollectionLoadAsync(object resultSetId, ISessionImplementor session, ICollectionPersister collectionPersister, HashSet uncacheableCollections, CancellationToken cancellationToken) { if (cancellationToken.IsCancellationRequested) { diff --git a/src/NHibernate/Async/Multi/QueryBatchItemBase.cs b/src/NHibernate/Async/Multi/QueryBatchItemBase.cs index 7f0e8a4ca54..0915a2c37c3 100644 --- a/src/NHibernate/Async/Multi/QueryBatchItemBase.cs +++ b/src/NHibernate/Async/Multi/QueryBatchItemBase.cs @@ -134,7 +134,7 @@ private async Task InitializeEntitiesAndCollectionsAsync(DbDataReader reader, Li continue; await (queryInfo.Loader.InitializeEntitiesAndCollectionsAsync( hydratedObjects[i], reader, Session, queryInfo.Parameters.IsReadOnly(Session), - queryInfo.CacheBatcher, queryInfo.Parameters.UncacheableCollections, cancellationToken)).ConfigureAwait(false); + queryInfo.CacheBatcher, cancellationToken: cancellationToken)).ConfigureAwait(false); } } } diff --git a/src/NHibernate/Engine/Loading/CollectionLoadContext.cs b/src/NHibernate/Engine/Loading/CollectionLoadContext.cs index 03e786e123c..7275d865700 100644 --- a/src/NHibernate/Engine/Loading/CollectionLoadContext.cs +++ b/src/NHibernate/Engine/Loading/CollectionLoadContext.cs @@ -145,8 +145,8 @@ public IPersistentCollection GetLoadingCollection(ICollectionPersister persister /// complete. /// /// The persister for which to complete loading. - /// Indicates if collcetions can be put in cache - public void EndLoadingCollections(ICollectionPersister persister, HashSet uncacheableCollections) + /// Indicates if collcetions can be put in cache + public void EndLoadingCollections(ICollectionPersister persister, HashSet uncacheableCollections) { if (!loadContexts.HasLoadingCollectionEntries && (localLoadingCollectionKeys.Count == 0)) { @@ -191,7 +191,7 @@ public void EndLoadingCollections(ICollectionPersister persister, HashSet matchedCollectionEntries, HashSet uncacheableCollections) + private void EndLoadingCollections(ICollectionPersister persister, IList matchedCollectionEntries, HashSet uncacheableCollections) { if (matchedCollectionEntries == null || matchedCollectionEntries.Count == 0) { @@ -225,7 +225,7 @@ private void EndLoadingCollections(ICollectionPersister persister, IList cacheBatcher.AddToBatch(persister, data), uncacheableCollections); + data => cacheBatcher.AddToBatch(persister, data), uncacheableCollections); } cacheBatcher.ExecuteBatch(); @@ -236,7 +236,7 @@ private void EndLoadingCollections(ICollectionPersister persister, IList cacheBatchingHandler, HashSet uncacheableCollections) + Action cacheBatchingHandler, HashSet uncacheableCollections) { if (log.IsDebugEnabled()) { @@ -299,7 +299,7 @@ private void EndLoadingCollection(LoadingCollectionEntry lce, ICollectionPersist /// The persister /// The action for handling cache batching private void AddCollectionToCache(LoadingCollectionEntry lce, ICollectionPersister persister, - Action cacheBatchingHandler) + Action cacheBatchingHandler) { ISessionImplementor session = LoadContext.PersistenceContext.Session; ISessionFactoryImplementor factory = session.Factory; @@ -353,8 +353,8 @@ private void AddCollectionToCache(LoadingCollectionEntry lce, ICollectionPersist else { bool put = persister.Cache.Put(cacheKey, persister.CacheEntryStructure.Structure(entry), - session.Timestamp, version, versionComparator, - factory.Settings.IsMinimalPutsEnabled && session.CacheMode != CacheMode.Refresh); + session.Timestamp, version, versionComparator, + factory.Settings.IsMinimalPutsEnabled && session.CacheMode != CacheMode.Refresh); if (put && factory.Statistics.IsStatisticsEnabled) { diff --git a/src/NHibernate/Engine/QueryParameters.cs b/src/NHibernate/Engine/QueryParameters.cs index 10ed698e686..46e252c1568 100644 --- a/src/NHibernate/Engine/QueryParameters.cs +++ b/src/NHibernate/Engine/QueryParameters.cs @@ -40,25 +40,25 @@ public QueryParameters(IType[] positionalParameterTypes, object[] postionalParam : this(positionalParameterTypes, postionalParameterValues, namedParameters, null, null, false, false, false, null, null, collectionKeys, null, null) {} public QueryParameters(IType[] positionalParameterTypes, object[] positionalParameterValues, IDictionary lockModes, RowSelection rowSelection, - bool isReadOnlyInitialized, bool readOnly, bool cacheable, string cacheRegion, string comment, bool isLookupByNaturalKey, IResultTransformer transformer) + bool isReadOnlyInitialized, bool readOnly, bool cacheable, string cacheRegion, string comment, bool isLookupByNaturalKey, IResultTransformer transformer) : this(positionalParameterTypes, positionalParameterValues, null, lockModes, rowSelection, isReadOnlyInitialized, readOnly, cacheable, cacheRegion, comment, null, transformer, null) { NaturalKeyLookup = isLookupByNaturalKey; } public QueryParameters(IDictionary namedParameters, IDictionary lockModes, RowSelection rowSelection, bool isReadOnlyInitialized, - bool readOnly, bool cacheable, string cacheRegion, string comment, bool isLookupByNaturalKey, IResultTransformer transformer, HashSet uncacheableCollections) + bool readOnly, bool cacheable, string cacheRegion, string comment, bool isLookupByNaturalKey, IResultTransformer transformer, HashSet uncacheableCollections) : this( TypeHelper.EmptyTypeArray, Array.Empty(), namedParameters, lockModes, rowSelection, isReadOnlyInitialized, readOnly, cacheable, cacheRegion, comment, null, - transformer, uncacheableCollections) + transformer, uncacheableCollections) { // used by CriteriaTranslator NaturalKeyLookup = isLookupByNaturalKey; } public QueryParameters(IType[] positionalParameterTypes, object[] positionalParameterValues, IDictionary namedParameters, - IDictionary lockModes, RowSelection rowSelection, bool isReadOnlyInitialized, bool readOnly, bool cacheable, string cacheRegion, - string comment, object[] collectionKeys, IResultTransformer transformer, HashSet uncacheableCollections) + IDictionary lockModes, RowSelection rowSelection, bool isReadOnlyInitialized, bool readOnly, bool cacheable, string cacheRegion, + string comment, object[] collectionKeys, IResultTransformer transformer, HashSet uncacheableCollections) { PositionalParameterTypes = positionalParameterTypes ?? Array.Empty(); PositionalParameterValues = positionalParameterValues ?? Array.Empty(); @@ -76,8 +76,8 @@ public QueryParameters(IType[] positionalParameterTypes, object[] positionalPara } public QueryParameters(IType[] positionalParameterTypes, object[] positionalParameterValues, IDictionary namedParameters, - IDictionary lockModes, RowSelection rowSelection, bool isReadOnlyInitialized, bool readOnly, bool cacheable, string cacheRegion, - string comment, object[] collectionKeys, object optionalObject, string optionalEntityName, object optionalId, IResultTransformer transformer) + IDictionary lockModes, RowSelection rowSelection, bool isReadOnlyInitialized, bool readOnly, bool cacheable, string cacheRegion, + string comment, object[] collectionKeys, object optionalObject, string optionalEntityName, object optionalId, IResultTransformer transformer) : this( positionalParameterTypes, positionalParameterValues, namedParameters, lockModes, rowSelection, isReadOnlyInitialized, readOnly, cacheable, cacheRegion, comment, collectionKeys, transformer, null) @@ -140,10 +140,10 @@ public bool HasRowSelection public bool Callable { get; set; } - /// - /// Indicates if we can add loaded child collections to second lvl cache. - /// - public HashSet UncacheableCollections { get; set; } + /// + /// Indicates if we can add loaded child collections to second lvl cache. + /// + public HashSet UncacheableCollections { get; set; } public bool ReadOnly { @@ -202,7 +202,7 @@ public void ValidateParameters() if (typesLength != valuesLength) { throw new QueryException("Number of positional parameter types (" + typesLength - + ") does not match number of positional parameter values (" + valuesLength + ")"); + + ") does not match number of positional parameter values (" + valuesLength + ")"); } } diff --git a/src/NHibernate/Impl/MultiCriteriaImpl.cs b/src/NHibernate/Impl/MultiCriteriaImpl.cs index f9bc6bb0e5d..40d5712b256 100644 --- a/src/NHibernate/Impl/MultiCriteriaImpl.cs +++ b/src/NHibernate/Impl/MultiCriteriaImpl.cs @@ -273,7 +273,7 @@ private void GetResultsFromDatabase(IList results) for (int i = 0; i < loaders.Count; i++) { CriteriaLoader loader = loaders[i]; - loader.InitializeEntitiesAndCollections(hydratedObjects[i], reader, session, session.DefaultReadOnly, null, parameters[i].UncacheableCollections); + loader.InitializeEntitiesAndCollections(hydratedObjects[i], reader, session, session.DefaultReadOnly, null, parameters[i].UncacheableCollections); if (createSubselects[i]) { diff --git a/src/NHibernate/Impl/MultiQueryImpl.cs b/src/NHibernate/Impl/MultiQueryImpl.cs index 982c4b43f46..fc103d7f7cf 100644 --- a/src/NHibernate/Impl/MultiQueryImpl.cs +++ b/src/NHibernate/Impl/MultiQueryImpl.cs @@ -616,7 +616,7 @@ protected List DoList() ITranslator translator = translators[i]; QueryParameters parameter = parameters[i]; - translator.Loader.InitializeEntitiesAndCollections(hydratedObjects[i], reader, session, false, null, parameters[i].UncacheableCollections); + translator.Loader.InitializeEntitiesAndCollections(hydratedObjects[i], reader, session, false, null, parameters[i].UncacheableCollections); if (createSubselects[i]) { diff --git a/src/NHibernate/Loader/Criteria/CriteriaQueryTranslator.cs b/src/NHibernate/Loader/Criteria/CriteriaQueryTranslator.cs index 8d52fbeca67..e018b3ed6c6 100644 --- a/src/NHibernate/Loader/Criteria/CriteriaQueryTranslator.cs +++ b/src/NHibernate/Loader/Criteria/CriteriaQueryTranslator.cs @@ -139,7 +139,7 @@ public QueryParameters GetQueryParameters() selection.FetchSize = rootCriteria.FetchSize; var lockModes = new Dictionary(); - var uncacheableCollections = new HashSet(); + var uncacheableCollections = new HashSet(); foreach (KeyValuePair me in rootCriteria.LockModes) { @@ -922,10 +922,10 @@ private void CreateSubQuerySpaces() var subQueries = rootCriteria.IterateExpressionEntries() - .Select(x => x.Criterion) - .OfType() - .Select(x => x.Criteria) - .OfType(); + .Select(x => x.Criterion) + .OfType() + .Select(x => x.Criteria) + .OfType(); foreach (var criteriaImpl in subQueries) { diff --git a/src/NHibernate/Loader/Loader.cs b/src/NHibernate/Loader/Loader.cs index 910364f3705..8914c220a44 100644 --- a/src/NHibernate/Loader/Loader.cs +++ b/src/NHibernate/Loader/Loader.cs @@ -315,7 +315,7 @@ protected object LoadSingleRow(DbDataReader resultSet, ISessionImplementor sessi queryParameters.NamedParameters); } - InitializeEntitiesAndCollections(hydratedObjects, resultSet, session, queryParameters.IsReadOnly(session), null, queryParameters.UncacheableCollections); + InitializeEntitiesAndCollections(hydratedObjects, resultSet, session, queryParameters.IsReadOnly(session), null, queryParameters.UncacheableCollections); session.PersistenceContext.InitializeNonLazyCollections(); return result; } @@ -517,7 +517,7 @@ private IList DoQuery(ISessionImplementor session, QueryParameters queryParamete session.Batcher.CloseCommand(st, rs); } - InitializeEntitiesAndCollections(hydratedObjects, rs, session, queryParameters.IsReadOnly(session), null, queryParameters.UncacheableCollections); + InitializeEntitiesAndCollections(hydratedObjects, rs, session, queryParameters.IsReadOnly(session), null, queryParameters.UncacheableCollections); if (createSubselects) { @@ -615,7 +615,7 @@ internal void InitializeEntitiesAndCollections( //during loading //TODO: or we could do this polymorphically, and have two // different operations implemented differently for arrays - EndCollectionLoad(resultSetId, session, collectionPersisters[i], uncacheableCollections); + EndCollectionLoad(resultSetId, session, collectionPersisters[i], uncacheableCollections); } } } @@ -666,13 +666,13 @@ internal void InitializeEntitiesAndCollections( //the entities, since we might call hashCode() on the elements //TODO: or we could do this polymorphically, and have two // different operations implemented differently for arrays - EndCollectionLoad(resultSetId, session, collectionPersisters[i], uncacheableCollections); + EndCollectionLoad(resultSetId, session, collectionPersisters[i], uncacheableCollections); } } } } - private static void EndCollectionLoad(object resultSetId, ISessionImplementor session, ICollectionPersister collectionPersister, HashSet uncacheableCollections) + private static void EndCollectionLoad(object resultSetId, ISessionImplementor session, ICollectionPersister collectionPersister, HashSet uncacheableCollections) { //this is a query and we are loading multiple instances of the same collection role session.PersistenceContext.LoadContexts.GetCollectionLoadContext((DbDataReader)resultSetId).EndLoadingCollections( @@ -849,7 +849,7 @@ internal void HandleEmptyCollections(object[] keys, object resultSetId, ISession if (Log.IsDebugEnabled()) { Log.Debug("result set contains (possibly empty) collection: {0}", - MessageHelper.CollectionInfoString(collectionPersisters[j], keys[i])); + MessageHelper.CollectionInfoString(collectionPersisters[j], keys[i])); } session.PersistenceContext.LoadContexts.GetCollectionLoadContext((DbDataReader)resultSetId).GetLoadingCollection( collectionPersisters[j], keys[i]); From 2f1b4e4231d71d943591b51e7ea48b6a066aa4b0 Mon Sep 17 00:00:00 2001 From: "Iwanow, Mihail (EXT)" Date: Thu, 15 Nov 2018 17:27:46 +0100 Subject: [PATCH 07/20] Mark methods without uncacheableCollections parameter as obsolete --- .../Engine/Loading/CollectionLoadContext.cs | 33 ++++++++++++++++--- .../Async/Loader/Criteria/CriteriaLoader.cs | 2 +- .../Engine/Loading/CollectionLoadContext.cs | 28 +++++++++++++--- src/NHibernate/Engine/QueryParameters.cs | 27 ++++++++++++--- src/NHibernate/Impl/CriteriaImpl.cs | 12 +++---- .../Loader/Criteria/CriteriaLoader.cs | 2 +- .../Criteria/CriteriaQueryTranslator.cs | 8 ++--- src/NHibernate/Loader/Loader.cs | 2 +- 8 files changed, 86 insertions(+), 28 deletions(-) diff --git a/src/NHibernate/Async/Engine/Loading/CollectionLoadContext.cs b/src/NHibernate/Async/Engine/Loading/CollectionLoadContext.cs index cdd1e673b48..89fd5433502 100644 --- a/src/NHibernate/Async/Engine/Loading/CollectionLoadContext.cs +++ b/src/NHibernate/Async/Engine/Loading/CollectionLoadContext.cs @@ -33,7 +33,24 @@ public partial class CollectionLoadContext /// complete. /// /// The persister for which to complete loading. - /// Indicates if collcetions can be put in cache + /// A cancellation token that can be used to cancel the work + [Obsolete("Please use EndLoadingCollections(ICollectionPersister, HashSet) instead.")] + public Task EndLoadingCollectionsAsync(ICollectionPersister persister, CancellationToken cancellationToken) + { + if (cancellationToken.IsCancellationRequested) + { + return Task.FromCanceled(cancellationToken); + } + return EndLoadingCollectionsAsync(persister, null, cancellationToken); + } + + /// + /// Finish the process of collection-loading for this bound result set. Mainly this + /// involves cleaning up resources and notifying the collections that loading is + /// complete. + /// + /// The persister for which to complete loading. + /// Indicates if collections can be put in cache /// A cancellation token that can be used to cancel the work public async Task EndLoadingCollectionsAsync(ICollectionPersister persister, HashSet uncacheableCollections, CancellationToken cancellationToken) { @@ -115,8 +132,11 @@ private async Task EndLoadingCollectionsAsync(ICollectionPersister persister, IL var cacheBatcher = new CacheBatcher(LoadContext.PersistenceContext.Session); for (int i = 0; i < count; i++) { - await (EndLoadingCollectionAsync(matchedCollectionEntries[i], persister, - data => cacheBatcher.AddToBatch(persister, data), uncacheableCollections, cancellationToken)).ConfigureAwait(false); + await (EndLoadingCollectionAsync( + matchedCollectionEntries[i], + persister, + data => cacheBatcher.AddToBatch(persister, data), + uncacheableCollections, cancellationToken)).ConfigureAwait(false); } await (cacheBatcher.ExecuteBatchAsync(cancellationToken)).ConfigureAwait(false); @@ -126,8 +146,11 @@ private async Task EndLoadingCollectionsAsync(ICollectionPersister persister, IL } } - private async Task EndLoadingCollectionAsync(LoadingCollectionEntry lce, ICollectionPersister persister, - Action cacheBatchingHandler, HashSet uncacheableCollections, CancellationToken cancellationToken) + private async Task EndLoadingCollectionAsync( + LoadingCollectionEntry lce, + ICollectionPersister persister, + Action cacheBatchingHandler, + HashSet uncacheableCollections, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); if (log.IsDebugEnabled()) diff --git a/src/NHibernate/Async/Loader/Criteria/CriteriaLoader.cs b/src/NHibernate/Async/Loader/Criteria/CriteriaLoader.cs index 4765a911a99..ab3d62dc218 100644 --- a/src/NHibernate/Async/Loader/Criteria/CriteriaLoader.cs +++ b/src/NHibernate/Async/Loader/Criteria/CriteriaLoader.cs @@ -36,7 +36,7 @@ public Task ListAsync(ISessionImplementor session, CancellationToken canc } try { - return ListAsync(session, translator.GetQueryParameters(), querySpaces, cancellationToken); + return ListAsync(session, translator.GetQueryParameters(), querySpaces, cancellationToken); } catch (Exception ex) { diff --git a/src/NHibernate/Engine/Loading/CollectionLoadContext.cs b/src/NHibernate/Engine/Loading/CollectionLoadContext.cs index 7275d865700..1a7c6b3f6f5 100644 --- a/src/NHibernate/Engine/Loading/CollectionLoadContext.cs +++ b/src/NHibernate/Engine/Loading/CollectionLoadContext.cs @@ -145,7 +145,19 @@ public IPersistentCollection GetLoadingCollection(ICollectionPersister persister /// complete. /// /// The persister for which to complete loading. - /// Indicates if collcetions can be put in cache + [Obsolete("Please use EndLoadingCollections(ICollectionPersister, HashSet) instead.")] + public void EndLoadingCollections(ICollectionPersister persister) + { + EndLoadingCollections(persister, null); + } + + /// + /// Finish the process of collection-loading for this bound result set. Mainly this + /// involves cleaning up resources and notifying the collections that loading is + /// complete. + /// + /// The persister for which to complete loading. + /// Indicates if collections can be put in cache public void EndLoadingCollections(ICollectionPersister persister, HashSet uncacheableCollections) { if (!loadContexts.HasLoadingCollectionEntries && (localLoadingCollectionKeys.Count == 0)) @@ -224,8 +236,11 @@ private void EndLoadingCollections(ICollectionPersister persister, IList cacheBatcher.AddToBatch(persister, data), uncacheableCollections); + EndLoadingCollection( + matchedCollectionEntries[i], + persister, + data => cacheBatcher.AddToBatch(persister, data), + uncacheableCollections); } cacheBatcher.ExecuteBatch(); @@ -235,8 +250,11 @@ private void EndLoadingCollections(ICollectionPersister persister, IList cacheBatchingHandler, HashSet uncacheableCollections) + private void EndLoadingCollection( + LoadingCollectionEntry lce, + ICollectionPersister persister, + Action cacheBatchingHandler, + HashSet uncacheableCollections) { if (log.IsDebugEnabled()) { diff --git a/src/NHibernate/Engine/QueryParameters.cs b/src/NHibernate/Engine/QueryParameters.cs index 46e252c1568..06fe1078eee 100644 --- a/src/NHibernate/Engine/QueryParameters.cs +++ b/src/NHibernate/Engine/QueryParameters.cs @@ -40,14 +40,22 @@ public QueryParameters(IType[] positionalParameterTypes, object[] postionalParam : this(positionalParameterTypes, postionalParameterValues, namedParameters, null, null, false, false, false, null, null, collectionKeys, null, null) {} public QueryParameters(IType[] positionalParameterTypes, object[] positionalParameterValues, IDictionary lockModes, RowSelection rowSelection, - bool isReadOnlyInitialized, bool readOnly, bool cacheable, string cacheRegion, string comment, bool isLookupByNaturalKey, IResultTransformer transformer) + bool isReadOnlyInitialized, bool readOnly, bool cacheable, string cacheRegion, string comment, bool isLookupByNaturalKey, IResultTransformer transformer) : this(positionalParameterTypes, positionalParameterValues, null, lockModes, rowSelection, isReadOnlyInitialized, readOnly, cacheable, cacheRegion, comment, null, transformer, null) { NaturalKeyLookup = isLookupByNaturalKey; } + [Obsolete("Please use QueryParameters(IDictionary, IDictionary, " + + "RowSelection, bool, bool, bool, string, string, bool, IResultTransformer, HashSet) instead.")] public QueryParameters(IDictionary namedParameters, IDictionary lockModes, RowSelection rowSelection, bool isReadOnlyInitialized, - bool readOnly, bool cacheable, string cacheRegion, string comment, bool isLookupByNaturalKey, IResultTransformer transformer, HashSet uncacheableCollections) + bool readOnly, bool cacheable, string cacheRegion, string comment, bool isLookupByNaturalKey, IResultTransformer transformer) + : this( + namedParameters, lockModes, rowSelection, isReadOnlyInitialized, readOnly, cacheable, cacheRegion, comment, isLookupByNaturalKey, + transformer, null) {} + + public QueryParameters(IDictionary namedParameters, IDictionary lockModes, RowSelection rowSelection, bool isReadOnlyInitialized, + bool readOnly, bool cacheable, string cacheRegion, string comment, bool isLookupByNaturalKey, IResultTransformer transformer, HashSet uncacheableCollections) : this( TypeHelper.EmptyTypeArray, Array.Empty(), namedParameters, lockModes, rowSelection, isReadOnlyInitialized, readOnly, cacheable, cacheRegion, comment, null, transformer, uncacheableCollections) @@ -56,9 +64,18 @@ public QueryParameters(IDictionary namedParameters, IDiction NaturalKeyLookup = isLookupByNaturalKey; } + [Obsolete("Please use QueryParameters(IType[], object[], IDictionary, " + + "IDictionary, RowSelection, bool, bool, bool, string, string, object[], IResultTransformer, HashSet) instead.")] public QueryParameters(IType[] positionalParameterTypes, object[] positionalParameterValues, IDictionary namedParameters, - IDictionary lockModes, RowSelection rowSelection, bool isReadOnlyInitialized, bool readOnly, bool cacheable, string cacheRegion, - string comment, object[] collectionKeys, IResultTransformer transformer, HashSet uncacheableCollections) + IDictionary lockModes, RowSelection rowSelection, bool isReadOnlyInitialized, bool readOnly, bool cacheable, string cacheRegion, + string comment, object[] collectionKeys, IResultTransformer transformer) + : this( + TypeHelper.EmptyTypeArray, Array.Empty(), namedParameters, lockModes, rowSelection, isReadOnlyInitialized, readOnly, cacheable, cacheRegion, comment, null, + transformer, null) {} + + public QueryParameters(IType[] positionalParameterTypes, object[] positionalParameterValues, IDictionary namedParameters, + IDictionary lockModes, RowSelection rowSelection, bool isReadOnlyInitialized, bool readOnly, bool cacheable, string cacheRegion, + string comment, object[] collectionKeys, IResultTransformer transformer, HashSet uncacheableCollections) { PositionalParameterTypes = positionalParameterTypes ?? Array.Empty(); PositionalParameterValues = positionalParameterValues ?? Array.Empty(); @@ -141,7 +158,7 @@ public bool HasRowSelection public bool Callable { get; set; } /// - /// Indicates if we can add loaded child collections to second lvl cache. + /// Indicates if we can add loaded child collections to second level cache. /// public HashSet UncacheableCollections { get; set; } diff --git a/src/NHibernate/Impl/CriteriaImpl.cs b/src/NHibernate/Impl/CriteriaImpl.cs index 76a8c8efb2a..a86f5062fb6 100644 --- a/src/NHibernate/Impl/CriteriaImpl.cs +++ b/src/NHibernate/Impl/CriteriaImpl.cs @@ -699,7 +699,7 @@ internal Subcriteria(CriteriaImpl root, ICriteria parent, string path, string al this.joinType = joinType; this.withClause = withClause; JoinEntityName = joinEntityName; - hasRestrictions = withClause != null; + hasRestrictions = withClause != null; root.subcriteriaList.Add(this); @@ -733,10 +733,10 @@ public string Path get { return path; } } - public bool HasRestrictions - { - get { return hasRestrictions; } - } + public bool HasRestrictions + { + get { return hasRestrictions; } + } public ICriteria Parent { @@ -777,7 +777,7 @@ public ICriteria SetLockMode(LockMode lockMode) public ICriteria Add(ICriterion expression) { - hasRestrictions = true; + hasRestrictions = true; root.Add(this, expression); return this; } diff --git a/src/NHibernate/Loader/Criteria/CriteriaLoader.cs b/src/NHibernate/Loader/Criteria/CriteriaLoader.cs index 8d2da586db5..658896a5096 100644 --- a/src/NHibernate/Loader/Criteria/CriteriaLoader.cs +++ b/src/NHibernate/Loader/Criteria/CriteriaLoader.cs @@ -94,7 +94,7 @@ protected override bool IsChildFetchEntity(int i) public IList List(ISessionImplementor session) { - return List(session, translator.GetQueryParameters(), querySpaces); + return List(session, translator.GetQueryParameters(), querySpaces); } protected override IResultTransformer ResolveResultTransformer(IResultTransformer resultTransformer) diff --git a/src/NHibernate/Loader/Criteria/CriteriaQueryTranslator.cs b/src/NHibernate/Loader/Criteria/CriteriaQueryTranslator.cs index e018b3ed6c6..ab957e2f8c3 100644 --- a/src/NHibernate/Loader/Criteria/CriteriaQueryTranslator.cs +++ b/src/NHibernate/Loader/Criteria/CriteriaQueryTranslator.cs @@ -922,10 +922,10 @@ private void CreateSubQuerySpaces() var subQueries = rootCriteria.IterateExpressionEntries() - .Select(x => x.Criterion) - .OfType() - .Select(x => x.Criteria) - .OfType(); + .Select(x => x.Criterion) + .OfType() + .Select(x => x.Criteria) + .OfType(); foreach (var criteriaImpl in subQueries) { diff --git a/src/NHibernate/Loader/Loader.cs b/src/NHibernate/Loader/Loader.cs index 8914c220a44..ac3c24e0810 100644 --- a/src/NHibernate/Loader/Loader.cs +++ b/src/NHibernate/Loader/Loader.cs @@ -849,7 +849,7 @@ internal void HandleEmptyCollections(object[] keys, object resultSetId, ISession if (Log.IsDebugEnabled()) { Log.Debug("result set contains (possibly empty) collection: {0}", - MessageHelper.CollectionInfoString(collectionPersisters[j], keys[i])); + MessageHelper.CollectionInfoString(collectionPersisters[j], keys[i])); } session.PersistenceContext.LoadContexts.GetCollectionLoadContext((DbDataReader)resultSetId).GetLoadingCollection( collectionPersisters[j], keys[i]); From 772f4b7868b9f30f1d70271fa127ecf98e53f51e Mon Sep 17 00:00:00 2001 From: "Iwanow, Mihail (EXT)" Date: Thu, 15 Nov 2018 17:38:52 +0100 Subject: [PATCH 08/20] Reformat --- .../Engine/Loading/CollectionLoadContext.cs | 6 +++--- src/NHibernate/Engine/QueryParameters.cs | 16 ++++++++-------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/NHibernate/Engine/Loading/CollectionLoadContext.cs b/src/NHibernate/Engine/Loading/CollectionLoadContext.cs index 1a7c6b3f6f5..1829c089529 100644 --- a/src/NHibernate/Engine/Loading/CollectionLoadContext.cs +++ b/src/NHibernate/Engine/Loading/CollectionLoadContext.cs @@ -317,7 +317,7 @@ private void EndLoadingCollection( /// The persister /// The action for handling cache batching private void AddCollectionToCache(LoadingCollectionEntry lce, ICollectionPersister persister, - Action cacheBatchingHandler) + Action cacheBatchingHandler) { ISessionImplementor session = LoadContext.PersistenceContext.Session; ISessionFactoryImplementor factory = session.Factory; @@ -371,8 +371,8 @@ private void AddCollectionToCache(LoadingCollectionEntry lce, ICollectionPersist else { bool put = persister.Cache.Put(cacheKey, persister.CacheEntryStructure.Structure(entry), - session.Timestamp, version, versionComparator, - factory.Settings.IsMinimalPutsEnabled && session.CacheMode != CacheMode.Refresh); + session.Timestamp, version, versionComparator, + factory.Settings.IsMinimalPutsEnabled && session.CacheMode != CacheMode.Refresh); if (put && factory.Statistics.IsStatisticsEnabled) { diff --git a/src/NHibernate/Engine/QueryParameters.cs b/src/NHibernate/Engine/QueryParameters.cs index 06fe1078eee..7428d1f0235 100644 --- a/src/NHibernate/Engine/QueryParameters.cs +++ b/src/NHibernate/Engine/QueryParameters.cs @@ -40,7 +40,7 @@ public QueryParameters(IType[] positionalParameterTypes, object[] postionalParam : this(positionalParameterTypes, postionalParameterValues, namedParameters, null, null, false, false, false, null, null, collectionKeys, null, null) {} public QueryParameters(IType[] positionalParameterTypes, object[] positionalParameterValues, IDictionary lockModes, RowSelection rowSelection, - bool isReadOnlyInitialized, bool readOnly, bool cacheable, string cacheRegion, string comment, bool isLookupByNaturalKey, IResultTransformer transformer) + bool isReadOnlyInitialized, bool readOnly, bool cacheable, string cacheRegion, string comment, bool isLookupByNaturalKey, IResultTransformer transformer) : this(positionalParameterTypes, positionalParameterValues, null, lockModes, rowSelection, isReadOnlyInitialized, readOnly, cacheable, cacheRegion, comment, null, transformer, null) { NaturalKeyLookup = isLookupByNaturalKey; @@ -67,15 +67,15 @@ public QueryParameters(IDictionary namedParameters, IDiction [Obsolete("Please use QueryParameters(IType[], object[], IDictionary, " + "IDictionary, RowSelection, bool, bool, bool, string, string, object[], IResultTransformer, HashSet) instead.")] public QueryParameters(IType[] positionalParameterTypes, object[] positionalParameterValues, IDictionary namedParameters, - IDictionary lockModes, RowSelection rowSelection, bool isReadOnlyInitialized, bool readOnly, bool cacheable, string cacheRegion, - string comment, object[] collectionKeys, IResultTransformer transformer) + IDictionary lockModes, RowSelection rowSelection, bool isReadOnlyInitialized, bool readOnly, bool cacheable, string cacheRegion, + string comment, object[] collectionKeys, IResultTransformer transformer) : this( TypeHelper.EmptyTypeArray, Array.Empty(), namedParameters, lockModes, rowSelection, isReadOnlyInitialized, readOnly, cacheable, cacheRegion, comment, null, transformer, null) {} public QueryParameters(IType[] positionalParameterTypes, object[] positionalParameterValues, IDictionary namedParameters, - IDictionary lockModes, RowSelection rowSelection, bool isReadOnlyInitialized, bool readOnly, bool cacheable, string cacheRegion, - string comment, object[] collectionKeys, IResultTransformer transformer, HashSet uncacheableCollections) + IDictionary lockModes, RowSelection rowSelection, bool isReadOnlyInitialized, bool readOnly, bool cacheable, string cacheRegion, + string comment, object[] collectionKeys, IResultTransformer transformer, HashSet uncacheableCollections) { PositionalParameterTypes = positionalParameterTypes ?? Array.Empty(); PositionalParameterValues = positionalParameterValues ?? Array.Empty(); @@ -93,8 +93,8 @@ public QueryParameters(IType[] positionalParameterTypes, object[] positionalPara } public QueryParameters(IType[] positionalParameterTypes, object[] positionalParameterValues, IDictionary namedParameters, - IDictionary lockModes, RowSelection rowSelection, bool isReadOnlyInitialized, bool readOnly, bool cacheable, string cacheRegion, - string comment, object[] collectionKeys, object optionalObject, string optionalEntityName, object optionalId, IResultTransformer transformer) + IDictionary lockModes, RowSelection rowSelection, bool isReadOnlyInitialized, bool readOnly, bool cacheable, string cacheRegion, + string comment, object[] collectionKeys, object optionalObject, string optionalEntityName, object optionalId, IResultTransformer transformer) : this( positionalParameterTypes, positionalParameterValues, namedParameters, lockModes, rowSelection, isReadOnlyInitialized, readOnly, cacheable, cacheRegion, comment, collectionKeys, transformer, null) @@ -219,7 +219,7 @@ public void ValidateParameters() if (typesLength != valuesLength) { throw new QueryException("Number of positional parameter types (" + typesLength - + ") does not match number of positional parameter values (" + valuesLength + ")"); + + ") does not match number of positional parameter values (" + valuesLength + ")"); } } From 39350bbd54b911156829418ea4674efc7b326338 Mon Sep 17 00:00:00 2001 From: "Iwanow, Mihail (EXT)" Date: Fri, 16 Nov 2018 08:56:07 +0100 Subject: [PATCH 09/20] Fix mapping in tests for Oracle --- src/NHibernate.Test/Async/NHSpecificTest/NH3848/Fixture.cs | 2 +- src/NHibernate.Test/NHSpecificTest/NH3848/Fixture.cs | 2 +- .../Async/Engine/Loading/CollectionLoadContext.cs | 6 +++--- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/NHibernate.Test/Async/NHSpecificTest/NH3848/Fixture.cs b/src/NHibernate.Test/Async/NHSpecificTest/NH3848/Fixture.cs index c4e88457d77..698f564a5b8 100644 --- a/src/NHibernate.Test/Async/NHSpecificTest/NH3848/Fixture.cs +++ b/src/NHibernate.Test/Async/NHSpecificTest/NH3848/Fixture.cs @@ -69,7 +69,7 @@ protected override HbmMapping GetMappings() { rc.Table("Orders"); rc.Id(x => x.Id, m => m.Generator(Generators.GuidComb)); - rc.Property(x => x.Number); + rc.Property(x => x.Number, m => m.Column("`Number`")); rc.ManyToOne(x => x.Customer, m => m.Column("CustomerId")); rc.Cache(c => c.Usage(CacheUsage.ReadWrite)); }); diff --git a/src/NHibernate.Test/NHSpecificTest/NH3848/Fixture.cs b/src/NHibernate.Test/NHSpecificTest/NH3848/Fixture.cs index 51ad8e0ed1c..3401fe29e07 100644 --- a/src/NHibernate.Test/NHSpecificTest/NH3848/Fixture.cs +++ b/src/NHibernate.Test/NHSpecificTest/NH3848/Fixture.cs @@ -57,7 +57,7 @@ protected override HbmMapping GetMappings() { rc.Table("Orders"); rc.Id(x => x.Id, m => m.Generator(Generators.GuidComb)); - rc.Property(x => x.Number); + rc.Property(x => x.Number, m => m.Column("`Number`")); rc.ManyToOne(x => x.Customer, m => m.Column("CustomerId")); rc.Cache(c => c.Usage(CacheUsage.ReadWrite)); }); diff --git a/src/NHibernate/Async/Engine/Loading/CollectionLoadContext.cs b/src/NHibernate/Async/Engine/Loading/CollectionLoadContext.cs index 89fd5433502..1181482bf2c 100644 --- a/src/NHibernate/Async/Engine/Loading/CollectionLoadContext.cs +++ b/src/NHibernate/Async/Engine/Loading/CollectionLoadContext.cs @@ -215,7 +215,7 @@ private async Task EndLoadingCollectionAsync( /// The action for handling cache batching /// A cancellation token that can be used to cancel the work private async Task AddCollectionToCacheAsync(LoadingCollectionEntry lce, ICollectionPersister persister, - Action cacheBatchingHandler, CancellationToken cancellationToken) + Action cacheBatchingHandler, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); ISessionImplementor session = LoadContext.PersistenceContext.Session; @@ -270,8 +270,8 @@ private async Task AddCollectionToCacheAsync(LoadingCollectionEntry lce, ICollec else { bool put = await (persister.Cache.PutAsync(cacheKey, persister.CacheEntryStructure.Structure(entry), - session.Timestamp, version, versionComparator, - factory.Settings.IsMinimalPutsEnabled && session.CacheMode != CacheMode.Refresh, cancellationToken)).ConfigureAwait(false); + session.Timestamp, version, versionComparator, + factory.Settings.IsMinimalPutsEnabled && session.CacheMode != CacheMode.Refresh, cancellationToken)).ConfigureAwait(false); if (put && factory.Statistics.IsStatisticsEnabled) { From e4c72c8be8945fca8b050e166a45dfde0a4af984 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Delaporte?= <12201973+fredericdelaporte@users.noreply.github.com> Date: Fri, 16 Nov 2018 12:18:42 +0100 Subject: [PATCH 10/20] Fix Oracle failures --- .../Async/NHSpecificTest/NH3848/Fixture.cs | 10 +++++----- src/NHibernate.Test/NHSpecificTest/NH3848/Fixture.cs | 10 +++++----- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/NHibernate.Test/Async/NHSpecificTest/NH3848/Fixture.cs b/src/NHibernate.Test/Async/NHSpecificTest/NH3848/Fixture.cs index 698f564a5b8..67069915ee9 100644 --- a/src/NHibernate.Test/Async/NHSpecificTest/NH3848/Fixture.cs +++ b/src/NHibernate.Test/Async/NHSpecificTest/NH3848/Fixture.cs @@ -208,7 +208,7 @@ public async Task ChildCollectionsEagerFetchedShouldBeInSecondLevelCacheAsync() using (var session = OpenSession()) using (IDbCommand cmd = session.Connection.CreateCommand()) { - cmd.CommandText = "DELETE FROM Orders;"; + cmd.CommandText = "DELETE FROM Orders"; cmd.ExecuteNonQuery(); cmd.Connection.Close(); } @@ -236,7 +236,7 @@ public async Task ChildCollectionsFromLeftOuterJoinWithWhereClauseRestrictionOnR using (var session = OpenSession()) using (IDbCommand cmd = session.Connection.CreateCommand()) { - cmd.CommandText = "DELETE FROM Orders;"; + cmd.CommandText = "DELETE FROM Orders"; cmd.ExecuteNonQuery(); cmd.Connection.Close(); } @@ -265,7 +265,7 @@ public async Task ChildCollectionsFromLeftOuterJoinShouldBeInSecondLevelCacheIfQ using (var thirdSession = OpenSession()) using (IDbCommand cmd = thirdSession.Connection.CreateCommand()) { - cmd.CommandText = "DELETE FROM Orders;"; + cmd.CommandText = "DELETE FROM Orders"; cmd.ExecuteNonQuery(); cmd.Connection.Close(); } @@ -287,7 +287,7 @@ public virtual async Task ChildCollectionsFromLeftOuterJoinOnlyWithRestrictionSh using (var session = OpenSession()) using (IDbCommand cmd = session.Connection.CreateCommand()) { - cmd.CommandText = "DELETE FROM Orders;"; + cmd.CommandText = "DELETE FROM Orders"; cmd.ExecuteNonQuery(); cmd.Connection.Close(); } @@ -295,7 +295,7 @@ public virtual async Task ChildCollectionsFromLeftOuterJoinOnlyWithRestrictionSh using (var session = OpenSession()) using (IDbCommand cmd = session.Connection.CreateCommand()) { - cmd.CommandText = "DELETE FROM Companies;"; + cmd.CommandText = "DELETE FROM Companies"; cmd.ExecuteNonQuery(); cmd.Connection.Close(); } diff --git a/src/NHibernate.Test/NHSpecificTest/NH3848/Fixture.cs b/src/NHibernate.Test/NHSpecificTest/NH3848/Fixture.cs index 3401fe29e07..9a9f7550ead 100644 --- a/src/NHibernate.Test/NHSpecificTest/NH3848/Fixture.cs +++ b/src/NHibernate.Test/NHSpecificTest/NH3848/Fixture.cs @@ -196,7 +196,7 @@ public void ChildCollectionsEagerFetchedShouldBeInSecondLevelCache() using (var session = OpenSession()) using (IDbCommand cmd = session.Connection.CreateCommand()) { - cmd.CommandText = "DELETE FROM Orders;"; + cmd.CommandText = "DELETE FROM Orders"; cmd.ExecuteNonQuery(); cmd.Connection.Close(); } @@ -224,7 +224,7 @@ public void ChildCollectionsFromLeftOuterJoinWithWhereClauseRestrictionOnRootSho using (var session = OpenSession()) using (IDbCommand cmd = session.Connection.CreateCommand()) { - cmd.CommandText = "DELETE FROM Orders;"; + cmd.CommandText = "DELETE FROM Orders"; cmd.ExecuteNonQuery(); cmd.Connection.Close(); } @@ -253,7 +253,7 @@ public void ChildCollectionsFromLeftOuterJoinShouldBeInSecondLevelCacheIfQueryCo using (var thirdSession = OpenSession()) using (IDbCommand cmd = thirdSession.Connection.CreateCommand()) { - cmd.CommandText = "DELETE FROM Orders;"; + cmd.CommandText = "DELETE FROM Orders"; cmd.ExecuteNonQuery(); cmd.Connection.Close(); } @@ -275,7 +275,7 @@ public virtual void ChildCollectionsFromLeftOuterJoinOnlyWithRestrictionShouldNo using (var session = OpenSession()) using (IDbCommand cmd = session.Connection.CreateCommand()) { - cmd.CommandText = "DELETE FROM Orders;"; + cmd.CommandText = "DELETE FROM Orders"; cmd.ExecuteNonQuery(); cmd.Connection.Close(); } @@ -283,7 +283,7 @@ public virtual void ChildCollectionsFromLeftOuterJoinOnlyWithRestrictionShouldNo using (var session = OpenSession()) using (IDbCommand cmd = session.Connection.CreateCommand()) { - cmd.CommandText = "DELETE FROM Companies;"; + cmd.CommandText = "DELETE FROM Companies"; cmd.ExecuteNonQuery(); cmd.Connection.Close(); } From f05c212bcf227eb58356c6bffe031c606856b226 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Delaporte?= <12201973+fredericdelaporte@users.noreply.github.com> Date: Fri, 16 Nov 2018 12:27:57 +0100 Subject: [PATCH 11/20] Reformat tests for better readability on GitHub And modernize some constructs --- .../NH3848/CriteriaTestFixture.cs | 93 ++++++++----- .../Async/NHSpecificTest/NH3848/Fixture.cs | 96 ++++++++++---- .../NH3848/QueryOverTestFixture.cs | 125 +++++++++++------- .../NH3848/CriteriaTestFixture.cs | 93 ++++++++----- .../NHSpecificTest/NH3848/Customer.cs | 10 +- .../NHSpecificTest/NH3848/Fixture.cs | 94 +++++++++---- .../NH3848/QueryOverTestFixture.cs | 125 +++++++++++------- 7 files changed, 411 insertions(+), 225 deletions(-) diff --git a/src/NHibernate.Test/Async/NHSpecificTest/NH3848/CriteriaTestFixture.cs b/src/NHibernate.Test/Async/NHSpecificTest/NH3848/CriteriaTestFixture.cs index 6db16313fa4..3cd343205ff 100644 --- a/src/NHibernate.Test/Async/NHSpecificTest/NH3848/CriteriaTestFixture.cs +++ b/src/NHibernate.Test/Async/NHSpecificTest/NH3848/CriteriaTestFixture.cs @@ -23,10 +23,12 @@ public class CriteriaTestFixtureAsync : FixtureAsync { try { - return session.CreateCriteria() - .CreateAlias("Orders", "order", JoinType.LeftOuterJoin) - .SetResultTransformer(new DistinctRootEntityResultTransformer()) - .ListAsync(cancellationToken); + return + session + .CreateCriteria() + .CreateAlias("Orders", "order", JoinType.LeftOuterJoin) + .SetResultTransformer(new DistinctRootEntityResultTransformer()) + .ListAsync(cancellationToken); } catch (System.Exception ex) { @@ -38,10 +40,12 @@ public class CriteriaTestFixtureAsync : FixtureAsync { try { - return session.CreateCriteria() - .Fetch("Orders") - .SetResultTransformer(new DistinctRootEntityResultTransformer()) - .ListAsync(cancellationToken); + return + session + .CreateCriteria() + .Fetch("Orders") + .SetResultTransformer(new DistinctRootEntityResultTransformer()) + .ListAsync(cancellationToken); } catch (System.Exception ex) { @@ -53,9 +57,11 @@ public class CriteriaTestFixtureAsync : FixtureAsync { try { - return session.CreateCriteria() - .CreateAlias("Orders", "order", JoinType.LeftOuterJoin, Restrictions.Eq("Number", orderNumber)) - .ListAsync(cancellationToken); + return + session + .CreateCriteria() + .CreateAlias("Orders", "order", JoinType.LeftOuterJoin, Restrictions.Eq("Number", orderNumber)) + .ListAsync(cancellationToken); } catch (System.Exception ex) { @@ -63,14 +69,18 @@ public class CriteriaTestFixtureAsync : FixtureAsync } } - protected override Task> GetCustomersWithCompaniesByOrderNumberUsingOnClauseAsync(ISession session, int orderNumber, CancellationToken cancellationToken = default(CancellationToken)) + protected override Task> GetCustomersWithCompaniesByOrderNumberUsingOnClauseAsync( + ISession session, + int orderNumber, CancellationToken cancellationToken = default(CancellationToken)) { try { - return session.CreateCriteria() - .CreateAlias("Orders", "order", JoinType.LeftOuterJoin, Restrictions.Eq("Number", orderNumber)) - .CreateAlias("Companies", "company", JoinType.LeftOuterJoin) - .ListAsync(cancellationToken); + return + session + .CreateCriteria() + .CreateAlias("Orders", "order", JoinType.LeftOuterJoin, Restrictions.Eq("Number", orderNumber)) + .CreateAlias("Companies", "company", JoinType.LeftOuterJoin) + .ListAsync(cancellationToken); } catch (System.Exception ex) { @@ -82,11 +92,13 @@ public class CriteriaTestFixtureAsync : FixtureAsync { try { - return session.CreateCriteria() - .CreateCriteria("Orders", "Order", JoinType.LeftOuterJoin) - .Add(Restrictions.Eq("Number", orderNumber)) - .SetResultTransformer(new DistinctRootEntityResultTransformer()) - .ListAsync(cancellationToken); + return + session + .CreateCriteria() + .CreateCriteria("Orders", "Order", JoinType.LeftOuterJoin) + .Add(Restrictions.Eq("Number", orderNumber)) + .SetResultTransformer(new DistinctRootEntityResultTransformer()) + .ListAsync(cancellationToken); } catch (System.Exception ex) { @@ -98,11 +110,13 @@ public class CriteriaTestFixtureAsync : FixtureAsync { try { - return session.CreateCriteria() - .CreateAlias("Orders", "order", JoinType.LeftOuterJoin) - .Add(Restrictions.Eq("Name", "First Customer")) - .SetResultTransformer(new DistinctRootEntityResultTransformer()) - .ListAsync(cancellationToken); + return + session + .CreateCriteria() + .CreateAlias("Orders", "order", JoinType.LeftOuterJoin) + .Add(Restrictions.Eq("Name", "First Customer")) + .SetResultTransformer(new DistinctRootEntityResultTransformer()) + .ListAsync(cancellationToken); } catch (System.Exception ex) { @@ -110,20 +124,27 @@ public class CriteriaTestFixtureAsync : FixtureAsync } } - protected override Task> GetCustomersByOrderNumberUsingSubqueriesAndByNameUsingWhereClauseAsync(ISession session, int orderNumber, string customerName, CancellationToken cancellationToken = default(CancellationToken)) + protected override Task> GetCustomersByOrderNumberUsingSubqueriesAndByNameUsingWhereClauseAsync( + ISession session, + int orderNumber, + string customerName, CancellationToken cancellationToken = default(CancellationToken)) { try { - var detachedCriteria = DetachedCriteria.For() - .CreateAlias("Orders", "order", JoinType.LeftOuterJoin, Restrictions.Eq("Number", orderNumber)) - .SetProjection(Projections.Id()); + var detachedCriteria = + DetachedCriteria + .For() + .CreateAlias("Orders", "order", JoinType.LeftOuterJoin, Restrictions.Eq("Number", orderNumber)) + .SetProjection(Projections.Id()); - return session.CreateCriteria() - .CreateAlias("Orders", "order1", JoinType.LeftOuterJoin) - .Add(Subqueries.PropertyIn("Id", detachedCriteria)) - .Add(Restrictions.Eq("Name", customerName)) - .SetResultTransformer(new DistinctRootEntityResultTransformer()) - .ListAsync(cancellationToken); + return + session + .CreateCriteria() + .CreateAlias("Orders", "order1", JoinType.LeftOuterJoin) + .Add(Subqueries.PropertyIn("Id", detachedCriteria)) + .Add(Restrictions.Eq("Name", customerName)) + .SetResultTransformer(new DistinctRootEntityResultTransformer()) + .ListAsync(cancellationToken); } catch (System.Exception ex) { diff --git a/src/NHibernate.Test/Async/NHSpecificTest/NH3848/Fixture.cs b/src/NHibernate.Test/Async/NHSpecificTest/NH3848/Fixture.cs index 67069915ee9..56d2ecb8bb5 100644 --- a/src/NHibernate.Test/Async/NHSpecificTest/NH3848/Fixture.cs +++ b/src/NHibernate.Test/Async/NHSpecificTest/NH3848/Fixture.cs @@ -167,9 +167,15 @@ public virtual async Task ChildCollectionsFromLeftOuterJoinWithOnClauseRestricti var secondSession = OpenSession(); var customers = await (GetAllCustomersAsync(secondSession)); - Assert.That(customersWithOrderNumberEqualsTo2.Single(n => n.Id == Customer1.Id).Orders, Has.Count.EqualTo(Customer1.Orders.Count(n => n.Number == OrderNumber))); - Assert.That(customersWithOrderNumberEqualsTo2.Single(n => n.Id == Customer2.Id).Orders, Has.Count.EqualTo(Customer2.Orders.Count(n => n.Number == OrderNumber))); - Assert.That(customersWithOrderNumberEqualsTo2.Single(n => n.Id == Customer3.Id).Orders, Has.Count.EqualTo(Customer3.Orders.Count(n => n.Number == OrderNumber))); + Assert.That( + customersWithOrderNumberEqualsTo2.Single(n => n.Id == Customer1.Id).Orders, + Has.Count.EqualTo(Customer1.Orders.Count(n => n.Number == OrderNumber))); + Assert.That( + customersWithOrderNumberEqualsTo2.Single(n => n.Id == Customer2.Id).Orders, + Has.Count.EqualTo(Customer2.Orders.Count(n => n.Number == OrderNumber))); + Assert.That( + customersWithOrderNumberEqualsTo2.Single(n => n.Id == Customer3.Id).Orders, + Has.Count.EqualTo(Customer3.Orders.Count(n => n.Number == OrderNumber))); Assert.That(customers.Single(n => n.Id == Customer1.Id).Orders, Has.Count.EqualTo(Customer1.Orders.Count)); Assert.That(customers.Single(n => n.Id == Customer2.Id).Orders, Has.Count.EqualTo(Customer2.Orders.Count)); @@ -188,8 +194,12 @@ public async Task ChildCollectionsFromLeftOuterJoinWithWhereClauseRestrictionOnC var secondSession = OpenSession(); var customers = await (GetAllCustomersAsync(secondSession)); - Assert.That(customersWithOrderNumberEqualsTo2.Single(n => n.Id == Customer1.Id).Orders, Has.Count.EqualTo(Customer1.Orders.Count(n => n.Number == OrderNumber))); - Assert.That(customersWithOrderNumberEqualsTo2.Single(n => n.Id == Customer2.Id).Orders, Has.Count.EqualTo(Customer2.Orders.Count(n => n.Number == OrderNumber))); + Assert.That( + customersWithOrderNumberEqualsTo2.Single(n => n.Id == Customer1.Id).Orders, + Has.Count.EqualTo(Customer1.Orders.Count(n => n.Number == OrderNumber))); + Assert.That( + customersWithOrderNumberEqualsTo2.Single(n => n.Id == Customer2.Id).Orders, + Has.Count.EqualTo(Customer2.Orders.Count(n => n.Number == OrderNumber))); Assert.That(customers.Single(n => n.Id == Customer3.Id).Orders, Has.Count.EqualTo(Customer3.Orders.Count)); Assert.That(customers.Single(n => n.Id == Customer1.Id).Orders, Has.Count.EqualTo(Customer1.Orders.Count)); @@ -216,8 +226,12 @@ public async Task ChildCollectionsEagerFetchedShouldBeInSecondLevelCacheAsync() var secondSession = OpenSession(); var customers = await (GetAllCustomersAsync(secondSession)); - Assert.That(customersWithOrderNumberEqualsTo2.Single(n => n.Id == Customer1.Id).Orders, Has.Count.EqualTo(Customer1.Orders.Count)); - Assert.That(customersWithOrderNumberEqualsTo2.Single(n => n.Id == Customer2.Id).Orders, Has.Count.EqualTo(Customer2.Orders.Count)); + Assert.That( + customersWithOrderNumberEqualsTo2.Single(n => n.Id == Customer1.Id).Orders, + Has.Count.EqualTo(Customer1.Orders.Count)); + Assert.That( + customersWithOrderNumberEqualsTo2.Single(n => n.Id == Customer2.Id).Orders, + Has.Count.EqualTo(Customer2.Orders.Count)); Assert.That(customers.Single(n => n.Id == Customer3.Id).Orders, Has.Count.EqualTo(Customer3.Orders.Count)); Assert.That(customers.Single(n => n.Id == Customer1.Id).Orders, Has.Count.EqualTo(Customer1.Orders.Count)); @@ -244,7 +258,9 @@ public async Task ChildCollectionsFromLeftOuterJoinWithWhereClauseRestrictionOnR var secondSession = OpenSession(); var customers = await (secondSession.GetAsync(Customer1.Id)); - Assert.That(customersWithOrderNumberEqualsTo2.Single(n => n.Id == Customer1.Id).Orders, Has.Count.EqualTo(Customer1.Orders.Count)); + Assert.That( + customersWithOrderNumberEqualsTo2.Single(n => n.Id == Customer1.Id).Orders, + Has.Count.EqualTo(Customer1.Orders.Count)); Assert.That(customers.Orders, Has.Count.EqualTo(Customer1.Orders.Count)); firstSession.Dispose(); @@ -255,12 +271,18 @@ public async Task ChildCollectionsFromLeftOuterJoinWithWhereClauseRestrictionOnR public async Task ChildCollectionsFromLeftOuterJoinShouldBeInSecondLevelCacheIfQueryContainsSubqueryWithRestrictionOnLeftOuterJoinAsync() { var firstSession = OpenSession(); - var customersWithOrderNumberEqualsTo2 = await (GetCustomersByOrderNumberUsingSubqueriesAndByNameUsingWhereClauseAsync(firstSession, OrderNumber, Customer1.Name)); + var customersWithOrderNumberEqualsTo2 = + await (GetCustomersByOrderNumberUsingSubqueriesAndByNameUsingWhereClauseAsync( + firstSession, + OrderNumber, + Customer1.Name)); var secondSession = OpenSession(); var customers = await (GetAllCustomersAsync(secondSession)); - Assert.That(customersWithOrderNumberEqualsTo2.Single(n => n.Id == Customer1.Id).Orders, Has.Count.EqualTo(Customer1.Orders.Count)); + Assert.That( + customersWithOrderNumberEqualsTo2.Single(n => n.Id == Customer1.Id).Orders, + Has.Count.EqualTo(Customer1.Orders.Count)); using (var thirdSession = OpenSession()) using (IDbCommand cmd = thirdSession.Connection.CreateCommand()) @@ -282,7 +304,8 @@ public async Task ChildCollectionsFromLeftOuterJoinShouldBeInSecondLevelCacheIfQ public virtual async Task ChildCollectionsFromLeftOuterJoinOnlyWithRestrictionShouldNotBeIn2LvlCacheAsync() { var firstSession = OpenSession(); - var customersWithOrderNumberEqualsTo2AndCompanies = await (GetCustomersWithCompaniesByOrderNumberUsingOnClauseAsync(firstSession, OrderNumber)); + var customersWithOrderNumberEqualsTo2AndCompanies = + await (GetCustomersWithCompaniesByOrderNumberUsingOnClauseAsync(firstSession, OrderNumber)); using (var session = OpenSession()) using (IDbCommand cmd = session.Connection.CreateCommand()) @@ -303,21 +326,39 @@ public virtual async Task ChildCollectionsFromLeftOuterJoinOnlyWithRestrictionSh var secondSession = OpenSession(); var customers = await (GetAllCustomersAsync(secondSession)); - Assert.That(customersWithOrderNumberEqualsTo2AndCompanies.First(n => n.Id == Customer1.Id).Orders, Has.Count.EqualTo(Customer1.Orders.Count(n => n.Number == OrderNumber))); - Assert.That(customersWithOrderNumberEqualsTo2AndCompanies.First(n => n.Id == Customer2.Id).Orders, Has.Count.EqualTo(Customer2.Orders.Count(n => n.Number == OrderNumber))); - Assert.That(customersWithOrderNumberEqualsTo2AndCompanies.First(n => n.Id == Customer3.Id).Orders, Has.Count.EqualTo(Customer3.Orders.Count(n => n.Number == OrderNumber))); - - Assert.That(customersWithOrderNumberEqualsTo2AndCompanies.First(n => n.Id == Customer1.Id).Companies, Has.Count.EqualTo(Customer1.Companies.Count())); - Assert.That(customersWithOrderNumberEqualsTo2AndCompanies.First(n => n.Id == Customer2.Id).Companies, Has.Count.EqualTo(Customer2.Companies.Count())); - Assert.That(customersWithOrderNumberEqualsTo2AndCompanies.First(n => n.Id == Customer3.Id).Companies, Has.Count.EqualTo(Customer3.Companies.Count())); + Assert.That( + customersWithOrderNumberEqualsTo2AndCompanies.First(n => n.Id == Customer1.Id).Orders, + Has.Count.EqualTo(Customer1.Orders.Count(n => n.Number == OrderNumber))); + Assert.That( + customersWithOrderNumberEqualsTo2AndCompanies.First(n => n.Id == Customer2.Id).Orders, + Has.Count.EqualTo(Customer2.Orders.Count(n => n.Number == OrderNumber))); + Assert.That( + customersWithOrderNumberEqualsTo2AndCompanies.First(n => n.Id == Customer3.Id).Orders, + Has.Count.EqualTo(Customer3.Orders.Count(n => n.Number == OrderNumber))); + + Assert.That( + customersWithOrderNumberEqualsTo2AndCompanies.First(n => n.Id == Customer1.Id).Companies, + Has.Count.EqualTo(Customer1.Companies.Count())); + Assert.That( + customersWithOrderNumberEqualsTo2AndCompanies.First(n => n.Id == Customer2.Id).Companies, + Has.Count.EqualTo(Customer2.Companies.Count())); + Assert.That( + customersWithOrderNumberEqualsTo2AndCompanies.First(n => n.Id == Customer3.Id).Companies, + Has.Count.EqualTo(Customer3.Companies.Count())); Assert.That(customers.Single(n => n.Id == Customer1.Id).Orders, Has.Count.EqualTo(0)); Assert.That(customers.Single(n => n.Id == Customer2.Id).Orders, Has.Count.EqualTo(0)); Assert.That(customers.Single(n => n.Id == Customer3.Id).Orders, Has.Count.EqualTo(0)); - Assert.That(customers.Single(n => n.Id == Customer1.Id).Companies, Has.Count.EqualTo(Customer1.Companies.Count)); - Assert.That(customers.Single(n => n.Id == Customer2.Id).Companies, Has.Count.EqualTo(Customer2.Companies.Count)); - Assert.That(customers.Single(n => n.Id == Customer3.Id).Companies, Has.Count.EqualTo(Customer3.Companies.Count)); + Assert.That( + customers.Single(n => n.Id == Customer1.Id).Companies, + Has.Count.EqualTo(Customer1.Companies.Count)); + Assert.That( + customers.Single(n => n.Id == Customer2.Id).Companies, + Has.Count.EqualTo(Customer2.Companies.Count)); + Assert.That( + customers.Single(n => n.Id == Customer3.Id).Companies, + Has.Count.EqualTo(Customer3.Companies.Count)); firstSession.Dispose(); secondSession.Dispose(); @@ -356,7 +397,7 @@ protected void ClearSecondLevelCacheFor(System.Type entity) if (memberExpression == null) return Task.FromException(new ArgumentException("pathToCollection should be member expression")); - var role = string.Format("{0}.{1}", rootEntityTypeFullPath, memberExpression.Member.Name); + var role = $"{rootEntityTypeFullPath}.{memberExpression.Member.Name}"; return Sfi.EvictCollectionAsync(role, cancellationToken); } catch (Exception ex) @@ -372,7 +413,7 @@ protected void ClearCollectionCache(Expression> pathToCo if (memberExpression == null) throw new ArgumentException("pathToCollection should be member expression"); - var role = string.Format("{0}.{1}", rootEntityTypeFullPath, memberExpression.Member.Name); + var role = $"{rootEntityTypeFullPath}.{memberExpression.Member.Name}"; Sfi.EvictCollection(role); } @@ -381,8 +422,13 @@ protected void ClearCollectionCache(Expression> pathToCo protected abstract Task> GetCustomersByOrderNumberUsingOnClauseAsync(ISession session, int orderNumber, CancellationToken cancellationToken = default(CancellationToken)); protected abstract Task> GetCustomersByOrderNumberUsingWhereClauseAsync(ISession session, int orderNumber, CancellationToken cancellationToken = default(CancellationToken)); protected abstract Task> GetCustomersByNameUsingWhereClauseAsync(ISession session, string customerName, CancellationToken cancellationToken = default(CancellationToken)); - protected abstract Task> GetCustomersByOrderNumberUsingSubqueriesAndByNameUsingWhereClauseAsync(ISession session, int orderNumber, string customerName, CancellationToken cancellationToken = default(CancellationToken)); - protected abstract Task> GetCustomersWithCompaniesByOrderNumberUsingOnClauseAsync(ISession session, int orderNumber, CancellationToken cancellationToken = default(CancellationToken)); + protected abstract Task> GetCustomersByOrderNumberUsingSubqueriesAndByNameUsingWhereClauseAsync( + ISession session, + int orderNumber, + string customerName, CancellationToken cancellationToken = default(CancellationToken)); + protected abstract Task> GetCustomersWithCompaniesByOrderNumberUsingOnClauseAsync( + ISession session, + int orderNumber, CancellationToken cancellationToken = default(CancellationToken)); protected abstract Task> GetAllCustomersAsync(ISession session, CancellationToken cancellationToken = default(CancellationToken)); } } diff --git a/src/NHibernate.Test/Async/NHSpecificTest/NH3848/QueryOverTestFixture.cs b/src/NHibernate.Test/Async/NHSpecificTest/NH3848/QueryOverTestFixture.cs index f56a4ddc9a8..99fbc019314 100644 --- a/src/NHibernate.Test/Async/NHSpecificTest/NH3848/QueryOverTestFixture.cs +++ b/src/NHibernate.Test/Async/NHSpecificTest/NH3848/QueryOverTestFixture.cs @@ -24,12 +24,15 @@ public class QueryOverTestFixtureAsync : FixtureAsync try { Order ordersAlias = null; - return session.QueryOver() - .JoinAlias(n => n.Orders, - () => ordersAlias, - JoinType.LeftOuterJoin) - .TransformUsing(new DistinctRootEntityResultTransformer()) - .ListAsync(cancellationToken); + return + session + .QueryOver() + .JoinAlias( + n => n.Orders, + () => ordersAlias, + JoinType.LeftOuterJoin) + .TransformUsing(new DistinctRootEntityResultTransformer()) + .ListAsync(cancellationToken); } catch (System.Exception ex) { @@ -41,10 +44,12 @@ public class QueryOverTestFixtureAsync : FixtureAsync { try { - return session.QueryOver() - .Fetch(SelectMode.Fetch, n => n.Orders) - .TransformUsing(new DistinctRootEntityResultTransformer()) - .ListAsync(cancellationToken); + return + session + .QueryOver() + .Fetch(SelectMode.Fetch, n => n.Orders) + .TransformUsing(new DistinctRootEntityResultTransformer()) + .ListAsync(cancellationToken); } catch (System.Exception ex) { @@ -57,12 +62,15 @@ public class QueryOverTestFixtureAsync : FixtureAsync try { Order ordersAlias = null; - return session.QueryOver() - .JoinAlias(n => n.Orders, - () => ordersAlias, - JoinType.LeftOuterJoin, - Restrictions.Eq("Number", orderNumber)) - .ListAsync(cancellationToken); + return + session + .QueryOver() + .JoinAlias( + n => n.Orders, + () => ordersAlias, + JoinType.LeftOuterJoin, + Restrictions.Eq("Number", orderNumber)) + .ListAsync(cancellationToken); } catch (System.Exception ex) { @@ -70,22 +78,28 @@ public class QueryOverTestFixtureAsync : FixtureAsync } } - protected override Task> GetCustomersWithCompaniesByOrderNumberUsingOnClauseAsync(ISession session, int orderNumber, CancellationToken cancellationToken = default(CancellationToken)) + protected override Task> GetCustomersWithCompaniesByOrderNumberUsingOnClauseAsync( + ISession session, + int orderNumber, CancellationToken cancellationToken = default(CancellationToken)) { try { Order ordersAlias = null; Company companiesAlias = null; - return session.QueryOver() - .JoinAlias(n => n.Orders, - () => ordersAlias, - JoinType.LeftOuterJoin, - Restrictions.Eq("Number", orderNumber)) - .JoinAlias(n => n.Companies, - () => companiesAlias, - JoinType.LeftOuterJoin) - .ListAsync(cancellationToken); + return + session + .QueryOver() + .JoinAlias( + n => n.Orders, + () => ordersAlias, + JoinType.LeftOuterJoin, + Restrictions.Eq("Number", orderNumber)) + .JoinAlias( + n => n.Companies, + () => companiesAlias, + JoinType.LeftOuterJoin) + .ListAsync(cancellationToken); } catch (System.Exception ex) { @@ -98,10 +112,12 @@ public class QueryOverTestFixtureAsync : FixtureAsync try { Order ordersAlias = null; - return session.QueryOver() - .JoinQueryOver(n => n.Orders, () => ordersAlias, JoinType.LeftOuterJoin) - .Where(Restrictions.Eq("Number", orderNumber)) - .ListAsync(cancellationToken); + return + session + .QueryOver() + .JoinQueryOver(n => n.Orders, () => ordersAlias, JoinType.LeftOuterJoin) + .Where(Restrictions.Eq("Number", orderNumber)) + .ListAsync(cancellationToken); } catch (System.Exception ex) { @@ -114,14 +130,16 @@ public class QueryOverTestFixtureAsync : FixtureAsync try { Order ordersAlias = null; - return session.QueryOver() - .JoinAlias(n => n.Orders, - () => ordersAlias, - JoinType.LeftOuterJoin - ) - .Where(Restrictions.Eq("Name", customerName)) - .TransformUsing(new DistinctRootEntityResultTransformer()) - .ListAsync(cancellationToken); + return + session + .QueryOver() + .JoinAlias( + n => n.Orders, + () => ordersAlias, + JoinType.LeftOuterJoin) + .Where(Restrictions.Eq("Name", customerName)) + .TransformUsing(new DistinctRootEntityResultTransformer()) + .ListAsync(cancellationToken); } catch (System.Exception ex) { @@ -129,22 +147,33 @@ public class QueryOverTestFixtureAsync : FixtureAsync } } - protected override Task> GetCustomersByOrderNumberUsingSubqueriesAndByNameUsingWhereClauseAsync(ISession session, int orderNumber, string customerName, CancellationToken cancellationToken = default(CancellationToken)) + protected override Task> GetCustomersByOrderNumberUsingSubqueriesAndByNameUsingWhereClauseAsync( + ISession session, + int orderNumber, + string customerName, CancellationToken cancellationToken = default(CancellationToken)) { try { Order ordersAlias = null; Order ordersAlias2 = null; - var subquery = QueryOver.Of() - .JoinAlias(n => n.Orders, () => ordersAlias, JoinType.LeftOuterJoin, Restrictions.Eq("Number", orderNumber)) - .Select(n => n.Id); + var subquery = + QueryOver + .Of() + .JoinAlias( + n => n.Orders, + () => ordersAlias, + JoinType.LeftOuterJoin, + Restrictions.Eq("Number", orderNumber)) + .Select(n => n.Id); - return session.QueryOver() - .JoinAlias(n => n.Orders, () => ordersAlias2, JoinType.LeftOuterJoin) - .WithSubquery.WhereProperty(n => n.Id).In(subquery) - .Where(Restrictions.Eq("Name", customerName)) - .TransformUsing(new DistinctRootEntityResultTransformer()) - .ListAsync(cancellationToken); + return + session + .QueryOver() + .JoinAlias(n => n.Orders, () => ordersAlias2, JoinType.LeftOuterJoin) + .WithSubquery.WhereProperty(n => n.Id).In(subquery) + .Where(Restrictions.Eq("Name", customerName)) + .TransformUsing(new DistinctRootEntityResultTransformer()) + .ListAsync(cancellationToken); } catch (System.Exception ex) { diff --git a/src/NHibernate.Test/NHSpecificTest/NH3848/CriteriaTestFixture.cs b/src/NHibernate.Test/NHSpecificTest/NH3848/CriteriaTestFixture.cs index c3f90b196ad..62c89aa9b28 100644 --- a/src/NHibernate.Test/NHSpecificTest/NH3848/CriteriaTestFixture.cs +++ b/src/NHibernate.Test/NHSpecificTest/NH3848/CriteriaTestFixture.cs @@ -9,65 +9,86 @@ public class CriteriaTestFixture : Fixture { protected override IList GetCustomersWithFetchedOrdersWithoutRestrictions(ISession session) { - return session.CreateCriteria() - .CreateAlias("Orders", "order", JoinType.LeftOuterJoin) - .SetResultTransformer(new DistinctRootEntityResultTransformer()) - .List(); + return + session + .CreateCriteria() + .CreateAlias("Orders", "order", JoinType.LeftOuterJoin) + .SetResultTransformer(new DistinctRootEntityResultTransformer()) + .List(); } protected override IList GetCustomersWithOrdersEagerLoaded(ISession session) { - return session.CreateCriteria() - .Fetch("Orders") - .SetResultTransformer(new DistinctRootEntityResultTransformer()) - .List(); + return + session + .CreateCriteria() + .Fetch("Orders") + .SetResultTransformer(new DistinctRootEntityResultTransformer()) + .List(); } protected override IList GetCustomersByOrderNumberUsingOnClause(ISession session, int orderNumber) { - return session.CreateCriteria() - .CreateAlias("Orders", "order", JoinType.LeftOuterJoin, Restrictions.Eq("Number", orderNumber)) - .List(); + return + session + .CreateCriteria() + .CreateAlias("Orders", "order", JoinType.LeftOuterJoin, Restrictions.Eq("Number", orderNumber)) + .List(); } - protected override IList GetCustomersWithCompaniesByOrderNumberUsingOnClause(ISession session, int orderNumber) + protected override IList GetCustomersWithCompaniesByOrderNumberUsingOnClause( + ISession session, + int orderNumber) { - return session.CreateCriteria() - .CreateAlias("Orders", "order", JoinType.LeftOuterJoin, Restrictions.Eq("Number", orderNumber)) - .CreateAlias("Companies", "company", JoinType.LeftOuterJoin) - .List(); + return + session + .CreateCriteria() + .CreateAlias("Orders", "order", JoinType.LeftOuterJoin, Restrictions.Eq("Number", orderNumber)) + .CreateAlias("Companies", "company", JoinType.LeftOuterJoin) + .List(); } protected override IList GetCustomersByOrderNumberUsingWhereClause(ISession session, int orderNumber) { - return session.CreateCriteria() - .CreateCriteria("Orders", "Order", JoinType.LeftOuterJoin) - .Add(Restrictions.Eq("Number", orderNumber)) - .SetResultTransformer(new DistinctRootEntityResultTransformer()) - .List(); + return + session + .CreateCriteria() + .CreateCriteria("Orders", "Order", JoinType.LeftOuterJoin) + .Add(Restrictions.Eq("Number", orderNumber)) + .SetResultTransformer(new DistinctRootEntityResultTransformer()) + .List(); } protected override IList GetCustomersByNameUsingWhereClause(ISession session, string customerName) { - return session.CreateCriteria() - .CreateAlias("Orders", "order", JoinType.LeftOuterJoin) - .Add(Restrictions.Eq("Name", "First Customer")) - .SetResultTransformer(new DistinctRootEntityResultTransformer()) - .List(); + return + session + .CreateCriteria() + .CreateAlias("Orders", "order", JoinType.LeftOuterJoin) + .Add(Restrictions.Eq("Name", "First Customer")) + .SetResultTransformer(new DistinctRootEntityResultTransformer()) + .List(); } - protected override IList GetCustomersByOrderNumberUsingSubqueriesAndByNameUsingWhereClause(ISession session, int orderNumber, string customerName) + protected override IList GetCustomersByOrderNumberUsingSubqueriesAndByNameUsingWhereClause( + ISession session, + int orderNumber, + string customerName) { - var detachedCriteria = DetachedCriteria.For() - .CreateAlias("Orders", "order", JoinType.LeftOuterJoin, Restrictions.Eq("Number", orderNumber)) - .SetProjection(Projections.Id()); + var detachedCriteria = + DetachedCriteria + .For() + .CreateAlias("Orders", "order", JoinType.LeftOuterJoin, Restrictions.Eq("Number", orderNumber)) + .SetProjection(Projections.Id()); - return session.CreateCriteria() - .CreateAlias("Orders", "order1", JoinType.LeftOuterJoin) - .Add(Subqueries.PropertyIn("Id", detachedCriteria)) - .Add(Restrictions.Eq("Name", customerName)) - .SetResultTransformer(new DistinctRootEntityResultTransformer()) - .List(); + return + session + .CreateCriteria() + .CreateAlias("Orders", "order1", JoinType.LeftOuterJoin) + .Add(Subqueries.PropertyIn("Id", detachedCriteria)) + .Add(Restrictions.Eq("Name", customerName)) + .SetResultTransformer(new DistinctRootEntityResultTransformer()) + .List(); } protected override IList GetAllCustomers(ISession session) diff --git a/src/NHibernate.Test/NHSpecificTest/NH3848/Customer.cs b/src/NHibernate.Test/NHSpecificTest/NH3848/Customer.cs index e3520fcd1d3..135b848c641 100644 --- a/src/NHibernate.Test/NHSpecificTest/NH3848/Customer.cs +++ b/src/NHibernate.Test/NHSpecificTest/NH3848/Customer.cs @@ -6,16 +6,10 @@ namespace NHibernate.Test.NHSpecificTest.NH3848 public class Customer { public virtual Guid Id { get; set; } - public virtual ISet Orders { get; set; } - public virtual ISet Companies { get; set; } + public virtual ISet Orders { get; set; } = new HashSet(); + public virtual ISet Companies { get; set; } = new HashSet(); public virtual string Name { get; set; } - public Customer() - { - Orders = new HashSet(); - Companies = new HashSet(); - } - public virtual void AddOrder(Order order) { Orders.Add(order); diff --git a/src/NHibernate.Test/NHSpecificTest/NH3848/Fixture.cs b/src/NHibernate.Test/NHSpecificTest/NH3848/Fixture.cs index 9a9f7550ead..530a2514254 100644 --- a/src/NHibernate.Test/NHSpecificTest/NH3848/Fixture.cs +++ b/src/NHibernate.Test/NHSpecificTest/NH3848/Fixture.cs @@ -155,9 +155,15 @@ public virtual void ChildCollectionsFromLeftOuterJoinWithOnClauseRestrictionOnCo var secondSession = OpenSession(); var customers = GetAllCustomers(secondSession); - Assert.That(customersWithOrderNumberEqualsTo2.Single(n => n.Id == Customer1.Id).Orders, Has.Count.EqualTo(Customer1.Orders.Count(n => n.Number == OrderNumber))); - Assert.That(customersWithOrderNumberEqualsTo2.Single(n => n.Id == Customer2.Id).Orders, Has.Count.EqualTo(Customer2.Orders.Count(n => n.Number == OrderNumber))); - Assert.That(customersWithOrderNumberEqualsTo2.Single(n => n.Id == Customer3.Id).Orders, Has.Count.EqualTo(Customer3.Orders.Count(n => n.Number == OrderNumber))); + Assert.That( + customersWithOrderNumberEqualsTo2.Single(n => n.Id == Customer1.Id).Orders, + Has.Count.EqualTo(Customer1.Orders.Count(n => n.Number == OrderNumber))); + Assert.That( + customersWithOrderNumberEqualsTo2.Single(n => n.Id == Customer2.Id).Orders, + Has.Count.EqualTo(Customer2.Orders.Count(n => n.Number == OrderNumber))); + Assert.That( + customersWithOrderNumberEqualsTo2.Single(n => n.Id == Customer3.Id).Orders, + Has.Count.EqualTo(Customer3.Orders.Count(n => n.Number == OrderNumber))); Assert.That(customers.Single(n => n.Id == Customer1.Id).Orders, Has.Count.EqualTo(Customer1.Orders.Count)); Assert.That(customers.Single(n => n.Id == Customer2.Id).Orders, Has.Count.EqualTo(Customer2.Orders.Count)); @@ -176,8 +182,12 @@ public void ChildCollectionsFromLeftOuterJoinWithWhereClauseRestrictionOnCollect var secondSession = OpenSession(); var customers = GetAllCustomers(secondSession); - Assert.That(customersWithOrderNumberEqualsTo2.Single(n => n.Id == Customer1.Id).Orders, Has.Count.EqualTo(Customer1.Orders.Count(n => n.Number == OrderNumber))); - Assert.That(customersWithOrderNumberEqualsTo2.Single(n => n.Id == Customer2.Id).Orders, Has.Count.EqualTo(Customer2.Orders.Count(n => n.Number == OrderNumber))); + Assert.That( + customersWithOrderNumberEqualsTo2.Single(n => n.Id == Customer1.Id).Orders, + Has.Count.EqualTo(Customer1.Orders.Count(n => n.Number == OrderNumber))); + Assert.That( + customersWithOrderNumberEqualsTo2.Single(n => n.Id == Customer2.Id).Orders, + Has.Count.EqualTo(Customer2.Orders.Count(n => n.Number == OrderNumber))); Assert.That(customers.Single(n => n.Id == Customer3.Id).Orders, Has.Count.EqualTo(Customer3.Orders.Count)); Assert.That(customers.Single(n => n.Id == Customer1.Id).Orders, Has.Count.EqualTo(Customer1.Orders.Count)); @@ -204,8 +214,12 @@ public void ChildCollectionsEagerFetchedShouldBeInSecondLevelCache() var secondSession = OpenSession(); var customers = GetAllCustomers(secondSession); - Assert.That(customersWithOrderNumberEqualsTo2.Single(n => n.Id == Customer1.Id).Orders, Has.Count.EqualTo(Customer1.Orders.Count)); - Assert.That(customersWithOrderNumberEqualsTo2.Single(n => n.Id == Customer2.Id).Orders, Has.Count.EqualTo(Customer2.Orders.Count)); + Assert.That( + customersWithOrderNumberEqualsTo2.Single(n => n.Id == Customer1.Id).Orders, + Has.Count.EqualTo(Customer1.Orders.Count)); + Assert.That( + customersWithOrderNumberEqualsTo2.Single(n => n.Id == Customer2.Id).Orders, + Has.Count.EqualTo(Customer2.Orders.Count)); Assert.That(customers.Single(n => n.Id == Customer3.Id).Orders, Has.Count.EqualTo(Customer3.Orders.Count)); Assert.That(customers.Single(n => n.Id == Customer1.Id).Orders, Has.Count.EqualTo(Customer1.Orders.Count)); @@ -232,7 +246,9 @@ public void ChildCollectionsFromLeftOuterJoinWithWhereClauseRestrictionOnRootSho var secondSession = OpenSession(); var customers = secondSession.Get(Customer1.Id); - Assert.That(customersWithOrderNumberEqualsTo2.Single(n => n.Id == Customer1.Id).Orders, Has.Count.EqualTo(Customer1.Orders.Count)); + Assert.That( + customersWithOrderNumberEqualsTo2.Single(n => n.Id == Customer1.Id).Orders, + Has.Count.EqualTo(Customer1.Orders.Count)); Assert.That(customers.Orders, Has.Count.EqualTo(Customer1.Orders.Count)); firstSession.Dispose(); @@ -243,12 +259,18 @@ public void ChildCollectionsFromLeftOuterJoinWithWhereClauseRestrictionOnRootSho public void ChildCollectionsFromLeftOuterJoinShouldBeInSecondLevelCacheIfQueryContainsSubqueryWithRestrictionOnLeftOuterJoin() { var firstSession = OpenSession(); - var customersWithOrderNumberEqualsTo2 = GetCustomersByOrderNumberUsingSubqueriesAndByNameUsingWhereClause(firstSession, OrderNumber, Customer1.Name); + var customersWithOrderNumberEqualsTo2 = + GetCustomersByOrderNumberUsingSubqueriesAndByNameUsingWhereClause( + firstSession, + OrderNumber, + Customer1.Name); var secondSession = OpenSession(); var customers = GetAllCustomers(secondSession); - Assert.That(customersWithOrderNumberEqualsTo2.Single(n => n.Id == Customer1.Id).Orders, Has.Count.EqualTo(Customer1.Orders.Count)); + Assert.That( + customersWithOrderNumberEqualsTo2.Single(n => n.Id == Customer1.Id).Orders, + Has.Count.EqualTo(Customer1.Orders.Count)); using (var thirdSession = OpenSession()) using (IDbCommand cmd = thirdSession.Connection.CreateCommand()) @@ -270,7 +292,8 @@ public void ChildCollectionsFromLeftOuterJoinShouldBeInSecondLevelCacheIfQueryCo public virtual void ChildCollectionsFromLeftOuterJoinOnlyWithRestrictionShouldNotBeIn2LvlCache() { var firstSession = OpenSession(); - var customersWithOrderNumberEqualsTo2AndCompanies = GetCustomersWithCompaniesByOrderNumberUsingOnClause(firstSession, OrderNumber); + var customersWithOrderNumberEqualsTo2AndCompanies = + GetCustomersWithCompaniesByOrderNumberUsingOnClause(firstSession, OrderNumber); using (var session = OpenSession()) using (IDbCommand cmd = session.Connection.CreateCommand()) @@ -291,21 +314,39 @@ public virtual void ChildCollectionsFromLeftOuterJoinOnlyWithRestrictionShouldNo var secondSession = OpenSession(); var customers = GetAllCustomers(secondSession); - Assert.That(customersWithOrderNumberEqualsTo2AndCompanies.First(n => n.Id == Customer1.Id).Orders, Has.Count.EqualTo(Customer1.Orders.Count(n => n.Number == OrderNumber))); - Assert.That(customersWithOrderNumberEqualsTo2AndCompanies.First(n => n.Id == Customer2.Id).Orders, Has.Count.EqualTo(Customer2.Orders.Count(n => n.Number == OrderNumber))); - Assert.That(customersWithOrderNumberEqualsTo2AndCompanies.First(n => n.Id == Customer3.Id).Orders, Has.Count.EqualTo(Customer3.Orders.Count(n => n.Number == OrderNumber))); - - Assert.That(customersWithOrderNumberEqualsTo2AndCompanies.First(n => n.Id == Customer1.Id).Companies, Has.Count.EqualTo(Customer1.Companies.Count())); - Assert.That(customersWithOrderNumberEqualsTo2AndCompanies.First(n => n.Id == Customer2.Id).Companies, Has.Count.EqualTo(Customer2.Companies.Count())); - Assert.That(customersWithOrderNumberEqualsTo2AndCompanies.First(n => n.Id == Customer3.Id).Companies, Has.Count.EqualTo(Customer3.Companies.Count())); + Assert.That( + customersWithOrderNumberEqualsTo2AndCompanies.First(n => n.Id == Customer1.Id).Orders, + Has.Count.EqualTo(Customer1.Orders.Count(n => n.Number == OrderNumber))); + Assert.That( + customersWithOrderNumberEqualsTo2AndCompanies.First(n => n.Id == Customer2.Id).Orders, + Has.Count.EqualTo(Customer2.Orders.Count(n => n.Number == OrderNumber))); + Assert.That( + customersWithOrderNumberEqualsTo2AndCompanies.First(n => n.Id == Customer3.Id).Orders, + Has.Count.EqualTo(Customer3.Orders.Count(n => n.Number == OrderNumber))); + + Assert.That( + customersWithOrderNumberEqualsTo2AndCompanies.First(n => n.Id == Customer1.Id).Companies, + Has.Count.EqualTo(Customer1.Companies.Count())); + Assert.That( + customersWithOrderNumberEqualsTo2AndCompanies.First(n => n.Id == Customer2.Id).Companies, + Has.Count.EqualTo(Customer2.Companies.Count())); + Assert.That( + customersWithOrderNumberEqualsTo2AndCompanies.First(n => n.Id == Customer3.Id).Companies, + Has.Count.EqualTo(Customer3.Companies.Count())); Assert.That(customers.Single(n => n.Id == Customer1.Id).Orders, Has.Count.EqualTo(0)); Assert.That(customers.Single(n => n.Id == Customer2.Id).Orders, Has.Count.EqualTo(0)); Assert.That(customers.Single(n => n.Id == Customer3.Id).Orders, Has.Count.EqualTo(0)); - Assert.That(customers.Single(n => n.Id == Customer1.Id).Companies, Has.Count.EqualTo(Customer1.Companies.Count)); - Assert.That(customers.Single(n => n.Id == Customer2.Id).Companies, Has.Count.EqualTo(Customer2.Companies.Count)); - Assert.That(customers.Single(n => n.Id == Customer3.Id).Companies, Has.Count.EqualTo(Customer3.Companies.Count)); + Assert.That( + customers.Single(n => n.Id == Customer1.Id).Companies, + Has.Count.EqualTo(Customer1.Companies.Count)); + Assert.That( + customers.Single(n => n.Id == Customer2.Id).Companies, + Has.Count.EqualTo(Customer2.Companies.Count)); + Assert.That( + customers.Single(n => n.Id == Customer3.Id).Companies, + Has.Count.EqualTo(Customer3.Companies.Count)); firstSession.Dispose(); secondSession.Dispose(); @@ -330,7 +371,7 @@ protected void ClearCollectionCache(Expression> pathToCo if (memberExpression == null) throw new ArgumentException("pathToCollection should be member expression"); - var role = string.Format("{0}.{1}", rootEntityTypeFullPath, memberExpression.Member.Name); + var role = $"{rootEntityTypeFullPath}.{memberExpression.Member.Name}"; Sfi.EvictCollection(role); } @@ -339,8 +380,13 @@ protected void ClearCollectionCache(Expression> pathToCo protected abstract IList GetCustomersByOrderNumberUsingOnClause(ISession session, int orderNumber); protected abstract IList GetCustomersByOrderNumberUsingWhereClause(ISession session, int orderNumber); protected abstract IList GetCustomersByNameUsingWhereClause(ISession session, string customerName); - protected abstract IList GetCustomersByOrderNumberUsingSubqueriesAndByNameUsingWhereClause(ISession session, int orderNumber, string customerName); - protected abstract IList GetCustomersWithCompaniesByOrderNumberUsingOnClause(ISession session, int orderNumber); + protected abstract IList GetCustomersByOrderNumberUsingSubqueriesAndByNameUsingWhereClause( + ISession session, + int orderNumber, + string customerName); + protected abstract IList GetCustomersWithCompaniesByOrderNumberUsingOnClause( + ISession session, + int orderNumber); protected abstract IList GetAllCustomers(ISession session); } } diff --git a/src/NHibernate.Test/NHSpecificTest/NH3848/QueryOverTestFixture.cs b/src/NHibernate.Test/NHSpecificTest/NH3848/QueryOverTestFixture.cs index f7b1ad1ba1a..c338125886c 100644 --- a/src/NHibernate.Test/NHSpecificTest/NH3848/QueryOverTestFixture.cs +++ b/src/NHibernate.Test/NHSpecificTest/NH3848/QueryOverTestFixture.cs @@ -10,85 +10,114 @@ public class QueryOverTestFixture : Fixture protected override IList GetCustomersWithFetchedOrdersWithoutRestrictions(ISession session) { Order ordersAlias = null; - return session.QueryOver() - .JoinAlias(n => n.Orders, - () => ordersAlias, - JoinType.LeftOuterJoin) - .TransformUsing(new DistinctRootEntityResultTransformer()) - .List(); + return + session + .QueryOver() + .JoinAlias( + n => n.Orders, + () => ordersAlias, + JoinType.LeftOuterJoin) + .TransformUsing(new DistinctRootEntityResultTransformer()) + .List(); } protected override IList GetCustomersWithOrdersEagerLoaded(ISession session) { - return session.QueryOver() - .Fetch(SelectMode.Fetch, n => n.Orders) - .TransformUsing(new DistinctRootEntityResultTransformer()) - .List(); + return + session + .QueryOver() + .Fetch(SelectMode.Fetch, n => n.Orders) + .TransformUsing(new DistinctRootEntityResultTransformer()) + .List(); } protected override IList GetCustomersByOrderNumberUsingOnClause(ISession session, int orderNumber) { Order ordersAlias = null; - return session.QueryOver() - .JoinAlias(n => n.Orders, - () => ordersAlias, - JoinType.LeftOuterJoin, - Restrictions.Eq("Number", orderNumber)) - .List(); + return + session + .QueryOver() + .JoinAlias( + n => n.Orders, + () => ordersAlias, + JoinType.LeftOuterJoin, + Restrictions.Eq("Number", orderNumber)) + .List(); } - protected override IList GetCustomersWithCompaniesByOrderNumberUsingOnClause(ISession session, int orderNumber) + protected override IList GetCustomersWithCompaniesByOrderNumberUsingOnClause( + ISession session, + int orderNumber) { Order ordersAlias = null; Company companiesAlias = null; - return session.QueryOver() - .JoinAlias(n => n.Orders, - () => ordersAlias, - JoinType.LeftOuterJoin, - Restrictions.Eq("Number", orderNumber)) - .JoinAlias(n => n.Companies, - () => companiesAlias, - JoinType.LeftOuterJoin) - .List(); + return + session + .QueryOver() + .JoinAlias( + n => n.Orders, + () => ordersAlias, + JoinType.LeftOuterJoin, + Restrictions.Eq("Number", orderNumber)) + .JoinAlias( + n => n.Companies, + () => companiesAlias, + JoinType.LeftOuterJoin) + .List(); } protected override IList GetCustomersByOrderNumberUsingWhereClause(ISession session, int orderNumber) { Order ordersAlias = null; - return session.QueryOver() - .JoinQueryOver(n => n.Orders, () => ordersAlias, JoinType.LeftOuterJoin) - .Where(Restrictions.Eq("Number", orderNumber)) - .List(); + return + session + .QueryOver() + .JoinQueryOver(n => n.Orders, () => ordersAlias, JoinType.LeftOuterJoin) + .Where(Restrictions.Eq("Number", orderNumber)) + .List(); } protected override IList GetCustomersByNameUsingWhereClause(ISession session, string customerName) { Order ordersAlias = null; - return session.QueryOver() - .JoinAlias(n => n.Orders, - () => ordersAlias, - JoinType.LeftOuterJoin - ) - .Where(Restrictions.Eq("Name", customerName)) - .TransformUsing(new DistinctRootEntityResultTransformer()) - .List(); + return + session + .QueryOver() + .JoinAlias( + n => n.Orders, + () => ordersAlias, + JoinType.LeftOuterJoin) + .Where(Restrictions.Eq("Name", customerName)) + .TransformUsing(new DistinctRootEntityResultTransformer()) + .List(); } - protected override IList GetCustomersByOrderNumberUsingSubqueriesAndByNameUsingWhereClause(ISession session, int orderNumber, string customerName) + protected override IList GetCustomersByOrderNumberUsingSubqueriesAndByNameUsingWhereClause( + ISession session, + int orderNumber, + string customerName) { Order ordersAlias = null; Order ordersAlias2 = null; - var subquery = QueryOver.Of() - .JoinAlias(n => n.Orders, () => ordersAlias, JoinType.LeftOuterJoin, Restrictions.Eq("Number", orderNumber)) - .Select(n => n.Id); + var subquery = + QueryOver + .Of() + .JoinAlias( + n => n.Orders, + () => ordersAlias, + JoinType.LeftOuterJoin, + Restrictions.Eq("Number", orderNumber)) + .Select(n => n.Id); - return session.QueryOver() - .JoinAlias(n => n.Orders, () => ordersAlias2, JoinType.LeftOuterJoin) - .WithSubquery.WhereProperty(n => n.Id).In(subquery) - .Where(Restrictions.Eq("Name", customerName)) - .TransformUsing(new DistinctRootEntityResultTransformer()) - .List(); + return + session + .QueryOver() + .JoinAlias(n => n.Orders, () => ordersAlias2, JoinType.LeftOuterJoin) + .WithSubquery.WhereProperty(n => n.Id).In(subquery) + .Where(Restrictions.Eq("Name", customerName)) + .TransformUsing(new DistinctRootEntityResultTransformer()) + .List(); } protected override IList GetAllCustomers(ISession session) From be6c08e32ace0cd486585d0636630ff45b86cb3d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Delaporte?= <12201973+fredericdelaporte@users.noreply.github.com> Date: Fri, 16 Nov 2018 12:46:17 +0100 Subject: [PATCH 12/20] Do some more reformatting --- .../Engine/Loading/CollectionLoadContext.cs | 29 ++++++++++--------- .../Engine/Loading/CollectionLoadContext.cs | 29 ++++++++++--------- src/NHibernate/Engine/QueryParameters.cs | 12 ++++---- 3 files changed, 39 insertions(+), 31 deletions(-) diff --git a/src/NHibernate/Async/Engine/Loading/CollectionLoadContext.cs b/src/NHibernate/Async/Engine/Loading/CollectionLoadContext.cs index 1181482bf2c..748f2a75d7c 100644 --- a/src/NHibernate/Async/Engine/Loading/CollectionLoadContext.cs +++ b/src/NHibernate/Async/Engine/Loading/CollectionLoadContext.cs @@ -27,14 +27,15 @@ namespace NHibernate.Engine.Loading public partial class CollectionLoadContext { - /// - /// Finish the process of collection-loading for this bound result set. Mainly this + /// + /// Finish the process of collection-loading for this bound result set. Mainly this /// involves cleaning up resources and notifying the collections that loading is - /// complete. + /// complete. /// - /// The persister for which to complete loading. + /// The persister for which to complete loading. /// A cancellation token that can be used to cancel the work - [Obsolete("Please use EndLoadingCollections(ICollectionPersister, HashSet) instead.")] + // Since v5.2 + [Obsolete("Please use overload with uncacheableCollections parameter instead.")] public Task EndLoadingCollectionsAsync(ICollectionPersister persister, CancellationToken cancellationToken) { if (cancellationToken.IsCancellationRequested) @@ -44,13 +45,13 @@ public Task EndLoadingCollectionsAsync(ICollectionPersister persister, Cancellat return EndLoadingCollectionsAsync(persister, null, cancellationToken); } - /// - /// Finish the process of collection-loading for this bound result set. Mainly this + /// + /// Finish the process of collection-loading for this bound result set. Mainly this /// involves cleaning up resources and notifying the collections that loading is - /// complete. + /// complete. /// - /// The persister for which to complete loading. - /// Indicates if collections can be put in cache + /// The persister for which to complete loading. + /// Indicates which collections must not be put in cache. /// A cancellation token that can be used to cancel the work public async Task EndLoadingCollectionsAsync(ICollectionPersister persister, HashSet uncacheableCollections, CancellationToken cancellationToken) { @@ -185,9 +186,11 @@ private async Task EndLoadingCollectionAsync( ce.PostInitialize(lce.Collection, persistenceContext); } - bool addToCache = hasNoQueuedOperations && persister.HasCache && - session.CacheMode.HasFlag(CacheMode.Put) && - (uncacheableCollections == null || !uncacheableCollections.Contains(lce.Persister.Role)) && !ce.IsDoremove; // and this is not a forced initialization during flush + bool addToCache = hasNoQueuedOperations && persister.HasCache && + session.CacheMode.HasFlag(CacheMode.Put) && + (uncacheableCollections == null || !uncacheableCollections.Contains(lce.Persister.Role)) && + // and this is not a forced initialization during flush + !ce.IsDoremove; if (addToCache) { diff --git a/src/NHibernate/Engine/Loading/CollectionLoadContext.cs b/src/NHibernate/Engine/Loading/CollectionLoadContext.cs index 1829c089529..5383c66bf8d 100644 --- a/src/NHibernate/Engine/Loading/CollectionLoadContext.cs +++ b/src/NHibernate/Engine/Loading/CollectionLoadContext.cs @@ -139,25 +139,26 @@ public IPersistentCollection GetLoadingCollection(ICollectionPersister persister } } - /// - /// Finish the process of collection-loading for this bound result set. Mainly this + /// + /// Finish the process of collection-loading for this bound result set. Mainly this /// involves cleaning up resources and notifying the collections that loading is - /// complete. + /// complete. /// - /// The persister for which to complete loading. - [Obsolete("Please use EndLoadingCollections(ICollectionPersister, HashSet) instead.")] + /// The persister for which to complete loading. + // Since v5.2 + [Obsolete("Please use overload with uncacheableCollections parameter instead.")] public void EndLoadingCollections(ICollectionPersister persister) { EndLoadingCollections(persister, null); } - /// - /// Finish the process of collection-loading for this bound result set. Mainly this + /// + /// Finish the process of collection-loading for this bound result set. Mainly this /// involves cleaning up resources and notifying the collections that loading is - /// complete. + /// complete. /// - /// The persister for which to complete loading. - /// Indicates if collections can be put in cache + /// The persister for which to complete loading. + /// Indicates which collections must not be put in cache. public void EndLoadingCollections(ICollectionPersister persister, HashSet uncacheableCollections) { if (!loadContexts.HasLoadingCollectionEntries && (localLoadingCollectionKeys.Count == 0)) @@ -288,9 +289,11 @@ private void EndLoadingCollection( ce.PostInitialize(lce.Collection, persistenceContext); } - bool addToCache = hasNoQueuedOperations && persister.HasCache && - session.CacheMode.HasFlag(CacheMode.Put) && - (uncacheableCollections == null || !uncacheableCollections.Contains(lce.Persister.Role)) && !ce.IsDoremove; // and this is not a forced initialization during flush + bool addToCache = hasNoQueuedOperations && persister.HasCache && + session.CacheMode.HasFlag(CacheMode.Put) && + (uncacheableCollections == null || !uncacheableCollections.Contains(lce.Persister.Role)) && + // and this is not a forced initialization during flush + !ce.IsDoremove; if (addToCache) { diff --git a/src/NHibernate/Engine/QueryParameters.cs b/src/NHibernate/Engine/QueryParameters.cs index 7428d1f0235..01ca4664eaa 100644 --- a/src/NHibernate/Engine/QueryParameters.cs +++ b/src/NHibernate/Engine/QueryParameters.cs @@ -46,16 +46,17 @@ public QueryParameters(IType[] positionalParameterTypes, object[] positionalPara NaturalKeyLookup = isLookupByNaturalKey; } + // Since v5.2 [Obsolete("Please use QueryParameters(IDictionary, IDictionary, " + "RowSelection, bool, bool, bool, string, string, bool, IResultTransformer, HashSet) instead.")] public QueryParameters(IDictionary namedParameters, IDictionary lockModes, RowSelection rowSelection, bool isReadOnlyInitialized, - bool readOnly, bool cacheable, string cacheRegion, string comment, bool isLookupByNaturalKey, IResultTransformer transformer) + bool readOnly, bool cacheable, string cacheRegion, string comment, bool isLookupByNaturalKey, IResultTransformer transformer) : this( namedParameters, lockModes, rowSelection, isReadOnlyInitialized, readOnly, cacheable, cacheRegion, comment, isLookupByNaturalKey, transformer, null) {} public QueryParameters(IDictionary namedParameters, IDictionary lockModes, RowSelection rowSelection, bool isReadOnlyInitialized, - bool readOnly, bool cacheable, string cacheRegion, string comment, bool isLookupByNaturalKey, IResultTransformer transformer, HashSet uncacheableCollections) + bool readOnly, bool cacheable, string cacheRegion, string comment, bool isLookupByNaturalKey, IResultTransformer transformer, HashSet uncacheableCollections) : this( TypeHelper.EmptyTypeArray, Array.Empty(), namedParameters, lockModes, rowSelection, isReadOnlyInitialized, readOnly, cacheable, cacheRegion, comment, null, transformer, uncacheableCollections) @@ -64,6 +65,7 @@ public QueryParameters(IDictionary namedParameters, IDiction NaturalKeyLookup = isLookupByNaturalKey; } + // Since v5.2 [Obsolete("Please use QueryParameters(IType[], object[], IDictionary, " + "IDictionary, RowSelection, bool, bool, bool, string, string, object[], IResultTransformer, HashSet) instead.")] public QueryParameters(IType[] positionalParameterTypes, object[] positionalParameterValues, IDictionary namedParameters, @@ -74,8 +76,8 @@ public QueryParameters(IType[] positionalParameterTypes, object[] positionalPara transformer, null) {} public QueryParameters(IType[] positionalParameterTypes, object[] positionalParameterValues, IDictionary namedParameters, - IDictionary lockModes, RowSelection rowSelection, bool isReadOnlyInitialized, bool readOnly, bool cacheable, string cacheRegion, - string comment, object[] collectionKeys, IResultTransformer transformer, HashSet uncacheableCollections) + IDictionary lockModes, RowSelection rowSelection, bool isReadOnlyInitialized, bool readOnly, bool cacheable, string cacheRegion, + string comment, object[] collectionKeys, IResultTransformer transformer, HashSet uncacheableCollections) { PositionalParameterTypes = positionalParameterTypes ?? Array.Empty(); PositionalParameterValues = positionalParameterValues ?? Array.Empty(); @@ -160,7 +162,7 @@ public bool HasRowSelection /// /// Indicates if we can add loaded child collections to second level cache. /// - public HashSet UncacheableCollections { get; set; } + public HashSet UncacheableCollections { get; } public bool ReadOnly { From c57a703e11b5badc91873456a6e1fcaab2686130 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Delaporte?= <12201973+fredericdelaporte@users.noreply.github.com> Date: Fri, 16 Nov 2018 13:18:27 +0100 Subject: [PATCH 13/20] Do some additional cleanup --- .../NH3848/CriteriaTestFixture.cs | 17 --------------- .../Async/NHSpecificTest/NH3848/Fixture.cs | 11 +++++----- .../NH3848/QueryOverTestFixture.cs | 21 ------------------- .../NH3848/CriteriaTestFixture.cs | 10 --------- .../NHSpecificTest/NH3848/Fixture.cs | 9 ++++---- .../NH3848/QueryOverTestFixture.cs | 14 ------------- 6 files changed, 9 insertions(+), 73 deletions(-) diff --git a/src/NHibernate.Test/Async/NHSpecificTest/NH3848/CriteriaTestFixture.cs b/src/NHibernate.Test/Async/NHSpecificTest/NH3848/CriteriaTestFixture.cs index 3cd343205ff..196fa8fc36e 100644 --- a/src/NHibernate.Test/Async/NHSpecificTest/NH3848/CriteriaTestFixture.cs +++ b/src/NHibernate.Test/Async/NHSpecificTest/NH3848/CriteriaTestFixture.cs @@ -19,23 +19,6 @@ namespace NHibernate.Test.NHSpecificTest.NH3848 using System.Threading; public class CriteriaTestFixtureAsync : FixtureAsync { - protected override Task> GetCustomersWithFetchedOrdersWithoutRestrictionsAsync(ISession session, CancellationToken cancellationToken = default(CancellationToken)) - { - try - { - return - session - .CreateCriteria() - .CreateAlias("Orders", "order", JoinType.LeftOuterJoin) - .SetResultTransformer(new DistinctRootEntityResultTransformer()) - .ListAsync(cancellationToken); - } - catch (System.Exception ex) - { - return Task.FromException>(ex); - } - } - protected override Task> GetCustomersWithOrdersEagerLoadedAsync(ISession session, CancellationToken cancellationToken = default(CancellationToken)) { try diff --git a/src/NHibernate.Test/Async/NHSpecificTest/NH3848/Fixture.cs b/src/NHibernate.Test/Async/NHSpecificTest/NH3848/Fixture.cs index 56d2ecb8bb5..df1381aef34 100644 --- a/src/NHibernate.Test/Async/NHSpecificTest/NH3848/Fixture.cs +++ b/src/NHibernate.Test/Async/NHSpecificTest/NH3848/Fixture.cs @@ -338,13 +338,13 @@ public virtual async Task ChildCollectionsFromLeftOuterJoinOnlyWithRestrictionSh Assert.That( customersWithOrderNumberEqualsTo2AndCompanies.First(n => n.Id == Customer1.Id).Companies, - Has.Count.EqualTo(Customer1.Companies.Count())); + Has.Count.EqualTo(Customer1.Companies.Count)); Assert.That( customersWithOrderNumberEqualsTo2AndCompanies.First(n => n.Id == Customer2.Id).Companies, - Has.Count.EqualTo(Customer2.Companies.Count())); + Has.Count.EqualTo(Customer2.Companies.Count)); Assert.That( customersWithOrderNumberEqualsTo2AndCompanies.First(n => n.Id == Customer3.Id).Companies, - Has.Count.EqualTo(Customer3.Companies.Count())); + Has.Count.EqualTo(Customer3.Companies.Count)); Assert.That(customers.Single(n => n.Id == Customer1.Id).Orders, Has.Count.EqualTo(0)); Assert.That(customers.Single(n => n.Id == Customer2.Id).Orders, Has.Count.EqualTo(0)); @@ -372,7 +372,7 @@ public virtual async Task ChildCollectionsFromLeftOuterJoinOnlyWithRestrictionSh if (!entityPersister.HasCache) return; - var querySpaces = entityPersister.QuerySpaces.Cast().ToList().AsReadOnly(); + var querySpaces = entityPersister.QuerySpaces.ToList().AsReadOnly(); await (Sfi.UpdateTimestampsCache.PreInvalidateAsync(querySpaces, cancellationToken)); } @@ -384,7 +384,7 @@ protected void ClearSecondLevelCacheFor(System.Type entity) if (!entityPersister.HasCache) return; - var querySpaces = entityPersister.QuerySpaces.Cast().ToList().AsReadOnly(); + var querySpaces = entityPersister.QuerySpaces.ToList().AsReadOnly(); Sfi.UpdateTimestampsCache.PreInvalidate(querySpaces); } @@ -417,7 +417,6 @@ protected void ClearCollectionCache(Expression> pathToCo Sfi.EvictCollection(role); } - protected abstract Task> GetCustomersWithFetchedOrdersWithoutRestrictionsAsync(ISession session, CancellationToken cancellationToken = default(CancellationToken)); protected abstract Task> GetCustomersWithOrdersEagerLoadedAsync(ISession session, CancellationToken cancellationToken = default(CancellationToken)); protected abstract Task> GetCustomersByOrderNumberUsingOnClauseAsync(ISession session, int orderNumber, CancellationToken cancellationToken = default(CancellationToken)); protected abstract Task> GetCustomersByOrderNumberUsingWhereClauseAsync(ISession session, int orderNumber, CancellationToken cancellationToken = default(CancellationToken)); diff --git a/src/NHibernate.Test/Async/NHSpecificTest/NH3848/QueryOverTestFixture.cs b/src/NHibernate.Test/Async/NHSpecificTest/NH3848/QueryOverTestFixture.cs index 99fbc019314..3d3ee9685f2 100644 --- a/src/NHibernate.Test/Async/NHSpecificTest/NH3848/QueryOverTestFixture.cs +++ b/src/NHibernate.Test/Async/NHSpecificTest/NH3848/QueryOverTestFixture.cs @@ -19,27 +19,6 @@ namespace NHibernate.Test.NHSpecificTest.NH3848 using System.Threading; public class QueryOverTestFixtureAsync : FixtureAsync { - protected override Task> GetCustomersWithFetchedOrdersWithoutRestrictionsAsync(ISession session, CancellationToken cancellationToken = default(CancellationToken)) - { - try - { - Order ordersAlias = null; - return - session - .QueryOver() - .JoinAlias( - n => n.Orders, - () => ordersAlias, - JoinType.LeftOuterJoin) - .TransformUsing(new DistinctRootEntityResultTransformer()) - .ListAsync(cancellationToken); - } - catch (System.Exception ex) - { - return Task.FromException>(ex); - } - } - protected override Task> GetCustomersWithOrdersEagerLoadedAsync(ISession session, CancellationToken cancellationToken = default(CancellationToken)) { try diff --git a/src/NHibernate.Test/NHSpecificTest/NH3848/CriteriaTestFixture.cs b/src/NHibernate.Test/NHSpecificTest/NH3848/CriteriaTestFixture.cs index 62c89aa9b28..1a5f026ea7a 100644 --- a/src/NHibernate.Test/NHSpecificTest/NH3848/CriteriaTestFixture.cs +++ b/src/NHibernate.Test/NHSpecificTest/NH3848/CriteriaTestFixture.cs @@ -7,16 +7,6 @@ namespace NHibernate.Test.NHSpecificTest.NH3848 { public class CriteriaTestFixture : Fixture { - protected override IList GetCustomersWithFetchedOrdersWithoutRestrictions(ISession session) - { - return - session - .CreateCriteria() - .CreateAlias("Orders", "order", JoinType.LeftOuterJoin) - .SetResultTransformer(new DistinctRootEntityResultTransformer()) - .List(); - } - protected override IList GetCustomersWithOrdersEagerLoaded(ISession session) { return diff --git a/src/NHibernate.Test/NHSpecificTest/NH3848/Fixture.cs b/src/NHibernate.Test/NHSpecificTest/NH3848/Fixture.cs index 530a2514254..67d280f2728 100644 --- a/src/NHibernate.Test/NHSpecificTest/NH3848/Fixture.cs +++ b/src/NHibernate.Test/NHSpecificTest/NH3848/Fixture.cs @@ -326,13 +326,13 @@ public virtual void ChildCollectionsFromLeftOuterJoinOnlyWithRestrictionShouldNo Assert.That( customersWithOrderNumberEqualsTo2AndCompanies.First(n => n.Id == Customer1.Id).Companies, - Has.Count.EqualTo(Customer1.Companies.Count())); + Has.Count.EqualTo(Customer1.Companies.Count)); Assert.That( customersWithOrderNumberEqualsTo2AndCompanies.First(n => n.Id == Customer2.Id).Companies, - Has.Count.EqualTo(Customer2.Companies.Count())); + Has.Count.EqualTo(Customer2.Companies.Count)); Assert.That( customersWithOrderNumberEqualsTo2AndCompanies.First(n => n.Id == Customer3.Id).Companies, - Has.Count.EqualTo(Customer3.Companies.Count())); + Has.Count.EqualTo(Customer3.Companies.Count)); Assert.That(customers.Single(n => n.Id == Customer1.Id).Orders, Has.Count.EqualTo(0)); Assert.That(customers.Single(n => n.Id == Customer2.Id).Orders, Has.Count.EqualTo(0)); @@ -360,7 +360,7 @@ protected void ClearSecondLevelCacheFor(System.Type entity) if (!entityPersister.HasCache) return; - var querySpaces = entityPersister.QuerySpaces.Cast().ToList().AsReadOnly(); + var querySpaces = entityPersister.QuerySpaces.ToList().AsReadOnly(); Sfi.UpdateTimestampsCache.PreInvalidate(querySpaces); } @@ -375,7 +375,6 @@ protected void ClearCollectionCache(Expression> pathToCo Sfi.EvictCollection(role); } - protected abstract IList GetCustomersWithFetchedOrdersWithoutRestrictions(ISession session); protected abstract IList GetCustomersWithOrdersEagerLoaded(ISession session); protected abstract IList GetCustomersByOrderNumberUsingOnClause(ISession session, int orderNumber); protected abstract IList GetCustomersByOrderNumberUsingWhereClause(ISession session, int orderNumber); diff --git a/src/NHibernate.Test/NHSpecificTest/NH3848/QueryOverTestFixture.cs b/src/NHibernate.Test/NHSpecificTest/NH3848/QueryOverTestFixture.cs index c338125886c..c6c05f02275 100644 --- a/src/NHibernate.Test/NHSpecificTest/NH3848/QueryOverTestFixture.cs +++ b/src/NHibernate.Test/NHSpecificTest/NH3848/QueryOverTestFixture.cs @@ -7,20 +7,6 @@ namespace NHibernate.Test.NHSpecificTest.NH3848 { public class QueryOverTestFixture : Fixture { - protected override IList GetCustomersWithFetchedOrdersWithoutRestrictions(ISession session) - { - Order ordersAlias = null; - return - session - .QueryOver() - .JoinAlias( - n => n.Orders, - () => ordersAlias, - JoinType.LeftOuterJoin) - .TransformUsing(new DistinctRootEntityResultTransformer()) - .List(); - } - protected override IList GetCustomersWithOrdersEagerLoaded(ISession session) { return From 32c1f8bffa0fbf88088a8e65148049118b6235aa Mon Sep 17 00:00:00 2001 From: "Iwanow, Mihail (EXT)" Date: Tue, 20 Nov 2018 12:32:31 +0100 Subject: [PATCH 14/20] Skipping collections cache inside JoinWalker --- .../NH3848/CriteriaTestFixture.cs | 71 +++++++ .../Async/NHSpecificTest/NH3848/Fixture.cs | 187 ++++++++++++++++++ .../NH3848/QueryOverTestFixture.cs | 97 +++++++++ .../NH3848/CriteriaTestFixture.cs | 43 ++++ .../NHSpecificTest/NH3848/Fixture.cs | 187 ++++++++++++++++++ .../NH3848/QueryOverTestFixture.cs | 69 +++++++ .../Engine/Loading/CollectionLoadContext.cs | 20 +- .../Async/Impl/MultiCriteriaImpl.cs | 2 +- src/NHibernate/Async/Impl/MultiQueryImpl.cs | 2 +- .../Async/Loader/Criteria/CriteriaLoader.cs | 1 + src/NHibernate/Async/Loader/Loader.cs | 14 +- .../Async/Multi/QueryBatchItemBase.cs | 2 +- .../Engine/Loading/CollectionLoadContext.cs | 20 +- src/NHibernate/Engine/QueryParameters.cs | 33 +--- src/NHibernate/Impl/MultiCriteriaImpl.cs | 2 +- src/NHibernate/Impl/MultiQueryImpl.cs | 2 +- .../Loader/Criteria/CriteriaJoinWalker.cs | 10 + .../Loader/Criteria/CriteriaLoader.cs | 7 + .../Criteria/CriteriaQueryTranslator.cs | 12 +- src/NHibernate/Loader/JoinWalker.cs | 13 +- src/NHibernate/Loader/Loader.cs | 16 +- .../Loader/OuterJoinableAssociation.cs | 18 ++ 22 files changed, 748 insertions(+), 80 deletions(-) diff --git a/src/NHibernate.Test/Async/NHSpecificTest/NH3848/CriteriaTestFixture.cs b/src/NHibernate.Test/Async/NHSpecificTest/NH3848/CriteriaTestFixture.cs index 196fa8fc36e..3955fe3e783 100644 --- a/src/NHibernate.Test/Async/NHSpecificTest/NH3848/CriteriaTestFixture.cs +++ b/src/NHibernate.Test/Async/NHSpecificTest/NH3848/CriteriaTestFixture.cs @@ -52,6 +52,77 @@ public class CriteriaTestFixtureAsync : FixtureAsync } } + protected override Task> GetCustomersByOrderNumberUsingFetchAsync(ISession session, int orderNumber, CancellationToken cancellationToken = default(CancellationToken)) + { + try + { + return + session + .CreateCriteria() + .CreateAlias("Orders", "order", JoinType.InnerJoin, Restrictions.Eq("Number", orderNumber)) + .Fetch(SelectMode.Fetch, "Orders") + .ListAsync(cancellationToken); + } + catch (System.Exception ex) + { + return Task.FromException>(ex); + } + } + + protected override Task> GetCustomersByOrderNumberUsingFetchAndWhereClauseAsync(ISession session, int orderNumber, CancellationToken cancellationToken = default(CancellationToken)) + { + try + { + return + session + .CreateCriteria() + .Fetch(SelectMode.Fetch, "Orders") + .CreateCriteria("Orders", JoinType.InnerJoin) + .Add(Restrictions.Eq("Number", orderNumber)) + .ListAsync(cancellationToken); + } + catch (System.Exception ex) + { + return Task.FromException>(ex); + } + } + + protected override Task> GetCustomersAndCompaniesByOrderNumberUsingFetchAndWhereClauseAsync(ISession session, int orderNumber, string name, CancellationToken cancellationToken = default(CancellationToken)) + { + try + { + return + session + .CreateCriteria() + .CreateAlias("Orders", "order", JoinType.InnerJoin, Restrictions.Eq("Number", orderNumber)) + .Fetch(SelectMode.Fetch, "Orders") + .CreateAlias("Companies", "company", JoinType.LeftOuterJoin, Restrictions.Eq("Name", name)) + .ListAsync(cancellationToken); + } + catch (System.Exception ex) + { + return Task.FromException>(ex); + } + } + + protected override Task> GetCustomersAndCompaniesByOrderNumberUsingFetchWithoutRestrictionsAsync(ISession session, CancellationToken cancellationToken = default(CancellationToken)) + { + try + { + return + session + .CreateCriteria() + .Fetch(SelectMode.Fetch, "Orders") + .CreateAlias("Orders", "order", JoinType.InnerJoin) + .CreateAlias("Companies", "company", JoinType.LeftOuterJoin) + .ListAsync(cancellationToken); + } + catch (System.Exception ex) + { + return Task.FromException>(ex); + } + } + protected override Task> GetCustomersWithCompaniesByOrderNumberUsingOnClauseAsync( ISession session, int orderNumber, CancellationToken cancellationToken = default(CancellationToken)) diff --git a/src/NHibernate.Test/Async/NHSpecificTest/NH3848/Fixture.cs b/src/NHibernate.Test/Async/NHSpecificTest/NH3848/Fixture.cs index df1381aef34..be5a8125f28 100644 --- a/src/NHibernate.Test/Async/NHSpecificTest/NH3848/Fixture.cs +++ b/src/NHibernate.Test/Async/NHSpecificTest/NH3848/Fixture.cs @@ -185,6 +185,61 @@ public virtual async Task ChildCollectionsFromLeftOuterJoinWithOnClauseRestricti secondSession.Dispose(); } + [Test] + public virtual async Task ChildCollectionsWithSelectModeFetchOnCollectionShouldNotBeInSecondLevelCacheAsync() + { + var firstSession = OpenSession(); + var customersWithOrderNumberEqualsTo2 = await (GetCustomersByOrderNumberUsingFetchAsync(firstSession, OrderNumber)); + + var secondSession = OpenSession(); + var customers = await (GetAllCustomersAsync(secondSession)); + + Assert.That( + customersWithOrderNumberEqualsTo2.Single(n => n.Id == Customer1.Id).Orders, + Has.Count.EqualTo(Customer1.Orders.Count(n => n.Number == OrderNumber))); + Assert.That( + customersWithOrderNumberEqualsTo2.Single(n => n.Id == Customer2.Id).Orders, + Has.Count.EqualTo(Customer2.Orders.Count(n => n.Number == OrderNumber))); + Assert.That( + customersWithOrderNumberEqualsTo2, + Has.Count.EqualTo(2)); + + Assert.That(customers.Single(n => n.Id == Customer1.Id).Orders, Has.Count.EqualTo(Customer1.Orders.Count)); + Assert.That(customers.Single(n => n.Id == Customer2.Id).Orders, Has.Count.EqualTo(Customer2.Orders.Count)); + Assert.That(customers.Single(n => n.Id == Customer3.Id).Orders, Has.Count.EqualTo(Customer3.Orders.Count)); + + firstSession.Dispose(); + secondSession.Dispose(); + } + + [Test] + public virtual async Task ChildCollectionsWithSelectModeFetchAndWhereClauseShouldNotBeInSecondLevelCacheAsync() + { + var firstSession = OpenSession(); + var customersWithOrderNumberEqualsTo2 = await (GetCustomersByOrderNumberUsingFetchAndWhereClauseAsync(firstSession, OrderNumber)); + + var secondSession = OpenSession(); + var customers = await (GetAllCustomersAsync(secondSession)); + + Assert.That( + customersWithOrderNumberEqualsTo2.Single(n => n.Id == Customer1.Id).Orders, + Has.Count.EqualTo(Customer1.Orders.Count(n => n.Number == OrderNumber))); + Assert.That( + customersWithOrderNumberEqualsTo2.Single(n => n.Id == Customer2.Id).Orders, + Has.Count.EqualTo(Customer2.Orders.Count(n => n.Number == OrderNumber))); + Assert.That( + customersWithOrderNumberEqualsTo2, + Has.Count.EqualTo(2)); + + Assert.That(customers.Single(n => n.Id == Customer1.Id).Orders, Has.Count.EqualTo(Customer1.Orders.Count)); + Assert.That(customers.Single(n => n.Id == Customer2.Id).Orders, Has.Count.EqualTo(Customer2.Orders.Count)); + Assert.That(customers.Single(n => n.Id == Customer3.Id).Orders, Has.Count.EqualTo(Customer3.Orders.Count)); + + firstSession.Dispose(); + secondSession.Dispose(); + } + + [Test] public async Task ChildCollectionsFromLeftOuterJoinWithWhereClauseRestrictionOnCollectionShouldNotBeInSecondLevelCacheAsync() { @@ -364,6 +419,134 @@ public virtual async Task ChildCollectionsFromLeftOuterJoinOnlyWithRestrictionSh secondSession.Dispose(); } + [Test] + public virtual async Task ChildCollectionsWithoutRestrictionShouldBeIn2LvlCacheAsync() + { + var firstSession = OpenSession(); + var customersWithOrdersAndCompaniesWithoutRestrictions = + await (GetCustomersAndCompaniesByOrderNumberUsingFetchWithoutRestrictionsAsync(firstSession)); + + using (var session = OpenSession()) + using (IDbCommand cmd = session.Connection.CreateCommand()) + { + cmd.CommandText = "DELETE FROM Orders"; + cmd.ExecuteNonQuery(); + cmd.Connection.Close(); + } + + using (var session = OpenSession()) + using (IDbCommand cmd = session.Connection.CreateCommand()) + { + cmd.CommandText = "DELETE FROM Companies"; + cmd.ExecuteNonQuery(); + cmd.Connection.Close(); + } + + var secondSession = OpenSession(); + var customers = await (GetAllCustomersAsync(secondSession)); + + Assert.That( + customersWithOrdersAndCompaniesWithoutRestrictions.First(n => n.Id == Customer1.Id).Orders, + Has.Count.EqualTo(Customer1.Orders.Count())); + Assert.That( + customersWithOrdersAndCompaniesWithoutRestrictions.First(n => n.Id == Customer2.Id).Orders, + Has.Count.EqualTo(Customer2.Orders.Count())); + Assert.That( + customersWithOrdersAndCompaniesWithoutRestrictions.First(n => n.Id == Customer3.Id).Orders, + Has.Count.EqualTo(Customer3.Orders.Count())); + + Assert.That( + customersWithOrdersAndCompaniesWithoutRestrictions.First(n => n.Id == Customer1.Id).Companies, + Has.Count.EqualTo(Customer1.Companies.Count)); + Assert.That( + customersWithOrdersAndCompaniesWithoutRestrictions.First(n => n.Id == Customer2.Id).Companies, + Has.Count.EqualTo(Customer2.Companies.Count)); + Assert.That( + customersWithOrdersAndCompaniesWithoutRestrictions.First(n => n.Id == Customer3.Id).Companies, + Has.Count.EqualTo(Customer3.Companies.Count)); + + Assert.That( + customers.First(n => n.Id == Customer1.Id).Orders, + Has.Count.EqualTo(Customer1.Orders.Count())); + Assert.That( + customers.First(n => n.Id == Customer2.Id).Orders, + Has.Count.EqualTo(Customer2.Orders.Count())); + Assert.That( + customers.First(n => n.Id == Customer3.Id).Orders, + Has.Count.EqualTo(Customer3.Orders.Count())); + + Assert.That( + customers.First(n => n.Id == Customer1.Id).Companies, + Has.Count.EqualTo(Customer1.Companies.Count())); + Assert.That( + customers.First(n => n.Id == Customer2.Id).Companies, + Has.Count.EqualTo(Customer2.Companies.Count())); + Assert.That( + customers.First(n => n.Id == Customer3.Id).Companies, + Has.Count.EqualTo(Customer3.Companies.Count())); + + firstSession.Dispose(); + secondSession.Dispose(); + } + + [Test] + public virtual async Task ChildCollectionsWithRestrictionShouldNotBeIn2LvlCacheAsync() + { + var firstSession = OpenSession(); + var customersWithOrdersAndCompaniesWithRestrictions = + await (GetCustomersAndCompaniesByOrderNumberUsingFetchAndWhereClauseAsync(firstSession, OrderNumber, "Second")); + + using (var session = OpenSession()) + using (IDbCommand cmd = session.Connection.CreateCommand()) + { + cmd.CommandText = "DELETE FROM Orders"; + cmd.ExecuteNonQuery(); + cmd.Connection.Close(); + } + + using (var session = OpenSession()) + using (IDbCommand cmd = session.Connection.CreateCommand()) + { + cmd.CommandText = "DELETE FROM Companies"; + cmd.ExecuteNonQuery(); + cmd.Connection.Close(); + } + + var secondSession = OpenSession(); + var customers = await (GetAllCustomersAsync(secondSession)); + + Assert.That( + customersWithOrdersAndCompaniesWithRestrictions.First(n => n.Id == Customer1.Id).Orders, + Has.Count.EqualTo(Customer1.Orders.Count(n => n.Number == OrderNumber))); + Assert.That( + customersWithOrdersAndCompaniesWithRestrictions.First(n => n.Id == Customer2.Id).Orders, + Has.Count.EqualTo(Customer2.Orders.Count(n => n.Number == OrderNumber))); + + Assert.That( + customersWithOrdersAndCompaniesWithRestrictions.First(n => n.Id == Customer1.Id).Companies, + Has.Count.EqualTo(Customer1.Companies.Count(n => n.Name == "Second"))); + Assert.That( + customersWithOrdersAndCompaniesWithRestrictions.First(n => n.Id == Customer2.Id).Companies, + Has.Count.EqualTo(Customer2.Companies.Count(n => n.Name == "Second"))); + + Assert.That(customers.Single(n => n.Id == Customer1.Id).Orders, Has.Count.EqualTo(0)); + Assert.That(customers.Single(n => n.Id == Customer2.Id).Orders, Has.Count.EqualTo(0)); + Assert.That(customers.Single(n => n.Id == Customer3.Id).Orders, Has.Count.EqualTo(0)); + + Assert.That( + customers.Single(n => n.Id == Customer1.Id).Companies, + Has.Count.EqualTo(0)); + Assert.That( + customers.Single(n => n.Id == Customer2.Id).Companies, + Has.Count.EqualTo(0)); + Assert.That( + customers.Single(n => n.Id == Customer3.Id).Companies, + Has.Count.EqualTo(0)); + + firstSession.Dispose(); + secondSession.Dispose(); + } + protected async Task ClearSecondLevelCacheForAsync(System.Type entity, CancellationToken cancellationToken = default(CancellationToken)) { var entityName = entity.FullName; @@ -421,6 +604,10 @@ protected void ClearCollectionCache(Expression> pathToCo protected abstract Task> GetCustomersByOrderNumberUsingOnClauseAsync(ISession session, int orderNumber, CancellationToken cancellationToken = default(CancellationToken)); protected abstract Task> GetCustomersByOrderNumberUsingWhereClauseAsync(ISession session, int orderNumber, CancellationToken cancellationToken = default(CancellationToken)); protected abstract Task> GetCustomersByNameUsingWhereClauseAsync(ISession session, string customerName, CancellationToken cancellationToken = default(CancellationToken)); + protected abstract Task> GetCustomersByOrderNumberUsingFetchAsync(ISession session, int orderNumber, CancellationToken cancellationToken = default(CancellationToken)); + protected abstract Task> GetCustomersByOrderNumberUsingFetchAndWhereClauseAsync(ISession session, int orderNumber, CancellationToken cancellationToken = default(CancellationToken)); + protected abstract Task> GetCustomersAndCompaniesByOrderNumberUsingFetchAndWhereClauseAsync(ISession session, int orderNumber, string name, CancellationToken cancellationToken = default(CancellationToken)); + protected abstract Task> GetCustomersAndCompaniesByOrderNumberUsingFetchWithoutRestrictionsAsync(ISession session, CancellationToken cancellationToken = default(CancellationToken)); protected abstract Task> GetCustomersByOrderNumberUsingSubqueriesAndByNameUsingWhereClauseAsync( ISession session, int orderNumber, diff --git a/src/NHibernate.Test/Async/NHSpecificTest/NH3848/QueryOverTestFixture.cs b/src/NHibernate.Test/Async/NHSpecificTest/NH3848/QueryOverTestFixture.cs index 3d3ee9685f2..d38763a2748 100644 --- a/src/NHibernate.Test/Async/NHSpecificTest/NH3848/QueryOverTestFixture.cs +++ b/src/NHibernate.Test/Async/NHSpecificTest/NH3848/QueryOverTestFixture.cs @@ -57,6 +57,103 @@ public class QueryOverTestFixtureAsync : FixtureAsync } } + protected override Task> GetCustomersByOrderNumberUsingFetchAsync(ISession session, int orderNumber, CancellationToken cancellationToken = default(CancellationToken)) + { + try + { + Order ordersAlias = null; + + return + session + .QueryOver() + .JoinQueryOver(ec => ec.Orders, () => ordersAlias, JoinType.InnerJoin, Restrictions.Eq("Number", orderNumber)) + .Fetch(SelectMode.Fetch, () => ordersAlias) + .TransformUsing(Transformers.DistinctRootEntity) + .ListAsync(cancellationToken); + } + catch (System.Exception ex) + { + return Task.FromException>(ex); + } + } + + protected override Task> GetCustomersByOrderNumberUsingFetchAndWhereClauseAsync(ISession session, int orderNumber, CancellationToken cancellationToken = default(CancellationToken)) + { + try + { + Order ordersAlias = null; + + return + session + .QueryOver() + .JoinQueryOver(ec => ec.Orders, () => ordersAlias, JoinType.InnerJoin) + .Where(Restrictions.Eq("Number", orderNumber)) + .Fetch(SelectMode.Fetch, () => ordersAlias) + .TransformUsing(Transformers.DistinctRootEntity) + .ListAsync(cancellationToken); + } + catch (System.Exception ex) + { + return Task.FromException>(ex); + } + } + + protected override Task> GetCustomersAndCompaniesByOrderNumberUsingFetchAndWhereClauseAsync(ISession session, int orderNumber, string name, CancellationToken cancellationToken = default(CancellationToken)) + { + try + { + Order ordersAlias = null; + Company companiesAlias = null; + + return + session + .QueryOver() + .JoinAlias( + n => n.Orders, + () => ordersAlias, + JoinType.InnerJoin, + Restrictions.Eq("Number", orderNumber)) + .Fetch(SelectMode.Fetch, () => ordersAlias) + .JoinAlias( + n => n.Companies, + () => companiesAlias, + JoinType.LeftOuterJoin, + Restrictions.Eq("Name", name)) + .ListAsync(cancellationToken); + } + catch (System.Exception ex) + { + return Task.FromException>(ex); + } + } + + protected override Task> GetCustomersAndCompaniesByOrderNumberUsingFetchWithoutRestrictionsAsync(ISession session, CancellationToken cancellationToken = default(CancellationToken)) + { + try + { + Order ordersAlias = null; + Company companiesAlias = null; + + return + session + .QueryOver() + .JoinAlias( + n => n.Orders, + () => ordersAlias, + JoinType.InnerJoin) + .Fetch(SelectMode.Fetch, () => ordersAlias) + .JoinAlias( + n => n.Companies, + () => companiesAlias, + JoinType.LeftOuterJoin) + .ListAsync(cancellationToken); + } + catch (System.Exception ex) + { + return Task.FromException>(ex); + } + } + protected override Task> GetCustomersWithCompaniesByOrderNumberUsingOnClauseAsync( ISession session, int orderNumber, CancellationToken cancellationToken = default(CancellationToken)) diff --git a/src/NHibernate.Test/NHSpecificTest/NH3848/CriteriaTestFixture.cs b/src/NHibernate.Test/NHSpecificTest/NH3848/CriteriaTestFixture.cs index 1a5f026ea7a..c0d6597ebe3 100644 --- a/src/NHibernate.Test/NHSpecificTest/NH3848/CriteriaTestFixture.cs +++ b/src/NHibernate.Test/NHSpecificTest/NH3848/CriteriaTestFixture.cs @@ -26,6 +26,49 @@ protected override IList GetCustomersByOrderNumberUsingOnClause(ISessi .List(); } + protected override IList GetCustomersByOrderNumberUsingFetch(ISession session, int orderNumber) + { + return + session + .CreateCriteria() + .CreateAlias("Orders", "order", JoinType.InnerJoin, Restrictions.Eq("Number", orderNumber)) + .Fetch(SelectMode.Fetch, "Orders") + .List(); + } + + protected override IList GetCustomersByOrderNumberUsingFetchAndWhereClause(ISession session, int orderNumber) + { + return + session + .CreateCriteria() + .Fetch(SelectMode.Fetch, "Orders") + .CreateCriteria("Orders", JoinType.InnerJoin) + .Add(Restrictions.Eq("Number", orderNumber)) + .List(); + } + + protected override IList GetCustomersAndCompaniesByOrderNumberUsingFetchAndWhereClause(ISession session, int orderNumber, string name) + { + return + session + .CreateCriteria() + .CreateAlias("Orders", "order", JoinType.InnerJoin, Restrictions.Eq("Number", orderNumber)) + .Fetch(SelectMode.Fetch, "Orders") + .CreateAlias("Companies", "company", JoinType.LeftOuterJoin, Restrictions.Eq("Name", name)) + .List(); + } + + protected override IList GetCustomersAndCompaniesByOrderNumberUsingFetchWithoutRestrictions(ISession session) + { + return + session + .CreateCriteria() + .Fetch(SelectMode.Fetch, "Orders") + .CreateAlias("Orders", "order", JoinType.InnerJoin) + .CreateAlias("Companies", "company", JoinType.LeftOuterJoin) + .List(); + } + protected override IList GetCustomersWithCompaniesByOrderNumberUsingOnClause( ISession session, int orderNumber) diff --git a/src/NHibernate.Test/NHSpecificTest/NH3848/Fixture.cs b/src/NHibernate.Test/NHSpecificTest/NH3848/Fixture.cs index 67d280f2728..84fca8232d3 100644 --- a/src/NHibernate.Test/NHSpecificTest/NH3848/Fixture.cs +++ b/src/NHibernate.Test/NHSpecificTest/NH3848/Fixture.cs @@ -173,6 +173,61 @@ public virtual void ChildCollectionsFromLeftOuterJoinWithOnClauseRestrictionOnCo secondSession.Dispose(); } + [Test] + public virtual void ChildCollectionsWithSelectModeFetchOnCollectionShouldNotBeInSecondLevelCache() + { + var firstSession = OpenSession(); + var customersWithOrderNumberEqualsTo2 = GetCustomersByOrderNumberUsingFetch(firstSession, OrderNumber); + + var secondSession = OpenSession(); + var customers = GetAllCustomers(secondSession); + + Assert.That( + customersWithOrderNumberEqualsTo2.Single(n => n.Id == Customer1.Id).Orders, + Has.Count.EqualTo(Customer1.Orders.Count(n => n.Number == OrderNumber))); + Assert.That( + customersWithOrderNumberEqualsTo2.Single(n => n.Id == Customer2.Id).Orders, + Has.Count.EqualTo(Customer2.Orders.Count(n => n.Number == OrderNumber))); + Assert.That( + customersWithOrderNumberEqualsTo2, + Has.Count.EqualTo(2)); + + Assert.That(customers.Single(n => n.Id == Customer1.Id).Orders, Has.Count.EqualTo(Customer1.Orders.Count)); + Assert.That(customers.Single(n => n.Id == Customer2.Id).Orders, Has.Count.EqualTo(Customer2.Orders.Count)); + Assert.That(customers.Single(n => n.Id == Customer3.Id).Orders, Has.Count.EqualTo(Customer3.Orders.Count)); + + firstSession.Dispose(); + secondSession.Dispose(); + } + + [Test] + public virtual void ChildCollectionsWithSelectModeFetchAndWhereClauseShouldNotBeInSecondLevelCache() + { + var firstSession = OpenSession(); + var customersWithOrderNumberEqualsTo2 = GetCustomersByOrderNumberUsingFetchAndWhereClause(firstSession, OrderNumber); + + var secondSession = OpenSession(); + var customers = GetAllCustomers(secondSession); + + Assert.That( + customersWithOrderNumberEqualsTo2.Single(n => n.Id == Customer1.Id).Orders, + Has.Count.EqualTo(Customer1.Orders.Count(n => n.Number == OrderNumber))); + Assert.That( + customersWithOrderNumberEqualsTo2.Single(n => n.Id == Customer2.Id).Orders, + Has.Count.EqualTo(Customer2.Orders.Count(n => n.Number == OrderNumber))); + Assert.That( + customersWithOrderNumberEqualsTo2, + Has.Count.EqualTo(2)); + + Assert.That(customers.Single(n => n.Id == Customer1.Id).Orders, Has.Count.EqualTo(Customer1.Orders.Count)); + Assert.That(customers.Single(n => n.Id == Customer2.Id).Orders, Has.Count.EqualTo(Customer2.Orders.Count)); + Assert.That(customers.Single(n => n.Id == Customer3.Id).Orders, Has.Count.EqualTo(Customer3.Orders.Count)); + + firstSession.Dispose(); + secondSession.Dispose(); + } + + [Test] public void ChildCollectionsFromLeftOuterJoinWithWhereClauseRestrictionOnCollectionShouldNotBeInSecondLevelCache() { @@ -352,6 +407,134 @@ public virtual void ChildCollectionsFromLeftOuterJoinOnlyWithRestrictionShouldNo secondSession.Dispose(); } + [Test] + public virtual void ChildCollectionsWithoutRestrictionShouldBeIn2LvlCache() + { + var firstSession = OpenSession(); + var customersWithOrdersAndCompaniesWithoutRestrictions = + GetCustomersAndCompaniesByOrderNumberUsingFetchWithoutRestrictions(firstSession); + + using (var session = OpenSession()) + using (IDbCommand cmd = session.Connection.CreateCommand()) + { + cmd.CommandText = "DELETE FROM Orders"; + cmd.ExecuteNonQuery(); + cmd.Connection.Close(); + } + + using (var session = OpenSession()) + using (IDbCommand cmd = session.Connection.CreateCommand()) + { + cmd.CommandText = "DELETE FROM Companies"; + cmd.ExecuteNonQuery(); + cmd.Connection.Close(); + } + + var secondSession = OpenSession(); + var customers = GetAllCustomers(secondSession); + + Assert.That( + customersWithOrdersAndCompaniesWithoutRestrictions.First(n => n.Id == Customer1.Id).Orders, + Has.Count.EqualTo(Customer1.Orders.Count())); + Assert.That( + customersWithOrdersAndCompaniesWithoutRestrictions.First(n => n.Id == Customer2.Id).Orders, + Has.Count.EqualTo(Customer2.Orders.Count())); + Assert.That( + customersWithOrdersAndCompaniesWithoutRestrictions.First(n => n.Id == Customer3.Id).Orders, + Has.Count.EqualTo(Customer3.Orders.Count())); + + Assert.That( + customersWithOrdersAndCompaniesWithoutRestrictions.First(n => n.Id == Customer1.Id).Companies, + Has.Count.EqualTo(Customer1.Companies.Count)); + Assert.That( + customersWithOrdersAndCompaniesWithoutRestrictions.First(n => n.Id == Customer2.Id).Companies, + Has.Count.EqualTo(Customer2.Companies.Count)); + Assert.That( + customersWithOrdersAndCompaniesWithoutRestrictions.First(n => n.Id == Customer3.Id).Companies, + Has.Count.EqualTo(Customer3.Companies.Count)); + + Assert.That( + customers.First(n => n.Id == Customer1.Id).Orders, + Has.Count.EqualTo(Customer1.Orders.Count())); + Assert.That( + customers.First(n => n.Id == Customer2.Id).Orders, + Has.Count.EqualTo(Customer2.Orders.Count())); + Assert.That( + customers.First(n => n.Id == Customer3.Id).Orders, + Has.Count.EqualTo(Customer3.Orders.Count())); + + Assert.That( + customers.First(n => n.Id == Customer1.Id).Companies, + Has.Count.EqualTo(Customer1.Companies.Count())); + Assert.That( + customers.First(n => n.Id == Customer2.Id).Companies, + Has.Count.EqualTo(Customer2.Companies.Count())); + Assert.That( + customers.First(n => n.Id == Customer3.Id).Companies, + Has.Count.EqualTo(Customer3.Companies.Count())); + + firstSession.Dispose(); + secondSession.Dispose(); + } + + [Test] + public virtual void ChildCollectionsWithRestrictionShouldNotBeIn2LvlCache() + { + var firstSession = OpenSession(); + var customersWithOrdersAndCompaniesWithRestrictions = + GetCustomersAndCompaniesByOrderNumberUsingFetchAndWhereClause(firstSession, OrderNumber, "Second"); + + using (var session = OpenSession()) + using (IDbCommand cmd = session.Connection.CreateCommand()) + { + cmd.CommandText = "DELETE FROM Orders"; + cmd.ExecuteNonQuery(); + cmd.Connection.Close(); + } + + using (var session = OpenSession()) + using (IDbCommand cmd = session.Connection.CreateCommand()) + { + cmd.CommandText = "DELETE FROM Companies"; + cmd.ExecuteNonQuery(); + cmd.Connection.Close(); + } + + var secondSession = OpenSession(); + var customers = GetAllCustomers(secondSession); + + Assert.That( + customersWithOrdersAndCompaniesWithRestrictions.First(n => n.Id == Customer1.Id).Orders, + Has.Count.EqualTo(Customer1.Orders.Count(n => n.Number == OrderNumber))); + Assert.That( + customersWithOrdersAndCompaniesWithRestrictions.First(n => n.Id == Customer2.Id).Orders, + Has.Count.EqualTo(Customer2.Orders.Count(n => n.Number == OrderNumber))); + + Assert.That( + customersWithOrdersAndCompaniesWithRestrictions.First(n => n.Id == Customer1.Id).Companies, + Has.Count.EqualTo(Customer1.Companies.Count(n => n.Name == "Second"))); + Assert.That( + customersWithOrdersAndCompaniesWithRestrictions.First(n => n.Id == Customer2.Id).Companies, + Has.Count.EqualTo(Customer2.Companies.Count(n => n.Name == "Second"))); + + Assert.That(customers.Single(n => n.Id == Customer1.Id).Orders, Has.Count.EqualTo(0)); + Assert.That(customers.Single(n => n.Id == Customer2.Id).Orders, Has.Count.EqualTo(0)); + Assert.That(customers.Single(n => n.Id == Customer3.Id).Orders, Has.Count.EqualTo(0)); + + Assert.That( + customers.Single(n => n.Id == Customer1.Id).Companies, + Has.Count.EqualTo(0)); + Assert.That( + customers.Single(n => n.Id == Customer2.Id).Companies, + Has.Count.EqualTo(0)); + Assert.That( + customers.Single(n => n.Id == Customer3.Id).Companies, + Has.Count.EqualTo(0)); + + firstSession.Dispose(); + secondSession.Dispose(); + } + protected void ClearSecondLevelCacheFor(System.Type entity) { var entityName = entity.FullName; @@ -379,6 +562,10 @@ protected void ClearCollectionCache(Expression> pathToCo protected abstract IList GetCustomersByOrderNumberUsingOnClause(ISession session, int orderNumber); protected abstract IList GetCustomersByOrderNumberUsingWhereClause(ISession session, int orderNumber); protected abstract IList GetCustomersByNameUsingWhereClause(ISession session, string customerName); + protected abstract IList GetCustomersByOrderNumberUsingFetch(ISession session, int orderNumber); + protected abstract IList GetCustomersByOrderNumberUsingFetchAndWhereClause(ISession session, int orderNumber); + protected abstract IList GetCustomersAndCompaniesByOrderNumberUsingFetchAndWhereClause(ISession session, int orderNumber, string name); + protected abstract IList GetCustomersAndCompaniesByOrderNumberUsingFetchWithoutRestrictions(ISession session); protected abstract IList GetCustomersByOrderNumberUsingSubqueriesAndByNameUsingWhereClause( ISession session, int orderNumber, diff --git a/src/NHibernate.Test/NHSpecificTest/NH3848/QueryOverTestFixture.cs b/src/NHibernate.Test/NHSpecificTest/NH3848/QueryOverTestFixture.cs index c6c05f02275..bc8533de113 100644 --- a/src/NHibernate.Test/NHSpecificTest/NH3848/QueryOverTestFixture.cs +++ b/src/NHibernate.Test/NHSpecificTest/NH3848/QueryOverTestFixture.cs @@ -31,6 +31,75 @@ protected override IList GetCustomersByOrderNumberUsingOnClause(ISessi .List(); } + protected override IList GetCustomersByOrderNumberUsingFetch(ISession session, int orderNumber) + { + Order ordersAlias = null; + + return + session + .QueryOver() + .JoinQueryOver(ec => ec.Orders, () => ordersAlias, JoinType.InnerJoin, Restrictions.Eq("Number", orderNumber)) + .Fetch(SelectMode.Fetch, () => ordersAlias) + .TransformUsing(Transformers.DistinctRootEntity) + .List(); + } + + protected override IList GetCustomersByOrderNumberUsingFetchAndWhereClause(ISession session, int orderNumber) + { + Order ordersAlias = null; + + return + session + .QueryOver() + .JoinQueryOver(ec => ec.Orders, () => ordersAlias, JoinType.InnerJoin) + .Where(Restrictions.Eq("Number", orderNumber)) + .Fetch(SelectMode.Fetch, () => ordersAlias) + .TransformUsing(Transformers.DistinctRootEntity) + .List(); + } + + protected override IList GetCustomersAndCompaniesByOrderNumberUsingFetchAndWhereClause(ISession session, int orderNumber, string name) + { + Order ordersAlias = null; + Company companiesAlias = null; + + return + session + .QueryOver() + .JoinAlias( + n => n.Orders, + () => ordersAlias, + JoinType.InnerJoin, + Restrictions.Eq("Number", orderNumber)) + .Fetch(SelectMode.Fetch, () => ordersAlias) + .JoinAlias( + n => n.Companies, + () => companiesAlias, + JoinType.LeftOuterJoin, + Restrictions.Eq("Name", name)) + .List(); + } + + protected override IList GetCustomersAndCompaniesByOrderNumberUsingFetchWithoutRestrictions(ISession session) + { + Order ordersAlias = null; + Company companiesAlias = null; + + return + session + .QueryOver() + .JoinAlias( + n => n.Orders, + () => ordersAlias, + JoinType.InnerJoin) + .Fetch(SelectMode.Fetch, () => ordersAlias) + .JoinAlias( + n => n.Companies, + () => companiesAlias, + JoinType.LeftOuterJoin) + .List(); + } + protected override IList GetCustomersWithCompaniesByOrderNumberUsingOnClause( ISession session, int orderNumber) diff --git a/src/NHibernate/Async/Engine/Loading/CollectionLoadContext.cs b/src/NHibernate/Async/Engine/Loading/CollectionLoadContext.cs index 748f2a75d7c..7f10c0a3e7a 100644 --- a/src/NHibernate/Async/Engine/Loading/CollectionLoadContext.cs +++ b/src/NHibernate/Async/Engine/Loading/CollectionLoadContext.cs @@ -35,14 +35,14 @@ public partial class CollectionLoadContext /// The persister for which to complete loading. /// A cancellation token that can be used to cancel the work // Since v5.2 - [Obsolete("Please use overload with uncacheableCollections parameter instead.")] + [Obsolete("Please use overload with skipCache parameter instead.")] public Task EndLoadingCollectionsAsync(ICollectionPersister persister, CancellationToken cancellationToken) { if (cancellationToken.IsCancellationRequested) { return Task.FromCanceled(cancellationToken); } - return EndLoadingCollectionsAsync(persister, null, cancellationToken); + return EndLoadingCollectionsAsync(persister, false, cancellationToken); } /// @@ -51,9 +51,9 @@ public Task EndLoadingCollectionsAsync(ICollectionPersister persister, Cancellat /// complete. /// /// The persister for which to complete loading. - /// Indicates which collections must not be put in cache. + /// Indicates if collection must not be put in cache. /// A cancellation token that can be used to cancel the work - public async Task EndLoadingCollectionsAsync(ICollectionPersister persister, HashSet uncacheableCollections, CancellationToken cancellationToken) + public async Task EndLoadingCollectionsAsync(ICollectionPersister persister, bool skipCache, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); if (!loadContexts.HasLoadingCollectionEntries && (localLoadingCollectionKeys.Count == 0)) @@ -99,7 +99,7 @@ public async Task EndLoadingCollectionsAsync(ICollectionPersister persister, Has } localLoadingCollectionKeys.ExceptWith(toRemove); - await (EndLoadingCollectionsAsync(persister, matches, uncacheableCollections, cancellationToken)).ConfigureAwait(false); + await (EndLoadingCollectionsAsync(persister, matches, skipCache, cancellationToken)).ConfigureAwait(false); if ((localLoadingCollectionKeys.Count == 0)) { // todo : hack!!! @@ -112,7 +112,7 @@ public async Task EndLoadingCollectionsAsync(ICollectionPersister persister, Has } } - private async Task EndLoadingCollectionsAsync(ICollectionPersister persister, IList matchedCollectionEntries, HashSet uncacheableCollections, CancellationToken cancellationToken) + private async Task EndLoadingCollectionsAsync(ICollectionPersister persister, IList matchedCollectionEntries, bool skipCache, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); if (matchedCollectionEntries == null || matchedCollectionEntries.Count == 0) @@ -137,7 +137,7 @@ private async Task EndLoadingCollectionsAsync(ICollectionPersister persister, IL matchedCollectionEntries[i], persister, data => cacheBatcher.AddToBatch(persister, data), - uncacheableCollections, cancellationToken)).ConfigureAwait(false); + skipCache, cancellationToken)).ConfigureAwait(false); } await (cacheBatcher.ExecuteBatchAsync(cancellationToken)).ConfigureAwait(false); @@ -150,8 +150,8 @@ private async Task EndLoadingCollectionsAsync(ICollectionPersister persister, IL private async Task EndLoadingCollectionAsync( LoadingCollectionEntry lce, ICollectionPersister persister, - Action cacheBatchingHandler, - HashSet uncacheableCollections, CancellationToken cancellationToken) + Action cacheBatchingHandler, + bool skipCache, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); if (log.IsDebugEnabled()) @@ -188,7 +188,7 @@ private async Task EndLoadingCollectionAsync( bool addToCache = hasNoQueuedOperations && persister.HasCache && session.CacheMode.HasFlag(CacheMode.Put) && - (uncacheableCollections == null || !uncacheableCollections.Contains(lce.Persister.Role)) && + !skipCache && // and this is not a forced initialization during flush !ce.IsDoremove; diff --git a/src/NHibernate/Async/Impl/MultiCriteriaImpl.cs b/src/NHibernate/Async/Impl/MultiCriteriaImpl.cs index 0ac4d998f8f..311b1ad303b 100644 --- a/src/NHibernate/Async/Impl/MultiCriteriaImpl.cs +++ b/src/NHibernate/Async/Impl/MultiCriteriaImpl.cs @@ -200,7 +200,7 @@ private async Task GetResultsFromDatabaseAsync(IList results, CancellationToken for (int i = 0; i < loaders.Count; i++) { CriteriaLoader loader = loaders[i]; - await (loader.InitializeEntitiesAndCollectionsAsync(hydratedObjects[i], reader, session, session.DefaultReadOnly, null, parameters[i].UncacheableCollections, cancellationToken)).ConfigureAwait(false); + await (loader.InitializeEntitiesAndCollectionsAsync(hydratedObjects[i], reader, session, session.DefaultReadOnly, null, cancellationToken)).ConfigureAwait(false); if (createSubselects[i]) { diff --git a/src/NHibernate/Async/Impl/MultiQueryImpl.cs b/src/NHibernate/Async/Impl/MultiQueryImpl.cs index 8ac041b2919..775cbff6138 100644 --- a/src/NHibernate/Async/Impl/MultiQueryImpl.cs +++ b/src/NHibernate/Async/Impl/MultiQueryImpl.cs @@ -172,7 +172,7 @@ protected async Task> DoListAsync(CancellationToken cancellationTok ITranslator translator = translators[i]; QueryParameters parameter = parameters[i]; - await (translator.Loader.InitializeEntitiesAndCollectionsAsync(hydratedObjects[i], reader, session, false, null, parameters[i].UncacheableCollections, cancellationToken)).ConfigureAwait(false); + await (translator.Loader.InitializeEntitiesAndCollectionsAsync(hydratedObjects[i], reader, session, false, null, cancellationToken)).ConfigureAwait(false); if (createSubselects[i]) { diff --git a/src/NHibernate/Async/Loader/Criteria/CriteriaLoader.cs b/src/NHibernate/Async/Loader/Criteria/CriteriaLoader.cs index ab3d62dc218..769b203d806 100644 --- a/src/NHibernate/Async/Loader/Criteria/CriteriaLoader.cs +++ b/src/NHibernate/Async/Loader/Criteria/CriteriaLoader.cs @@ -15,6 +15,7 @@ using NHibernate.Engine; using NHibernate.Impl; using NHibernate.Param; +using NHibernate.Persister.Collection; using NHibernate.Persister.Entity; using NHibernate.SqlCommand; using NHibernate.Transform; diff --git a/src/NHibernate/Async/Loader/Loader.cs b/src/NHibernate/Async/Loader/Loader.cs index 4cc8e2cecd2..766e8852627 100644 --- a/src/NHibernate/Async/Loader/Loader.cs +++ b/src/NHibernate/Async/Loader/Loader.cs @@ -129,7 +129,7 @@ protected async Task LoadSingleRowAsync(DbDataReader resultSet, ISession queryParameters.NamedParameters); } - await (InitializeEntitiesAndCollectionsAsync(hydratedObjects, resultSet, session, queryParameters.IsReadOnly(session), null, queryParameters.UncacheableCollections, cancellationToken)).ConfigureAwait(false); + await (InitializeEntitiesAndCollectionsAsync(hydratedObjects, resultSet, session, queryParameters.IsReadOnly(session), null, cancellationToken)).ConfigureAwait(false); await (session.PersistenceContext.InitializeNonLazyCollectionsAsync(cancellationToken)).ConfigureAwait(false); return result; } @@ -321,7 +321,7 @@ private async Task DoQueryAsync(ISessionImplementor session, QueryParamet session.Batcher.CloseCommand(st, rs); } - await (InitializeEntitiesAndCollectionsAsync(hydratedObjects, rs, session, queryParameters.IsReadOnly(session), null, queryParameters.UncacheableCollections, cancellationToken)).ConfigureAwait(false); + await (InitializeEntitiesAndCollectionsAsync(hydratedObjects, rs, session, queryParameters.IsReadOnly(session), null, cancellationToken)).ConfigureAwait(false); if (createSubselects) { @@ -334,7 +334,7 @@ private async Task DoQueryAsync(ISessionImplementor session, QueryParamet internal async Task InitializeEntitiesAndCollectionsAsync( IList hydratedObjects, object resultSetId, ISessionImplementor session, bool readOnly, - CacheBatcher cacheBatcher = null, HashSet uncacheableCollections = null, CancellationToken cancellationToken = default(CancellationToken)) + CacheBatcher cacheBatcher = null, CancellationToken cancellationToken = default(CancellationToken)) { cancellationToken.ThrowIfCancellationRequested(); ICollectionPersister[] collectionPersisters = CollectionPersisters; @@ -349,7 +349,7 @@ internal async Task InitializeEntitiesAndCollectionsAsync( //during loading //TODO: or we could do this polymorphically, and have two // different operations implemented differently for arrays - await (EndCollectionLoadAsync(resultSetId, session, collectionPersisters[i], uncacheableCollections, cancellationToken)).ConfigureAwait(false); + await (EndCollectionLoadAsync(resultSetId, session, collectionPersisters[i], UncacheableCollectionPersisters?[i] == true, cancellationToken)).ConfigureAwait(false); } } } @@ -400,13 +400,13 @@ internal async Task InitializeEntitiesAndCollectionsAsync( //the entities, since we might call hashCode() on the elements //TODO: or we could do this polymorphically, and have two // different operations implemented differently for arrays - await (EndCollectionLoadAsync(resultSetId, session, collectionPersisters[i], uncacheableCollections, cancellationToken)).ConfigureAwait(false); + await (EndCollectionLoadAsync(resultSetId, session, collectionPersisters[i], UncacheableCollectionPersisters?[i] == true, cancellationToken)).ConfigureAwait(false); } } } } - private static Task EndCollectionLoadAsync(object resultSetId, ISessionImplementor session, ICollectionPersister collectionPersister, HashSet uncacheableCollections, CancellationToken cancellationToken) + private static Task EndCollectionLoadAsync(object resultSetId, ISessionImplementor session, ICollectionPersister collectionPersister, bool skipCache, CancellationToken cancellationToken) { if (cancellationToken.IsCancellationRequested) { @@ -416,7 +416,7 @@ private static Task EndCollectionLoadAsync(object resultSetId, ISessionImplement { //this is a query and we are loading multiple instances of the same collection role return session.PersistenceContext.LoadContexts.GetCollectionLoadContext((DbDataReader)resultSetId).EndLoadingCollectionsAsync( - collectionPersister, uncacheableCollections, cancellationToken); + collectionPersister, skipCache, cancellationToken); } catch (Exception ex) { diff --git a/src/NHibernate/Async/Multi/QueryBatchItemBase.cs b/src/NHibernate/Async/Multi/QueryBatchItemBase.cs index 0915a2c37c3..47e7bf0a952 100644 --- a/src/NHibernate/Async/Multi/QueryBatchItemBase.cs +++ b/src/NHibernate/Async/Multi/QueryBatchItemBase.cs @@ -134,7 +134,7 @@ private async Task InitializeEntitiesAndCollectionsAsync(DbDataReader reader, Li continue; await (queryInfo.Loader.InitializeEntitiesAndCollectionsAsync( hydratedObjects[i], reader, Session, queryInfo.Parameters.IsReadOnly(Session), - queryInfo.CacheBatcher, cancellationToken: cancellationToken)).ConfigureAwait(false); + queryInfo.CacheBatcher, cancellationToken)).ConfigureAwait(false); } } } diff --git a/src/NHibernate/Engine/Loading/CollectionLoadContext.cs b/src/NHibernate/Engine/Loading/CollectionLoadContext.cs index 5383c66bf8d..6d5db7cd34c 100644 --- a/src/NHibernate/Engine/Loading/CollectionLoadContext.cs +++ b/src/NHibernate/Engine/Loading/CollectionLoadContext.cs @@ -146,10 +146,10 @@ public IPersistentCollection GetLoadingCollection(ICollectionPersister persister /// /// The persister for which to complete loading. // Since v5.2 - [Obsolete("Please use overload with uncacheableCollections parameter instead.")] + [Obsolete("Please use overload with skipCache parameter instead.")] public void EndLoadingCollections(ICollectionPersister persister) { - EndLoadingCollections(persister, null); + EndLoadingCollections(persister, false); } /// @@ -158,8 +158,8 @@ public void EndLoadingCollections(ICollectionPersister persister) /// complete. /// /// The persister for which to complete loading. - /// Indicates which collections must not be put in cache. - public void EndLoadingCollections(ICollectionPersister persister, HashSet uncacheableCollections) + /// Indicates if collection must not be put in cache. + public void EndLoadingCollections(ICollectionPersister persister, bool skipCache) { if (!loadContexts.HasLoadingCollectionEntries && (localLoadingCollectionKeys.Count == 0)) { @@ -204,7 +204,7 @@ public void EndLoadingCollections(ICollectionPersister persister, HashSet matchedCollectionEntries, HashSet uncacheableCollections) + private void EndLoadingCollections(ICollectionPersister persister, IList matchedCollectionEntries, bool skipCache) { if (matchedCollectionEntries == null || matchedCollectionEntries.Count == 0) { @@ -241,7 +241,7 @@ private void EndLoadingCollections(ICollectionPersister persister, IList cacheBatcher.AddToBatch(persister, data), - uncacheableCollections); + skipCache); } cacheBatcher.ExecuteBatch(); @@ -254,8 +254,8 @@ private void EndLoadingCollections(ICollectionPersister persister, IList cacheBatchingHandler, - HashSet uncacheableCollections) + Action cacheBatchingHandler, + bool skipCache) { if (log.IsDebugEnabled()) { @@ -291,7 +291,7 @@ private void EndLoadingCollection( bool addToCache = hasNoQueuedOperations && persister.HasCache && session.CacheMode.HasFlag(CacheMode.Put) && - (uncacheableCollections == null || !uncacheableCollections.Contains(lce.Persister.Role)) && + !skipCache && // and this is not a forced initialization during flush !ce.IsDoremove; diff --git a/src/NHibernate/Engine/QueryParameters.cs b/src/NHibernate/Engine/QueryParameters.cs index 01ca4664eaa..517e4770607 100644 --- a/src/NHibernate/Engine/QueryParameters.cs +++ b/src/NHibernate/Engine/QueryParameters.cs @@ -37,47 +37,28 @@ public QueryParameters(IType[] positionalParameterTypes, object[] postionalParam : this(positionalParameterTypes, postionalParameterValues, null, collectionKeys) {} public QueryParameters(IType[] positionalParameterTypes, object[] postionalParameterValues, IDictionary namedParameters, object[] collectionKeys) - : this(positionalParameterTypes, postionalParameterValues, namedParameters, null, null, false, false, false, null, null, collectionKeys, null, null) {} + : this(positionalParameterTypes, postionalParameterValues, namedParameters, null, null, false, false, false, null, null, collectionKeys, null) {} public QueryParameters(IType[] positionalParameterTypes, object[] positionalParameterValues, IDictionary lockModes, RowSelection rowSelection, bool isReadOnlyInitialized, bool readOnly, bool cacheable, string cacheRegion, string comment, bool isLookupByNaturalKey, IResultTransformer transformer) - : this(positionalParameterTypes, positionalParameterValues, null, lockModes, rowSelection, isReadOnlyInitialized, readOnly, cacheable, cacheRegion, comment, null, transformer, null) + : this(positionalParameterTypes, positionalParameterValues, null, lockModes, rowSelection, isReadOnlyInitialized, readOnly, cacheable, cacheRegion, comment, null, transformer) { NaturalKeyLookup = isLookupByNaturalKey; } - // Since v5.2 - [Obsolete("Please use QueryParameters(IDictionary, IDictionary, " + - "RowSelection, bool, bool, bool, string, string, bool, IResultTransformer, HashSet) instead.")] public QueryParameters(IDictionary namedParameters, IDictionary lockModes, RowSelection rowSelection, bool isReadOnlyInitialized, bool readOnly, bool cacheable, string cacheRegion, string comment, bool isLookupByNaturalKey, IResultTransformer transformer) - : this( - namedParameters, lockModes, rowSelection, isReadOnlyInitialized, readOnly, cacheable, cacheRegion, comment, isLookupByNaturalKey, - transformer, null) {} - - public QueryParameters(IDictionary namedParameters, IDictionary lockModes, RowSelection rowSelection, bool isReadOnlyInitialized, - bool readOnly, bool cacheable, string cacheRegion, string comment, bool isLookupByNaturalKey, IResultTransformer transformer, HashSet uncacheableCollections) : this( TypeHelper.EmptyTypeArray, Array.Empty(), namedParameters, lockModes, rowSelection, isReadOnlyInitialized, readOnly, cacheable, cacheRegion, comment, null, - transformer, uncacheableCollections) + transformer) { // used by CriteriaTranslator NaturalKeyLookup = isLookupByNaturalKey; } - // Since v5.2 - [Obsolete("Please use QueryParameters(IType[], object[], IDictionary, " + - "IDictionary, RowSelection, bool, bool, bool, string, string, object[], IResultTransformer, HashSet) instead.")] public QueryParameters(IType[] positionalParameterTypes, object[] positionalParameterValues, IDictionary namedParameters, IDictionary lockModes, RowSelection rowSelection, bool isReadOnlyInitialized, bool readOnly, bool cacheable, string cacheRegion, string comment, object[] collectionKeys, IResultTransformer transformer) - : this( - TypeHelper.EmptyTypeArray, Array.Empty(), namedParameters, lockModes, rowSelection, isReadOnlyInitialized, readOnly, cacheable, cacheRegion, comment, null, - transformer, null) {} - - public QueryParameters(IType[] positionalParameterTypes, object[] positionalParameterValues, IDictionary namedParameters, - IDictionary lockModes, RowSelection rowSelection, bool isReadOnlyInitialized, bool readOnly, bool cacheable, string cacheRegion, - string comment, object[] collectionKeys, IResultTransformer transformer, HashSet uncacheableCollections) { PositionalParameterTypes = positionalParameterTypes ?? Array.Empty(); PositionalParameterValues = positionalParameterValues ?? Array.Empty(); @@ -91,7 +72,6 @@ public QueryParameters(IType[] positionalParameterTypes, object[] positionalPara IsReadOnlyInitialized = isReadOnlyInitialized; this.readOnly = readOnly; ResultTransformer = transformer; - UncacheableCollections = uncacheableCollections; } public QueryParameters(IType[] positionalParameterTypes, object[] positionalParameterValues, IDictionary namedParameters, @@ -99,7 +79,7 @@ public QueryParameters(IType[] positionalParameterTypes, object[] positionalPara string comment, object[] collectionKeys, object optionalObject, string optionalEntityName, object optionalId, IResultTransformer transformer) : this( positionalParameterTypes, positionalParameterValues, namedParameters, lockModes, rowSelection, isReadOnlyInitialized, readOnly, cacheable, cacheRegion, comment, collectionKeys, - transformer, null) + transformer) { OptionalEntityName = optionalEntityName; OptionalId = optionalId; @@ -159,11 +139,6 @@ public bool HasRowSelection public bool Callable { get; set; } - /// - /// Indicates if we can add loaded child collections to second level cache. - /// - public HashSet UncacheableCollections { get; } - public bool ReadOnly { get diff --git a/src/NHibernate/Impl/MultiCriteriaImpl.cs b/src/NHibernate/Impl/MultiCriteriaImpl.cs index 40d5712b256..7cb3b6c3c16 100644 --- a/src/NHibernate/Impl/MultiCriteriaImpl.cs +++ b/src/NHibernate/Impl/MultiCriteriaImpl.cs @@ -273,7 +273,7 @@ private void GetResultsFromDatabase(IList results) for (int i = 0; i < loaders.Count; i++) { CriteriaLoader loader = loaders[i]; - loader.InitializeEntitiesAndCollections(hydratedObjects[i], reader, session, session.DefaultReadOnly, null, parameters[i].UncacheableCollections); + loader.InitializeEntitiesAndCollections(hydratedObjects[i], reader, session, session.DefaultReadOnly, null); if (createSubselects[i]) { diff --git a/src/NHibernate/Impl/MultiQueryImpl.cs b/src/NHibernate/Impl/MultiQueryImpl.cs index fc103d7f7cf..5b533a915b5 100644 --- a/src/NHibernate/Impl/MultiQueryImpl.cs +++ b/src/NHibernate/Impl/MultiQueryImpl.cs @@ -616,7 +616,7 @@ protected List DoList() ITranslator translator = translators[i]; QueryParameters parameter = parameters[i]; - translator.Loader.InitializeEntitiesAndCollections(hydratedObjects[i], reader, session, false, null, parameters[i].UncacheableCollections); + translator.Loader.InitializeEntitiesAndCollections(hydratedObjects[i], reader, session, false, null); if (createSubselects[i]) { diff --git a/src/NHibernate/Loader/Criteria/CriteriaJoinWalker.cs b/src/NHibernate/Loader/Criteria/CriteriaJoinWalker.cs index 75ba986cc20..3a77e3310f6 100644 --- a/src/NHibernate/Loader/Criteria/CriteriaJoinWalker.cs +++ b/src/NHibernate/Loader/Criteria/CriteriaJoinWalker.cs @@ -77,6 +77,7 @@ public CriteriaJoinWalker(IOuterJoinLoadable persister, CriteriaQueryTranslator protected override void AddAssociations() { base.AddAssociations(); + foreach (var entityJoinInfo in translator.GetEntityJoins().Values) { var tableAlias = translator.GetSQLAlias(entityJoinInfo.Criteria); @@ -121,6 +122,15 @@ protected override SelectMode GetSelectMode(string path) return translator.RootCriteria.GetSelectMode(path); } + protected override bool HasRestrictions(string associationFullPath) + { + return translator + .RootCriteria + .IterateSubcriteria() + .Any(c => c.HasRestrictions + && associationFullPath == $"{translator.RootCriteria.EntityOrClassName}.{c.Path}"); ; + } + private void WalkCompositeComponentIdTree(IOuterJoinLoadable persister, string alias, string path) { IType type = persister.IdentifierType; diff --git a/src/NHibernate/Loader/Criteria/CriteriaLoader.cs b/src/NHibernate/Loader/Criteria/CriteriaLoader.cs index 658896a5096..aaece1621d9 100644 --- a/src/NHibernate/Loader/Criteria/CriteriaLoader.cs +++ b/src/NHibernate/Loader/Criteria/CriteriaLoader.cs @@ -5,6 +5,7 @@ using NHibernate.Engine; using NHibernate.Impl; using NHibernate.Param; +using NHibernate.Persister.Collection; using NHibernate.Persister.Entity; using NHibernate.SqlCommand; using NHibernate.Transform; @@ -29,6 +30,9 @@ public partial class CriteriaLoader : OuterJoinLoader private readonly string[] userAliases; private readonly bool[] includeInResultRow; private readonly int resultRowLength; + + private readonly bool[] _uncacheableCollectionPersisters; + // caching NH-3486 private readonly string[] cachedProjectedColumnAliases; private bool[] childFetchEntities; @@ -44,6 +48,8 @@ public CriteriaLoader(IOuterJoinLoadable persister, ISessionFactoryImplementor f CriteriaJoinWalker walker = new CriteriaJoinWalker(persister, translator, factory, rootCriteria, rootEntityName, enabledFilters); + _uncacheableCollectionPersisters = walker.UncacheableCollectionPersisters; + InitFromWalker(walker); userAliases = walker.UserAliases; @@ -146,6 +152,7 @@ protected override object[] GetResultRow(object[] row, DbDataReader rs, ISession return result; } + protected override bool[] UncacheableCollectionPersisters => _uncacheableCollectionPersisters; private object[] ToResultRow(object[] row) { diff --git a/src/NHibernate/Loader/Criteria/CriteriaQueryTranslator.cs b/src/NHibernate/Loader/Criteria/CriteriaQueryTranslator.cs index ab957e2f8c3..3f020cd7c5e 100644 --- a/src/NHibernate/Loader/Criteria/CriteriaQueryTranslator.cs +++ b/src/NHibernate/Loader/Criteria/CriteriaQueryTranslator.cs @@ -139,7 +139,6 @@ public QueryParameters GetQueryParameters() selection.FetchSize = rootCriteria.FetchSize; var lockModes = new Dictionary(); - var uncacheableCollections = new HashSet(); foreach (KeyValuePair me in rootCriteria.LockModes) { @@ -154,14 +153,6 @@ public QueryParameters GetQueryParameters() { lockModes[GetSQLAlias(subcriteria)] = lm; } - - if (subcriteria.HasRestrictions && subcriteria.JoinType == JoinType.LeftOuterJoin) - { - var collectionName = $"{rootCriteria.EntityOrClassName}.{subcriteria.Path}"; - - if (!uncacheableCollections.Contains(collectionName)) - uncacheableCollections.Add(collectionName); - } } IDictionary queryNamedParameters = CollectedParameters.ToDictionary(np => np.Name, np => new TypedValue(np.Type, np.Value)); @@ -177,8 +168,7 @@ public QueryParameters GetQueryParameters() rootCriteria.CacheRegion, rootCriteria.Comment, rootCriteria.LookupByNaturalKey, - rootCriteria.ResultTransformer, - uncacheableCollections) + rootCriteria.ResultTransformer) { CacheMode = rootCriteria.CacheMode }; diff --git a/src/NHibernate/Loader/JoinWalker.cs b/src/NHibernate/Loader/JoinWalker.cs index fe3ea666811..4dcab2c1018 100644 --- a/src/NHibernate/Loader/JoinWalker.cs +++ b/src/NHibernate/Loader/JoinWalker.cs @@ -26,6 +26,7 @@ public class JoinWalker private int[] owners; private EntityType[] ownerAssociationTypes; private ICollectionPersister[] collectionPersisters; + private bool[] uncacheableCollectionPersisters; private int[] collectionOwners; private string[] aliases; private LockMode[] lockModeArray; @@ -94,6 +95,8 @@ public SqlString SqlString set { sql = value; } } + public bool[] UncacheableCollectionPersisters => uncacheableCollectionPersisters; + protected ISessionFactoryImplementor Factory { get { return factory; } @@ -159,7 +162,7 @@ private void AddAssociationToJoinTree(IAssociationType type, string[] aliasedLhs string subalias = GenerateTableAlias(associations.Count + 1, path, joinable); OuterJoinableAssociation assoc = - new OuterJoinableAssociation(type, alias, aliasedLhsColumns, subalias, joinType, GetWithClause(path), Factory, enabledFilters, GetSelectMode(path)); + new OuterJoinableAssociation(type, alias, aliasedLhsColumns, subalias, joinType, GetWithClause(path), Factory, enabledFilters, GetSelectMode(path), HasRestrictions(joinable.Name)); assoc.ValidateJoin(path); AddAssociation(subalias, assoc); @@ -184,6 +187,11 @@ protected virtual SelectMode GetSelectMode(string path) return SelectMode.Undefined; } + protected virtual bool HasRestrictions(string associationFullPath) + { + return false; + } + private static int[] GetTopologicalSortOrder(List fields) { TopologicalSorter g = new TopologicalSorter(fields.Count); @@ -840,6 +848,7 @@ protected void InitPersisters(IList associations, Lock collectionOwners = collections == 0 ? null : new int[collections]; collectionPersisters = collections == 0 ? null : new ICollectionPersister[collections]; collectionSuffixes = BasicLoader.GenerateSuffixes(joins + 1, collections); + uncacheableCollectionPersisters = collections == 0 ? null : new bool[collections]; persisters = new ILoadable[joins]; EagerPropertyFetches = new bool[joins]; @@ -873,6 +882,8 @@ protected void InitPersisters(IList associations, Lock //it must be a collection fetch collectionPersisters[j] = collPersister; collectionOwners[j] = oj.GetOwner(associations); + uncacheableCollectionPersisters[j] = oj.HasRestrictions; + j++; } diff --git a/src/NHibernate/Loader/Loader.cs b/src/NHibernate/Loader/Loader.cs index ac3c24e0810..a08daea0dd4 100644 --- a/src/NHibernate/Loader/Loader.cs +++ b/src/NHibernate/Loader/Loader.cs @@ -91,6 +91,8 @@ protected virtual bool[] EntityEagerPropertyFetches get { return null; } } + protected virtual bool[] UncacheableCollectionPersisters => null; + /// /// An array of indexes of the entity that owns a one-to-one association /// to the entity at the given index (-1 if there is no "owner") @@ -315,7 +317,7 @@ protected object LoadSingleRow(DbDataReader resultSet, ISessionImplementor sessi queryParameters.NamedParameters); } - InitializeEntitiesAndCollections(hydratedObjects, resultSet, session, queryParameters.IsReadOnly(session), null, queryParameters.UncacheableCollections); + InitializeEntitiesAndCollections(hydratedObjects, resultSet, session, queryParameters.IsReadOnly(session), null); session.PersistenceContext.InitializeNonLazyCollections(); return result; } @@ -517,7 +519,7 @@ private IList DoQuery(ISessionImplementor session, QueryParameters queryParamete session.Batcher.CloseCommand(st, rs); } - InitializeEntitiesAndCollections(hydratedObjects, rs, session, queryParameters.IsReadOnly(session), null, queryParameters.UncacheableCollections); + InitializeEntitiesAndCollections(hydratedObjects, rs, session, queryParameters.IsReadOnly(session), null); if (createSubselects) { @@ -601,7 +603,7 @@ private IEnumerable CreateSubselects(IList keys, Qu internal void InitializeEntitiesAndCollections( IList hydratedObjects, object resultSetId, ISessionImplementor session, bool readOnly, - CacheBatcher cacheBatcher = null, HashSet uncacheableCollections = null) + CacheBatcher cacheBatcher = null) { ICollectionPersister[] collectionPersisters = CollectionPersisters; if (collectionPersisters != null) @@ -615,7 +617,7 @@ internal void InitializeEntitiesAndCollections( //during loading //TODO: or we could do this polymorphically, and have two // different operations implemented differently for arrays - EndCollectionLoad(resultSetId, session, collectionPersisters[i], uncacheableCollections); + EndCollectionLoad(resultSetId, session, collectionPersisters[i], UncacheableCollectionPersisters?[i] == true); } } } @@ -666,17 +668,17 @@ internal void InitializeEntitiesAndCollections( //the entities, since we might call hashCode() on the elements //TODO: or we could do this polymorphically, and have two // different operations implemented differently for arrays - EndCollectionLoad(resultSetId, session, collectionPersisters[i], uncacheableCollections); + EndCollectionLoad(resultSetId, session, collectionPersisters[i], UncacheableCollectionPersisters?[i] == true); } } } } - private static void EndCollectionLoad(object resultSetId, ISessionImplementor session, ICollectionPersister collectionPersister, HashSet uncacheableCollections) + private static void EndCollectionLoad(object resultSetId, ISessionImplementor session, ICollectionPersister collectionPersister, bool skipCache) { //this is a query and we are loading multiple instances of the same collection role session.PersistenceContext.LoadContexts.GetCollectionLoadContext((DbDataReader)resultSetId).EndLoadingCollections( - collectionPersister, uncacheableCollections); + collectionPersister, skipCache); } diff --git a/src/NHibernate/Loader/OuterJoinableAssociation.cs b/src/NHibernate/Loader/OuterJoinableAssociation.cs index db8f07b25fd..c5ed226b6f9 100644 --- a/src/NHibernate/Loader/OuterJoinableAssociation.cs +++ b/src/NHibernate/Loader/OuterJoinableAssociation.cs @@ -21,6 +21,22 @@ public sealed class OuterJoinableAssociation private readonly SqlString on; private readonly IDictionary enabledFilters; private readonly SelectMode _selectMode; + private readonly bool _hasRestrictions; + + public OuterJoinableAssociation( + IAssociationType joinableType, + String lhsAlias, + String[] lhsColumns, + String rhsAlias, + JoinType joinType, + SqlString withClause, + ISessionFactoryImplementor factory, + IDictionary enabledFilters, + SelectMode selectMode, + bool hasRestrictions) : this(joinableType, lhsAlias, lhsColumns, rhsAlias, joinType, withClause, factory, enabledFilters, selectMode) + { + _hasRestrictions = hasRestrictions; + } public OuterJoinableAssociation( IAssociationType joinableType, @@ -68,6 +84,8 @@ public SqlString On get { return on; } } + public bool HasRestrictions => _hasRestrictions; + private bool IsOneToOne { get From bbb4d9ee0f40f628099d5eac155a4baa331744e4 Mon Sep 17 00:00:00 2001 From: "Iwanow, Mihail (EXT)" Date: Tue, 20 Nov 2018 16:11:56 +0100 Subject: [PATCH 15/20] CriteriaQueryTranslator is responsible now for creating UncacheableCriteriaCollectionPersisters --- src/NHibernate/Async/Loader/Loader.cs | 4 ++-- .../Loader/Criteria/CriteriaJoinWalker.cs | 10 -------- .../Loader/Criteria/CriteriaLoader.cs | 8 +++---- .../Criteria/CriteriaQueryTranslator.cs | 23 ++++++++++++++++++- src/NHibernate/Loader/JoinWalker.cs | 12 +--------- src/NHibernate/Loader/Loader.cs | 6 ++--- .../Loader/OuterJoinableAssociation.cs | 18 --------------- 7 files changed, 32 insertions(+), 49 deletions(-) diff --git a/src/NHibernate/Async/Loader/Loader.cs b/src/NHibernate/Async/Loader/Loader.cs index 766e8852627..8efc0d8d1b7 100644 --- a/src/NHibernate/Async/Loader/Loader.cs +++ b/src/NHibernate/Async/Loader/Loader.cs @@ -349,7 +349,7 @@ internal async Task InitializeEntitiesAndCollectionsAsync( //during loading //TODO: or we could do this polymorphically, and have two // different operations implemented differently for arrays - await (EndCollectionLoadAsync(resultSetId, session, collectionPersisters[i], UncacheableCollectionPersisters?[i] == true, cancellationToken)).ConfigureAwait(false); + await (EndCollectionLoadAsync(resultSetId, session, collectionPersisters[i], UncacheableCollectionPersisters?.Contains(collectionPersisters[i]) == true, cancellationToken)).ConfigureAwait(false); } } } @@ -400,7 +400,7 @@ internal async Task InitializeEntitiesAndCollectionsAsync( //the entities, since we might call hashCode() on the elements //TODO: or we could do this polymorphically, and have two // different operations implemented differently for arrays - await (EndCollectionLoadAsync(resultSetId, session, collectionPersisters[i], UncacheableCollectionPersisters?[i] == true, cancellationToken)).ConfigureAwait(false); + await (EndCollectionLoadAsync(resultSetId, session, collectionPersisters[i], UncacheableCollectionPersisters?.Contains(collectionPersisters[i]) == true, cancellationToken)).ConfigureAwait(false); } } } diff --git a/src/NHibernate/Loader/Criteria/CriteriaJoinWalker.cs b/src/NHibernate/Loader/Criteria/CriteriaJoinWalker.cs index 3a77e3310f6..75ba986cc20 100644 --- a/src/NHibernate/Loader/Criteria/CriteriaJoinWalker.cs +++ b/src/NHibernate/Loader/Criteria/CriteriaJoinWalker.cs @@ -77,7 +77,6 @@ public CriteriaJoinWalker(IOuterJoinLoadable persister, CriteriaQueryTranslator protected override void AddAssociations() { base.AddAssociations(); - foreach (var entityJoinInfo in translator.GetEntityJoins().Values) { var tableAlias = translator.GetSQLAlias(entityJoinInfo.Criteria); @@ -122,15 +121,6 @@ protected override SelectMode GetSelectMode(string path) return translator.RootCriteria.GetSelectMode(path); } - protected override bool HasRestrictions(string associationFullPath) - { - return translator - .RootCriteria - .IterateSubcriteria() - .Any(c => c.HasRestrictions - && associationFullPath == $"{translator.RootCriteria.EntityOrClassName}.{c.Path}"); ; - } - private void WalkCompositeComponentIdTree(IOuterJoinLoadable persister, string alias, string path) { IType type = persister.IdentifierType; diff --git a/src/NHibernate/Loader/Criteria/CriteriaLoader.cs b/src/NHibernate/Loader/Criteria/CriteriaLoader.cs index aaece1621d9..573254620a7 100644 --- a/src/NHibernate/Loader/Criteria/CriteriaLoader.cs +++ b/src/NHibernate/Loader/Criteria/CriteriaLoader.cs @@ -31,7 +31,7 @@ public partial class CriteriaLoader : OuterJoinLoader private readonly bool[] includeInResultRow; private readonly int resultRowLength; - private readonly bool[] _uncacheableCollectionPersisters; + private readonly ISet _uncacheableCollectionPersisters; // caching NH-3486 private readonly string[] cachedProjectedColumnAliases; @@ -45,11 +45,11 @@ public CriteriaLoader(IOuterJoinLoadable persister, ISessionFactoryImplementor f querySpaces = translator.GetQuerySpaces(); + _uncacheableCollectionPersisters = translator.UncacheableCriteriaCollectionPersisters; + CriteriaJoinWalker walker = new CriteriaJoinWalker(persister, translator, factory, rootCriteria, rootEntityName, enabledFilters); - _uncacheableCollectionPersisters = walker.UncacheableCollectionPersisters; - InitFromWalker(walker); userAliases = walker.UserAliases; @@ -152,7 +152,7 @@ protected override object[] GetResultRow(object[] row, DbDataReader rs, ISession return result; } - protected override bool[] UncacheableCollectionPersisters => _uncacheableCollectionPersisters; + protected override ISet UncacheableCollectionPersisters => _uncacheableCollectionPersisters; private object[] ToResultRow(object[] row) { diff --git a/src/NHibernate/Loader/Criteria/CriteriaQueryTranslator.cs b/src/NHibernate/Loader/Criteria/CriteriaQueryTranslator.cs index 3f020cd7c5e..fa4938df84a 100644 --- a/src/NHibernate/Loader/Criteria/CriteriaQueryTranslator.cs +++ b/src/NHibernate/Loader/Criteria/CriteriaQueryTranslator.cs @@ -41,6 +41,7 @@ public class EntityJoinInfo private readonly IDictionary nameCriteriaInfoMap = new Dictionary(); + private readonly ISet _uncacheableCriteriaCollectionPersisters = new HashSet(); private readonly ISet criteriaCollectionPersisters = new HashSet(); private readonly IDictionary criteriaSQLAliasMap = new Dictionary(); private readonly IDictionary aliasCriteriaMap = new Dictionary(); @@ -213,6 +214,8 @@ public string[] ProjectedAliases get { return rootCriteria.Projection.Aliases; } } + public ISet UncacheableCriteriaCollectionPersisters => _uncacheableCriteriaCollectionPersisters; + public IList GetEntityProjections() { return entityProjections; @@ -436,11 +439,29 @@ private void CreateCriteriaCollectionPersisters() NHibernate_Persister_Entity.IJoinable joinable = GetPathJoinable(me.Key); if (joinable != null && joinable.IsCollection) { - criteriaCollectionPersisters.Add((ICollectionPersister)joinable); + criteriaCollectionPersisters.Add((ICollectionPersister) joinable); + + if (!CanPersisterBeCached(me.Value)) + { + if (!_uncacheableCriteriaCollectionPersisters.Contains((ICollectionPersister) joinable)) + _uncacheableCriteriaCollectionPersisters.Add((ICollectionPersister) joinable); + } } } } + private bool CanPersisterBeCached(ICriteria criteria) + { + if (criteria is CriteriaImpl.Subcriteria subcriteria) + { + return !(subcriteria.HasRestrictions + && (subcriteria.JoinType == JoinType.LeftOuterJoin + || (subcriteria.JoinType == JoinType.InnerJoin && rootCriteria.GetSelectMode(subcriteria.Path) == SelectMode.Fetch))); + } + + return true; + } + private Persister.Entity.IJoinable GetPathJoinable(string path) { // start with the root diff --git a/src/NHibernate/Loader/JoinWalker.cs b/src/NHibernate/Loader/JoinWalker.cs index 4dcab2c1018..a4a71f5bd78 100644 --- a/src/NHibernate/Loader/JoinWalker.cs +++ b/src/NHibernate/Loader/JoinWalker.cs @@ -26,7 +26,6 @@ public class JoinWalker private int[] owners; private EntityType[] ownerAssociationTypes; private ICollectionPersister[] collectionPersisters; - private bool[] uncacheableCollectionPersisters; private int[] collectionOwners; private string[] aliases; private LockMode[] lockModeArray; @@ -95,8 +94,6 @@ public SqlString SqlString set { sql = value; } } - public bool[] UncacheableCollectionPersisters => uncacheableCollectionPersisters; - protected ISessionFactoryImplementor Factory { get { return factory; } @@ -162,7 +159,7 @@ private void AddAssociationToJoinTree(IAssociationType type, string[] aliasedLhs string subalias = GenerateTableAlias(associations.Count + 1, path, joinable); OuterJoinableAssociation assoc = - new OuterJoinableAssociation(type, alias, aliasedLhsColumns, subalias, joinType, GetWithClause(path), Factory, enabledFilters, GetSelectMode(path), HasRestrictions(joinable.Name)); + new OuterJoinableAssociation(type, alias, aliasedLhsColumns, subalias, joinType, GetWithClause(path), Factory, enabledFilters, GetSelectMode(path)); assoc.ValidateJoin(path); AddAssociation(subalias, assoc); @@ -187,11 +184,6 @@ protected virtual SelectMode GetSelectMode(string path) return SelectMode.Undefined; } - protected virtual bool HasRestrictions(string associationFullPath) - { - return false; - } - private static int[] GetTopologicalSortOrder(List fields) { TopologicalSorter g = new TopologicalSorter(fields.Count); @@ -848,7 +840,6 @@ protected void InitPersisters(IList associations, Lock collectionOwners = collections == 0 ? null : new int[collections]; collectionPersisters = collections == 0 ? null : new ICollectionPersister[collections]; collectionSuffixes = BasicLoader.GenerateSuffixes(joins + 1, collections); - uncacheableCollectionPersisters = collections == 0 ? null : new bool[collections]; persisters = new ILoadable[joins]; EagerPropertyFetches = new bool[joins]; @@ -882,7 +873,6 @@ protected void InitPersisters(IList associations, Lock //it must be a collection fetch collectionPersisters[j] = collPersister; collectionOwners[j] = oj.GetOwner(associations); - uncacheableCollectionPersisters[j] = oj.HasRestrictions; j++; } diff --git a/src/NHibernate/Loader/Loader.cs b/src/NHibernate/Loader/Loader.cs index a08daea0dd4..25aaddb24db 100644 --- a/src/NHibernate/Loader/Loader.cs +++ b/src/NHibernate/Loader/Loader.cs @@ -91,7 +91,7 @@ protected virtual bool[] EntityEagerPropertyFetches get { return null; } } - protected virtual bool[] UncacheableCollectionPersisters => null; + protected virtual ISet UncacheableCollectionPersisters => null; /// /// An array of indexes of the entity that owns a one-to-one association @@ -617,7 +617,7 @@ internal void InitializeEntitiesAndCollections( //during loading //TODO: or we could do this polymorphically, and have two // different operations implemented differently for arrays - EndCollectionLoad(resultSetId, session, collectionPersisters[i], UncacheableCollectionPersisters?[i] == true); + EndCollectionLoad(resultSetId, session, collectionPersisters[i], UncacheableCollectionPersisters?.Contains(collectionPersisters[i]) == true); } } } @@ -668,7 +668,7 @@ internal void InitializeEntitiesAndCollections( //the entities, since we might call hashCode() on the elements //TODO: or we could do this polymorphically, and have two // different operations implemented differently for arrays - EndCollectionLoad(resultSetId, session, collectionPersisters[i], UncacheableCollectionPersisters?[i] == true); + EndCollectionLoad(resultSetId, session, collectionPersisters[i], UncacheableCollectionPersisters?.Contains(collectionPersisters[i]) == true); } } } diff --git a/src/NHibernate/Loader/OuterJoinableAssociation.cs b/src/NHibernate/Loader/OuterJoinableAssociation.cs index c5ed226b6f9..db8f07b25fd 100644 --- a/src/NHibernate/Loader/OuterJoinableAssociation.cs +++ b/src/NHibernate/Loader/OuterJoinableAssociation.cs @@ -21,22 +21,6 @@ public sealed class OuterJoinableAssociation private readonly SqlString on; private readonly IDictionary enabledFilters; private readonly SelectMode _selectMode; - private readonly bool _hasRestrictions; - - public OuterJoinableAssociation( - IAssociationType joinableType, - String lhsAlias, - String[] lhsColumns, - String rhsAlias, - JoinType joinType, - SqlString withClause, - ISessionFactoryImplementor factory, - IDictionary enabledFilters, - SelectMode selectMode, - bool hasRestrictions) : this(joinableType, lhsAlias, lhsColumns, rhsAlias, joinType, withClause, factory, enabledFilters, selectMode) - { - _hasRestrictions = hasRestrictions; - } public OuterJoinableAssociation( IAssociationType joinableType, @@ -84,8 +68,6 @@ public SqlString On get { return on; } } - public bool HasRestrictions => _hasRestrictions; - private bool IsOneToOne { get From c25658e19988ea5178d82b82b8ef99c52fed5d3d Mon Sep 17 00:00:00 2001 From: Roman Artiukhin Date: Tue, 20 Nov 2018 22:42:44 +0300 Subject: [PATCH 16/20] Refactoring and clean up --- .../Engine/Loading/CollectionLoadContext.cs | 3 +- .../Async/Impl/MultiCriteriaImpl.cs | 2 +- src/NHibernate/Async/Impl/MultiQueryImpl.cs | 2 +- .../Async/Loader/Criteria/CriteriaLoader.cs | 2 +- src/NHibernate/Async/Loader/Loader.cs | 8 ++--- .../Engine/Loading/CollectionLoadContext.cs | 3 +- src/NHibernate/Impl/MultiCriteriaImpl.cs | 2 +- src/NHibernate/Impl/MultiQueryImpl.cs | 2 +- .../Loader/Criteria/CriteriaLoader.cs | 12 ++++--- .../Criteria/CriteriaQueryTranslator.cs | 35 ++++++------------- src/NHibernate/Loader/JoinWalker.cs | 1 - src/NHibernate/Loader/Loader.cs | 15 ++++---- 12 files changed, 37 insertions(+), 50 deletions(-) diff --git a/src/NHibernate/Async/Engine/Loading/CollectionLoadContext.cs b/src/NHibernate/Async/Engine/Loading/CollectionLoadContext.cs index 7f10c0a3e7a..921c2a40c52 100644 --- a/src/NHibernate/Async/Engine/Loading/CollectionLoadContext.cs +++ b/src/NHibernate/Async/Engine/Loading/CollectionLoadContext.cs @@ -186,9 +186,8 @@ private async Task EndLoadingCollectionAsync( ce.PostInitialize(lce.Collection, persistenceContext); } - bool addToCache = hasNoQueuedOperations && persister.HasCache && + bool addToCache = hasNoQueuedOperations && !skipCache && persister.HasCache && session.CacheMode.HasFlag(CacheMode.Put) && - !skipCache && // and this is not a forced initialization during flush !ce.IsDoremove; diff --git a/src/NHibernate/Async/Impl/MultiCriteriaImpl.cs b/src/NHibernate/Async/Impl/MultiCriteriaImpl.cs index 311b1ad303b..66e293c3b08 100644 --- a/src/NHibernate/Async/Impl/MultiCriteriaImpl.cs +++ b/src/NHibernate/Async/Impl/MultiCriteriaImpl.cs @@ -200,7 +200,7 @@ private async Task GetResultsFromDatabaseAsync(IList results, CancellationToken for (int i = 0; i < loaders.Count; i++) { CriteriaLoader loader = loaders[i]; - await (loader.InitializeEntitiesAndCollectionsAsync(hydratedObjects[i], reader, session, session.DefaultReadOnly, null, cancellationToken)).ConfigureAwait(false); + await (loader.InitializeEntitiesAndCollectionsAsync(hydratedObjects[i], reader, session, session.DefaultReadOnly, cancellationToken: cancellationToken)).ConfigureAwait(false); if (createSubselects[i]) { diff --git a/src/NHibernate/Async/Impl/MultiQueryImpl.cs b/src/NHibernate/Async/Impl/MultiQueryImpl.cs index 775cbff6138..93cf48d59ad 100644 --- a/src/NHibernate/Async/Impl/MultiQueryImpl.cs +++ b/src/NHibernate/Async/Impl/MultiQueryImpl.cs @@ -172,7 +172,7 @@ protected async Task> DoListAsync(CancellationToken cancellationTok ITranslator translator = translators[i]; QueryParameters parameter = parameters[i]; - await (translator.Loader.InitializeEntitiesAndCollectionsAsync(hydratedObjects[i], reader, session, false, null, cancellationToken)).ConfigureAwait(false); + await (translator.Loader.InitializeEntitiesAndCollectionsAsync(hydratedObjects[i], reader, session, false, cancellationToken: cancellationToken)).ConfigureAwait(false); if (createSubselects[i]) { diff --git a/src/NHibernate/Async/Loader/Criteria/CriteriaLoader.cs b/src/NHibernate/Async/Loader/Criteria/CriteriaLoader.cs index 769b203d806..47ac9679b6d 100644 --- a/src/NHibernate/Async/Loader/Criteria/CriteriaLoader.cs +++ b/src/NHibernate/Async/Loader/Criteria/CriteriaLoader.cs @@ -12,10 +12,10 @@ using System.Collections; using System.Collections.Generic; using System.Data.Common; +using System.Linq; using NHibernate.Engine; using NHibernate.Impl; using NHibernate.Param; -using NHibernate.Persister.Collection; using NHibernate.Persister.Entity; using NHibernate.SqlCommand; using NHibernate.Transform; diff --git a/src/NHibernate/Async/Loader/Loader.cs b/src/NHibernate/Async/Loader/Loader.cs index 8efc0d8d1b7..825813268bc 100644 --- a/src/NHibernate/Async/Loader/Loader.cs +++ b/src/NHibernate/Async/Loader/Loader.cs @@ -129,7 +129,7 @@ protected async Task LoadSingleRowAsync(DbDataReader resultSet, ISession queryParameters.NamedParameters); } - await (InitializeEntitiesAndCollectionsAsync(hydratedObjects, resultSet, session, queryParameters.IsReadOnly(session), null, cancellationToken)).ConfigureAwait(false); + await (InitializeEntitiesAndCollectionsAsync(hydratedObjects, resultSet, session, queryParameters.IsReadOnly(session), cancellationToken: cancellationToken)).ConfigureAwait(false); await (session.PersistenceContext.InitializeNonLazyCollectionsAsync(cancellationToken)).ConfigureAwait(false); return result; } @@ -321,7 +321,7 @@ private async Task DoQueryAsync(ISessionImplementor session, QueryParamet session.Batcher.CloseCommand(st, rs); } - await (InitializeEntitiesAndCollectionsAsync(hydratedObjects, rs, session, queryParameters.IsReadOnly(session), null, cancellationToken)).ConfigureAwait(false); + await (InitializeEntitiesAndCollectionsAsync(hydratedObjects, rs, session, queryParameters.IsReadOnly(session), cancellationToken: cancellationToken)).ConfigureAwait(false); if (createSubselects) { @@ -349,7 +349,7 @@ internal async Task InitializeEntitiesAndCollectionsAsync( //during loading //TODO: or we could do this polymorphically, and have two // different operations implemented differently for arrays - await (EndCollectionLoadAsync(resultSetId, session, collectionPersisters[i], UncacheableCollectionPersisters?.Contains(collectionPersisters[i]) == true, cancellationToken)).ConfigureAwait(false); + await (EndCollectionLoadAsync(resultSetId, session, collectionPersisters[i], UncacheableCollectionPersisters?[i] == true, cancellationToken)).ConfigureAwait(false); } } } @@ -400,7 +400,7 @@ internal async Task InitializeEntitiesAndCollectionsAsync( //the entities, since we might call hashCode() on the elements //TODO: or we could do this polymorphically, and have two // different operations implemented differently for arrays - await (EndCollectionLoadAsync(resultSetId, session, collectionPersisters[i], UncacheableCollectionPersisters?.Contains(collectionPersisters[i]) == true, cancellationToken)).ConfigureAwait(false); + await (EndCollectionLoadAsync(resultSetId, session, collectionPersisters[i], UncacheableCollectionPersisters?[i] == true, cancellationToken)).ConfigureAwait(false); } } } diff --git a/src/NHibernate/Engine/Loading/CollectionLoadContext.cs b/src/NHibernate/Engine/Loading/CollectionLoadContext.cs index 6d5db7cd34c..e41b0f9f404 100644 --- a/src/NHibernate/Engine/Loading/CollectionLoadContext.cs +++ b/src/NHibernate/Engine/Loading/CollectionLoadContext.cs @@ -289,9 +289,8 @@ private void EndLoadingCollection( ce.PostInitialize(lce.Collection, persistenceContext); } - bool addToCache = hasNoQueuedOperations && persister.HasCache && + bool addToCache = hasNoQueuedOperations && !skipCache && persister.HasCache && session.CacheMode.HasFlag(CacheMode.Put) && - !skipCache && // and this is not a forced initialization during flush !ce.IsDoremove; diff --git a/src/NHibernate/Impl/MultiCriteriaImpl.cs b/src/NHibernate/Impl/MultiCriteriaImpl.cs index 7cb3b6c3c16..98f87bd9bb5 100644 --- a/src/NHibernate/Impl/MultiCriteriaImpl.cs +++ b/src/NHibernate/Impl/MultiCriteriaImpl.cs @@ -273,7 +273,7 @@ private void GetResultsFromDatabase(IList results) for (int i = 0; i < loaders.Count; i++) { CriteriaLoader loader = loaders[i]; - loader.InitializeEntitiesAndCollections(hydratedObjects[i], reader, session, session.DefaultReadOnly, null); + loader.InitializeEntitiesAndCollections(hydratedObjects[i], reader, session, session.DefaultReadOnly); if (createSubselects[i]) { diff --git a/src/NHibernate/Impl/MultiQueryImpl.cs b/src/NHibernate/Impl/MultiQueryImpl.cs index 5b533a915b5..278c2822ff0 100644 --- a/src/NHibernate/Impl/MultiQueryImpl.cs +++ b/src/NHibernate/Impl/MultiQueryImpl.cs @@ -616,7 +616,7 @@ protected List DoList() ITranslator translator = translators[i]; QueryParameters parameter = parameters[i]; - translator.Loader.InitializeEntitiesAndCollections(hydratedObjects[i], reader, session, false, null); + translator.Loader.InitializeEntitiesAndCollections(hydratedObjects[i], reader, session, false); if (createSubselects[i]) { diff --git a/src/NHibernate/Loader/Criteria/CriteriaLoader.cs b/src/NHibernate/Loader/Criteria/CriteriaLoader.cs index 573254620a7..51a07952070 100644 --- a/src/NHibernate/Loader/Criteria/CriteriaLoader.cs +++ b/src/NHibernate/Loader/Criteria/CriteriaLoader.cs @@ -2,10 +2,10 @@ using System.Collections; using System.Collections.Generic; using System.Data.Common; +using System.Linq; using NHibernate.Engine; using NHibernate.Impl; using NHibernate.Param; -using NHibernate.Persister.Collection; using NHibernate.Persister.Entity; using NHibernate.SqlCommand; using NHibernate.Transform; @@ -31,7 +31,7 @@ public partial class CriteriaLoader : OuterJoinLoader private readonly bool[] includeInResultRow; private readonly int resultRowLength; - private readonly ISet _uncacheableCollectionPersisters; + private readonly bool[] _uncacheableCollectionPersisters; // caching NH-3486 private readonly string[] cachedProjectedColumnAliases; @@ -45,13 +45,15 @@ public CriteriaLoader(IOuterJoinLoadable persister, ISessionFactoryImplementor f querySpaces = translator.GetQuerySpaces(); - _uncacheableCollectionPersisters = translator.UncacheableCriteriaCollectionPersisters; - CriteriaJoinWalker walker = new CriteriaJoinWalker(persister, translator, factory, rootCriteria, rootEntityName, enabledFilters); InitFromWalker(walker); + if (translator.UncacheableCriteriaCollectionPersisters.Any()) + { + _uncacheableCollectionPersisters = CollectionPersisters?.Select(cp => translator.UncacheableCriteriaCollectionPersisters.Contains(cp)).ToArray(); + } userAliases = walker.UserAliases; ResultTypes = walker.ResultTypes; includeInResultRow = walker.IncludeInResultRow; @@ -152,7 +154,7 @@ protected override object[] GetResultRow(object[] row, DbDataReader rs, ISession return result; } - protected override ISet UncacheableCollectionPersisters => _uncacheableCollectionPersisters; + protected override bool[] UncacheableCollectionPersisters => _uncacheableCollectionPersisters; private object[] ToResultRow(object[] row) { diff --git a/src/NHibernate/Loader/Criteria/CriteriaQueryTranslator.cs b/src/NHibernate/Loader/Criteria/CriteriaQueryTranslator.cs index fa4938df84a..306030918e4 100644 --- a/src/NHibernate/Loader/Criteria/CriteriaQueryTranslator.cs +++ b/src/NHibernate/Loader/Criteria/CriteriaQueryTranslator.cs @@ -41,11 +41,11 @@ public class EntityJoinInfo private readonly IDictionary nameCriteriaInfoMap = new Dictionary(); - private readonly ISet _uncacheableCriteriaCollectionPersisters = new HashSet(); + private readonly HashSet uncacheableCollectionPersisters = new HashSet(); private readonly ISet criteriaCollectionPersisters = new HashSet(); private readonly IDictionary criteriaSQLAliasMap = new Dictionary(); private readonly IDictionary aliasCriteriaMap = new Dictionary(); - private readonly IDictionary associationPathCriteriaMap = new LinkedHashMap(); + private readonly Dictionary associationPathCriteriaMap = new Dictionary(); private readonly IDictionary associationPathJoinTypesMap = new LinkedHashMap(); private readonly IDictionary withClauseMap = new Dictionary(); private readonly ISessionFactoryImplementor sessionFactory; @@ -214,7 +214,7 @@ public string[] ProjectedAliases get { return rootCriteria.Projection.Aliases; } } - public ISet UncacheableCriteriaCollectionPersisters => _uncacheableCriteriaCollectionPersisters; + public ISet UncacheableCriteriaCollectionPersisters => uncacheableCollectionPersisters; public IList GetEntityProjections() { @@ -296,8 +296,7 @@ public JoinType GetJoinType(string path) public ICriteria GetCriteria(string path) { - ICriteria result; - associationPathCriteriaMap.TryGetValue(path, out result); + associationPathCriteriaMap.TryGetValue(path, out var result); logger.Debug("getCriteria for path={0} crit={1}", path, result); return result; } @@ -407,7 +406,7 @@ private void CreateCriteriaEntityNameMap() nameCriteriaInfoMap.Add(rootProvider.Name, rootProvider); - foreach (KeyValuePair me in associationPathCriteriaMap) + foreach (KeyValuePair me in associationPathCriteriaMap) { ICriteriaInfoProvider info = GetPathInfo(me.Key, rootProvider); criteriaInfoMap.Add(me.Value, info); @@ -434,34 +433,20 @@ private void CreateEntityJoinMap() private void CreateCriteriaCollectionPersisters() { - foreach (KeyValuePair me in associationPathCriteriaMap) + foreach (KeyValuePair me in associationPathCriteriaMap) { - NHibernate_Persister_Entity.IJoinable joinable = GetPathJoinable(me.Key); - if (joinable != null && joinable.IsCollection) + if (GetPathJoinable(me.Key) is ICollectionPersister collectionPersister) { - criteriaCollectionPersisters.Add((ICollectionPersister) joinable); + criteriaCollectionPersisters.Add(collectionPersister); - if (!CanPersisterBeCached(me.Value)) + if (collectionPersister.HasCache && me.Value.HasRestrictions) { - if (!_uncacheableCriteriaCollectionPersisters.Contains((ICollectionPersister) joinable)) - _uncacheableCriteriaCollectionPersisters.Add((ICollectionPersister) joinable); + uncacheableCollectionPersisters.Add(collectionPersister); } } } } - private bool CanPersisterBeCached(ICriteria criteria) - { - if (criteria is CriteriaImpl.Subcriteria subcriteria) - { - return !(subcriteria.HasRestrictions - && (subcriteria.JoinType == JoinType.LeftOuterJoin - || (subcriteria.JoinType == JoinType.InnerJoin && rootCriteria.GetSelectMode(subcriteria.Path) == SelectMode.Fetch))); - } - - return true; - } - private Persister.Entity.IJoinable GetPathJoinable(string path) { // start with the root diff --git a/src/NHibernate/Loader/JoinWalker.cs b/src/NHibernate/Loader/JoinWalker.cs index a4a71f5bd78..fe3ea666811 100644 --- a/src/NHibernate/Loader/JoinWalker.cs +++ b/src/NHibernate/Loader/JoinWalker.cs @@ -873,7 +873,6 @@ protected void InitPersisters(IList associations, Lock //it must be a collection fetch collectionPersisters[j] = collPersister; collectionOwners[j] = oj.GetOwner(associations); - j++; } diff --git a/src/NHibernate/Loader/Loader.cs b/src/NHibernate/Loader/Loader.cs index 25aaddb24db..36d321e8cec 100644 --- a/src/NHibernate/Loader/Loader.cs +++ b/src/NHibernate/Loader/Loader.cs @@ -91,8 +91,6 @@ protected virtual bool[] EntityEagerPropertyFetches get { return null; } } - protected virtual ISet UncacheableCollectionPersisters => null; - /// /// An array of indexes of the entity that owns a one-to-one association /// to the entity at the given index (-1 if there is no "owner") @@ -178,6 +176,11 @@ protected virtual ICollectionPersister[] CollectionPersisters get { return null; } } + /// + /// An array indicating whether cache should be skipped for given collection + /// + protected virtual bool[] UncacheableCollectionPersisters => null; + /// /// What lock mode does this load entities with? /// @@ -317,7 +320,7 @@ protected object LoadSingleRow(DbDataReader resultSet, ISessionImplementor sessi queryParameters.NamedParameters); } - InitializeEntitiesAndCollections(hydratedObjects, resultSet, session, queryParameters.IsReadOnly(session), null); + InitializeEntitiesAndCollections(hydratedObjects, resultSet, session, queryParameters.IsReadOnly(session)); session.PersistenceContext.InitializeNonLazyCollections(); return result; } @@ -519,7 +522,7 @@ private IList DoQuery(ISessionImplementor session, QueryParameters queryParamete session.Batcher.CloseCommand(st, rs); } - InitializeEntitiesAndCollections(hydratedObjects, rs, session, queryParameters.IsReadOnly(session), null); + InitializeEntitiesAndCollections(hydratedObjects, rs, session, queryParameters.IsReadOnly(session)); if (createSubselects) { @@ -617,7 +620,7 @@ internal void InitializeEntitiesAndCollections( //during loading //TODO: or we could do this polymorphically, and have two // different operations implemented differently for arrays - EndCollectionLoad(resultSetId, session, collectionPersisters[i], UncacheableCollectionPersisters?.Contains(collectionPersisters[i]) == true); + EndCollectionLoad(resultSetId, session, collectionPersisters[i], UncacheableCollectionPersisters?[i] == true); } } } @@ -668,7 +671,7 @@ internal void InitializeEntitiesAndCollections( //the entities, since we might call hashCode() on the elements //TODO: or we could do this polymorphically, and have two // different operations implemented differently for arrays - EndCollectionLoad(resultSetId, session, collectionPersisters[i], UncacheableCollectionPersisters?.Contains(collectionPersisters[i]) == true); + EndCollectionLoad(resultSetId, session, collectionPersisters[i], UncacheableCollectionPersisters?[i] == true); } } } From 73b083140ed9d3c0785f64fece7e2aff6a6ffeab Mon Sep 17 00:00:00 2001 From: Alexander Zaytsev Date: Wed, 21 Nov 2018 10:25:38 +1300 Subject: [PATCH 17/20] Hide implementation detail of CacheableCollectionPersister logic --- .../Async/Loader/Criteria/CriteriaLoader.cs | 6 ++--- src/NHibernate/Async/Loader/Loader.cs | 20 +++++++------- .../Loader/Criteria/CriteriaLoader.cs | 18 ++++++------- src/NHibernate/Loader/Loader.cs | 26 +++++++++++-------- 4 files changed, 35 insertions(+), 35 deletions(-) diff --git a/src/NHibernate/Async/Loader/Criteria/CriteriaLoader.cs b/src/NHibernate/Async/Loader/Criteria/CriteriaLoader.cs index 47ac9679b6d..ccae5a22c23 100644 --- a/src/NHibernate/Async/Loader/Criteria/CriteriaLoader.cs +++ b/src/NHibernate/Async/Loader/Criteria/CriteriaLoader.cs @@ -8,18 +8,16 @@ //------------------------------------------------------------------------------ -using System; using System.Collections; using System.Collections.Generic; using System.Data.Common; -using System.Linq; using NHibernate.Engine; using NHibernate.Impl; using NHibernate.Param; +using NHibernate.Persister.Collection; using NHibernate.Persister.Entity; using NHibernate.SqlCommand; using NHibernate.Transform; -using NHibernate.Type; using NHibernate.Util; namespace NHibernate.Loader.Criteria @@ -39,7 +37,7 @@ public Task ListAsync(ISessionImplementor session, CancellationToken canc { return ListAsync(session, translator.GetQueryParameters(), querySpaces, cancellationToken); } - catch (Exception ex) + catch (System.Exception ex) { return Task.FromException(ex); } diff --git a/src/NHibernate/Async/Loader/Loader.cs b/src/NHibernate/Async/Loader/Loader.cs index 825813268bc..d37de7757ca 100644 --- a/src/NHibernate/Async/Loader/Loader.cs +++ b/src/NHibernate/Async/Loader/Loader.cs @@ -333,23 +333,23 @@ private async Task DoQueryAsync(ISessionImplementor session, QueryParamet } internal async Task InitializeEntitiesAndCollectionsAsync( - IList hydratedObjects, object resultSetId, ISessionImplementor session, bool readOnly, + IList hydratedObjects, DbDataReader reader, ISessionImplementor session, bool readOnly, CacheBatcher cacheBatcher = null, CancellationToken cancellationToken = default(CancellationToken)) { cancellationToken.ThrowIfCancellationRequested(); ICollectionPersister[] collectionPersisters = CollectionPersisters; if (collectionPersisters != null) { - for (int i = 0; i < collectionPersisters.Length; i++) + foreach (var collectionPersister in collectionPersisters) { - if (collectionPersisters[i].IsArray) + if (collectionPersister.IsArray) { //for arrays, we should end the collection load before resolving //the entities, since the actual array instances are not instantiated //during loading //TODO: or we could do this polymorphically, and have two // different operations implemented differently for arrays - await (EndCollectionLoadAsync(resultSetId, session, collectionPersisters[i], UncacheableCollectionPersisters?[i] == true, cancellationToken)).ConfigureAwait(false); + await (EndCollectionLoadAsync(reader, session, collectionPersister, cancellationToken)).ConfigureAwait(false); } } } @@ -392,21 +392,21 @@ internal async Task InitializeEntitiesAndCollectionsAsync( if (collectionPersisters != null) { - for (int i = 0; i < collectionPersisters.Length; i++) + foreach (var collectionPersister in collectionPersisters) { - if (!collectionPersisters[i].IsArray) + if (!collectionPersister.IsArray) { //for sets, we should end the collection load after resolving //the entities, since we might call hashCode() on the elements //TODO: or we could do this polymorphically, and have two // different operations implemented differently for arrays - await (EndCollectionLoadAsync(resultSetId, session, collectionPersisters[i], UncacheableCollectionPersisters?[i] == true, cancellationToken)).ConfigureAwait(false); + await (EndCollectionLoadAsync(reader, session, collectionPersister, cancellationToken)).ConfigureAwait(false); } } } } - private static Task EndCollectionLoadAsync(object resultSetId, ISessionImplementor session, ICollectionPersister collectionPersister, bool skipCache, CancellationToken cancellationToken) + private Task EndCollectionLoadAsync(DbDataReader reader, ISessionImplementor session, ICollectionPersister collectionPersister, CancellationToken cancellationToken) { if (cancellationToken.IsCancellationRequested) { @@ -415,8 +415,8 @@ private static Task EndCollectionLoadAsync(object resultSetId, ISessionImplement try { //this is a query and we are loading multiple instances of the same collection role - return session.PersistenceContext.LoadContexts.GetCollectionLoadContext((DbDataReader)resultSetId).EndLoadingCollectionsAsync( - collectionPersister, skipCache, cancellationToken); + return session.PersistenceContext.LoadContexts.GetCollectionLoadContext(reader).EndLoadingCollectionsAsync( + collectionPersister, !IsCollectionPersisterCacheable(collectionPersister), cancellationToken); } catch (Exception ex) { diff --git a/src/NHibernate/Loader/Criteria/CriteriaLoader.cs b/src/NHibernate/Loader/Criteria/CriteriaLoader.cs index 51a07952070..45c740b3376 100644 --- a/src/NHibernate/Loader/Criteria/CriteriaLoader.cs +++ b/src/NHibernate/Loader/Criteria/CriteriaLoader.cs @@ -1,15 +1,13 @@ -using System; using System.Collections; using System.Collections.Generic; using System.Data.Common; -using System.Linq; using NHibernate.Engine; using NHibernate.Impl; using NHibernate.Param; +using NHibernate.Persister.Collection; using NHibernate.Persister.Entity; using NHibernate.SqlCommand; using NHibernate.Transform; -using NHibernate.Type; using NHibernate.Util; namespace NHibernate.Loader.Criteria @@ -31,7 +29,7 @@ public partial class CriteriaLoader : OuterJoinLoader private readonly bool[] includeInResultRow; private readonly int resultRowLength; - private readonly bool[] _uncacheableCollectionPersisters; + private readonly ISet _uncacheableCollectionPersisters; // caching NH-3486 private readonly string[] cachedProjectedColumnAliases; @@ -50,10 +48,7 @@ public CriteriaLoader(IOuterJoinLoadable persister, ISessionFactoryImplementor f InitFromWalker(walker); - if (translator.UncacheableCriteriaCollectionPersisters.Any()) - { - _uncacheableCollectionPersisters = CollectionPersisters?.Select(cp => translator.UncacheableCriteriaCollectionPersisters.Contains(cp)).ToArray(); - } + _uncacheableCollectionPersisters = translator.UncacheableCriteriaCollectionPersisters; userAliases = walker.UserAliases; ResultTypes = walker.ResultTypes; includeInResultRow = walker.IncludeInResultRow; @@ -154,8 +149,6 @@ protected override object[] GetResultRow(object[] row, DbDataReader rs, ISession return result; } - protected override bool[] UncacheableCollectionPersisters => _uncacheableCollectionPersisters; - private object[] ToResultRow(object[] row) { if (resultRowLength == row.Length) @@ -236,5 +229,10 @@ protected override IEnumerable GetParameterSpecificatio { return translator.CollectedParameterSpecifications; } + + protected override bool IsCollectionPersisterCacheable(ICollectionPersister collectionPersister) + { + return !_uncacheableCollectionPersisters.Contains(collectionPersister); + } } } diff --git a/src/NHibernate/Loader/Loader.cs b/src/NHibernate/Loader/Loader.cs index 36d321e8cec..95e74bd9c2d 100644 --- a/src/NHibernate/Loader/Loader.cs +++ b/src/NHibernate/Loader/Loader.cs @@ -605,22 +605,22 @@ private IEnumerable CreateSubselects(IList keys, Qu } internal void InitializeEntitiesAndCollections( - IList hydratedObjects, object resultSetId, ISessionImplementor session, bool readOnly, + IList hydratedObjects, DbDataReader reader, ISessionImplementor session, bool readOnly, CacheBatcher cacheBatcher = null) { ICollectionPersister[] collectionPersisters = CollectionPersisters; if (collectionPersisters != null) { - for (int i = 0; i < collectionPersisters.Length; i++) + foreach (var collectionPersister in collectionPersisters) { - if (collectionPersisters[i].IsArray) + if (collectionPersister.IsArray) { //for arrays, we should end the collection load before resolving //the entities, since the actual array instances are not instantiated //during loading //TODO: or we could do this polymorphically, and have two // different operations implemented differently for arrays - EndCollectionLoad(resultSetId, session, collectionPersisters[i], UncacheableCollectionPersisters?[i] == true); + EndCollectionLoad(reader, session, collectionPersister); } } } @@ -663,28 +663,32 @@ internal void InitializeEntitiesAndCollections( if (collectionPersisters != null) { - for (int i = 0; i < collectionPersisters.Length; i++) + foreach (var collectionPersister in collectionPersisters) { - if (!collectionPersisters[i].IsArray) + if (!collectionPersister.IsArray) { //for sets, we should end the collection load after resolving //the entities, since we might call hashCode() on the elements //TODO: or we could do this polymorphically, and have two // different operations implemented differently for arrays - EndCollectionLoad(resultSetId, session, collectionPersisters[i], UncacheableCollectionPersisters?[i] == true); + EndCollectionLoad(reader, session, collectionPersister); } } } } - private static void EndCollectionLoad(object resultSetId, ISessionImplementor session, ICollectionPersister collectionPersister, bool skipCache) + private void EndCollectionLoad(DbDataReader reader, ISessionImplementor session, ICollectionPersister collectionPersister) { //this is a query and we are loading multiple instances of the same collection role - session.PersistenceContext.LoadContexts.GetCollectionLoadContext((DbDataReader)resultSetId).EndLoadingCollections( - collectionPersister, skipCache); + session.PersistenceContext.LoadContexts.GetCollectionLoadContext(reader).EndLoadingCollections( + collectionPersister, !IsCollectionPersisterCacheable(collectionPersister)); + } + + protected virtual bool IsCollectionPersisterCacheable(ICollectionPersister collectionPersister) + { + return true; } - /// /// Determine the actual ResultTransformer that will be used to transform query results. /// From 5176e35f46962b8dea90f296b0ef527614a6cc4d Mon Sep 17 00:00:00 2001 From: Alexander Zaytsev Date: Wed, 21 Nov 2018 10:52:07 +1300 Subject: [PATCH 18/20] fixup! Hide implementation detail of CacheableCollectionPersister logic --- src/NHibernate/Loader/Loader.cs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/NHibernate/Loader/Loader.cs b/src/NHibernate/Loader/Loader.cs index 95e74bd9c2d..d50d0200dcc 100644 --- a/src/NHibernate/Loader/Loader.cs +++ b/src/NHibernate/Loader/Loader.cs @@ -176,11 +176,6 @@ protected virtual ICollectionPersister[] CollectionPersisters get { return null; } } - /// - /// An array indicating whether cache should be skipped for given collection - /// - protected virtual bool[] UncacheableCollectionPersisters => null; - /// /// What lock mode does this load entities with? /// From 937f1c2b12d2ffca634f280c575f65500ac9bcf7 Mon Sep 17 00:00:00 2001 From: Roman Artiukhin Date: Wed, 21 Nov 2018 10:40:30 +0300 Subject: [PATCH 19/20] fixup! Hide implementation detail of CacheableCollectionPersister logic --- src/NHibernate/Loader/Criteria/CriteriaLoader.cs | 2 +- src/NHibernate/Loader/Criteria/CriteriaQueryTranslator.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/NHibernate/Loader/Criteria/CriteriaLoader.cs b/src/NHibernate/Loader/Criteria/CriteriaLoader.cs index 45c740b3376..8f4b99e58c0 100644 --- a/src/NHibernate/Loader/Criteria/CriteriaLoader.cs +++ b/src/NHibernate/Loader/Criteria/CriteriaLoader.cs @@ -48,7 +48,7 @@ public CriteriaLoader(IOuterJoinLoadable persister, ISessionFactoryImplementor f InitFromWalker(walker); - _uncacheableCollectionPersisters = translator.UncacheableCriteriaCollectionPersisters; + _uncacheableCollectionPersisters = translator.UncacheableCollectionPersisters; userAliases = walker.UserAliases; ResultTypes = walker.ResultTypes; includeInResultRow = walker.IncludeInResultRow; diff --git a/src/NHibernate/Loader/Criteria/CriteriaQueryTranslator.cs b/src/NHibernate/Loader/Criteria/CriteriaQueryTranslator.cs index 306030918e4..f27a54502d4 100644 --- a/src/NHibernate/Loader/Criteria/CriteriaQueryTranslator.cs +++ b/src/NHibernate/Loader/Criteria/CriteriaQueryTranslator.cs @@ -214,7 +214,7 @@ public string[] ProjectedAliases get { return rootCriteria.Projection.Aliases; } } - public ISet UncacheableCriteriaCollectionPersisters => uncacheableCollectionPersisters; + public ISet UncacheableCollectionPersisters => uncacheableCollectionPersisters; public IList GetEntityProjections() { From 6cbbf464e9bb1381dce5e051063aefa1cdf9611d Mon Sep 17 00:00:00 2001 From: Roman Artiukhin Date: Wed, 21 Nov 2018 12:19:45 +0300 Subject: [PATCH 20/20] fixup! Hide implementation detail of CacheableCollectionPersister logic --- src/NHibernate/Loader/Criteria/CriteriaQueryTranslator.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/NHibernate/Loader/Criteria/CriteriaQueryTranslator.cs b/src/NHibernate/Loader/Criteria/CriteriaQueryTranslator.cs index f27a54502d4..9a9acd430fd 100644 --- a/src/NHibernate/Loader/Criteria/CriteriaQueryTranslator.cs +++ b/src/NHibernate/Loader/Criteria/CriteriaQueryTranslator.cs @@ -140,7 +140,6 @@ public QueryParameters GetQueryParameters() selection.FetchSize = rootCriteria.FetchSize; var lockModes = new Dictionary(); - foreach (KeyValuePair me in rootCriteria.LockModes) { ICriteria subcriteria = GetAliasedCriteria(me.Key);