public void CreateAndModifyMultithreaded()
        {
            Config config = ObjectTestHelper.GetConfigWithCaching();

            ObjectMetadataStore metadata = new ObjectMetadataStore(config);
            ObjectVersionStore versions = new ObjectVersionStore(config, metadata);
            ObjectIndexerCache cache = new ObjectIndexerCache(metadata, versions);

            try
            {
                // Create 4 actions to simulate 4 threads
                // This test simulates 4 concurrent processes adding and reading items to the cache
                // while they are also removed.
                Action[] actions = new Action[4];

                for (int i = 0; i < 4; i++)
                {
                    string nextObjName = "Obj" + i;
                    actions[i] = new Action(() => {

                        for (int loops = 0; loops < 100; loops++)
                        {
                            object[][] cacheParams = new object[100][];

                            // create 100 cache entries
                            for (int add = 0; add < 100; add++)
                            {
                                cacheParams[add] = new object[] { nextObjName, "Idx" + ((loops * 100) + add) };

                                cache.Set(
                                    new int[] { loops, loops + 1, loops + 2, loops + 3 },
                                    cacheParams[add]);
                            }

                            // read 1000 entries
                            for (int read = 0; read < 1000; read++)
                            {
                                int[] getIds = cache.Get(cacheParams[read % 100]);
                                // may get removed so check for null
                                if (null != getIds)
                                {
                                    Assert.AreEqual(loops, getIds[0]);
                                    Assert.AreEqual(loops + 1, getIds[1]);
                                }
                            }

                            // Remove 20 entries - this can remove objects added by other threads
                            // Linq query is executed once write lock is aquired which is why this works
                            cache.Remove(cache.EnumerateCache().Take(20));

                        }
                        // that 100 times - total at end will equal 8000 entries and 32000 object ids
                    });
                }

                Parallel.Invoke(actions);

                Assert.AreEqual(8000 * actions.Length, cache.EnumerateCache().Count());
                CacheTotals totals = cache.Totals;
                Assert.AreEqual(8000 * actions.Length, totals.TotalQueries);
                Assert.AreEqual(32000 * actions.Length, totals.TotalValues);

                cache.Reset();

                Assert.AreEqual(0, cache.EnumerateCache().Count());
                totals = cache.Totals;
                Assert.AreEqual(0, totals.TotalQueries);
                Assert.AreEqual(0, totals.TotalValues);
            }
            finally
            {
                cache.Dispose();
                versions.Dispose();
                metadata.Dispose();
            }
        }
        public void CreateAndModify()
        {
            Config config = ObjectTestHelper.GetConfigWithCaching();

            ObjectMetadataStore metadata = new ObjectMetadataStore(config);
            ObjectVersionStore versions = new ObjectVersionStore(config, metadata);
            ObjectIndexerCache cache = new ObjectIndexerCache(metadata, versions);

            string objectName = "TestObj";
            string intVal1 = ObjectIndex.Create("IntIdx", 5).ToString();
            string strVal1 = ObjectIndex.Create("StrIdx", "Val1").ToString();
            string strVal2 = ObjectIndex.Create("StrIdx", "Val3").ToString();

            object[] cacheParams = new object[] { objectName, intVal1, strVal1 };
            object[] cacheParams2 = new object[] { objectName, intVal1, strVal2 };
            int[] objectIds = new int[] { 1000, 2000 };
            int[] objectIds2 = new int[] { 3000, 4000 };

            try
            {
                Assert.AreEqual(0, cache.EnumerateCache().Count());
                CacheTotals totals = cache.Totals;
                Assert.AreEqual(0, totals.TotalQueries);
                Assert.AreEqual(0, totals.TotalValues);
                Assert.IsNull(cache.Get(cacheParams));

                cache.Reset();

                Assert.AreEqual(0, cache.EnumerateCache().Count());
                totals = cache.Totals;
                Assert.AreEqual(0, totals.TotalQueries);
                Assert.AreEqual(0, totals.TotalValues);
                Assert.IsNull(cache.Get(cacheParams));

                // add one query to cache
                cache.Set(objectIds, cacheParams);

                Assert.AreEqual(1, cache.EnumerateCache().Count());
                totals = cache.Totals;
                Assert.AreEqual(1, totals.TotalQueries);
                Assert.AreEqual(2, totals.TotalValues);

                int[] lookupIds = cache.Get(cacheParams);
                Assert.AreEqual(objectIds.Length, lookupIds.Length);
                Assert.AreEqual(objectIds[0], lookupIds[0]);
                Assert.AreEqual(objectIds[1], lookupIds[1]);

                cache.Reset();

                Assert.AreEqual(0, cache.EnumerateCache().Count());
                totals = cache.Totals;
                Assert.AreEqual(0, totals.TotalQueries);
                Assert.AreEqual(0, totals.TotalValues);
                Assert.IsNull(cache.Get(cacheParams));

                // add more than one query to cache
                cache.Set(objectIds, cacheParams);
                cache.Set(objectIds2, cacheParams2);

                Assert.AreEqual(2, cache.EnumerateCache().Count());
                totals = cache.Totals;
                Assert.AreEqual(2, totals.TotalQueries);
                Assert.AreEqual(4, totals.TotalValues);

                lookupIds = cache.Get(cacheParams);
                Assert.AreEqual(objectIds.Length, lookupIds.Length);
                Assert.AreEqual(objectIds[0], lookupIds[0]);
                Assert.AreEqual(objectIds[1], lookupIds[1]);

                lookupIds = cache.Get(cacheParams2);
                Assert.AreEqual(objectIds2.Length, lookupIds.Length);
                Assert.AreEqual(objectIds2[0], lookupIds[0]);
                Assert.AreEqual(objectIds2[1], lookupIds[1]);

                // add the same query to cache again - make sure it replaces the existing one
                cache.Set(objectIds2, cacheParams2);

                Assert.AreEqual(2, cache.EnumerateCache().Count());
                totals = cache.Totals;
                Assert.AreEqual(2, totals.TotalQueries);
                Assert.AreEqual(4, totals.TotalValues);

                lookupIds = cache.Get(cacheParams);
                Assert.AreEqual(objectIds.Length, lookupIds.Length);
                Assert.AreEqual(objectIds[0], lookupIds[0]);
                Assert.AreEqual(objectIds[1], lookupIds[1]);

                lookupIds = cache.Get(cacheParams2);
                Assert.AreEqual(objectIds2.Length, lookupIds.Length);
                Assert.AreEqual(objectIds2[0], lookupIds[0]);
                Assert.AreEqual(objectIds2[1], lookupIds[1]);

                // remove query from cache
                var entryToRemove = cache.EnumerateCache().Last();
                // we don't know what order they will be returned so find out
                // which value is being removed
                object[] remainingCacheParams = cacheParams;
                int[] remainingObjectIds = objectIds;
                if (entryToRemove.Hash == ObjectIndexerCache.ConstructHash(cacheParams))
                {
                    // then the first entry as removed so the second will be the
                    // remaining entry
                    remainingCacheParams = cacheParams2;
                    remainingObjectIds = objectIds2;
                }
                cache.Remove(new ICacheEntry[] { entryToRemove });

                Assert.AreEqual(1, cache.EnumerateCache().Count());
                totals = cache.Totals;
                Assert.AreEqual(1, totals.TotalQueries);
                Assert.AreEqual(2, totals.TotalValues);

                lookupIds = cache.Get(remainingCacheParams);
                Assert.AreEqual(remainingObjectIds.Length, lookupIds.Length);
                Assert.AreEqual(remainingObjectIds[0], lookupIds[0]);
                Assert.AreEqual(remainingObjectIds[1], lookupIds[1]);

                Assert.IsNull(cache.Get(cacheParams2));
            }
            finally
            {
                cache.Dispose();
                versions.Dispose();
                metadata.Dispose();
            }
        }