/// <summary>
        /// Check the cache.
        /// </summary>
        /// <param name="key">Key to look up.</param>
        /// <param name="result">The value result - either from cache or freshly determined.</param>
        /// <param name="valueFactory">A callback that will provide the values.</param>
        /// <returns></returns>

        protected bool TryGetOrAdd(TKey key, out TValue result, Func <TKey, TValue> valueFactory)
        {
            bool fromCache = false;

            // Wrap value-factory to handle cache invalidator
            Func <TKey, TValue> valueFactoryImpl = (k) =>
            {
                TValue innerResult;

                using (CacheContext cacheContext = new CacheContext())
                {
                    innerResult = valueFactory(key);

                    // Add the cache context entries to the appropriate CacheInvalidator
                    _cacheInvalidator.AddInvalidations(cacheContext, key);
                }

                return(innerResult);
            };

            // Check cache
            fromCache = Cache.TryGetOrAdd(key, out result, valueFactoryImpl);
            if (fromCache && CacheContext.IsSet())
            {
                // Add the already stored changes that should invalidate this cache
                // entry to any outer or containing cache contexts.
                using (CacheContext cacheContext = CacheContext.GetContext())
                {
                    cacheContext.AddInvalidationsFor(_cacheInvalidator, key);
                }
            }

            return(fromCache);
        }
Example #2
0
        public void Test_ItemsRemoved()
        {
            CacheInvalidator <int, string> cacheInvalidator;
            ICache <int, string>           cache;
            const int    testKey1   = 42;
            const int    testKey2   = 54;
            const string testValue1 = "foo";
            const string testValue2 = "bar";

            cache = new DictionaryCache <int, string>();
            cache.Add(testKey1, testValue1);
            cache.Add(testKey2, testValue2);

            cacheInvalidator = new CacheInvalidator <int, string>(cache, "foo");

            using (CacheContext cacheContext = new CacheContext())
            {
                cacheContext.Entities.Add(testKey1);
                cacheInvalidator.AddInvalidations(cacheContext, testKey1);
            }
            using (CacheContext cacheContext = new CacheContext())
            {
                cacheContext.Entities.Add(testKey2);
                cacheInvalidator.AddInvalidations(cacheContext, testKey2);
            }

            cache.Remove(testKey1);

            Assert.That(cacheInvalidator.EntityToCacheKey.Keys, Has.None.EqualTo(testKey1));
            Assert.That(cacheInvalidator.EntityToCacheKey.Keys, Has.Exactly(1).EqualTo(testKey2));
        }
Example #3
0
        public void Test_OnEntityChange_EntityTypes()
        {
            MockRepository mockRepository;
            CacheInvalidator <long, string> cacheInvalidator;
            ICache <long, string>           cache;

            IEntity[]      testEntities;
            Mock <IEntity> mockEntity;
            const int      numEntities  = 10;
            const long     typeIdOffset = 100;

            mockRepository = new MockRepository(MockBehavior.Loose);

            testEntities = new IEntity[numEntities];
            for (int i = 0; i < testEntities.Length; i++)
            {
                mockEntity = mockRepository.Create <IEntity>();
                mockEntity.SetupGet(e => e.Id).Returns(i);
                mockEntity.SetupGet(e => e.TypeIds).Returns(new [] { typeIdOffset + 100 });
                testEntities[i] = mockEntity.Object;
            }

            cache = new DictionaryCache <long, string>();
            for (int i = 0; i < numEntities; i++)
            {
                cache.Add(i, i.ToString());
            }

            cacheInvalidator = new CacheInvalidator <long, string>(cache, "a");

            // Make the second and third entities depend on the type of the first.
            using (CacheContext cacheContext = new CacheContext())
            {
                cacheContext.EntityTypes.Add(testEntities[0].TypeIds.First()); // Will convert to EntityRef using this ID
                cacheInvalidator.AddInvalidations(cacheContext, testEntities[1].Id);
                cacheInvalidator.AddInvalidations(cacheContext, testEntities[2].Id);
            }

            // Save the first entity (only)
            cacheInvalidator.OnEntityChange(new [] { testEntities[0] }, InvalidationCause.Save, null);

            Assert.That(
                cache.Select(ce => ce.Key),
                Is.EquivalentTo(
                    testEntities.Select(er => er.Id)
                    .Where(id => id != testEntities[1].Id && id != testEntities[2].Id)),
                "Second and third entities (only) have not been removed");
        }
Example #4
0
        public void Test_OnRelationshipChange()
        {
            CacheInvalidator <int, string> cacheInvalidator;
            ICache <int, string>           cache;

            EntityRef[]       testRelationshipTypes;
            const int         numRelationshipTypes = 10;
            Func <long, bool> relationshipTypesToRemove;

            cache = new DictionaryCache <int, string>();
            for (int i = 0; i < numRelationshipTypes; i++)
            {
                cache.Add(i, i.ToString());
            }

            relationshipTypesToRemove = e => e % 2 == 0; // All even numbered relationship types.
            testRelationshipTypes     = Enumerable.Range(0, numRelationshipTypes)
                                        .Where(i => relationshipTypesToRemove(i))
                                        .Select(i => new EntityRef(i)).ToArray();

            cacheInvalidator = new CacheInvalidator <int, string>(cache, "a");

            for (int i = 0; i < numRelationshipTypes; i++)
            {
                using (CacheContext cacheContext = new CacheContext())
                {
                    cacheContext.RelationshipTypes.Add(i);
                    cacheInvalidator.AddInvalidations(cacheContext, i);
                }
            }

            cacheInvalidator.OnRelationshipChange(testRelationshipTypes);

            Assert.That(cache.Where(ce => relationshipTypesToRemove(ce.Key)), Is.Empty);
        }
Example #5
0
        /// <summary>
        /// Execute a request for bulk data from the SQL database.
        /// </summary>
        /// <param name="request">The requested data</param>
        /// <returns></returns>
        public BulkRequestResult GetBulkResult(EntityRequest request)
        {
            if (request == null)
            {
                throw new ArgumentNullException("request");
            }

            // Bypass cache
            if (request.IgnoreResultCache)
            {
                return(CreateResult(request));
            }

            BulkRequestResult             result;
            CachingBulkRequestRunnerValue cacheValue;
            CachingBulkRequestRunnerKey   key = CachingBulkRequestRunnerKey.Create(request);

            // Check cache
            bool inCache = Cache.TryGetValue(key, out cacheValue);

            // Should parent cache contexts be notified of invalidations
            // .. no for now, for compatibility with previous system. Consider changing
            bool notifyParentCacheContext = false;

            if (!inCache)
            {
                using (var cacheContext = new CacheContext(notifyParentCacheContext ? ContextType.New : ContextType.Detached))                      // Detached for now..
                {
                    result     = CreateResult(request);
                    cacheValue = new CachingBulkRequestRunnerValue(result);

                    Cache.Add(key, cacheValue);

                    // Add the cache context entries to the appropriate CacheInvalidator
                    cacheContext.Entities.Add(result.AllEntities.Keys);
                    cacheContext.EntityInvalidatingRelationshipTypes.Add(GetRelationshipTypesUsed(result));

                    _cacheInvalidator.AddInvalidations(cacheContext, key);
                }
            }
            else
            {
                if (notifyParentCacheContext && CacheContext.IsSet( ))
                {
                    using (CacheContext cacheContext = new CacheContext(ContextType.Attached))
                    {
                        // Add the already stored changes that should invalidate this cache
                        // entry to any outer or containing cache contexts.
                        cacheContext.AddInvalidationsFor(_cacheInvalidator, key);
                    }
                }
            }

            result = cacheValue.BulkRequestResult;
            request.ResultFromCache = inCache;  // TODO: Find a better channel to return this info. (It can't be in the response, because that's cached)

            return(result);
        }
Example #6
0
        /// <summary>
        /// Build an <see cref="EntityMemberRequest"/> used to look for entities
        /// to perform additional security checks on.
        /// </summary>
        /// <param name="entityType">
        /// The type of entity whose security is being checked. This cannot be null.
        /// </param>
        /// <param name="permissions">The type of permissions required.</param>
        /// <returns>
        /// The <see cref="EntityMemberRequest"/>.
        /// </returns>
        /// <exception cref="ArgumentNullException">
        /// <paramref name="entityType"/> cannot be null.
        /// </exception>
        public EntityMemberRequest BuildEntityMemberRequest(EntityType entityType, IList <EntityRef> permissions)
        {
            if (entityType == null)
            {
                throw new ArgumentNullException("entityType");
            }

            bool isModify = false;

            if (permissions != null)
            {
                foreach (EntityRef perm in permissions)
                {
                    if (perm.Id == Permissions.Modify.Id || perm.Id == Permissions.Delete.Id)
                    {
                        isModify = true;
                        break;
                    }
                }
            }

            // negative keys for 'modify', +ve keys for read
            // because #27911 popped up just hours before the branch of a feature that got promised to someone
            long cacheKey = isModify ? -entityType.Id : entityType.Id;

            EntityMemberRequest result;

            if (!Cache.TryGetValue(cacheKey, out result))
            {
                using (CacheContext cacheContext = new CacheContext())
                {
                    result = Factory.BuildEntityMemberRequest(entityType, permissions);

                    Cache.Add(cacheKey, result);

                    _cacheInvalidator.AddInvalidations(cacheContext, cacheKey);
                }
            }
            else if (CacheContext.IsSet( ))
            {
                // Add the already stored changes that should invalidate this cache
                // entry to any outer or containing cache contexts.
                using (CacheContext cacheContext = CacheContext.GetContext( ))
                {
                    cacheContext.AddInvalidationsFor(_cacheInvalidator, cacheKey);
                }
            }

            return(result);
        }
Example #7
0
        public void Test_AddInvalidations()
        {
            CacheInvalidator <string, string> cacheInvalidator;
            long testEntity;
            long testRelationshipType;
            long testFieldType;
            long testEntityInvalidatingRelationshipTypeRef;
            long testEntityTypeRef;

            cacheInvalidator = new CacheInvalidator <string, string>(
                new DictionaryCache <string, string>(), "foo");

            testEntity           = 1;
            testRelationshipType = 2;
            testFieldType        = 3;
            testEntityInvalidatingRelationshipTypeRef = 4;
            testEntityTypeRef = 5;
            using (CacheContext cacheContext = new CacheContext())
            {
                cacheContext.Entities.Add(testEntity);
                cacheContext.RelationshipTypes.Add(testRelationshipType);
                cacheContext.FieldTypes.Add(testFieldType);
                cacheContext.EntityInvalidatingRelationshipTypes.Add(testEntityInvalidatingRelationshipTypeRef);
                cacheContext.EntityTypes.Add(testEntityTypeRef);

                // Sanity check
                Assert.That(cacheInvalidator.EntityToCacheKey, Has.Property("Keys").Empty);
                Assert.That(cacheInvalidator.RelationshipTypeToCacheKey, Has.Property("Keys").Empty);
                Assert.That(cacheInvalidator.FieldTypeToCacheKey, Has.Property("Keys").Empty);
                Assert.That(cacheInvalidator.EntityInvalidatingRelationshipTypesToCacheKey, Has.Property("Keys").Empty);
                Assert.That(cacheInvalidator.EntityTypeToCacheKey, Has.Property("Keys").Empty);

                cacheInvalidator.AddInvalidations(cacheContext, "foo");

                Assert.That(cacheInvalidator.EntityToCacheKey,
                            Has.Property("Keys").Exactly(1).EqualTo(testEntity));
                Assert.That(cacheInvalidator.RelationshipTypeToCacheKey,
                            Has.Property("Keys").Exactly(1).EqualTo(testRelationshipType));
                Assert.That(cacheInvalidator.FieldTypeToCacheKey,
                            Has.Property("Keys").Exactly(1).EqualTo(testFieldType));
                Assert.That(cacheInvalidator.EntityInvalidatingRelationshipTypesToCacheKey,
                            Has.Property("Keys").Exactly(1).EqualTo(testEntityInvalidatingRelationshipTypeRef));
                Assert.That(cacheInvalidator.EntityTypeToCacheKey,
                            Has.Property("Keys").Exactly(1).EqualTo(testEntityTypeRef));
            }
        }
Example #8
0
        public void Test_AddInvalidationsFor(long[] entities, long[] relationshipTypes, long[] fieldTypes, long[] entityInvalidatingRelationshipTypes)
        {
            CacheInvalidator <long, long> cacheInvalidator;
            const int    testKey = 1;
            IList <long> entityRefs;
            IList <long> relationshipTypeRefs;
            IList <long> fieldTypeRefs;
            IList <long> entityInvalidatingRelationshipTypeRefs;

            entityRefs           = entities.ToList();
            relationshipTypeRefs = relationshipTypes.ToList();
            fieldTypeRefs        = fieldTypes.ToList();
            entityInvalidatingRelationshipTypeRefs = entityInvalidatingRelationshipTypes.ToList();

            cacheInvalidator = new CacheInvalidator <long, long>(new DictionaryCache <long, long>(), "test");
            using (CacheContext originalCacheContext = new CacheContext())
            {
                originalCacheContext.Entities.Add(entityRefs);
                originalCacheContext.RelationshipTypes.Add(relationshipTypeRefs);
                originalCacheContext.FieldTypes.Add(fieldTypeRefs);
                originalCacheContext.EntityInvalidatingRelationshipTypes.Add(entityInvalidatingRelationshipTypeRefs);

                cacheInvalidator.AddInvalidations(originalCacheContext, testKey);
            }

            using (CacheContext outerCacheContext = new CacheContext())
                using (CacheContext innerCacheContext = new CacheContext())
                {
                    Assert.That(innerCacheContext.Entities, Is.Empty, "Entities not initially empty");
                    Assert.That(innerCacheContext.RelationshipTypes, Is.Empty, "RelationshipTypes not initially empty");
                    Assert.That(innerCacheContext.FieldTypes, Is.Empty, "FieldTypes not initially empty");
                    Assert.That(innerCacheContext.EntityInvalidatingRelationshipTypes, Is.Empty, "EntityInvalidatingRelationshipTypes not initially empty");

                    innerCacheContext.AddInvalidationsFor(cacheInvalidator, testKey);
                    Assert.That(innerCacheContext.Entities,
                                Is.EquivalentTo(entityRefs).Using(EntityRefComparer.Instance),
                                "Unexpected Entities");
                    Assert.That(innerCacheContext.RelationshipTypes,
                                Is.EquivalentTo(relationshipTypeRefs).Using(EntityRefComparer.Instance),
                                "Unexpected RelationshipTypes");
                    Assert.That(innerCacheContext.FieldTypes,
                                Is.EquivalentTo(fieldTypeRefs).Using(EntityRefComparer.Instance),
                                "Unexpected FieldTypes");
                    Assert.That(innerCacheContext.EntityInvalidatingRelationshipTypes,
                                Is.EquivalentTo(entityInvalidatingRelationshipTypeRefs).Using(EntityRefComparer.Instance),
                                "Unexpected EntityInvalidatingRelationshipTypes");

                    Assert.That(outerCacheContext.Entities,
                                Is.EquivalentTo(entityRefs).Using(EntityRefComparer.Instance),
                                "Unexpected Entities in outer cache context");
                    Assert.That(outerCacheContext.RelationshipTypes,
                                Is.EquivalentTo(relationshipTypeRefs).Using(EntityRefComparer.Instance),
                                "Unexpected RelationshipTypes in outer cache context");
                    Assert.That(outerCacheContext.FieldTypes,
                                Is.EquivalentTo(fieldTypeRefs).Using(EntityRefComparer.Instance),
                                "Unexpected FieldTypes in outer cache context");
                    Assert.That(outerCacheContext.EntityInvalidatingRelationshipTypes,
                                Is.EquivalentTo(entityInvalidatingRelationshipTypeRefs).Using(EntityRefComparer.Instance),
                                "Unexpected EntityInvalidatingRelationshipTypes in outer cache context");
                }
        }