private static async Task PerformBeforeAssembleAsync(
            ICacheAssembler[] returnTypes,
            ISessionImplementor session,
            IList cacheable, CancellationToken cancellationToken)
        {
            cancellationToken.ThrowIfCancellationRequested();
            if (returnTypes.Length == 1)
            {
                var returnType = returnTypes[0];

                // Skip first element, it is the timestamp
                for (var i = 1; i < cacheable.Count; i++)
                {
                    await(returnType.BeforeAssembleAsync(cacheable[i], session, cancellationToken)).ConfigureAwait(false);
                }
            }
            else
            {
                // Skip first element, it is the timestamp
                for (var i = 1; i < cacheable.Count; i++)
                {
                    await(TypeHelper.BeforeAssembleAsync((object[])cacheable[i], returnTypes, session, cancellationToken)).ConfigureAwait(false);
                }
            }
        }
        public async Task <IList> GetAsync(QueryKey key, ICacheAssembler[] returnTypes, bool isNaturalKeyLookup, ISet <string> spaces, ISessionImplementor session, CancellationToken cancellationToken)
        {
            cancellationToken.ThrowIfCancellationRequested();
            if (Log.IsDebugEnabled())
            {
                Log.Debug("checking cached query results in region: '{0}'; {1}", _regionName, key);
            }

            var cacheable = (IList)await(_queryCache.GetAsync(key, cancellationToken)).ConfigureAwait(false);

            if (cacheable == null)
            {
                Log.Debug("query results were not found in cache: {0}", key);
                return(null);
            }

            var timestamp = (long)cacheable[0];

            if (Log.IsDebugEnabled())
            {
                Log.Debug("Checking query spaces for up-to-dateness [{0}]", StringHelper.CollectionToString(spaces));
            }

            if (!isNaturalKeyLookup && !await(IsUpToDateAsync(spaces, timestamp, cancellationToken)).ConfigureAwait(false))
            {
                Log.Debug("cached query results were not up to date for: {0}", key);
                return(null);
            }

            Log.Debug("returning cached query results for: {0}", key);
            for (int i = 1; i < cacheable.Count; i++)
            {
                if (returnTypes.Length == 1)
                {
                    await(returnTypes[0].BeforeAssembleAsync(cacheable[i], session, cancellationToken)).ConfigureAwait(false);
                }
                else
                {
                    await(TypeHelper.BeforeAssembleAsync((object[])cacheable[i], returnTypes, session, cancellationToken)).ConfigureAwait(false);
                }
            }

            IList result = new List <object>(cacheable.Count - 1);

            for (int i = 1; i < cacheable.Count; i++)
            {
                try
                {
                    if (returnTypes.Length == 1)
                    {
                        result.Add(await(returnTypes[0].AssembleAsync(cacheable[i], session, null, cancellationToken)).ConfigureAwait(false));
                    }
                    else
                    {
                        result.Add(await(TypeHelper.AssembleAsync((object[])cacheable[i], returnTypes, session, null, cancellationToken)).ConfigureAwait(false));
                    }
                }
                catch (UnresolvableObjectException ex)
                {
                    if (isNaturalKeyLookup)
                    {
                        //TODO: not really completely correct, since
                        //      the UnresolvableObjectException could occur while resolving
                        //      associations, leaving the PC in an inconsistent state
                        Log.Debug(ex, "could not reassemble cached result set");
                        await(_queryCache.RemoveAsync(key, cancellationToken)).ConfigureAwait(false);
                        return(null);
                    }

                    throw;
                }
            }

            return(result);
        }
        private async Task <IList> GetResultFromCacheableAsync(
            QueryKey key,
            ICacheAssembler[] returnTypes,
            bool isNaturalKeyLookup,
            ISessionImplementor session,
            IList cacheable, CancellationToken cancellationToken)
        {
            cancellationToken.ThrowIfCancellationRequested();
            Log.Debug("returning cached query results for: {0}", key);
            if (key.ResultTransformer?.AutoDiscoverTypes == true && cacheable.Count > 0)
            {
                returnTypes = GuessTypes(cacheable);
            }

            try
            {
                var result = new List <object>(cacheable.Count - 1);
                if (returnTypes.Length == 1)
                {
                    var returnType = returnTypes[0];

                    // Skip first element, it is the timestamp
                    for (var i = 1; i < cacheable.Count; i++)
                    {
                        await(returnType.BeforeAssembleAsync(cacheable[i], session, cancellationToken)).ConfigureAwait(false);
                    }

                    for (var i = 1; i < cacheable.Count; i++)
                    {
                        result.Add(await(returnType.AssembleAsync(cacheable[i], session, null, cancellationToken)).ConfigureAwait(false));
                    }
                }
                else
                {
                    var collectionIndexes        = new Dictionary <int, ICollectionPersister>();
                    var nonCollectionTypeIndexes = new List <int>();
                    for (var i = 0; i < returnTypes.Length; i++)
                    {
                        if (returnTypes[i] is CollectionType collectionType)
                        {
                            collectionIndexes.Add(i, session.Factory.GetCollectionPersister(collectionType.Role));
                        }
                        else
                        {
                            nonCollectionTypeIndexes.Add(i);
                        }
                    }

                    // Skip first element, it is the timestamp
                    for (var i = 1; i < cacheable.Count; i++)
                    {
                        await(TypeHelper.BeforeAssembleAsync((object[])cacheable[i], returnTypes, session, cancellationToken)).ConfigureAwait(false);
                    }

                    for (var i = 1; i < cacheable.Count; i++)
                    {
                        result.Add(await(TypeHelper.AssembleAsync((object[])cacheable[i], returnTypes, nonCollectionTypeIndexes, session, cancellationToken)).ConfigureAwait(false));
                    }

                    // Initialization of the fetched collection must be done at the end in order to be able to batch fetch them
                    // from the cache or database. The collections were already created in the previous for statement so we only
                    // have to initialize them.
                    if (collectionIndexes.Count > 0)
                    {
                        for (var i = 1; i < cacheable.Count; i++)
                        {
                            await(TypeHelper.InitializeCollectionsAsync((object[])cacheable[i], (object[])result[i - 1], collectionIndexes, session, cancellationToken)).ConfigureAwait(false);
                        }
                    }
                }

                return(result);
            }
            catch (UnresolvableObjectException ex)
            {
                if (isNaturalKeyLookup)
                {
                    //TODO: not really completely correct, since
                    //      the UnresolvableObjectException could occur while resolving
                    //      associations, leaving the PC in an inconsistent state
                    Log.Debug(ex, "could not reassemble cached result set");
                    // Handling a RemoveMany here does not look worth it, as this case short-circuits
                    // the result-set. So a Many could only benefit batched queries, and only if many
                    // of them are natural key lookup with an unresolvable object case.
                    await(Cache.RemoveAsync(key, cancellationToken)).ConfigureAwait(false);
                    return(null);
                }

                throw;
            }
        }
Beispiel #4
0
        private async Task <IList> GetResultFromCacheableAsync(
            QueryKey key,
            ICacheAssembler[] returnTypes,
            bool isNaturalKeyLookup,
            ISessionImplementor session,
            IList cacheable, CancellationToken cancellationToken)
        {
            cancellationToken.ThrowIfCancellationRequested();
            Log.Debug("returning cached query results for: {0}", key);
            if (key.ResultTransformer?.AutoDiscoverTypes == true && cacheable.Count > 0)
            {
                returnTypes = GuessTypes(cacheable);
            }

            try
            {
                var result = new List <object>(cacheable.Count - 1);
                if (returnTypes.Length == 1)
                {
                    var returnType = returnTypes[0];

                    // Skip first element, it is the timestamp
                    var rows = new List <object>(cacheable.Count - 1);
                    for (var i = 1; i < cacheable.Count; i++)
                    {
                        rows.Add(cacheable[i]);
                    }

                    foreach (var row in rows)
                    {
                        await(returnType.BeforeAssembleAsync(row, session, cancellationToken)).ConfigureAwait(false);
                    }

                    foreach (var row in rows)
                    {
                        result.Add(await(returnType.AssembleAsync(row, session, null, cancellationToken)).ConfigureAwait(false));
                    }
                }
                else
                {
                    // Skip first element, it is the timestamp
                    var rows = new List <object[]>(cacheable.Count - 1);
                    for (var i = 1; i < cacheable.Count; i++)
                    {
                        rows.Add((object[])cacheable[i]);
                    }

                    foreach (var row in rows)
                    {
                        await(TypeHelper.BeforeAssembleAsync(row, returnTypes, session, cancellationToken)).ConfigureAwait(false);
                    }

                    foreach (var row in rows)
                    {
                        result.Add(await(TypeHelper.AssembleAsync(row, returnTypes, session, null, cancellationToken)).ConfigureAwait(false));
                    }
                }

                return(result);
            }
            catch (UnresolvableObjectException ex)
            {
                if (isNaturalKeyLookup)
                {
                    //TODO: not really completely correct, since
                    //      the UnresolvableObjectException could occur while resolving
                    //      associations, leaving the PC in an inconsistent state
                    Log.Debug(ex, "could not reassemble cached result set");
                    // Handling a RemoveMany here does not look worth it, as this case short-circuits
                    // the result-set. So a Many could only benefit batched queries, and only if many
                    // of them are natural key lookup with an unresolvable object case.
                    await(Cache.RemoveAsync(key, cancellationToken)).ConfigureAwait(false);
                    return(null);
                }

                throw;
            }
        }