Beispiel #1
0
 public CacheUpdateResult(CacheTransactionType transactionTypes, IReadOnlyDictionary <TKey, TValue> addedItems, IReadOnlyList <TKey> removedItems, IReadOnlyDictionary <TKey, TValue> updatedItems)
 {
     TransactionTypes = transactionTypes;
     AddedItems       = addedItems;
     RemovedItems     = removedItems;
     UpdatedItems     = updatedItems;
 }
Beispiel #2
0
        /// <summary>
        /// Removes an item if it exists.
        /// </summary>
        /// <returns><see cref="CacheTransactionType.Remove"/> if the item was added, or <see cref="CacheTransactionType.None"/> if it wasn't in the cache.</returns>
        public CacheTransactionType Remove(TKey key)
        {
            if (key == null)
            {
                throw new ArgumentNullException(nameof(key));
            }

            CacheTransactionType transactionType = CacheTransactionType.None;

            using (_itemsCacheLock.ScopedWriteLock())
            {
                if (_itemsCache.Remove(key))
                {
                    // If the item was found and removed, indicate a Remove transaction
                    // otherwise, leave it as a None transaction
                    transactionType = CacheTransactionType.Remove;

                    // Remove it from the expirable keys to ensure it doesn't count
                    // against max items anymore. If it wasn't expirable, this is a no-op
                    _expirableKeys.Remove(key);
                }
            }

            return(transactionType);
        }
Beispiel #3
0
        /// <summary>
        /// Processes items in batch, adding or updating values in <paramref name="itemsToAdd"/> and removing those in <paramref name="itemKeysToRemove"/>
        /// If an item is in both <paramref name="itemsToAdd"/> and <paramref name="itemKeysToRemove"/>, the item will be removed.
        /// </summary>
        /// <param name="itemsToAdd">Items to be added or updated.</param>
        /// <param name="itemKeysToRemove">Item keys to be removed.</param>
        /// <param name="expirationPolicy">The caching policy that should be applied to these items.</param>
        /// <returns>An <see cref="CacheUpdateResult"/> with <see cref="CacheUpdateResult.TransactionTypes"/> flags set according to which transactions occured
        /// and collections of each of the items that were impaced.
        /// </returns>
        public CacheUpdateResult Update(IReadOnlyDictionary <TKey, TValue> itemsToAdd, IReadOnlyList <TKey> itemKeysToRemove, CacheItemExpirationPolicy expirationPolicy)
        {
            if (itemsToAdd == null)
            {
                throw new ArgumentNullException(nameof(itemsToAdd));
            }

            if (itemKeysToRemove == null)
            {
                throw new ArgumentNullException(nameof(itemKeysToRemove));
            }

            var resultAddedItems   = new Dictionary <TKey, TValue>();
            var resultRemovedItems = new List <TKey>();
            var resultUpdatedItems = new Dictionary <TKey, TValue>();

            foreach (var addedKey in itemsToAdd.Keys)
            {
                // Add or update the cached item as appropriate
                // and add to the corresponding collection for the
                // update result. The item can't already be in one
                // of the two result lists, because it can't be
                // in itemsToAdd more than once
                var addedValue      = itemsToAdd[addedKey];
                var transactionType = AddOrUpdateInternal(addedKey, addedValue, expirationPolicy, enforceCacheSize: false);
                if (transactionType == CacheTransactionType.Add)
                {
                    resultAddedItems[addedKey] = addedValue;
                }
                else if (transactionType == CacheTransactionType.Update)
                {
                    resultUpdatedItems[addedKey] = addedValue;
                }
            }

            foreach (var removedKey in itemKeysToRemove)
            {
                // Remove the cached item if it exists
                var transactionType = Remove(removedKey);
                if (transactionType == CacheTransactionType.Remove)
                {
                    // If it existed and was actually removed
                    // add it to the collection of removed items
                    // for the update result
                    resultRemovedItems.Add(removedKey);

                    // Remove it from the added or updated
                    // result collections since we will have
                    // removed any items that appeared in both
                    // itemsToAdd and itemKeysToRemove due to
                    // the order of operations here
                    resultAddedItems.Remove(removedKey);
                    resultUpdatedItems.Remove(removedKey);
                }
            }

            EnsureCacheSize();

            // Iterate over added items and find which ones have been removed
            // due to ensure cache size so we have an accurate set of resulting
            // added items
            var purgedItemKeys = new HashSet <TKey>();

            foreach (var addedItemKey in resultAddedItems.Keys)
            {
                TValue value = default(TValue);
                if (!TryGetValue(addedItemKey, out value))
                {
                    // Item is no longer there, mark for removal from resulting added items
                    purgedItemKeys.Add(addedItemKey);
                }
            }

            foreach (var itemKey in purgedItemKeys)
            {
                resultAddedItems.Remove(itemKey);
            }

            // Determine transaction types
            CacheTransactionType transactionTypes = CacheTransactionType.Indeterminate;

            if (resultAddedItems.Count > 0)
            {
                transactionTypes |= CacheTransactionType.Add;
            }

            if (resultRemovedItems.Count > 0)
            {
                transactionTypes |= CacheTransactionType.Remove;
            }

            if (resultUpdatedItems.Count > 0)
            {
                transactionTypes |= CacheTransactionType.Update;
            }

            if (resultAddedItems.Count == 0 && resultRemovedItems.Count == 0 && resultUpdatedItems.Count == 0)
            {
                transactionTypes |= CacheTransactionType.None;
            }

            return(new CacheUpdateResult(transactionTypes, resultAddedItems, resultRemovedItems, resultUpdatedItems));
        }
Beispiel #4
0
        private CacheTransactionType AddOrUpdateInternal(TKey key, TValue item, CacheItemExpirationPolicy expirationPolicy, bool enforceCacheSize = true)
        {
            if (key == null)
            {
                throw new ArgumentNullException(nameof(key));
            }

            if (item == null)
            {
                throw new ArgumentNullException(nameof(item));
            }

            CacheTransactionType transactionType = CacheTransactionType.None;

            // Create the new cache item to use for adding or updating from
            var newCacheItem = new CacheItem <TValue>(item, expirationPolicy);

            using (_itemsCacheLock.ScopedWriteLock())
            {
                CacheItem <TValue> existingCachedItem;

                // Attempt to retrieve the cached item
                bool itemExistsInCache = _itemsCache.TryGetValue(key, out existingCachedItem);
                if (itemExistsInCache && existingCachedItem != null)
                {
                    // If it exists and is not null, attempt to update it
                    if (existingCachedItem.Update(newCacheItem))
                    {
                        transactionType = CacheTransactionType.Update;
                    }
                    else
                    {
                        // If it was already updated, mark the transaction as None
                        transactionType = CacheTransactionType.None;
                    }
                }
                else if (itemExistsInCache && existingCachedItem == null)
                {
                    // If the cached item exists but is somehow null
                    // remove the existing item and replace it with the new value.
                    _itemsCache[key] = newCacheItem;
                }
                else
                {
                    // If it wasn't in the cache already, add it
                    transactionType = CacheTransactionType.Add;
                    _itemsCache.Add(key, newCacheItem);
                }

                if (expirationPolicy != CacheItemExpirationPolicy.Indefinite)
                {
                    // If the item is expirable, add it to the expirable keys
                    // to ensure it counts towards max items and is available for
                    // removal if max is reached.
                    _expirableKeys.Add(key);
                }

                if (enforceCacheSize)
                {
                    EnsureCacheSize();
                }
            }

            return(transactionType);
        }