From 17122a6f0e4d96cf6d498a440d9b0b3bba9bc1fb 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, 13 Jul 2018 20:51:34 +0200 Subject: [PATCH 1/5] Support CacheMode in QueryBatch And add a test for ReadOnly --- .../Async/Futures/QueryBatchFixture.cs | 111 ++++++++++++++ .../Futures/QueryBatchFixture.cs | 111 ++++++++++++++ .../Async/Cache/StandardQueryCache.cs | 13 ++ src/NHibernate/Async/Loader/Loader.cs | 4 +- .../Async/Multi/QueryBatchItemBase.cs | 132 +++++++++------- src/NHibernate/Cache/StandardQueryCache.cs | 13 ++ src/NHibernate/Engine/QueryParameters.cs | 29 +++- src/NHibernate/Impl/AbstractQueryImpl.cs | 33 ++-- src/NHibernate/Impl/CriteriaImpl.cs | 2 + .../Criteria/CriteriaQueryTranslator.cs | 5 +- src/NHibernate/Loader/Loader.cs | 9 +- src/NHibernate/Multi/QueryBatchItemBase.cs | 142 ++++++++++-------- 12 files changed, 453 insertions(+), 151 deletions(-) diff --git a/src/NHibernate.Test/Async/Futures/QueryBatchFixture.cs b/src/NHibernate.Test/Async/Futures/QueryBatchFixture.cs index 87261158243..2fe7614ce42 100644 --- a/src/NHibernate.Test/Async/Futures/QueryBatchFixture.cs +++ b/src/NHibernate.Test/Async/Futures/QueryBatchFixture.cs @@ -388,6 +388,116 @@ public async Task UsingHqlToFutureWithCacheAndTransformerDoesntThrowAsync() } } + [Test] + public async Task ReadOnlyWorksWithFutureAsync() + { + using (var s = OpenSession()) + using (var t = s.BeginTransaction()) + { + var futureSimples = + s + .CreateQuery("from EntitySimpleChild") + .SetReadOnly(true) + .Future(); + var futureSubselect = + s + .CreateQuery("from EntitySubselectChild") + .Future(); + + var simples = (await (futureSimples.GetEnumerableAsync())).ToList(); + Assert.That(simples, Has.Count.GreaterThan(0)); + foreach (var entity in simples) + { + Assert.That(s.IsReadOnly(entity), Is.True, entity.Name); + } + + var subselect = (await (futureSubselect.GetEnumerableAsync())).ToList(); + Assert.That(subselect, Has.Count.GreaterThan(0)); + foreach (var entity in subselect) + { + Assert.That(s.IsReadOnly(entity), Is.False, entity.Name); + } + + await (t.CommitAsync()); + } + } + + [Test] + public async Task CacheModeWorksWithFutureAsync() + { + Sfi.Statistics.IsStatisticsEnabled = true; + + using (var s = OpenSession()) + using (var t = s.BeginTransaction()) + { + s + .CreateQuery("from EntitySimpleChild") + .SetCacheable(true) + .SetCacheMode(CacheMode.Get) + .Future(); + s + .CreateQuery("from EntityComplex") + .SetCacheable(true) + .SetCacheMode(CacheMode.Put) + .Future(); + await (s + .CreateQuery("from EntitySubselectChild") + .SetCacheable(true) + .Future() + .GetEnumerableAsync()); + Assert.That(Sfi.Statistics.QueryCachePutCount, Is.EqualTo(2), "Future put"); + + await (t.CommitAsync()); + } + + using (var s = OpenSession()) + using (var t = s.BeginTransaction()) + { + Sfi.Statistics.Clear(); + await (s + .CreateQuery("from EntitySimpleChild") + .SetCacheable(true) + .ListAsync()); + Assert.That(Sfi.Statistics.QueryCacheHitCount, Is.EqualTo(0), "EntitySimpleChild query hit"); + + Sfi.Statistics.Clear(); + await (s + .CreateQuery("from EntityComplex") + .SetCacheable(true) + .ListAsync()); + Assert.That(Sfi.Statistics.QueryCacheHitCount, Is.EqualTo(1), "EntityComplex query hit"); + + Sfi.Statistics.Clear(); + await (s + .CreateQuery("from EntitySubselectChild") + .SetCacheable(true) + .ListAsync()); + Assert.That(Sfi.Statistics.QueryCacheHitCount, Is.EqualTo(1), "EntitySubselectChild query hit"); + + await (t.CommitAsync()); + } + + using (var s = OpenSession()) + using (var t = s.BeginTransaction()) + { + Sfi.Statistics.Clear(); + s + .CreateQuery("from EntitySimpleChild") + .SetCacheable(true) + .SetCacheMode(CacheMode.Get) + .Future(); + await (s + .CreateQuery("from EntitySubselectChild") + .SetCacheable(true) + .Future() + .GetEnumerableAsync()); + Assert.That(Sfi.Statistics.QueryCachePutCount, Is.EqualTo(0), "Second future put"); + Assert.That(Sfi.Statistics.QueryCacheHitCount, Is.EqualTo(2), "Second future hit"); + + await (t.CommitAsync()); + } + } + #region Test Setup protected override HbmMapping GetMappings() @@ -471,6 +581,7 @@ protected override void OnTearDown() session.Flush(); transaction.Commit(); } + Sfi.Statistics.IsStatisticsEnabled = false; } protected override void OnSetUp() diff --git a/src/NHibernate.Test/Futures/QueryBatchFixture.cs b/src/NHibernate.Test/Futures/QueryBatchFixture.cs index c872d8bc2fc..cb79faedae1 100644 --- a/src/NHibernate.Test/Futures/QueryBatchFixture.cs +++ b/src/NHibernate.Test/Futures/QueryBatchFixture.cs @@ -376,6 +376,116 @@ public void UsingHqlToFutureWithCacheAndTransformerDoesntThrow() } } + [Test] + public void ReadOnlyWorksWithFuture() + { + using (var s = OpenSession()) + using (var t = s.BeginTransaction()) + { + var futureSimples = + s + .CreateQuery("from EntitySimpleChild") + .SetReadOnly(true) + .Future(); + var futureSubselect = + s + .CreateQuery("from EntitySubselectChild") + .Future(); + + var simples = futureSimples.GetEnumerable().ToList(); + Assert.That(simples, Has.Count.GreaterThan(0)); + foreach (var entity in simples) + { + Assert.That(s.IsReadOnly(entity), Is.True, entity.Name); + } + + var subselect = futureSubselect.GetEnumerable().ToList(); + Assert.That(subselect, Has.Count.GreaterThan(0)); + foreach (var entity in subselect) + { + Assert.That(s.IsReadOnly(entity), Is.False, entity.Name); + } + + t.Commit(); + } + } + + [Test] + public void CacheModeWorksWithFuture() + { + Sfi.Statistics.IsStatisticsEnabled = true; + + using (var s = OpenSession()) + using (var t = s.BeginTransaction()) + { + s + .CreateQuery("from EntitySimpleChild") + .SetCacheable(true) + .SetCacheMode(CacheMode.Get) + .Future(); + s + .CreateQuery("from EntityComplex") + .SetCacheable(true) + .SetCacheMode(CacheMode.Put) + .Future(); + s + .CreateQuery("from EntitySubselectChild") + .SetCacheable(true) + .Future() + .GetEnumerable(); + Assert.That(Sfi.Statistics.QueryCachePutCount, Is.EqualTo(2), "Future put"); + + t.Commit(); + } + + using (var s = OpenSession()) + using (var t = s.BeginTransaction()) + { + Sfi.Statistics.Clear(); + s + .CreateQuery("from EntitySimpleChild") + .SetCacheable(true) + .List(); + Assert.That(Sfi.Statistics.QueryCacheHitCount, Is.EqualTo(0), "EntitySimpleChild query hit"); + + Sfi.Statistics.Clear(); + s + .CreateQuery("from EntityComplex") + .SetCacheable(true) + .List(); + Assert.That(Sfi.Statistics.QueryCacheHitCount, Is.EqualTo(1), "EntityComplex query hit"); + + Sfi.Statistics.Clear(); + s + .CreateQuery("from EntitySubselectChild") + .SetCacheable(true) + .List(); + Assert.That(Sfi.Statistics.QueryCacheHitCount, Is.EqualTo(1), "EntitySubselectChild query hit"); + + t.Commit(); + } + + using (var s = OpenSession()) + using (var t = s.BeginTransaction()) + { + Sfi.Statistics.Clear(); + s + .CreateQuery("from EntitySimpleChild") + .SetCacheable(true) + .SetCacheMode(CacheMode.Get) + .Future(); + s + .CreateQuery("from EntitySubselectChild") + .SetCacheable(true) + .Future() + .GetEnumerable(); + Assert.That(Sfi.Statistics.QueryCachePutCount, Is.EqualTo(0), "Second future put"); + Assert.That(Sfi.Statistics.QueryCacheHitCount, Is.EqualTo(2), "Second future hit"); + + t.Commit(); + } + } + #region Test Setup protected override HbmMapping GetMappings() @@ -459,6 +569,7 @@ protected override void OnTearDown() session.Flush(); transaction.Commit(); } + Sfi.Statistics.IsStatisticsEnabled = false; } protected override void OnSetUp() diff --git a/src/NHibernate/Async/Cache/StandardQueryCache.cs b/src/NHibernate/Async/Cache/StandardQueryCache.cs index 81a3dfabe92..c5a61906700 100644 --- a/src/NHibernate/Async/Cache/StandardQueryCache.cs +++ b/src/NHibernate/Async/Cache/StandardQueryCache.cs @@ -226,6 +226,7 @@ public async Task GetManyAsync( var upToDatesIndex = 0; var persistenceContext = session.PersistenceContext; var defaultReadOnlyOrig = persistenceContext.DefaultReadOnly; + var cacheModeOrig = session.CacheMode; for (var i = 0; i < keys.Length; i++) { var cacheable = cacheables[i]; @@ -245,6 +246,16 @@ public async Task GetManyAsync( else queryParams.ReadOnly = persistenceContext.DefaultReadOnly; + // Adjust the session cache mode, as GetResultFromCacheable assemble types which may cause + // entity loads, which may interact with the cache. + if (queryParams.CacheMode.HasValue && + // Avoid setting the cache mode with the same value as the setter is not just affecting + // the backing field + queryParams.CacheMode != cacheModeOrig) + { + session.CacheMode = queryParams.CacheMode.Value; + } + try { results[i] = await (GetResultFromCacheableAsync(key, returnTypes[i], queryParams.NaturalKeyLookup, session, cacheable, cancellationToken)).ConfigureAwait(false); @@ -252,6 +263,8 @@ public async Task GetManyAsync( finally { persistenceContext.DefaultReadOnly = defaultReadOnlyOrig; + if (session.CacheMode != cacheModeOrig) + session.CacheMode = cacheModeOrig; } } diff --git a/src/NHibernate/Async/Loader/Loader.cs b/src/NHibernate/Async/Loader/Loader.cs index 58af26e2c36..04413af7f30 100644 --- a/src/NHibernate/Async/Loader/Loader.cs +++ b/src/NHibernate/Async/Loader/Loader.cs @@ -1227,7 +1227,7 @@ private async Task GetResultFromQueryCacheAsync( IQueryCache queryCache, QueryKey key, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); - if (!CanGetFromCache(session, queryParameters)) + if (!queryParameters.CanGetFromCache(session)) return null; var result = await (queryCache.GetAsync( @@ -1256,7 +1256,7 @@ private async Task PutResultInQueryCacheAsync(ISessionImplementor session, Query IQueryCache queryCache, QueryKey key, IList result, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); - if (!session.CacheMode.HasFlag(CacheMode.Put)) + if (!queryParameters.CanPutToCache(session)) return; var put = await (queryCache.PutAsync( diff --git a/src/NHibernate/Async/Multi/QueryBatchItemBase.cs b/src/NHibernate/Async/Multi/QueryBatchItemBase.cs index 9360351debb..f0d097384d2 100644 --- a/src/NHibernate/Async/Multi/QueryBatchItemBase.cs +++ b/src/NHibernate/Async/Multi/QueryBatchItemBase.cs @@ -35,80 +35,96 @@ public async Task ProcessResultsSetAsync(DbDataReader reader, CancellationT var dialect = Session.Factory.Dialect; var hydratedObjects = new List[_queryInfos.Count]; - var rowCount = 0; - for (var i = 0; i < _queryInfos.Count; i++) + var cacheModeOrig = Session.CacheMode; + if (_cacheMode.HasValue && + // Avoid setting the cache mode with the same value as the setter is not just affecting + // the backing field + _cacheMode != cacheModeOrig) { - var queryInfo = _queryInfos[i]; - var loader = queryInfo.Loader; - var queryParameters = queryInfo.Parameters; - - //Skip processing for items already loaded from cache - if (queryInfo.IsResultFromCache) + Session.CacheMode = _cacheMode.Value; + } + try + { + var rowCount = 0; + for (var i = 0; i < _queryInfos.Count; i++) { - continue; - } + var queryInfo = _queryInfos[i]; + var loader = queryInfo.Loader; + var queryParameters = queryInfo.Parameters; - var entitySpan = loader.EntityPersisters.Length; - hydratedObjects[i] = entitySpan == 0 ? null : new List(entitySpan); - var keys = new EntityKey[entitySpan]; + //Skip processing for items already loaded from cache + if (queryInfo.IsResultFromCache) + { + continue; + } - var selection = queryParameters.RowSelection; - var createSubselects = loader.IsSubselectLoadingEnabled; + var entitySpan = loader.EntityPersisters.Length; + hydratedObjects[i] = entitySpan == 0 ? null : new List(entitySpan); + var keys = new EntityKey[entitySpan]; - _subselectResultKeys[i] = createSubselects ? new List() : null; - var maxRows = Loader.Loader.HasMaxRows(selection) ? selection.MaxRows : int.MaxValue; - var advanceSelection = !dialect.SupportsLimitOffset || !loader.UseLimit(selection, dialect); + var selection = queryParameters.RowSelection; + var createSubselects = loader.IsSubselectLoadingEnabled; - if (advanceSelection) - { - await (Loader.Loader.AdvanceAsync(reader, selection, cancellationToken)).ConfigureAwait(false); - } + _subselectResultKeys[i] = createSubselects ? new List() : null; + var maxRows = Loader.Loader.HasMaxRows(selection) ? selection.MaxRows : int.MaxValue; + var advanceSelection = !dialect.SupportsLimitOffset || !loader.UseLimit(selection, dialect); - var forcedResultTransformer = queryInfo.CacheKey?.ResultTransformer; - if (queryParameters.HasAutoDiscoverScalarTypes) - { - loader.AutoDiscoverTypes(reader, queryParameters, forcedResultTransformer); - } + if (advanceSelection) + { + await (Loader.Loader.AdvanceAsync(reader, selection, cancellationToken)).ConfigureAwait(false); + } - var lockModeArray = loader.GetLockModes(queryParameters.LockModes); - var optionalObjectKey = Loader.Loader.GetOptionalObjectKey(queryParameters, Session); - var tmpResults = new List(); + var forcedResultTransformer = queryInfo.CacheKey?.ResultTransformer; + if (queryParameters.HasAutoDiscoverScalarTypes) + { + loader.AutoDiscoverTypes(reader, queryParameters, forcedResultTransformer); + } - for (var count = 0; count < maxRows && await (reader.ReadAsync(cancellationToken)).ConfigureAwait(false); count++) - { - rowCount++; - - var o = - await (loader.GetRowFromResultSetAsync( - reader, - Session, - queryParameters, - lockModeArray, - optionalObjectKey, - hydratedObjects[i], - keys, - true, - forcedResultTransformer -, cancellationToken )).ConfigureAwait(false); - if (loader.IsSubselectLoadingEnabled) + var lockModeArray = loader.GetLockModes(queryParameters.LockModes); + var optionalObjectKey = Loader.Loader.GetOptionalObjectKey(queryParameters, Session); + var tmpResults = new List(); + + for (var count = 0; count < maxRows && await (reader.ReadAsync(cancellationToken)).ConfigureAwait(false); count++) { - _subselectResultKeys[i].Add(keys); - keys = new EntityKey[entitySpan]; //can't reuse in this case + rowCount++; + + var o = + await (loader.GetRowFromResultSetAsync( + reader, + Session, + queryParameters, + lockModeArray, + optionalObjectKey, + hydratedObjects[i], + keys, + true, + forcedResultTransformer +, cancellationToken )).ConfigureAwait(false); + if (loader.IsSubselectLoadingEnabled) + { + _subselectResultKeys[i].Add(keys); + keys = new EntityKey[entitySpan]; //can't reuse in this case + } + + tmpResults.Add(o); } - tmpResults.Add(o); + queryInfo.Result = tmpResults; + if (queryInfo.CanPutToCache) + queryInfo.ResultToCache = tmpResults; + + await (reader.NextResultAsync(cancellationToken)).ConfigureAwait(false); } - queryInfo.Result = tmpResults; - if (queryInfo.CanPutToCache) - queryInfo.ResultToCache = tmpResults; + await (InitializeEntitiesAndCollectionsAsync(reader, hydratedObjects, cancellationToken)).ConfigureAwait(false); - await (reader.NextResultAsync(cancellationToken)).ConfigureAwait(false); + return rowCount; + } + finally + { + if (Session.CacheMode != cacheModeOrig) + Session.CacheMode = cacheModeOrig; } - - await (InitializeEntitiesAndCollectionsAsync(reader, hydratedObjects, cancellationToken)).ConfigureAwait(false); - - return rowCount; } /// diff --git a/src/NHibernate/Cache/StandardQueryCache.cs b/src/NHibernate/Cache/StandardQueryCache.cs index 453101bc13c..92d9824b2e1 100644 --- a/src/NHibernate/Cache/StandardQueryCache.cs +++ b/src/NHibernate/Cache/StandardQueryCache.cs @@ -241,6 +241,7 @@ public IList[] GetMany( var upToDatesIndex = 0; var persistenceContext = session.PersistenceContext; var defaultReadOnlyOrig = persistenceContext.DefaultReadOnly; + var cacheModeOrig = session.CacheMode; for (var i = 0; i < keys.Length; i++) { var cacheable = cacheables[i]; @@ -260,6 +261,16 @@ public IList[] GetMany( else queryParams.ReadOnly = persistenceContext.DefaultReadOnly; + // Adjust the session cache mode, as GetResultFromCacheable assemble types which may cause + // entity loads, which may interact with the cache. + if (queryParams.CacheMode.HasValue && + // Avoid setting the cache mode with the same value as the setter is not just affecting + // the backing field + queryParams.CacheMode != cacheModeOrig) + { + session.CacheMode = queryParams.CacheMode.Value; + } + try { results[i] = GetResultFromCacheable(key, returnTypes[i], queryParams.NaturalKeyLookup, session, cacheable); @@ -267,6 +278,8 @@ public IList[] GetMany( finally { persistenceContext.DefaultReadOnly = defaultReadOnlyOrig; + if (session.CacheMode != cacheModeOrig) + session.CacheMode = cacheModeOrig; } } diff --git a/src/NHibernate/Engine/QueryParameters.cs b/src/NHibernate/Engine/QueryParameters.cs index 9417b0b2346..517e4770607 100644 --- a/src/NHibernate/Engine/QueryParameters.cs +++ b/src/NHibernate/Engine/QueryParameters.cs @@ -7,7 +7,6 @@ using NHibernate.SqlCommand; using NHibernate.Transform; using NHibernate.Type; -using NHibernate.Util; namespace NHibernate.Engine { @@ -124,6 +123,8 @@ public bool HasRowSelection public string CacheRegion { get; set; } + public CacheMode? CacheMode { get; set; } + public string Comment { get; set; } public bool ForceCacheRefresh { get; set; } @@ -201,13 +202,15 @@ public void ValidateParameters() public QueryParameters CreateCopyUsing(RowSelection selection) { - var copy = new QueryParameters(PositionalParameterTypes, PositionalParameterValues, NamedParameters, LockModes, - selection, IsReadOnlyInitialized, readOnly, Cacheable, CacheRegion, Comment, CollectionKeys, - OptionalObject, OptionalEntityName, OptionalId, ResultTransformer) - { - ProcessedSql = ProcessedSql, - ProcessedSqlParameters = ProcessedSqlParameters != null ? ProcessedSqlParameters.ToList() : null - }; + var copy = new QueryParameters( + PositionalParameterTypes, PositionalParameterValues, NamedParameters, LockModes, selection, + IsReadOnlyInitialized, readOnly, Cacheable, CacheRegion, Comment, CollectionKeys, OptionalObject, + OptionalEntityName, OptionalId, ResultTransformer) + { + ProcessedSql = ProcessedSql, + ProcessedSqlParameters = ProcessedSqlParameters != null ? ProcessedSqlParameters.ToList() : null, + CacheMode = CacheMode + }; return copy; } @@ -215,5 +218,15 @@ public bool IsReadOnly(ISessionImplementor session) { return IsReadOnlyInitialized ? ReadOnly : session.PersistenceContext.DefaultReadOnly; } + + public bool CanGetFromCache(ISessionImplementor session) + { + return !ForceCacheRefresh && (CacheMode ?? session.CacheMode).HasFlag(NHibernate.CacheMode.Get); + } + + public bool CanPutToCache(ISessionImplementor session) + { + return (CacheMode ?? session.CacheMode).HasFlag(NHibernate.CacheMode.Put); + } } } diff --git a/src/NHibernate/Impl/AbstractQueryImpl.cs b/src/NHibernate/Impl/AbstractQueryImpl.cs index 3fa74af00f2..788db5cbda6 100644 --- a/src/NHibernate/Impl/AbstractQueryImpl.cs +++ b/src/NHibernate/Impl/AbstractQueryImpl.cs @@ -997,21 +997,24 @@ public virtual QueryParameters GetQueryParameters() public virtual QueryParameters GetQueryParameters(IDictionary namedParams) { return new QueryParameters( - TypeArray(), - ValueArray(), - namedParams, - LockModes, - Selection, - true, - IsReadOnly, - cacheable, - cacheRegion, - comment, - collectionKey == null ? null : new[] { collectionKey }, - optionalObject, - optionalEntityName, - optionalId, - resultTransformer); + TypeArray(), + ValueArray(), + namedParams, + LockModes, + Selection, + true, + IsReadOnly, + cacheable, + cacheRegion, + comment, + collectionKey == null ? null : new[] { collectionKey }, + optionalObject, + optionalEntityName, + optionalId, + resultTransformer) + { + CacheMode = cacheMode + }; } protected void Before() diff --git a/src/NHibernate/Impl/CriteriaImpl.cs b/src/NHibernate/Impl/CriteriaImpl.cs index 5a6d4547aa7..511ca3e651f 100644 --- a/src/NHibernate/Impl/CriteriaImpl.cs +++ b/src/NHibernate/Impl/CriteriaImpl.cs @@ -202,6 +202,8 @@ public string CacheRegion get { return cacheRegion; } } + public CacheMode? CacheMode => cacheMode; + public string Comment { get { return comment; } diff --git a/src/NHibernate/Loader/Criteria/CriteriaQueryTranslator.cs b/src/NHibernate/Loader/Criteria/CriteriaQueryTranslator.cs index 5b4199b0e30..c46efecfc8f 100644 --- a/src/NHibernate/Loader/Criteria/CriteriaQueryTranslator.cs +++ b/src/NHibernate/Loader/Criteria/CriteriaQueryTranslator.cs @@ -167,7 +167,10 @@ public QueryParameters GetQueryParameters() rootCriteria.CacheRegion, rootCriteria.Comment, rootCriteria.LookupByNaturalKey, - rootCriteria.ResultTransformer); + rootCriteria.ResultTransformer) + { + CacheMode = rootCriteria.CacheMode + }; } public SqlString GetGroupBy() diff --git a/src/NHibernate/Loader/Loader.cs b/src/NHibernate/Loader/Loader.cs index c63bd9fbcbf..a05ffc3eda1 100644 --- a/src/NHibernate/Loader/Loader.cs +++ b/src/NHibernate/Loader/Loader.cs @@ -1714,16 +1714,11 @@ private CacheableResultTransformer CreateCacheableResultTransformer(QueryParamet queryParameters.HasAutoDiscoverScalarTypes, SqlString); } - internal bool CanGetFromCache(ISessionImplementor session, QueryParameters queryParameters) - { - return !queryParameters.ForceCacheRefresh && session.CacheMode.HasFlag(CacheMode.Get); - } - private IList GetResultFromQueryCache( ISessionImplementor session, QueryParameters queryParameters, ISet querySpaces, IQueryCache queryCache, QueryKey key) { - if (!CanGetFromCache(session, queryParameters)) + if (!queryParameters.CanGetFromCache(session)) return null; var result = queryCache.Get( @@ -1751,7 +1746,7 @@ private IList GetResultFromQueryCache( private void PutResultInQueryCache(ISessionImplementor session, QueryParameters queryParameters, IQueryCache queryCache, QueryKey key, IList result) { - if (!session.CacheMode.HasFlag(CacheMode.Put)) + if (!queryParameters.CanPutToCache(session)) return; var put = queryCache.Put( diff --git a/src/NHibernate/Multi/QueryBatchItemBase.cs b/src/NHibernate/Multi/QueryBatchItemBase.cs index 89407c582a2..5b087cef16a 100644 --- a/src/NHibernate/Multi/QueryBatchItemBase.cs +++ b/src/NHibernate/Multi/QueryBatchItemBase.cs @@ -19,6 +19,8 @@ public abstract partial class QueryBatchItemBase : IQueryBatchItem[] _subselectResultKeys; private List _queryInfos; + private CacheMode? _cacheMode; + private bool? _isReadOnly; private IList _finalResults; protected class QueryInfo : ICachingInformation @@ -96,8 +98,8 @@ public QueryInfo( return; CacheKey = Loader.GenerateQueryKey(session, Parameters); - CanGetFromCache = Loader.CanGetFromCache(session, Parameters); - CanPutToCache = session.CacheMode.HasFlag(CacheMode.Put); + CanGetFromCache = Parameters.CanGetFromCache(session); + CanPutToCache = Parameters.CanPutToCache(session); } /// @@ -136,6 +138,10 @@ public virtual void Init(ISessionImplementor session) Session = session; _queryInfos = GetQueryInformation(session); + // Cache and readonly parameters are the same for all translators + var queryParameters = _queryInfos.First().Parameters; + _cacheMode = queryParameters.CacheMode; + _isReadOnly = queryParameters.IsReadOnlyInitialized ? queryParameters.ReadOnly : default(bool?); var count = _queryInfos.Count; _subselectResultKeys = new List[count]; @@ -171,80 +177,96 @@ public int ProcessResultsSet(DbDataReader reader) var dialect = Session.Factory.Dialect; var hydratedObjects = new List[_queryInfos.Count]; - var rowCount = 0; - for (var i = 0; i < _queryInfos.Count; i++) + var cacheModeOrig = Session.CacheMode; + if (_cacheMode.HasValue && + // Avoid setting the cache mode with the same value as the setter is not just affecting + // the backing field + _cacheMode != cacheModeOrig) { - var queryInfo = _queryInfos[i]; - var loader = queryInfo.Loader; - var queryParameters = queryInfo.Parameters; - - //Skip processing for items already loaded from cache - if (queryInfo.IsResultFromCache) + Session.CacheMode = _cacheMode.Value; + } + try + { + var rowCount = 0; + for (var i = 0; i < _queryInfos.Count; i++) { - continue; - } + var queryInfo = _queryInfos[i]; + var loader = queryInfo.Loader; + var queryParameters = queryInfo.Parameters; - var entitySpan = loader.EntityPersisters.Length; - hydratedObjects[i] = entitySpan == 0 ? null : new List(entitySpan); - var keys = new EntityKey[entitySpan]; + //Skip processing for items already loaded from cache + if (queryInfo.IsResultFromCache) + { + continue; + } - var selection = queryParameters.RowSelection; - var createSubselects = loader.IsSubselectLoadingEnabled; + var entitySpan = loader.EntityPersisters.Length; + hydratedObjects[i] = entitySpan == 0 ? null : new List(entitySpan); + var keys = new EntityKey[entitySpan]; - _subselectResultKeys[i] = createSubselects ? new List() : null; - var maxRows = Loader.Loader.HasMaxRows(selection) ? selection.MaxRows : int.MaxValue; - var advanceSelection = !dialect.SupportsLimitOffset || !loader.UseLimit(selection, dialect); + var selection = queryParameters.RowSelection; + var createSubselects = loader.IsSubselectLoadingEnabled; - if (advanceSelection) - { - Loader.Loader.Advance(reader, selection); - } + _subselectResultKeys[i] = createSubselects ? new List() : null; + var maxRows = Loader.Loader.HasMaxRows(selection) ? selection.MaxRows : int.MaxValue; + var advanceSelection = !dialect.SupportsLimitOffset || !loader.UseLimit(selection, dialect); - var forcedResultTransformer = queryInfo.CacheKey?.ResultTransformer; - if (queryParameters.HasAutoDiscoverScalarTypes) - { - loader.AutoDiscoverTypes(reader, queryParameters, forcedResultTransformer); - } + if (advanceSelection) + { + Loader.Loader.Advance(reader, selection); + } - var lockModeArray = loader.GetLockModes(queryParameters.LockModes); - var optionalObjectKey = Loader.Loader.GetOptionalObjectKey(queryParameters, Session); - var tmpResults = new List(); + var forcedResultTransformer = queryInfo.CacheKey?.ResultTransformer; + if (queryParameters.HasAutoDiscoverScalarTypes) + { + loader.AutoDiscoverTypes(reader, queryParameters, forcedResultTransformer); + } - for (var count = 0; count < maxRows && reader.Read(); count++) - { - rowCount++; - - var o = - loader.GetRowFromResultSet( - reader, - Session, - queryParameters, - lockModeArray, - optionalObjectKey, - hydratedObjects[i], - keys, - true, - forcedResultTransformer - ); - if (loader.IsSubselectLoadingEnabled) + var lockModeArray = loader.GetLockModes(queryParameters.LockModes); + var optionalObjectKey = Loader.Loader.GetOptionalObjectKey(queryParameters, Session); + var tmpResults = new List(); + + for (var count = 0; count < maxRows && reader.Read(); count++) { - _subselectResultKeys[i].Add(keys); - keys = new EntityKey[entitySpan]; //can't reuse in this case + rowCount++; + + var o = + loader.GetRowFromResultSet( + reader, + Session, + queryParameters, + lockModeArray, + optionalObjectKey, + hydratedObjects[i], + keys, + true, + forcedResultTransformer + ); + if (loader.IsSubselectLoadingEnabled) + { + _subselectResultKeys[i].Add(keys); + keys = new EntityKey[entitySpan]; //can't reuse in this case + } + + tmpResults.Add(o); } - tmpResults.Add(o); + queryInfo.Result = tmpResults; + if (queryInfo.CanPutToCache) + queryInfo.ResultToCache = tmpResults; + + reader.NextResult(); } - queryInfo.Result = tmpResults; - if (queryInfo.CanPutToCache) - queryInfo.ResultToCache = tmpResults; + InitializeEntitiesAndCollections(reader, hydratedObjects); - reader.NextResult(); + return rowCount; + } + finally + { + if (Session.CacheMode != cacheModeOrig) + Session.CacheMode = cacheModeOrig; } - - InitializeEntitiesAndCollections(reader, hydratedObjects); - - return rowCount; } /// From 27a0012330cd84e2cccdf54a990a708e84ceebb8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Delaporte?= <12201973+fredericDelaporte@users.noreply.github.com> Date: Sat, 14 Jul 2018 02:07:30 +0200 Subject: [PATCH 2/5] fixup! Support CacheMode in QueryBatch Add a cache mode switch --- .../Async/Cache/StandardQueryCache.cs | 31 +++++------ .../Default/DefaultDeleteEventListener.cs | 54 ++++++++++--------- .../Async/Multi/QueryBatchItemBase.cs | 15 +----- src/NHibernate/Cache/StandardQueryCache.cs | 31 +++++------ src/NHibernate/Engine/ISessionImplementor.cs | 37 ++++++++++++- .../Default/DefaultDeleteEventListener.cs | 54 ++++++++++--------- src/NHibernate/Multi/QueryBatchItemBase.cs | 15 +----- 7 files changed, 122 insertions(+), 115 deletions(-) diff --git a/src/NHibernate/Async/Cache/StandardQueryCache.cs b/src/NHibernate/Async/Cache/StandardQueryCache.cs index c5a61906700..5cc5213a8a2 100644 --- a/src/NHibernate/Async/Cache/StandardQueryCache.cs +++ b/src/NHibernate/Async/Cache/StandardQueryCache.cs @@ -226,7 +226,6 @@ public async Task GetManyAsync( var upToDatesIndex = 0; var persistenceContext = session.PersistenceContext; var defaultReadOnlyOrig = persistenceContext.DefaultReadOnly; - var cacheModeOrig = session.CacheMode; for (var i = 0; i < keys.Length; i++) { var cacheable = cacheables[i]; @@ -248,23 +247,21 @@ public async Task GetManyAsync( // Adjust the session cache mode, as GetResultFromCacheable assemble types which may cause // entity loads, which may interact with the cache. - if (queryParams.CacheMode.HasValue && - // Avoid setting the cache mode with the same value as the setter is not just affecting - // the backing field - queryParams.CacheMode != cacheModeOrig) + using (session.SwitchCacheMode(queryParams.CacheMode)) { - session.CacheMode = queryParams.CacheMode.Value; - } - - try - { - results[i] = await (GetResultFromCacheableAsync(key, returnTypes[i], queryParams.NaturalKeyLookup, session, cacheable, cancellationToken)).ConfigureAwait(false); - } - finally - { - persistenceContext.DefaultReadOnly = defaultReadOnlyOrig; - if (session.CacheMode != cacheModeOrig) - session.CacheMode = cacheModeOrig; + try + { + results[i] = await (GetResultFromCacheableAsync( + key, + returnTypes[i], + queryParams.NaturalKeyLookup, + session, + cacheable, cancellationToken)).ConfigureAwait(false); + } + finally + { + persistenceContext.DefaultReadOnly = defaultReadOnlyOrig; + } } } diff --git a/src/NHibernate/Async/Event/Default/DefaultDeleteEventListener.cs b/src/NHibernate/Async/Event/Default/DefaultDeleteEventListener.cs index afd64e3a6b2..fec2ff09a11 100644 --- a/src/NHibernate/Async/Event/Default/DefaultDeleteEventListener.cs +++ b/src/NHibernate/Async/Event/Default/DefaultDeleteEventListener.cs @@ -222,40 +222,42 @@ protected virtual async Task DeleteEntityAsync(IEventSource session, object enti protected virtual async Task CascadeBeforeDeleteAsync(IEventSource session, IEntityPersister persister, object entity, EntityEntry entityEntry, ISet transientEntities, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); - ISessionImplementor si = session; - CacheMode cacheMode = si.CacheMode; - si.CacheMode = CacheMode.Get; - session.PersistenceContext.IncrementCascadeLevel(); - try + using (session.SwitchCacheMode(CacheMode.Get)) { - // cascade-delete to collections BEFORE the collection owner is deleted - await (new Cascade(CascadingAction.Delete, CascadePoint.AfterInsertBeforeDelete, session).CascadeOnAsync(persister, entity, - transientEntities, cancellationToken)).ConfigureAwait(false); - } - finally - { - session.PersistenceContext.DecrementCascadeLevel(); - si.CacheMode = cacheMode; + session.PersistenceContext.IncrementCascadeLevel(); + try + { + // cascade-delete to collections BEFORE the collection owner is deleted + await (new Cascade(CascadingAction.Delete, CascadePoint.AfterInsertBeforeDelete, session).CascadeOnAsync( + persister, + entity, + transientEntities, cancellationToken)).ConfigureAwait(false); + } + finally + { + session.PersistenceContext.DecrementCascadeLevel(); + } } } protected virtual async Task CascadeAfterDeleteAsync(IEventSource session, IEntityPersister persister, object entity, ISet transientEntities, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); - ISessionImplementor si = session; - CacheMode cacheMode = si.CacheMode; - si.CacheMode = CacheMode.Get; - session.PersistenceContext.IncrementCascadeLevel(); - try + using (session.SwitchCacheMode(CacheMode.Get)) { - // cascade-delete to many-to-one AFTER the parent was deleted - await (new Cascade(CascadingAction.Delete, CascadePoint.BeforeInsertAfterDelete, session).CascadeOnAsync(persister, entity, - transientEntities, cancellationToken)).ConfigureAwait(false); - } - finally - { - session.PersistenceContext.DecrementCascadeLevel(); - si.CacheMode = cacheMode; + session.PersistenceContext.IncrementCascadeLevel(); + try + { + // cascade-delete to many-to-one AFTER the parent was deleted + await (new Cascade(CascadingAction.Delete, CascadePoint.BeforeInsertAfterDelete, session).CascadeOnAsync( + persister, + entity, + transientEntities, cancellationToken)).ConfigureAwait(false); + } + finally + { + session.PersistenceContext.DecrementCascadeLevel(); + } } } } diff --git a/src/NHibernate/Async/Multi/QueryBatchItemBase.cs b/src/NHibernate/Async/Multi/QueryBatchItemBase.cs index f0d097384d2..1325d045614 100644 --- a/src/NHibernate/Async/Multi/QueryBatchItemBase.cs +++ b/src/NHibernate/Async/Multi/QueryBatchItemBase.cs @@ -35,15 +35,7 @@ public async Task ProcessResultsSetAsync(DbDataReader reader, CancellationT var dialect = Session.Factory.Dialect; var hydratedObjects = new List[_queryInfos.Count]; - var cacheModeOrig = Session.CacheMode; - if (_cacheMode.HasValue && - // Avoid setting the cache mode with the same value as the setter is not just affecting - // the backing field - _cacheMode != cacheModeOrig) - { - Session.CacheMode = _cacheMode.Value; - } - try + using (Session.SwitchCacheMode(CacheMode.Get)) { var rowCount = 0; for (var i = 0; i < _queryInfos.Count; i++) @@ -120,11 +112,6 @@ public async Task ProcessResultsSetAsync(DbDataReader reader, CancellationT return rowCount; } - finally - { - if (Session.CacheMode != cacheModeOrig) - Session.CacheMode = cacheModeOrig; - } } /// diff --git a/src/NHibernate/Cache/StandardQueryCache.cs b/src/NHibernate/Cache/StandardQueryCache.cs index 92d9824b2e1..47c831772ba 100644 --- a/src/NHibernate/Cache/StandardQueryCache.cs +++ b/src/NHibernate/Cache/StandardQueryCache.cs @@ -241,7 +241,6 @@ public IList[] GetMany( var upToDatesIndex = 0; var persistenceContext = session.PersistenceContext; var defaultReadOnlyOrig = persistenceContext.DefaultReadOnly; - var cacheModeOrig = session.CacheMode; for (var i = 0; i < keys.Length; i++) { var cacheable = cacheables[i]; @@ -263,23 +262,21 @@ public IList[] GetMany( // Adjust the session cache mode, as GetResultFromCacheable assemble types which may cause // entity loads, which may interact with the cache. - if (queryParams.CacheMode.HasValue && - // Avoid setting the cache mode with the same value as the setter is not just affecting - // the backing field - queryParams.CacheMode != cacheModeOrig) + using (session.SwitchCacheMode(queryParams.CacheMode)) { - session.CacheMode = queryParams.CacheMode.Value; - } - - try - { - results[i] = GetResultFromCacheable(key, returnTypes[i], queryParams.NaturalKeyLookup, session, cacheable); - } - finally - { - persistenceContext.DefaultReadOnly = defaultReadOnlyOrig; - if (session.CacheMode != cacheModeOrig) - session.CacheMode = cacheModeOrig; + try + { + results[i] = GetResultFromCacheable( + key, + returnTypes[i], + queryParams.NaturalKeyLookup, + session, + cacheable); + } + finally + { + persistenceContext.DefaultReadOnly = defaultReadOnlyOrig; + } } } diff --git a/src/NHibernate/Engine/ISessionImplementor.cs b/src/NHibernate/Engine/ISessionImplementor.cs index 530f146b261..3cc8a123160 100644 --- a/src/NHibernate/Engine/ISessionImplementor.cs +++ b/src/NHibernate/Engine/ISessionImplementor.cs @@ -34,7 +34,7 @@ internal static IDisposable BeginProcess(this ISessionImplementor session) return null; return (session as AbstractSessionImpl)?.BeginProcess() ?? // This method has only replaced bare call to setting the id, so this fallback is enough for avoiding a - // breaking change in case in custom session implementation is used. + // breaking change in case a custom session implementation is used. new SessionIdLoggingContext(session.SessionId); } @@ -48,6 +48,41 @@ internal static void AutoFlushIfRequired(this ISessionImplementor implementor, I { (implementor as AbstractSessionImpl)?.AutoFlushIfRequired(querySpaces); } + + /// + /// Switch the session current cache mode. + /// + /// The session for which the cache mode has to be switched. + /// The desired cache mode. for not actually switching. + /// if no switch is required, otherwise an which + /// dispose will set the session cache mode back to its original value. + internal static IDisposable SwitchCacheMode(this ISessionImplementor session, CacheMode? cacheMode) + { + if (session == null || !cacheMode.HasValue || cacheMode == session.CacheMode) + return null; + return new CacheModeSwitch(session, cacheMode.Value); + } + + private sealed class CacheModeSwitch : IDisposable + { + private ISessionImplementor _session; + private readonly CacheMode _originalCacheMode; + + public CacheModeSwitch(ISessionImplementor session, CacheMode cacheMode) + { + _session = session; + _originalCacheMode = session.CacheMode; + _session.CacheMode = cacheMode; + } + + public void Dispose() + { + if (_session == null) + throw new ObjectDisposedException("The session cache mode switch has been disposed already"); + _session.CacheMode = _originalCacheMode; + _session = null; + } + } } /// diff --git a/src/NHibernate/Event/Default/DefaultDeleteEventListener.cs b/src/NHibernate/Event/Default/DefaultDeleteEventListener.cs index 6a0df1b5665..c4f699f863f 100644 --- a/src/NHibernate/Event/Default/DefaultDeleteEventListener.cs +++ b/src/NHibernate/Event/Default/DefaultDeleteEventListener.cs @@ -242,39 +242,41 @@ protected virtual bool InvokeDeleteLifecycle(IEventSource session, object entity protected virtual void CascadeBeforeDelete(IEventSource session, IEntityPersister persister, object entity, EntityEntry entityEntry, ISet transientEntities) { - ISessionImplementor si = session; - CacheMode cacheMode = si.CacheMode; - si.CacheMode = CacheMode.Get; - session.PersistenceContext.IncrementCascadeLevel(); - try + using (session.SwitchCacheMode(CacheMode.Get)) { - // cascade-delete to collections BEFORE the collection owner is deleted - new Cascade(CascadingAction.Delete, CascadePoint.AfterInsertBeforeDelete, session).CascadeOn(persister, entity, - transientEntities); - } - finally - { - session.PersistenceContext.DecrementCascadeLevel(); - si.CacheMode = cacheMode; + session.PersistenceContext.IncrementCascadeLevel(); + try + { + // cascade-delete to collections BEFORE the collection owner is deleted + new Cascade(CascadingAction.Delete, CascadePoint.AfterInsertBeforeDelete, session).CascadeOn( + persister, + entity, + transientEntities); + } + finally + { + session.PersistenceContext.DecrementCascadeLevel(); + } } } protected virtual void CascadeAfterDelete(IEventSource session, IEntityPersister persister, object entity, ISet transientEntities) { - ISessionImplementor si = session; - CacheMode cacheMode = si.CacheMode; - si.CacheMode = CacheMode.Get; - session.PersistenceContext.IncrementCascadeLevel(); - try + using (session.SwitchCacheMode(CacheMode.Get)) { - // cascade-delete to many-to-one AFTER the parent was deleted - new Cascade(CascadingAction.Delete, CascadePoint.BeforeInsertAfterDelete, session).CascadeOn(persister, entity, - transientEntities); - } - finally - { - session.PersistenceContext.DecrementCascadeLevel(); - si.CacheMode = cacheMode; + session.PersistenceContext.IncrementCascadeLevel(); + try + { + // cascade-delete to many-to-one AFTER the parent was deleted + new Cascade(CascadingAction.Delete, CascadePoint.BeforeInsertAfterDelete, session).CascadeOn( + persister, + entity, + transientEntities); + } + finally + { + session.PersistenceContext.DecrementCascadeLevel(); + } } } } diff --git a/src/NHibernate/Multi/QueryBatchItemBase.cs b/src/NHibernate/Multi/QueryBatchItemBase.cs index 5b087cef16a..a8f21aba373 100644 --- a/src/NHibernate/Multi/QueryBatchItemBase.cs +++ b/src/NHibernate/Multi/QueryBatchItemBase.cs @@ -177,15 +177,7 @@ public int ProcessResultsSet(DbDataReader reader) var dialect = Session.Factory.Dialect; var hydratedObjects = new List[_queryInfos.Count]; - var cacheModeOrig = Session.CacheMode; - if (_cacheMode.HasValue && - // Avoid setting the cache mode with the same value as the setter is not just affecting - // the backing field - _cacheMode != cacheModeOrig) - { - Session.CacheMode = _cacheMode.Value; - } - try + using (Session.SwitchCacheMode(CacheMode.Get)) { var rowCount = 0; for (var i = 0; i < _queryInfos.Count; i++) @@ -262,11 +254,6 @@ public int ProcessResultsSet(DbDataReader reader) return rowCount; } - finally - { - if (Session.CacheMode != cacheModeOrig) - Session.CacheMode = cacheModeOrig; - } } /// From 9fae7b93d50a8c715a8b83e932e969a28f725ddb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Delaporte?= <12201973+fredericDelaporte@users.noreply.github.com> Date: Sat, 14 Jul 2018 02:28:31 +0200 Subject: [PATCH 3/5] fixup! Support CacheMode in QueryBatch Fix the cache mode switch, and clean some trash. --- src/NHibernate/Async/Multi/QueryBatchItemBase.cs | 2 +- src/NHibernate/Multi/QueryBatchItemBase.cs | 7 ++----- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/src/NHibernate/Async/Multi/QueryBatchItemBase.cs b/src/NHibernate/Async/Multi/QueryBatchItemBase.cs index 1325d045614..47e7bf0a952 100644 --- a/src/NHibernate/Async/Multi/QueryBatchItemBase.cs +++ b/src/NHibernate/Async/Multi/QueryBatchItemBase.cs @@ -35,7 +35,7 @@ public async Task ProcessResultsSetAsync(DbDataReader reader, CancellationT var dialect = Session.Factory.Dialect; var hydratedObjects = new List[_queryInfos.Count]; - using (Session.SwitchCacheMode(CacheMode.Get)) + using (Session.SwitchCacheMode(_cacheMode)) { var rowCount = 0; for (var i = 0; i < _queryInfos.Count; i++) diff --git a/src/NHibernate/Multi/QueryBatchItemBase.cs b/src/NHibernate/Multi/QueryBatchItemBase.cs index a8f21aba373..273070d3f15 100644 --- a/src/NHibernate/Multi/QueryBatchItemBase.cs +++ b/src/NHibernate/Multi/QueryBatchItemBase.cs @@ -20,7 +20,6 @@ public abstract partial class QueryBatchItemBase : IQueryBatchItem[] _subselectResultKeys; private List _queryInfos; private CacheMode? _cacheMode; - private bool? _isReadOnly; private IList _finalResults; protected class QueryInfo : ICachingInformation @@ -139,9 +138,7 @@ public virtual void Init(ISessionImplementor session) _queryInfos = GetQueryInformation(session); // Cache and readonly parameters are the same for all translators - var queryParameters = _queryInfos.First().Parameters; - _cacheMode = queryParameters.CacheMode; - _isReadOnly = queryParameters.IsReadOnlyInitialized ? queryParameters.ReadOnly : default(bool?); + _cacheMode = _queryInfos.First().Parameters.CacheMode; var count = _queryInfos.Count; _subselectResultKeys = new List[count]; @@ -177,7 +174,7 @@ public int ProcessResultsSet(DbDataReader reader) var dialect = Session.Factory.Dialect; var hydratedObjects = new List[_queryInfos.Count]; - using (Session.SwitchCacheMode(CacheMode.Get)) + using (Session.SwitchCacheMode(_cacheMode)) { var rowCount = 0; for (var i = 0; i < _queryInfos.Count; i++) From b1ee4268b073171b30466c4dfc32ba5918f6f0b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Delaporte?= <12201973+fredericDelaporte@users.noreply.github.com> Date: Sat, 14 Jul 2018 11:39:04 +0200 Subject: [PATCH 4/5] fixup! Support CacheMode in QueryBatch Adjust a detail in the cache mode switch --- src/NHibernate/Engine/ISessionImplementor.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/NHibernate/Engine/ISessionImplementor.cs b/src/NHibernate/Engine/ISessionImplementor.cs index 3cc8a123160..12f105e2db4 100644 --- a/src/NHibernate/Engine/ISessionImplementor.cs +++ b/src/NHibernate/Engine/ISessionImplementor.cs @@ -58,7 +58,7 @@ internal static void AutoFlushIfRequired(this ISessionImplementor implementor, I /// dispose will set the session cache mode back to its original value. internal static IDisposable SwitchCacheMode(this ISessionImplementor session, CacheMode? cacheMode) { - if (session == null || !cacheMode.HasValue || cacheMode == session.CacheMode) + if (!cacheMode.HasValue || cacheMode == session.CacheMode) return null; return new CacheModeSwitch(session, cacheMode.Value); } From 3e9b4b710d26deaad105472e441caba511329a6d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Delaporte?= <12201973+fredericDelaporte@users.noreply.github.com> Date: Mon, 16 Jul 2018 15:11:39 +0200 Subject: [PATCH 5/5] fixup! Support CacheMode in QueryBatch Adjust a TODO --- src/NHibernate/Engine/ISessionImplementor.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/NHibernate/Engine/ISessionImplementor.cs b/src/NHibernate/Engine/ISessionImplementor.cs index 12f105e2db4..c9b6e36fe40 100644 --- a/src/NHibernate/Engine/ISessionImplementor.cs +++ b/src/NHibernate/Engine/ISessionImplementor.cs @@ -18,7 +18,7 @@ namespace NHibernate.Engine { - // 6.0 TODO: Convert to interface methods + // 6.0 TODO: Convert to interface methods, excepted SwitchCacheMode internal static partial class SessionImplementorExtensions { internal static IDisposable BeginContext(this ISessionImplementor session)