public CacheUpdateResult(CacheTransactionType transactionTypes, IReadOnlyDictionary <TKey, TValue> addedItems, IReadOnlyList <TKey> removedItems, IReadOnlyDictionary <TKey, TValue> updatedItems) { TransactionTypes = transactionTypes; AddedItems = addedItems; RemovedItems = removedItems; UpdatedItems = updatedItems; }
/// <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); }
/// <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)); }
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); }