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();
}
}
}