using System; using System.Collections.Generic; using System.Linq; using Umbraco.Core.Collections; using Umbraco.Core.Models.EntityBase; namespace Umbraco.Core.Cache { /// /// A caching policy that caches an entire dataset as a single collection /// /// /// /// /// This caching policy has no sliding expiration but uses the default ObjectCache.InfiniteAbsoluteExpiration as it's timeout, so it /// should not leave the cache unless the cache memory is exceeded and it gets thrown out. /// internal class FullDataSetRepositoryCachePolicy : DefaultRepositoryCachePolicy where TEntity : class, IAggregateRoot { private readonly Func _getEntityId; public FullDataSetRepositoryCachePolicy(IRuntimeCacheProvider cache, Func getEntityId) : base(cache, new RepositoryCachePolicyOptions { //Definitely allow zero'd cache entires since this is a full set, in many cases there will be none, // and we must cache this! GetAllCacheAllowZeroCount = true }) { _getEntityId = getEntityId; } private bool? _hasZeroCountCache; public override TEntity[] GetAll(TId[] ids, Func> getFromRepo) { //process the base logic without any Ids - we want to cache them all! var result = base.GetAll(new TId[] { }, getFromRepo); //now that the base result has been calculated, they will all be cached. // Now we can just filter by ids if they have been supplied return ids.Any() ? result.Where(x => ids.Contains(_getEntityId(x))).ToArray() : result; } /// /// For this type of caching policy, we don't cache individual items /// /// /// protected override void SetCacheAction(string cacheKey, TEntity entity) { //do nothing } /// /// Sets the action to execute on disposal for an entity collection /// /// /// protected override void SetCacheAction(TId[] ids, TEntity[] entityCollection) { //for this type of caching policy, we don't want to cache any GetAll request containing specific Ids if (ids.Any()) return; //set the disposal action SetCacheAction(() => { //We want to cache the result as a single collection Cache.InsertCacheItem(GetCacheTypeKey(), () => new DeepCloneableList(entityCollection)); }); } /// /// Looks up the zero count cache, must return null if it doesn't exist /// /// protected override bool HasZeroCountCache() { if (_hasZeroCountCache.HasValue) return _hasZeroCountCache.Value; _hasZeroCountCache = Cache.GetCacheItem>(GetCacheTypeKey()) != null; return _hasZeroCountCache.Value; } /// /// This policy will cache the full data set as a single collection /// /// protected override TEntity[] GetAllFromCache() { var found = Cache.GetCacheItem>(GetCacheTypeKey()); //This method will get called before checking for zero count cache, so we'll just set the flag here _hasZeroCountCache = found != null; return found == null ? new TEntity[] { } : found.WhereNotNull().ToArray(); } } }