Caching Application Block error: "Destination array is not long enough to copy all the items in the collection. Check array index and length"

Topics: Caching Application Block
Jun 16, 2014 at 5:12 PM
Edited Jun 16, 2014 at 5:12 PM
I'm getting an exception thrown when trying to copy the keys from a instance of the cache manager into an array. What essentially is happening is I'm getting the count of items currently in the cache manager, creating an array based on that count, and then copying the cache manager keys into the array. This works in our development environment, but under heavy load the following exception is thrown: "Destination array is not long enough to copy all the items in the collection. Check array index and length", which means [I assume] that the cache manager is getting keys added after the count is determined but before the keys are copied across, resulting in a "Index out of bounds" type exception.

Does anyone have any idea how to resolve this issue, since I am stumped.
ICacheManager cacheManager = CacheFactory.GetCacheManager();
if (cacheManager != null)
{
    int cachedItemCount = cacheManager.Count;
    if (cachedItemCount > 0)
    {
        object[] currentCacheKeys = new object[cachedItemCount];
        cacheManager.Keys.CopyTo(currentCacheKeys, 0); // this is where the exception occurs

        if (currentCacheKeys != null && currentCacheKeys.Length > 0)
        {
        // omitted for brevity
        }
    }
}   
Jun 17, 2014 at 5:55 AM
What version of the Caching Block are you using? I don't see a property called Keys on ICacheManager so I'm guessing you are using a custom build of Enterprise Library.

Your assumption is almost certainly correct. I would modify the Keys method to return a cloned version of the Keys. This way updates to the cache won't be reflected in the keys. You won't blow up but you won't have a truly accurate view of the keys (not sure how important that is for you).

Here's some code that avoids the issue using the out of the box Caching Block along with reflection:
    public static class CachingExtensions
    {
        public static ICollection Keys(this ICacheManager cacheManager)
        {
            CacheManager cm = (CacheManager)cacheManager;
            Cache myCache = (Cache)cm.GetType().GetField("realCache", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic).GetValue(cm);

            // CurrentCacheState clones the internal Hashtable so these Keys will not be modified by
            // any subsequent changes to the cache
            return myCache.CurrentCacheState.Keys;
        }
    }

ICacheManager cacheManager = CacheFactory.GetCacheManager();

if (cacheManager != null)
{
    // Extension method using reflection...these Keys will not change.
    ICollection keys = cacheManager.Keys();
    if (keys.Count > 0)
    {
        object[] currentCacheKeys = new object[keys.Count];
        keys.CopyTo(currentCacheKeys, 0); // this is where the exception occurs

        if (currentCacheKeys != null && currentCacheKeys.Length > 0)
        {
            // omitted for brevity
        }
    }
}   

If you want a truly accurate view of the keys you will need to implement locking to synchronize access to the cache to ensure that while getting the keys the cache is not modified.

~~
Randy Levy
entlib.support@live.com
Enterprise Library support engineer
Support How-to