U4-9322 - backport repository cache policy changes from v8
This commit is contained in:
@@ -1,23 +1,25 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Umbraco.Core.Logging;
|
||||
using Umbraco.Core.Models.EntityBase;
|
||||
|
||||
namespace Umbraco.Core.Cache
|
||||
{
|
||||
/// <summary>
|
||||
/// The default cache policy for retrieving a single entity
|
||||
/// Represents the default cache policy.
|
||||
/// </summary>
|
||||
/// <typeparam name="TEntity"></typeparam>
|
||||
/// <typeparam name="TId"></typeparam>
|
||||
/// <typeparam name="TEntity">The type of the entity.</typeparam>
|
||||
/// <typeparam name="TId">The type of the identifier.</typeparam>
|
||||
/// <remarks>
|
||||
/// This cache policy uses sliding expiration and caches instances for 5 minutes. However if allow zero count is true, then we use the
|
||||
/// default policy with no expiry.
|
||||
/// <para>The default cache policy caches entities with a 5 minutes sliding expiration.</para>
|
||||
/// <para>Each entity is cached individually.</para>
|
||||
/// <para>If options.GetAllCacheAllowZeroCount then a 'zero-count' array is cached when GetAll finds nothing.</para>
|
||||
/// <para>If options.GetAllCacheValidateCount then we check against the db when getting many entities.</para>
|
||||
/// </remarks>
|
||||
internal class DefaultRepositoryCachePolicy<TEntity, TId> : RepositoryCachePolicyBase<TEntity, TId>
|
||||
where TEntity : class, IAggregateRoot
|
||||
{
|
||||
private static readonly TEntity[] EmptyEntities = new TEntity[0]; // const
|
||||
private readonly RepositoryCachePolicyOptions _options;
|
||||
|
||||
public DefaultRepositoryCachePolicy(IRuntimeCacheProvider cache, RepositoryCachePolicyOptions options)
|
||||
@@ -27,242 +29,216 @@ namespace Umbraco.Core.Cache
|
||||
_options = options;
|
||||
}
|
||||
|
||||
protected string GetCacheIdKey(object id)
|
||||
protected string GetEntityCacheKey(object id)
|
||||
{
|
||||
if (id == null) throw new ArgumentNullException("id");
|
||||
|
||||
return string.Format("{0}{1}", GetCacheTypeKey(), id);
|
||||
return GetEntityTypeCacheKey() + id;
|
||||
}
|
||||
|
||||
protected string GetCacheTypeKey()
|
||||
protected string GetEntityTypeCacheKey()
|
||||
{
|
||||
return string.Format("uRepo_{0}_", typeof(TEntity).Name);
|
||||
}
|
||||
|
||||
public override void CreateOrUpdate(TEntity entity, Action<TEntity> persistMethod)
|
||||
protected virtual void InsertEntity(string cacheKey, TEntity entity)
|
||||
{
|
||||
Cache.InsertCacheItem(cacheKey, () => entity, TimeSpan.FromMinutes(5), true);
|
||||
}
|
||||
|
||||
protected virtual void InsertEntities(TId[] ids, TEntity[] entities)
|
||||
{
|
||||
if (ids.Length == 0 && entities.Length == 0 && _options.GetAllCacheAllowZeroCount)
|
||||
{
|
||||
// getting all of them, and finding nothing.
|
||||
// if we can cache a zero count, cache an empty array,
|
||||
// for as long as the cache is not cleared (no expiration)
|
||||
Cache.InsertCacheItem(GetEntityTypeCacheKey(), () => EmptyEntities);
|
||||
}
|
||||
else
|
||||
{
|
||||
// individually cache each item
|
||||
foreach (var entity in entities)
|
||||
{
|
||||
var capture = entity;
|
||||
Cache.InsertCacheItem(GetEntityCacheKey(entity.Id), () => capture, TimeSpan.FromMinutes(5), true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Create(TEntity entity, Action<TEntity> persistNew)
|
||||
{
|
||||
if (entity == null) throw new ArgumentNullException("entity");
|
||||
if (persistMethod == null) throw new ArgumentNullException("persistMethod");
|
||||
|
||||
try
|
||||
{
|
||||
persistMethod(entity);
|
||||
persistNew(entity);
|
||||
|
||||
//set the disposal action
|
||||
SetCacheAction(() =>
|
||||
// just to be safe, we cannot cache an item without an identity
|
||||
if (entity.HasIdentity)
|
||||
{
|
||||
//just to be safe, we cannot cache an item without an identity
|
||||
if (entity.HasIdentity)
|
||||
{
|
||||
Cache.InsertCacheItem(GetCacheIdKey(entity.Id), () => entity,
|
||||
timeout: TimeSpan.FromMinutes(5),
|
||||
isSliding: true);
|
||||
}
|
||||
|
||||
//If there's a GetAllCacheAllowZeroCount cache, ensure it is cleared
|
||||
Cache.ClearCacheItem(GetCacheTypeKey());
|
||||
});
|
||||
|
||||
Cache.InsertCacheItem(GetEntityCacheKey(entity.Id), () => entity, TimeSpan.FromMinutes(5), true);
|
||||
}
|
||||
|
||||
// if there's a GetAllCacheAllowZeroCount cache, ensure it is cleared
|
||||
Cache.ClearCacheItem(GetEntityTypeCacheKey());
|
||||
}
|
||||
catch
|
||||
{
|
||||
//set the disposal action
|
||||
SetCacheAction(() =>
|
||||
{
|
||||
//if an exception is thrown we need to remove the entry from cache, this is ONLY a work around because of the way
|
||||
// that we cache entities: http://issues.umbraco.org/issue/U4-4259
|
||||
Cache.ClearCacheItem(GetCacheIdKey(entity.Id));
|
||||
// if an exception is thrown we need to remove the entry from cache,
|
||||
// this is ONLY a work around because of the way
|
||||
// that we cache entities: http://issues.umbraco.org/issue/U4-4259
|
||||
Cache.ClearCacheItem(GetEntityCacheKey(entity.Id));
|
||||
|
||||
// if there's a GetAllCacheAllowZeroCount cache, ensure it is cleared
|
||||
Cache.ClearCacheItem(GetEntityTypeCacheKey());
|
||||
|
||||
//If there's a GetAllCacheAllowZeroCount cache, ensure it is cleared
|
||||
Cache.ClearCacheItem(GetCacheTypeKey());
|
||||
});
|
||||
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
public override void Remove(TEntity entity, Action<TEntity> persistMethod)
|
||||
/// <inheritdoc />
|
||||
public override void Update(TEntity entity, Action<TEntity> persistUpdated)
|
||||
{
|
||||
if (entity == null) throw new ArgumentNullException("entity");
|
||||
if (persistMethod == null) throw new ArgumentNullException("persistMethod");
|
||||
|
||||
try
|
||||
{
|
||||
persistMethod(entity);
|
||||
}
|
||||
finally
|
||||
{
|
||||
//set the disposal action
|
||||
var cacheKey = GetCacheIdKey(entity.Id);
|
||||
SetCacheAction(() =>
|
||||
persistUpdated(entity);
|
||||
|
||||
// just to be safe, we cannot cache an item without an identity
|
||||
if (entity.HasIdentity)
|
||||
{
|
||||
Cache.ClearCacheItem(cacheKey);
|
||||
//If there's a GetAllCacheAllowZeroCount cache, ensure it is cleared
|
||||
Cache.ClearCacheItem(GetCacheTypeKey());
|
||||
});
|
||||
Cache.InsertCacheItem(GetEntityCacheKey(entity.Id), () => entity, TimeSpan.FromMinutes(5), true);
|
||||
}
|
||||
|
||||
// if there's a GetAllCacheAllowZeroCount cache, ensure it is cleared
|
||||
Cache.ClearCacheItem(GetEntityTypeCacheKey());
|
||||
}
|
||||
catch
|
||||
{
|
||||
// if an exception is thrown we need to remove the entry from cache,
|
||||
// this is ONLY a work around because of the way
|
||||
// that we cache entities: http://issues.umbraco.org/issue/U4-4259
|
||||
Cache.ClearCacheItem(GetEntityCacheKey(entity.Id));
|
||||
|
||||
// if there's a GetAllCacheAllowZeroCount cache, ensure it is cleared
|
||||
Cache.ClearCacheItem(GetEntityTypeCacheKey());
|
||||
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
public override TEntity Get(TId id, Func<TId, TEntity> getFromRepo)
|
||||
/// <inheritdoc />
|
||||
public override void Delete(TEntity entity, Action<TEntity> persistDeleted)
|
||||
{
|
||||
if (getFromRepo == null) throw new ArgumentNullException("getFromRepo");
|
||||
if (entity == null) throw new ArgumentNullException("entity");
|
||||
|
||||
var cacheKey = GetCacheIdKey(id);
|
||||
try
|
||||
{
|
||||
persistDeleted(entity);
|
||||
}
|
||||
finally
|
||||
{
|
||||
// whatever happens, clear the cache
|
||||
var cacheKey = GetEntityCacheKey(entity.Id);
|
||||
Cache.ClearCacheItem(cacheKey);
|
||||
// if there's a GetAllCacheAllowZeroCount cache, ensure it is cleared
|
||||
Cache.ClearCacheItem(GetEntityTypeCacheKey());
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override TEntity Get(TId id, Func<TId, TEntity> performGet, Func<TId[], IEnumerable<TEntity>> performGetAll)
|
||||
{
|
||||
var cacheKey = GetEntityCacheKey(id);
|
||||
var fromCache = Cache.GetCacheItem<TEntity>(cacheKey);
|
||||
|
||||
// if found in cache then return else fetch and cache
|
||||
if (fromCache != null)
|
||||
return fromCache;
|
||||
|
||||
var entity = getFromRepo(id);
|
||||
var entity = performGet(id);
|
||||
|
||||
//set the disposal action
|
||||
SetCacheAction(cacheKey, entity);
|
||||
if (entity != null && entity.HasIdentity)
|
||||
InsertEntity(cacheKey, entity);
|
||||
|
||||
return entity;
|
||||
}
|
||||
|
||||
public override TEntity Get(TId id)
|
||||
/// <inheritdoc />
|
||||
public override TEntity GetCached(TId id)
|
||||
{
|
||||
var cacheKey = GetCacheIdKey(id);
|
||||
var cacheKey = GetEntityCacheKey(id);
|
||||
return Cache.GetCacheItem<TEntity>(cacheKey);
|
||||
}
|
||||
|
||||
public override bool Exists(TId id, Func<TId, bool> getFromRepo)
|
||||
/// <inheritdoc />
|
||||
public override bool Exists(TId id, Func<TId, bool> performExists, Func<TId[], IEnumerable<TEntity>> performGetAll)
|
||||
{
|
||||
if (getFromRepo == null) throw new ArgumentNullException("getFromRepo");
|
||||
|
||||
var cacheKey = GetCacheIdKey(id);
|
||||
// if found in cache the return else check
|
||||
var cacheKey = GetEntityCacheKey(id);
|
||||
var fromCache = Cache.GetCacheItem<TEntity>(cacheKey);
|
||||
return fromCache != null || getFromRepo(id);
|
||||
return fromCache != null || performExists(id);
|
||||
}
|
||||
|
||||
public override TEntity[] GetAll(TId[] ids, Func<TId[], IEnumerable<TEntity>> getFromRepo)
|
||||
/// <inheritdoc />
|
||||
public override TEntity[] GetAll(TId[] ids, Func<TId[], IEnumerable<TEntity>> performGetAll)
|
||||
{
|
||||
if (getFromRepo == null) throw new ArgumentNullException("getFromRepo");
|
||||
|
||||
if (ids.Any())
|
||||
if (ids.Length > 0)
|
||||
{
|
||||
var entities = ids.Select(Get).ToArray();
|
||||
if (ids.Length.Equals(entities.Length) && entities.Any(x => x == null) == false)
|
||||
return entities;
|
||||
// try to get each entity from the cache
|
||||
// if we can find all of them, return
|
||||
var entities = ids.Select(GetCached).WhereNotNull().ToArray();
|
||||
if (ids.Length.Equals(entities.Length))
|
||||
return entities; // no need for null checks, we are not caching nulls
|
||||
}
|
||||
else
|
||||
{
|
||||
var allEntities = GetAllFromCache();
|
||||
if (allEntities.Any())
|
||||
// get everything we have
|
||||
var entities = Cache.GetCacheItemsByKeySearch<TEntity>(GetEntityTypeCacheKey())
|
||||
.ToArray(); // no need for null checks, we are not caching nulls
|
||||
|
||||
if (entities.Length > 0)
|
||||
{
|
||||
// if some of them were in the cache...
|
||||
if (_options.GetAllCacheValidateCount)
|
||||
{
|
||||
//Get count of all entities of current type (TEntity) to ensure cached result is correct
|
||||
// need to validate the count, get the actual count and return if ok
|
||||
var totalCount = _options.PerformCount();
|
||||
if (allEntities.Length == totalCount)
|
||||
return allEntities;
|
||||
if (entities.Length == totalCount)
|
||||
return entities;
|
||||
}
|
||||
else
|
||||
{
|
||||
return allEntities;
|
||||
// no need to validate, just return what we have and assume it's all there is
|
||||
return entities;
|
||||
}
|
||||
}
|
||||
else if (_options.GetAllCacheAllowZeroCount)
|
||||
{
|
||||
//if the repository allows caching a zero count, then check the zero count cache
|
||||
if (HasZeroCountCache())
|
||||
{
|
||||
//there is a zero count cache so return an empty list
|
||||
return new TEntity[] {};
|
||||
}
|
||||
// if none of them were in the cache
|
||||
// and we allow zero count - check for the special (empty) entry
|
||||
var empty = Cache.GetCacheItem<TEntity[]>(GetEntityTypeCacheKey());
|
||||
if (empty != null) return empty;
|
||||
}
|
||||
}
|
||||
|
||||
//we need to do the lookup from the repo
|
||||
var entityCollection = getFromRepo(ids)
|
||||
//ensure we don't include any null refs in the returned collection!
|
||||
.WhereNotNull()
|
||||
// cache failed, get from repo and cache
|
||||
var repoEntities = performGetAll(ids)
|
||||
.WhereNotNull() // exclude nulls!
|
||||
.Where(x => x.HasIdentity) // be safe, though would be weird...
|
||||
.ToArray();
|
||||
|
||||
//set the disposal action
|
||||
SetCacheAction(ids, entityCollection);
|
||||
// note: if empty & allow zero count, will cache a special (empty) entry
|
||||
InsertEntities(ids, repoEntities);
|
||||
|
||||
return entityCollection;
|
||||
return repoEntities;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up the zero count cache, must return null if it doesn't exist
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
protected bool HasZeroCountCache()
|
||||
/// <inheritdoc />
|
||||
public override void ClearAll()
|
||||
{
|
||||
var zeroCount = Cache.GetCacheItem<TEntity[]>(GetCacheTypeKey());
|
||||
return (zeroCount != null && zeroCount.Any() == false);
|
||||
Cache.ClearCacheByKeySearch(GetEntityTypeCacheKey());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Performs the lookup for all entities of this type from the cache
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
protected TEntity[] GetAllFromCache()
|
||||
{
|
||||
var allEntities = Cache.GetCacheItemsByKeySearch<TEntity>(GetCacheTypeKey())
|
||||
.WhereNotNull()
|
||||
.ToArray();
|
||||
return allEntities.Any() ? allEntities : new TEntity[] {};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the action to execute on disposal for a single entity
|
||||
/// </summary>
|
||||
/// <param name="cacheKey"></param>
|
||||
/// <param name="entity"></param>
|
||||
protected virtual void SetCacheAction(string cacheKey, TEntity entity)
|
||||
{
|
||||
if (entity == null) return;
|
||||
|
||||
SetCacheAction(() =>
|
||||
{
|
||||
//just to be safe, we cannot cache an item without an identity
|
||||
if (entity.HasIdentity)
|
||||
{
|
||||
Cache.InsertCacheItem(cacheKey, () => entity,
|
||||
timeout: TimeSpan.FromMinutes(5),
|
||||
isSliding: true);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the action to execute on disposal for an entity collection
|
||||
/// </summary>
|
||||
/// <param name="ids"></param>
|
||||
/// <param name="entityCollection"></param>
|
||||
protected virtual void SetCacheAction(TId[] ids, TEntity[] entityCollection)
|
||||
{
|
||||
SetCacheAction(() =>
|
||||
{
|
||||
//This option cannot execute if we are looking up specific Ids
|
||||
if (ids.Any() == false && entityCollection.Length == 0 && _options.GetAllCacheAllowZeroCount)
|
||||
{
|
||||
//there was nothing returned but we want to cache a zero count result so add an TEntity[] to the cache
|
||||
// to signify that there is a zero count cache
|
||||
//NOTE: Don't set expiry/sliding for a zero count
|
||||
Cache.InsertCacheItem(GetCacheTypeKey(), () => new TEntity[] {});
|
||||
}
|
||||
else
|
||||
{
|
||||
//This is the default behavior, we'll individually cache each item so that if/when these items are resolved
|
||||
// by id, they are returned from the already existing cache.
|
||||
foreach (var entity in entityCollection.WhereNotNull())
|
||||
{
|
||||
var localCopy = entity;
|
||||
//just to be safe, we cannot cache an item without an identity
|
||||
if (localCopy.HasIdentity)
|
||||
{
|
||||
Cache.InsertCacheItem(GetCacheIdKey(entity.Id), () => localCopy,
|
||||
timeout: TimeSpan.FromMinutes(5),
|
||||
isSliding: true);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
using Umbraco.Core.Models.EntityBase;
|
||||
|
||||
namespace Umbraco.Core.Cache
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates cache policies
|
||||
/// </summary>
|
||||
/// <typeparam name="TEntity"></typeparam>
|
||||
/// <typeparam name="TId"></typeparam>
|
||||
internal class DefaultRepositoryCachePolicyFactory<TEntity, TId> : IRepositoryCachePolicyFactory<TEntity, TId>
|
||||
where TEntity : class, IAggregateRoot
|
||||
{
|
||||
private readonly IRuntimeCacheProvider _runtimeCache;
|
||||
private readonly RepositoryCachePolicyOptions _options;
|
||||
|
||||
public DefaultRepositoryCachePolicyFactory(IRuntimeCacheProvider runtimeCache, RepositoryCachePolicyOptions options)
|
||||
{
|
||||
_runtimeCache = runtimeCache;
|
||||
_options = options;
|
||||
}
|
||||
|
||||
public virtual IRepositoryCachePolicy<TEntity, TId> CreatePolicy()
|
||||
{
|
||||
return new DefaultRepositoryCachePolicy<TEntity, TId>(_runtimeCache, _options);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7,223 +7,168 @@ using Umbraco.Core.Models.EntityBase;
|
||||
namespace Umbraco.Core.Cache
|
||||
{
|
||||
/// <summary>
|
||||
/// A caching policy that caches an entire dataset as a single collection
|
||||
/// Represents a caching policy that caches the entire entities set as a single collection.
|
||||
/// </summary>
|
||||
/// <typeparam name="TEntity"></typeparam>
|
||||
/// <typeparam name="TId"></typeparam>
|
||||
/// <typeparam name="TEntity">The type of the entity.</typeparam>
|
||||
/// <typeparam name="TId">The type of the identifier.</typeparam>
|
||||
/// <remarks>
|
||||
/// <para>Caches the entire set of entities as a single collection.</para>
|
||||
/// <para>Used by Content-, Media- and MemberTypeRepository, DataTypeRepository, DomainRepository,
|
||||
/// LanguageRepository, PublicAccessRepository, TemplateRepository... things that make sense to
|
||||
/// keep as a whole in memory.</para>
|
||||
/// </remarks>
|
||||
internal class FullDataSetRepositoryCachePolicy<TEntity, TId> : RepositoryCachePolicyBase<TEntity, TId>
|
||||
where TEntity : class, IAggregateRoot
|
||||
{
|
||||
private readonly Func<TEntity, TId> _getEntityId;
|
||||
private readonly Func<IEnumerable<TEntity>> _getAllFromRepo;
|
||||
private readonly Func<TEntity, TId> _entityGetId;
|
||||
private readonly bool _expires;
|
||||
|
||||
public FullDataSetRepositoryCachePolicy(IRuntimeCacheProvider cache, Func<TEntity, TId> getEntityId, Func<IEnumerable<TEntity>> getAllFromRepo, bool expires)
|
||||
public FullDataSetRepositoryCachePolicy(IRuntimeCacheProvider cache, Func<TEntity, TId> entityGetId, bool expires)
|
||||
: base(cache)
|
||||
{
|
||||
_getEntityId = getEntityId;
|
||||
_getAllFromRepo = getAllFromRepo;
|
||||
_entityGetId = entityGetId;
|
||||
_expires = expires;
|
||||
}
|
||||
|
||||
private bool? _hasZeroCountCache;
|
||||
protected static readonly TId[] EmptyIds = new TId[0]; // const
|
||||
|
||||
|
||||
protected string GetCacheTypeKey()
|
||||
protected string GetEntityTypeCacheKey()
|
||||
{
|
||||
return string.Format("uRepo_{0}_", typeof(TEntity).Name);
|
||||
}
|
||||
|
||||
public override void CreateOrUpdate(TEntity entity, Action<TEntity> persistMethod)
|
||||
protected void InsertEntities(TEntity[] entities)
|
||||
{
|
||||
if (entity == null) throw new ArgumentNullException("entity");
|
||||
if (persistMethod == null) throw new ArgumentNullException("persistMethod");
|
||||
// cache is expected to be a deep-cloning cache ie it deep-clones whatever is
|
||||
// IDeepCloneable when it goes in, and out. it also resets dirty properties,
|
||||
// making sure that no 'dirty' entity is cached.
|
||||
//
|
||||
// this policy is caching the entire list of entities. to ensure that entities
|
||||
// are properly deep-clones when cached, it uses a DeepCloneableList. however,
|
||||
// we don't want to deep-clone *each* entity in the list when fetching it from
|
||||
// cache as that would not be efficient for Get(id). so the DeepCloneableList is
|
||||
// set to ListCloneBehavior.CloneOnce ie it will clone *once* when inserting,
|
||||
// and then will *not* clone when retrieving.
|
||||
|
||||
try
|
||||
if (_expires)
|
||||
{
|
||||
persistMethod(entity);
|
||||
|
||||
//set the disposal action
|
||||
SetCacheAction(() =>
|
||||
{
|
||||
//Clear all
|
||||
Cache.ClearCacheItem(GetCacheTypeKey());
|
||||
});
|
||||
Cache.InsertCacheItem(GetEntityTypeCacheKey(), () => new DeepCloneableList<TEntity>(entities), TimeSpan.FromMinutes(5), true);
|
||||
}
|
||||
catch
|
||||
else
|
||||
{
|
||||
//set the disposal action
|
||||
SetCacheAction(() =>
|
||||
{
|
||||
//Clear all
|
||||
Cache.ClearCacheItem(GetCacheTypeKey());
|
||||
});
|
||||
throw;
|
||||
Cache.InsertCacheItem(GetEntityTypeCacheKey(), () => new DeepCloneableList<TEntity>(entities));
|
||||
}
|
||||
}
|
||||
|
||||
public override void Remove(TEntity entity, Action<TEntity> persistMethod)
|
||||
/// <inheritdoc />
|
||||
public override void Create(TEntity entity, Action<TEntity> persistNew)
|
||||
{
|
||||
if (entity == null) throw new ArgumentNullException("entity");
|
||||
if (persistMethod == null) throw new ArgumentNullException("persistMethod");
|
||||
|
||||
try
|
||||
{
|
||||
persistMethod(entity);
|
||||
persistNew(entity);
|
||||
}
|
||||
finally
|
||||
{
|
||||
//set the disposal action
|
||||
SetCacheAction(() =>
|
||||
{
|
||||
//Clear all
|
||||
Cache.ClearCacheItem(GetCacheTypeKey());
|
||||
});
|
||||
ClearAll();
|
||||
}
|
||||
}
|
||||
|
||||
public override TEntity Get(TId id, Func<TId, TEntity> getFromRepo)
|
||||
/// <inheritdoc />
|
||||
public override void Update(TEntity entity, Action<TEntity> persistUpdated)
|
||||
{
|
||||
//Force get all with cache
|
||||
var found = GetAll(new TId[] { }, ids => _getAllFromRepo().WhereNotNull());
|
||||
if (entity == null) throw new ArgumentNullException("entity");
|
||||
|
||||
//we don't have anything in cache (this should never happen), just return from the repo
|
||||
if (found == null) return getFromRepo(id);
|
||||
var entity = found.FirstOrDefault(x => _getEntityId(x).Equals(id));
|
||||
if (entity == null) return null;
|
||||
|
||||
//We must ensure to deep clone each one out manually since the deep clone list only clones one way
|
||||
return (TEntity)entity.DeepClone();
|
||||
}
|
||||
|
||||
public override TEntity Get(TId id)
|
||||
{
|
||||
//Force get all with cache
|
||||
var found = GetAll(new TId[] { }, ids => _getAllFromRepo().WhereNotNull());
|
||||
|
||||
//we don't have anything in cache (this should never happen), just return null
|
||||
if (found == null) return null;
|
||||
var entity = found.FirstOrDefault(x => _getEntityId(x).Equals(id));
|
||||
if (entity == null) return null;
|
||||
|
||||
//We must ensure to deep clone each one out manually since the deep clone list only clones one way
|
||||
return (TEntity)entity.DeepClone();
|
||||
}
|
||||
|
||||
public override bool Exists(TId id, Func<TId, bool> getFromRepo)
|
||||
{
|
||||
//Force get all with cache
|
||||
var found = GetAll(new TId[] { }, ids => _getAllFromRepo().WhereNotNull());
|
||||
|
||||
//we don't have anything in cache (this should never happen), just return from the repo
|
||||
return found == null
|
||||
? getFromRepo(id)
|
||||
: found.Any(x => _getEntityId(x).Equals(id));
|
||||
}
|
||||
|
||||
public override TEntity[] GetAll(TId[] ids, Func<TId[], IEnumerable<TEntity>> getFromRepo)
|
||||
{
|
||||
//process getting all including setting the cache callback
|
||||
var result = PerformGetAll(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)
|
||||
//We must ensure to deep clone each one out manually since the deep clone list only clones one way
|
||||
.Select(x => (TEntity)x.DeepClone())
|
||||
.ToArray();
|
||||
}
|
||||
|
||||
private TEntity[] PerformGetAll(Func<TId[], IEnumerable<TEntity>> getFromRepo)
|
||||
{
|
||||
var allEntities = GetAllFromCache();
|
||||
if (allEntities.Any())
|
||||
try
|
||||
{
|
||||
return allEntities;
|
||||
persistUpdated(entity);
|
||||
}
|
||||
|
||||
//check the zero count cache
|
||||
if (HasZeroCountCache())
|
||||
finally
|
||||
{
|
||||
//there is a zero count cache so return an empty list
|
||||
return new TEntity[] { };
|
||||
ClearAll();
|
||||
}
|
||||
|
||||
//we need to do the lookup from the repo
|
||||
var entityCollection = getFromRepo(new TId[] { })
|
||||
//ensure we don't include any null refs in the returned collection!
|
||||
.WhereNotNull()
|
||||
.ToArray();
|
||||
|
||||
//set the disposal action
|
||||
SetCacheAction(entityCollection);
|
||||
|
||||
return entityCollection;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// For this type of caching policy, we don't cache individual items
|
||||
/// </summary>
|
||||
/// <param name="cacheKey"></param>
|
||||
/// <param name="entity"></param>
|
||||
protected void SetCacheAction(string cacheKey, TEntity entity)
|
||||
/// <inheritdoc />
|
||||
public override void Delete(TEntity entity, Action<TEntity> persistDeleted)
|
||||
{
|
||||
//No-op
|
||||
}
|
||||
if (entity == null) throw new ArgumentNullException("entity");
|
||||
|
||||
/// <summary>
|
||||
/// Sets the action to execute on disposal for an entity collection
|
||||
/// </summary>
|
||||
/// <param name="entityCollection"></param>
|
||||
protected void SetCacheAction(TEntity[] entityCollection)
|
||||
{
|
||||
//set the disposal action
|
||||
SetCacheAction(() =>
|
||||
try
|
||||
{
|
||||
//We want to cache the result as a single collection
|
||||
|
||||
if (_expires)
|
||||
{
|
||||
Cache.InsertCacheItem(GetCacheTypeKey(), () => new DeepCloneableList<TEntity>(entityCollection),
|
||||
timeout: TimeSpan.FromMinutes(5),
|
||||
isSliding: true);
|
||||
}
|
||||
else
|
||||
{
|
||||
Cache.InsertCacheItem(GetCacheTypeKey(), () => new DeepCloneableList<TEntity>(entityCollection));
|
||||
}
|
||||
});
|
||||
persistDeleted(entity);
|
||||
}
|
||||
finally
|
||||
{
|
||||
ClearAll();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up the zero count cache, must return null if it doesn't exist
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
protected bool HasZeroCountCache()
|
||||
/// <inheritdoc />
|
||||
public override TEntity Get(TId id, Func<TId, TEntity> performGet, Func<TId[], IEnumerable<TEntity>> performGetAll)
|
||||
{
|
||||
if (_hasZeroCountCache.HasValue)
|
||||
return _hasZeroCountCache.Value;
|
||||
// get all from the cache, then look for the entity
|
||||
var all = GetAllCached(performGetAll);
|
||||
var entity = all.FirstOrDefault(x => _entityGetId(x).Equals(id));
|
||||
|
||||
_hasZeroCountCache = Cache.GetCacheItem<DeepCloneableList<TEntity>>(GetCacheTypeKey()) != null;
|
||||
return _hasZeroCountCache.Value;
|
||||
// see note in InsertEntities - what we get here is the original
|
||||
// cached entity, not a clone, so we need to manually ensure it is deep-cloned.
|
||||
return entity == null ? null : (TEntity) entity.DeepClone();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This policy will cache the full data set as a single collection
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
protected TEntity[] GetAllFromCache()
|
||||
/// <inheritdoc />
|
||||
public override TEntity GetCached(TId id)
|
||||
{
|
||||
var found = Cache.GetCacheItem<DeepCloneableList<TEntity>>(GetCacheTypeKey());
|
||||
// get all from the cache -- and only the cache, then look for the entity
|
||||
var all = Cache.GetCacheItem<DeepCloneableList<TEntity>>(GetEntityTypeCacheKey());
|
||||
var entity = all == null ? null : all.FirstOrDefault(x => _entityGetId(x).Equals(id));
|
||||
|
||||
//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();
|
||||
// see note in InsertEntities - what we get here is the original
|
||||
// cached entity, not a clone, so we need to manually ensure it is deep-cloned.
|
||||
return entity == null ? null : (TEntity)entity.DeepClone();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool Exists(TId id, Func<TId, bool> performExits, Func<TId[], IEnumerable<TEntity>> performGetAll)
|
||||
{
|
||||
// get all as one set, then look for the entity
|
||||
var all = GetAllCached(performGetAll);
|
||||
return all.Any(x => _entityGetId(x).Equals(id));
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override TEntity[] GetAll(TId[] ids, Func<TId[], IEnumerable<TEntity>> performGetAll)
|
||||
{
|
||||
// get all as one set, from cache if possible, else repo
|
||||
var all = GetAllCached(performGetAll);
|
||||
|
||||
// if ids have been specified, filter
|
||||
if (ids.Length > 0) all = all.Where(x => ids.Contains(_entityGetId(x)));
|
||||
|
||||
// and return
|
||||
// see note in SetCacheActionToInsertEntities - what we get here is the original
|
||||
// cached entities, not clones, so we need to manually ensure they are deep-cloned.
|
||||
return all.Select(x => (TEntity) x.DeepClone()).ToArray();
|
||||
}
|
||||
|
||||
// does NOT clone anything, so be nice with the returned values
|
||||
private IEnumerable<TEntity> GetAllCached(Func<TId[], IEnumerable<TEntity>> performGetAll)
|
||||
{
|
||||
// try the cache first
|
||||
var all = Cache.GetCacheItem<DeepCloneableList<TEntity>>(GetEntityTypeCacheKey());
|
||||
if (all != null) return all.ToArray();
|
||||
|
||||
// else get from repo and cache
|
||||
var entities = performGetAll(EmptyIds).WhereNotNull().ToArray();
|
||||
InsertEntities(entities); // may be an empty array...
|
||||
return entities;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void ClearAll()
|
||||
{
|
||||
Cache.ClearCacheItem(GetEntityTypeCacheKey());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Umbraco.Core.Models.EntityBase;
|
||||
|
||||
namespace Umbraco.Core.Cache
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates cache policies
|
||||
/// </summary>
|
||||
/// <typeparam name="TEntity"></typeparam>
|
||||
/// <typeparam name="TId"></typeparam>
|
||||
internal class FullDataSetRepositoryCachePolicyFactory<TEntity, TId> : IRepositoryCachePolicyFactory<TEntity, TId>
|
||||
where TEntity : class, IAggregateRoot
|
||||
{
|
||||
private readonly IRuntimeCacheProvider _runtimeCache;
|
||||
private readonly Func<TEntity, TId> _getEntityId;
|
||||
private readonly Func<IEnumerable<TEntity>> _getAllFromRepo;
|
||||
private readonly bool _expires;
|
||||
|
||||
public FullDataSetRepositoryCachePolicyFactory(IRuntimeCacheProvider runtimeCache, Func<TEntity, TId> getEntityId, Func<IEnumerable<TEntity>> getAllFromRepo, bool expires)
|
||||
{
|
||||
_runtimeCache = runtimeCache;
|
||||
_getEntityId = getEntityId;
|
||||
_getAllFromRepo = getAllFromRepo;
|
||||
_expires = expires;
|
||||
}
|
||||
|
||||
public virtual IRepositoryCachePolicy<TEntity, TId> CreatePolicy()
|
||||
{
|
||||
return new FullDataSetRepositoryCachePolicy<TEntity, TId>(_runtimeCache, _getEntityId, _getAllFromRepo, _expires);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,15 +4,83 @@ using Umbraco.Core.Models.EntityBase;
|
||||
|
||||
namespace Umbraco.Core.Cache
|
||||
{
|
||||
internal interface IRepositoryCachePolicy<TEntity, TId> : IDisposable
|
||||
internal interface IRepositoryCachePolicy<TEntity, TId>
|
||||
where TEntity : class, IAggregateRoot
|
||||
{
|
||||
TEntity Get(TId id, Func<TId, TEntity> getFromRepo);
|
||||
TEntity Get(TId id);
|
||||
bool Exists(TId id, Func<TId, bool> getFromRepo);
|
||||
|
||||
void CreateOrUpdate(TEntity entity, Action<TEntity> persistMethod);
|
||||
void Remove(TEntity entity, Action<TEntity> persistMethod);
|
||||
TEntity[] GetAll(TId[] ids, Func<TId[], IEnumerable<TEntity>> getFromRepo);
|
||||
// note:
|
||||
// at the moment each repository instance creates its corresponding cache policy instance
|
||||
// we could reduce allocations by using static cache policy instances but then we would need
|
||||
// to modify all methods here to pass the repository and cache eg:
|
||||
//
|
||||
// TEntity Get(TRepository repository, IRuntimeCacheProvider cache, TId id);
|
||||
//
|
||||
// it is not *that* complicated but then RepositoryBase needs to have a TRepository generic
|
||||
// type parameter and it all becomes convoluted - keeping it simple for the time being.
|
||||
|
||||
/// <summary>
|
||||
/// Gets an entity from the cache, else from the repository.
|
||||
/// </summary>
|
||||
/// <param name="id">The identifier.</param>
|
||||
/// <param name="performGet">The repository PerformGet method.</param>
|
||||
/// <param name="performGetAll">The repository PerformGetAll method.</param>
|
||||
/// <returns>The entity with the specified identifier, if it exits, else null.</returns>
|
||||
/// <remarks>First considers the cache then the repository.</remarks>
|
||||
TEntity Get(TId id, Func<TId, TEntity> performGet, Func<TId[], IEnumerable<TEntity>> performGetAll);
|
||||
|
||||
/// <summary>
|
||||
/// Gets an entity from the cache.
|
||||
/// </summary>
|
||||
/// <param name="id">The identifier.</param>
|
||||
/// <returns>The entity with the specified identifier, if it is in the cache already, else null.</returns>
|
||||
/// <remarks>Does not consider the repository at all.</remarks>
|
||||
TEntity GetCached(TId id);
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether an entity with a specified identifier exists.
|
||||
/// </summary>
|
||||
/// <param name="id">The identifier.</param>
|
||||
/// <param name="performExists">The repository PerformExists method.</param>
|
||||
/// <param name="performGetAll">The repository PerformGetAll method.</param>
|
||||
/// <returns>A value indicating whether an entity with the specified identifier exists.</returns>
|
||||
/// <remarks>First considers the cache then the repository.</remarks>
|
||||
bool Exists(TId id, Func<TId, bool> performExists, Func<TId[], IEnumerable<TEntity>> performGetAll);
|
||||
|
||||
/// <summary>
|
||||
/// Creates an entity.
|
||||
/// </summary>
|
||||
/// <param name="entity">The entity.</param>
|
||||
/// <param name="persistNew">The repository PersistNewItem method.</param>
|
||||
/// <remarks>Creates the entity in the repository, and updates the cache accordingly.</remarks>
|
||||
void Create(TEntity entity, Action<TEntity> persistNew);
|
||||
|
||||
/// <summary>
|
||||
/// Updates an entity.
|
||||
/// </summary>
|
||||
/// <param name="entity">The entity.</param>
|
||||
/// <param name="persistUpdated">The reopsitory PersistUpdatedItem method.</param>
|
||||
/// <remarks>Updates the entity in the repository, and updates the cache accordingly.</remarks>
|
||||
void Update(TEntity entity, Action<TEntity> persistUpdated);
|
||||
|
||||
/// <summary>
|
||||
/// Removes an entity.
|
||||
/// </summary>
|
||||
/// <param name="entity">The entity.</param>
|
||||
/// <param name="persistDeleted">The repository PersistDeletedItem method.</param>
|
||||
/// <remarks>Removes the entity from the repository and clears the cache.</remarks>
|
||||
void Delete(TEntity entity, Action<TEntity> persistDeleted);
|
||||
|
||||
/// <summary>
|
||||
/// Gets entities.
|
||||
/// </summary>
|
||||
/// <param name="ids">The identifiers.</param>
|
||||
/// <param name="performGetAll">The repository PerformGetAll method.</param>
|
||||
/// <returns>If <paramref name="ids"/> is empty, all entities, else the entities with the specified identifiers.</returns>
|
||||
/// <remarks>Get all the entities. Either from the cache or the repository depending on the implementation.</remarks>
|
||||
TEntity[] GetAll(TId[] ids, Func<TId[], IEnumerable<TEntity>> performGetAll);
|
||||
|
||||
/// <summary>
|
||||
/// Clears the entire cache.
|
||||
/// </summary>
|
||||
void ClearAll();
|
||||
}
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
using Umbraco.Core.Models.EntityBase;
|
||||
|
||||
namespace Umbraco.Core.Cache
|
||||
{
|
||||
internal interface IRepositoryCachePolicyFactory<TEntity, TId> where TEntity : class, IAggregateRoot
|
||||
{
|
||||
IRepositoryCachePolicy<TEntity, TId> CreatePolicy();
|
||||
}
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
using Umbraco.Core.Models.EntityBase;
|
||||
|
||||
namespace Umbraco.Core.Cache
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates cache policies
|
||||
/// </summary>
|
||||
/// <typeparam name="TEntity"></typeparam>
|
||||
/// <typeparam name="TId"></typeparam>
|
||||
internal class OnlySingleItemsRepositoryCachePolicyFactory<TEntity, TId> : IRepositoryCachePolicyFactory<TEntity, TId>
|
||||
where TEntity : class, IAggregateRoot
|
||||
{
|
||||
private readonly IRuntimeCacheProvider _runtimeCache;
|
||||
private readonly RepositoryCachePolicyOptions _options;
|
||||
|
||||
public OnlySingleItemsRepositoryCachePolicyFactory(IRuntimeCacheProvider runtimeCache, RepositoryCachePolicyOptions options)
|
||||
{
|
||||
_runtimeCache = runtimeCache;
|
||||
_options = options;
|
||||
}
|
||||
|
||||
public virtual IRepositoryCachePolicy<TEntity, TId> CreatePolicy()
|
||||
{
|
||||
return new SingleItemsOnlyRepositoryCachePolicy<TEntity, TId>(_runtimeCache, _options);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,45 +4,45 @@ using Umbraco.Core.Models.EntityBase;
|
||||
|
||||
namespace Umbraco.Core.Cache
|
||||
{
|
||||
internal abstract class RepositoryCachePolicyBase<TEntity, TId> : DisposableObject, IRepositoryCachePolicy<TEntity, TId>
|
||||
/// <summary>
|
||||
/// A base class for repository cache policies.
|
||||
/// </summary>
|
||||
/// <typeparam name="TEntity">The type of the entity.</typeparam>
|
||||
/// <typeparam name="TId">The type of the identifier.</typeparam>
|
||||
internal abstract class RepositoryCachePolicyBase<TEntity, TId> : IRepositoryCachePolicy<TEntity, TId>
|
||||
where TEntity : class, IAggregateRoot
|
||||
{
|
||||
private Action _action;
|
||||
|
||||
protected RepositoryCachePolicyBase(IRuntimeCacheProvider cache)
|
||||
{
|
||||
if (cache == null) throw new ArgumentNullException("cache");
|
||||
|
||||
if (cache == null) throw new ArgumentNullException("cache");
|
||||
Cache = cache;
|
||||
}
|
||||
|
||||
protected IRuntimeCacheProvider Cache { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The disposal performs the caching
|
||||
/// </summary>
|
||||
protected override void DisposeResources()
|
||||
{
|
||||
if (_action != null)
|
||||
{
|
||||
_action();
|
||||
}
|
||||
}
|
||||
/// <inheritdoc />
|
||||
public abstract TEntity Get(TId id, Func<TId, TEntity> performGet, Func<TId[], IEnumerable<TEntity>> performGetAll);
|
||||
|
||||
/// <summary>
|
||||
/// Sets the action to execute on disposal
|
||||
/// </summary>
|
||||
/// <param name="action"></param>
|
||||
protected void SetCacheAction(Action action)
|
||||
{
|
||||
_action = action;
|
||||
}
|
||||
/// <inheritdoc />
|
||||
public abstract TEntity GetCached(TId id);
|
||||
|
||||
/// <inheritdoc />
|
||||
public abstract bool Exists(TId id, Func<TId, bool> performExists, Func<TId[], IEnumerable<TEntity>> performGetAll);
|
||||
|
||||
/// <inheritdoc />
|
||||
public abstract void Create(TEntity entity, Action<TEntity> persistNew);
|
||||
|
||||
/// <inheritdoc />
|
||||
public abstract void Update(TEntity entity, Action<TEntity> persistUpdated);
|
||||
|
||||
/// <inheritdoc />
|
||||
public abstract void Delete(TEntity entity, Action<TEntity> persistDeleted);
|
||||
|
||||
/// <inheritdoc />
|
||||
public abstract TEntity[] GetAll(TId[] ids, Func<TId[], IEnumerable<TEntity>> performGetAll);
|
||||
|
||||
/// <inheritdoc />
|
||||
public abstract void ClearAll();
|
||||
|
||||
public abstract TEntity Get(TId id, Func<TId, TEntity> getFromRepo);
|
||||
public abstract TEntity Get(TId id);
|
||||
public abstract bool Exists(TId id, Func<TId, bool> getFromRepo);
|
||||
public abstract void CreateOrUpdate(TEntity entity, Action<TEntity> persistMethod);
|
||||
public abstract void Remove(TEntity entity, Action<TEntity> persistMethod);
|
||||
public abstract TEntity[] GetAll(TId[] ids, Func<TId[], IEnumerable<TEntity>> getFromRepo);
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,9 @@ using System;
|
||||
|
||||
namespace Umbraco.Core.Cache
|
||||
{
|
||||
/// <summary>
|
||||
/// Specifies how a repository cache policy should cache entities.
|
||||
/// </summary>
|
||||
internal class RepositoryCachePolicyOptions
|
||||
{
|
||||
/// <summary>
|
||||
|
||||
@@ -1,24 +1,27 @@
|
||||
using System.Linq;
|
||||
using Umbraco.Core.Collections;
|
||||
using Umbraco.Core.Models.EntityBase;
|
||||
|
||||
namespace Umbraco.Core.Cache
|
||||
{
|
||||
/// <summary>
|
||||
/// A caching policy that ignores all caches for GetAll - it will only cache calls for individual items
|
||||
/// Represents a special policy that does not cache the result of GetAll.
|
||||
/// </summary>
|
||||
/// <typeparam name="TEntity"></typeparam>
|
||||
/// <typeparam name="TId"></typeparam>
|
||||
/// <typeparam name="TEntity">The type of the entity.</typeparam>
|
||||
/// <typeparam name="TId">The type of the identifier.</typeparam>
|
||||
/// <remarks>
|
||||
/// <para>Overrides the default repository cache policy and does not writes the result of GetAll
|
||||
/// to cache, but only the result of individual Gets. It does read the cache for GetAll, though.</para>
|
||||
/// <para>Used by DictionaryRepository.</para>
|
||||
/// </remarks>
|
||||
internal class SingleItemsOnlyRepositoryCachePolicy<TEntity, TId> : DefaultRepositoryCachePolicy<TEntity, TId>
|
||||
where TEntity : class, IAggregateRoot
|
||||
{
|
||||
public SingleItemsOnlyRepositoryCachePolicy(IRuntimeCacheProvider cache, RepositoryCachePolicyOptions options) : base(cache, options)
|
||||
public SingleItemsOnlyRepositoryCachePolicy(IRuntimeCacheProvider cache, RepositoryCachePolicyOptions options)
|
||||
: base(cache, options)
|
||||
{ }
|
||||
|
||||
protected override void InsertEntities(TId[] ids, TEntity[] entities)
|
||||
{
|
||||
}
|
||||
|
||||
protected override void SetCacheAction(TId[] ids, TEntity[] entityCollection)
|
||||
{
|
||||
//no-op
|
||||
// nop
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,19 +2,13 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Umbraco.Core.Cache;
|
||||
using Umbraco.Core.Events;
|
||||
using Umbraco.Core.Exceptions;
|
||||
using Umbraco.Core.Logging;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Models.EntityBase;
|
||||
using Umbraco.Core.Models.Rdbms;
|
||||
|
||||
using Umbraco.Core.Persistence.Factories;
|
||||
using Umbraco.Core.Persistence.Querying;
|
||||
using Umbraco.Core.Persistence.Relators;
|
||||
using Umbraco.Core.Persistence.SqlSyntax;
|
||||
using Umbraco.Core.Persistence.UnitOfWork;
|
||||
using Umbraco.Core.Services;
|
||||
|
||||
namespace Umbraco.Core.Persistence.Repositories
|
||||
{
|
||||
@@ -24,6 +18,7 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
internal class ContentTypeRepository : ContentTypeBaseRepository<IContentType>, IContentTypeRepository
|
||||
{
|
||||
private readonly ITemplateRepository _templateRepository;
|
||||
private IRepositoryCachePolicy<IContentType, int> _cachePolicy;
|
||||
|
||||
public ContentTypeRepository(IDatabaseUnitOfWork work, CacheHelper cache, ILogger logger, ISqlSyntaxProvider sqlSyntax, ITemplateRepository templateRepository)
|
||||
: base(work, cache, logger, sqlSyntax)
|
||||
@@ -31,16 +26,11 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
_templateRepository = templateRepository;
|
||||
}
|
||||
|
||||
private FullDataSetRepositoryCachePolicyFactory<IContentType, int> _cachePolicyFactory;
|
||||
protected override IRepositoryCachePolicyFactory<IContentType, int> CachePolicyFactory
|
||||
protected override IRepositoryCachePolicy<IContentType, int> CachePolicy
|
||||
{
|
||||
get
|
||||
{
|
||||
//Use a FullDataSet cache policy - this will cache the entire GetAll result in a single collection
|
||||
return _cachePolicyFactory ?? (_cachePolicyFactory = new FullDataSetRepositoryCachePolicyFactory<IContentType, int>(
|
||||
RuntimeCache, GetEntityId, () => PerformGetAll(),
|
||||
//allow this cache to expire
|
||||
expires:true));
|
||||
return _cachePolicy ?? (_cachePolicy = new FullDataSetRepositoryCachePolicy<IContentType, int>(RuntimeCache, GetEntityId, /*expires:*/ true));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -20,24 +20,27 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
/// </summary>
|
||||
internal class DictionaryRepository : PetaPocoRepositoryBase<int, IDictionaryItem>, IDictionaryRepository
|
||||
{
|
||||
private IRepositoryCachePolicy<IDictionaryItem, int> _cachePolicy;
|
||||
|
||||
public DictionaryRepository(IDatabaseUnitOfWork work, CacheHelper cache, ILogger logger, ISqlSyntaxProvider syntax)
|
||||
: base(work, cache, logger, syntax)
|
||||
{
|
||||
}
|
||||
{ }
|
||||
|
||||
private IRepositoryCachePolicyFactory<IDictionaryItem, int> _cachePolicyFactory;
|
||||
protected override IRepositoryCachePolicyFactory<IDictionaryItem, int> CachePolicyFactory
|
||||
protected override IRepositoryCachePolicy<IDictionaryItem, int> CachePolicy
|
||||
{
|
||||
get
|
||||
{
|
||||
//custom cache policy which will not cache any results for GetAll
|
||||
return _cachePolicyFactory ?? (_cachePolicyFactory = new OnlySingleItemsRepositoryCachePolicyFactory<IDictionaryItem, int>(
|
||||
RuntimeCache,
|
||||
new RepositoryCachePolicyOptions
|
||||
{
|
||||
//allow zero to be cached
|
||||
GetAllCacheAllowZeroCount = true
|
||||
}));
|
||||
if (_cachePolicy != null) return _cachePolicy;
|
||||
|
||||
var options = new RepositoryCachePolicyOptions
|
||||
{
|
||||
//allow zero to be cached
|
||||
GetAllCacheAllowZeroCount = true
|
||||
};
|
||||
|
||||
_cachePolicy = new SingleItemsOnlyRepositoryCachePolicy<IDictionaryItem, int>(RuntimeCache, options);
|
||||
|
||||
return _cachePolicy;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -52,7 +55,7 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
var dto = Database.Fetch<DictionaryDto, LanguageTextDto, DictionaryDto>(new DictionaryLanguageTextRelator().Map, sql).FirstOrDefault();
|
||||
if (dto == null)
|
||||
return null;
|
||||
|
||||
|
||||
var entity = ConvertFromDto(dto);
|
||||
|
||||
//on initial construction we don't want to have dirty properties tracked
|
||||
@@ -80,7 +83,7 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
var translator = new SqlTranslator<IDictionaryItem>(sqlClause, query);
|
||||
var sql = translator.Translate();
|
||||
sql.OrderBy<DictionaryDto>(x => x.UniqueId, SqlSyntax);
|
||||
|
||||
|
||||
return Database.Fetch<DictionaryDto, LanguageTextDto, DictionaryDto>(new DictionaryLanguageTextRelator().Map, sql)
|
||||
.Select(x => ConvertFromDto(x));
|
||||
}
|
||||
@@ -149,7 +152,7 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
translation.Key = dictionaryItem.Key;
|
||||
}
|
||||
|
||||
dictionaryItem.ResetDirtyProperties();
|
||||
dictionaryItem.ResetDirtyProperties();
|
||||
}
|
||||
|
||||
protected override void PersistUpdatedItem(IDictionaryItem entity)
|
||||
@@ -223,7 +226,7 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
|
||||
var list = new List<IDictionaryTranslation>();
|
||||
foreach (var textDto in dto.LanguageTextDtos)
|
||||
{
|
||||
{
|
||||
if (textDto.LanguageId <= 0)
|
||||
continue;
|
||||
|
||||
@@ -250,7 +253,7 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
return keyRepo.Get(key);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private IEnumerable<IDictionaryItem> GetRootDictionaryItems()
|
||||
{
|
||||
var query = Query<IDictionaryItem>.Builder.Where(x => x.ParentId == null);
|
||||
@@ -291,6 +294,7 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
|
||||
private class DictionaryByUniqueIdRepository : SimpleGetRepository<Guid, IDictionaryItem, DictionaryDto>
|
||||
{
|
||||
private IRepositoryCachePolicy<IDictionaryItem, Guid> _cachePolicy;
|
||||
private readonly DictionaryRepository _dictionaryRepository;
|
||||
|
||||
public DictionaryByUniqueIdRepository(DictionaryRepository dictionaryRepository, IDatabaseUnitOfWork work, CacheHelper cache, ILogger logger, ISqlSyntaxProvider sqlSyntax)
|
||||
@@ -329,25 +333,28 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
return "cmsDictionary." + SqlSyntax.GetQuotedColumnName("id") + " in (@ids)";
|
||||
}
|
||||
|
||||
private IRepositoryCachePolicyFactory<IDictionaryItem, Guid> _cachePolicyFactory;
|
||||
protected override IRepositoryCachePolicyFactory<IDictionaryItem, Guid> CachePolicyFactory
|
||||
protected override IRepositoryCachePolicy<IDictionaryItem, Guid> CachePolicy
|
||||
{
|
||||
get
|
||||
{
|
||||
//custom cache policy which will not cache any results for GetAll
|
||||
return _cachePolicyFactory ?? (_cachePolicyFactory = new OnlySingleItemsRepositoryCachePolicyFactory<IDictionaryItem, Guid>(
|
||||
RuntimeCache,
|
||||
new RepositoryCachePolicyOptions
|
||||
{
|
||||
//allow zero to be cached
|
||||
GetAllCacheAllowZeroCount = true
|
||||
}));
|
||||
if (_cachePolicy != null) return _cachePolicy;
|
||||
|
||||
var options = new RepositoryCachePolicyOptions
|
||||
{
|
||||
//allow zero to be cached
|
||||
GetAllCacheAllowZeroCount = true
|
||||
};
|
||||
|
||||
_cachePolicy = new SingleItemsOnlyRepositoryCachePolicy<IDictionaryItem, Guid>(RuntimeCache, options);
|
||||
|
||||
return _cachePolicy;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class DictionaryByKeyRepository : SimpleGetRepository<string, IDictionaryItem, DictionaryDto>
|
||||
{
|
||||
private IRepositoryCachePolicy<IDictionaryItem, string> _cachePolicy;
|
||||
private readonly DictionaryRepository _dictionaryRepository;
|
||||
|
||||
public DictionaryByKeyRepository(DictionaryRepository dictionaryRepository, IDatabaseUnitOfWork work, CacheHelper cache, ILogger logger, ISqlSyntaxProvider sqlSyntax)
|
||||
@@ -386,23 +393,23 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
return "cmsDictionary." + SqlSyntax.GetQuotedColumnName("key") + " in (@ids)";
|
||||
}
|
||||
|
||||
private IRepositoryCachePolicyFactory<IDictionaryItem, string> _cachePolicyFactory;
|
||||
protected override IRepositoryCachePolicyFactory<IDictionaryItem, string> CachePolicyFactory
|
||||
protected override IRepositoryCachePolicy<IDictionaryItem, string> CachePolicy
|
||||
{
|
||||
get
|
||||
{
|
||||
//custom cache policy which will not cache any results for GetAll
|
||||
return _cachePolicyFactory ?? (_cachePolicyFactory = new OnlySingleItemsRepositoryCachePolicyFactory<IDictionaryItem, string>(
|
||||
RuntimeCache,
|
||||
new RepositoryCachePolicyOptions
|
||||
{
|
||||
//allow zero to be cached
|
||||
GetAllCacheAllowZeroCount = true
|
||||
}));
|
||||
if (_cachePolicy != null) return _cachePolicy;
|
||||
|
||||
var options = new RepositoryCachePolicyOptions
|
||||
{
|
||||
//allow zero to be cached
|
||||
GetAllCacheAllowZeroCount = true
|
||||
};
|
||||
|
||||
_cachePolicy = new SingleItemsOnlyRepositoryCachePolicy<IDictionaryItem, string>(RuntimeCache, options);
|
||||
|
||||
return _cachePolicy;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -18,19 +18,22 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
|
||||
internal class DomainRepository : PetaPocoRepositoryBase<int, IDomain>, IDomainRepository
|
||||
{
|
||||
private IRepositoryCachePolicy<IDomain, int> _cachePolicy;
|
||||
|
||||
public DomainRepository(IDatabaseUnitOfWork work, CacheHelper cache, ILogger logger, ISqlSyntaxProvider sqlSyntax)
|
||||
: base(work, cache, logger, sqlSyntax)
|
||||
{
|
||||
}
|
||||
|
||||
private FullDataSetRepositoryCachePolicyFactory<IDomain, int> _cachePolicyFactory;
|
||||
protected override IRepositoryCachePolicyFactory<IDomain, int> CachePolicyFactory
|
||||
protected override IRepositoryCachePolicy<IDomain, int> CachePolicy
|
||||
{
|
||||
get
|
||||
{
|
||||
//Use a FullDataSet cache policy - this will cache the entire GetAll result in a single collection
|
||||
return _cachePolicyFactory ?? (_cachePolicyFactory = new FullDataSetRepositoryCachePolicyFactory<IDomain, int>(
|
||||
RuntimeCache, GetEntityId, () => PerformGetAll(), false));
|
||||
if (_cachePolicy != null) return _cachePolicy;
|
||||
|
||||
_cachePolicy = new FullDataSetRepositoryCachePolicy<IDomain, int>(RuntimeCache, GetEntityId, /*expires:*/ false);
|
||||
|
||||
return _cachePolicy;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -19,19 +19,22 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
/// </summary>
|
||||
internal class LanguageRepository : PetaPocoRepositoryBase<int, ILanguage>, ILanguageRepository
|
||||
{
|
||||
private IRepositoryCachePolicy<ILanguage, int> _cachePolicy;
|
||||
|
||||
public LanguageRepository(IDatabaseUnitOfWork work, CacheHelper cache, ILogger logger, ISqlSyntaxProvider sqlSyntax)
|
||||
: base(work, cache, logger, sqlSyntax)
|
||||
{
|
||||
}
|
||||
|
||||
private FullDataSetRepositoryCachePolicyFactory<ILanguage, int> _cachePolicyFactory;
|
||||
protected override IRepositoryCachePolicyFactory<ILanguage, int> CachePolicyFactory
|
||||
protected override IRepositoryCachePolicy<ILanguage, int> CachePolicy
|
||||
{
|
||||
get
|
||||
{
|
||||
//Use a FullDataSet cache policy - this will cache the entire GetAll result in a single collection
|
||||
return _cachePolicyFactory ?? (_cachePolicyFactory = new FullDataSetRepositoryCachePolicyFactory<ILanguage, int>(
|
||||
RuntimeCache, GetEntityId, () => PerformGetAll(), false));
|
||||
if (_cachePolicy != null) return _cachePolicy;
|
||||
|
||||
_cachePolicy = new FullDataSetRepositoryCachePolicy<ILanguage, int>(RuntimeCache, GetEntityId, /*expires:*/ false);
|
||||
|
||||
return _cachePolicy;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -22,22 +22,22 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
/// </summary>
|
||||
internal class MediaTypeRepository : ContentTypeBaseRepository<IMediaType>, IMediaTypeRepository
|
||||
{
|
||||
private IRepositoryCachePolicy<IMediaType, int> _cachePolicy;
|
||||
|
||||
public MediaTypeRepository(IDatabaseUnitOfWork work, CacheHelper cache, ILogger logger, ISqlSyntaxProvider sqlSyntax)
|
||||
: base(work, cache, logger, sqlSyntax)
|
||||
{
|
||||
}
|
||||
|
||||
private FullDataSetRepositoryCachePolicyFactory<IMediaType, int> _cachePolicyFactory;
|
||||
protected override IRepositoryCachePolicyFactory<IMediaType, int> CachePolicyFactory
|
||||
protected override IRepositoryCachePolicy<IMediaType, int> CachePolicy
|
||||
{
|
||||
get
|
||||
{
|
||||
//Use a FullDataSet cache policy - this will cache the entire GetAll result in a single collection
|
||||
return _cachePolicyFactory ?? (_cachePolicyFactory = new FullDataSetRepositoryCachePolicyFactory<IMediaType, int>(
|
||||
RuntimeCache, GetEntityId, () => PerformGetAll(),
|
||||
//allow this cache to expire
|
||||
expires: true));
|
||||
if (_cachePolicy != null) return _cachePolicy;
|
||||
|
||||
_cachePolicy = new FullDataSetRepositoryCachePolicy<IMediaType, int>(RuntimeCache, GetEntityId, /*expires:*/ true);
|
||||
|
||||
return _cachePolicy;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -21,25 +21,25 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
/// </summary>
|
||||
internal class MemberTypeRepository : ContentTypeBaseRepository<IMemberType>, IMemberTypeRepository
|
||||
{
|
||||
private IRepositoryCachePolicy<IMemberType, int> _cachePolicy;
|
||||
|
||||
public MemberTypeRepository(IDatabaseUnitOfWork work, CacheHelper cache, ILogger logger, ISqlSyntaxProvider sqlSyntax)
|
||||
: base(work, cache, logger, sqlSyntax)
|
||||
{
|
||||
}
|
||||
|
||||
private FullDataSetRepositoryCachePolicyFactory<IMemberType, int> _cachePolicyFactory;
|
||||
protected override IRepositoryCachePolicyFactory<IMemberType, int> CachePolicyFactory
|
||||
protected override IRepositoryCachePolicy<IMemberType, int> CachePolicy
|
||||
{
|
||||
get
|
||||
{
|
||||
//Use a FullDataSet cache policy - this will cache the entire GetAll result in a single collection
|
||||
return _cachePolicyFactory ?? (_cachePolicyFactory = new FullDataSetRepositoryCachePolicyFactory<IMemberType, int>(
|
||||
RuntimeCache, GetEntityId, () => PerformGetAll(),
|
||||
//allow this cache to expire
|
||||
expires: true));
|
||||
if (_cachePolicy != null) return _cachePolicy;
|
||||
|
||||
_cachePolicy = new FullDataSetRepositoryCachePolicy<IMemberType, int>(RuntimeCache, GetEntityId, /*expires:*/ true);
|
||||
|
||||
return _cachePolicy;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
protected override IMemberType PerformGet(int id)
|
||||
{
|
||||
//use the underlying GetAll which will force cache all content types
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data.SqlServerCe;
|
||||
using Umbraco.Core.Logging;
|
||||
using Umbraco.Core.Models.EntityBase;
|
||||
|
||||
|
||||
@@ -15,18 +15,21 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
{
|
||||
internal class PublicAccessRepository : PetaPocoRepositoryBase<Guid, PublicAccessEntry>, IPublicAccessRepository
|
||||
{
|
||||
private IRepositoryCachePolicy<PublicAccessEntry, Guid> _cachePolicy;
|
||||
|
||||
public PublicAccessRepository(IDatabaseUnitOfWork work, CacheHelper cache, ILogger logger, ISqlSyntaxProvider sqlSyntax)
|
||||
: base(work, cache, logger, sqlSyntax)
|
||||
{ }
|
||||
|
||||
private FullDataSetRepositoryCachePolicyFactory<PublicAccessEntry, Guid> _cachePolicyFactory;
|
||||
protected override IRepositoryCachePolicyFactory<PublicAccessEntry, Guid> CachePolicyFactory
|
||||
protected override IRepositoryCachePolicy<PublicAccessEntry, Guid> CachePolicy
|
||||
{
|
||||
get
|
||||
{
|
||||
//Use a FullDataSet cache policy - this will cache the entire GetAll result in a single collection
|
||||
return _cachePolicyFactory ?? (_cachePolicyFactory = new FullDataSetRepositoryCachePolicyFactory<PublicAccessEntry, Guid>(
|
||||
RuntimeCache, GetEntityId, () => PerformGetAll(), false));
|
||||
if (_cachePolicy != null) return _cachePolicy;
|
||||
|
||||
_cachePolicy = new FullDataSetRepositoryCachePolicy<PublicAccessEntry, Guid>(RuntimeCache, GetEntityId, /*expires:*/ false);
|
||||
|
||||
return _cachePolicy;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -19,20 +19,21 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
/// </summary>
|
||||
internal class RelationTypeRepository : PetaPocoRepositoryBase<int, IRelationType>, IRelationTypeRepository
|
||||
{
|
||||
private IRepositoryCachePolicy<IRelationType, int> _cachePolicy;
|
||||
|
||||
public RelationTypeRepository(IDatabaseUnitOfWork work, CacheHelper cache, ILogger logger, ISqlSyntaxProvider sqlSyntax)
|
||||
: base(work, cache, logger, sqlSyntax)
|
||||
{ }
|
||||
|
||||
// assuming we don't have tons of relation types, use a FullDataSet policy, ie
|
||||
// cache the entire GetAll result once in a single collection - which can expire
|
||||
private FullDataSetRepositoryCachePolicyFactory<IRelationType, int> _cachePolicyFactory;
|
||||
protected override IRepositoryCachePolicyFactory<IRelationType, int> CachePolicyFactory
|
||||
protected override IRepositoryCachePolicy<IRelationType, int> CachePolicy
|
||||
{
|
||||
get
|
||||
{
|
||||
return _cachePolicyFactory
|
||||
?? (_cachePolicyFactory = new FullDataSetRepositoryCachePolicyFactory<IRelationType, int>(
|
||||
RuntimeCache, GetEntityId, () => PerformGetAll(), expires: true));
|
||||
if (_cachePolicy != null) return _cachePolicy;
|
||||
|
||||
_cachePolicy = new FullDataSetRepositoryCachePolicy<IRelationType, int>(RuntimeCache, GetEntityId, /*expires:*/ true);
|
||||
|
||||
return _cachePolicy;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -82,7 +82,6 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
#region Static Queries
|
||||
|
||||
private IQuery<TEntity> _hasIdQuery;
|
||||
@@ -102,30 +101,25 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
get { return RepositoryCache.IsolatedRuntimeCache.GetOrCreateCache<TEntity>(); }
|
||||
}
|
||||
|
||||
private IRepositoryCachePolicyFactory<TEntity, TId> _cachePolicyFactory;
|
||||
/// <summary>
|
||||
/// Returns the Cache Policy for the repository
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The Cache Policy determines how each entity or entity collection is cached
|
||||
/// </remarks>
|
||||
protected virtual IRepositoryCachePolicyFactory<TEntity, TId> CachePolicyFactory
|
||||
private IRepositoryCachePolicy<TEntity, TId> _cachePolicy;
|
||||
|
||||
protected virtual IRepositoryCachePolicy<TEntity, TId> CachePolicy
|
||||
{
|
||||
get
|
||||
{
|
||||
return _cachePolicyFactory ?? (_cachePolicyFactory = new DefaultRepositoryCachePolicyFactory<TEntity, TId>(
|
||||
RuntimeCache,
|
||||
new RepositoryCachePolicyOptions(() =>
|
||||
{
|
||||
//create it once if it is needed (no need for locking here)
|
||||
if (_hasIdQuery == null)
|
||||
{
|
||||
_hasIdQuery = Query<TEntity>.Builder.Where(x => x.Id != 0);
|
||||
}
|
||||
if (_cachePolicy != null) return _cachePolicy;
|
||||
|
||||
//Get count of all entities of current type (TEntity) to ensure cached result is correct
|
||||
return PerformCount(_hasIdQuery);
|
||||
})));
|
||||
var options = new RepositoryCachePolicyOptions(() =>
|
||||
{
|
||||
//Get count of all entities of current type (TEntity) to ensure cached result is correct
|
||||
//create query once if it is needed (no need for locking here)
|
||||
var query = _hasIdQuery ?? (_hasIdQuery = Query<TEntity>.Builder.Where(x => x.Id != 0));
|
||||
return PerformCount(query);
|
||||
});
|
||||
|
||||
_cachePolicy = new DefaultRepositoryCachePolicy<TEntity, TId>(RuntimeCache, options);
|
||||
|
||||
return _cachePolicy;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -166,10 +160,7 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
/// <returns></returns>
|
||||
public TEntity Get(TId id)
|
||||
{
|
||||
using (var p = CachePolicyFactory.CreatePolicy())
|
||||
{
|
||||
return p.Get(id, PerformGet);
|
||||
}
|
||||
return CachePolicy.Get(id, PerformGet, PerformGetAll);
|
||||
}
|
||||
|
||||
protected abstract IEnumerable<TEntity> PerformGetAll(params TId[] ids);
|
||||
@@ -192,13 +183,9 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
throw new InvalidOperationException("Cannot perform a query with more than 2000 parameters");
|
||||
}
|
||||
|
||||
using (var p = CachePolicyFactory.CreatePolicy())
|
||||
{
|
||||
var result = p.GetAll(ids, PerformGetAll);
|
||||
return result;
|
||||
}
|
||||
return CachePolicy.GetAll(ids, PerformGetAll);
|
||||
}
|
||||
|
||||
|
||||
protected abstract IEnumerable<TEntity> PerformGetByQuery(IQuery<TEntity> query);
|
||||
/// <summary>
|
||||
/// Gets a list of entities by the passed in query
|
||||
@@ -220,10 +207,7 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
/// <returns></returns>
|
||||
public bool Exists(TId id)
|
||||
{
|
||||
using (var p = CachePolicyFactory.CreatePolicy())
|
||||
{
|
||||
return p.Exists(id, PerformExists);
|
||||
}
|
||||
return CachePolicy.Exists(id, PerformExists, PerformGetAll);
|
||||
}
|
||||
|
||||
protected abstract int PerformCount(IQuery<TEntity> query);
|
||||
@@ -236,19 +220,14 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
{
|
||||
return PerformCount(query);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Unit of work method that tells the repository to persist the new entity
|
||||
/// </summary>
|
||||
/// <param name="entity"></param>
|
||||
public virtual void PersistNewItem(IEntity entity)
|
||||
{
|
||||
var casted = (TEntity)entity;
|
||||
|
||||
using (var p = CachePolicyFactory.CreatePolicy())
|
||||
{
|
||||
p.CreateOrUpdate(casted, PersistNewItem);
|
||||
}
|
||||
CachePolicy.Create((TEntity) entity, PersistNewItem);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -257,12 +236,7 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
/// <param name="entity"></param>
|
||||
public virtual void PersistUpdatedItem(IEntity entity)
|
||||
{
|
||||
var casted = (TEntity)entity;
|
||||
|
||||
using (var p = CachePolicyFactory.CreatePolicy())
|
||||
{
|
||||
p.CreateOrUpdate(casted, PersistUpdatedItem);
|
||||
}
|
||||
CachePolicy.Update((TEntity) entity, PersistUpdatedItem);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -271,20 +245,13 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
/// <param name="entity"></param>
|
||||
public virtual void PersistDeletedItem(IEntity entity)
|
||||
{
|
||||
var casted = (TEntity)entity;
|
||||
|
||||
using (var p = CachePolicyFactory.CreatePolicy())
|
||||
{
|
||||
p.Remove(casted, PersistDeletedItem);
|
||||
}
|
||||
CachePolicy.Delete((TEntity) entity, PersistDeletedItem);
|
||||
}
|
||||
|
||||
|
||||
protected abstract void PersistNewItem(TEntity item);
|
||||
protected abstract void PersistUpdatedItem(TEntity item);
|
||||
protected abstract void PersistDeletedItem(TEntity item);
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Dispose disposable properties
|
||||
/// </summary>
|
||||
|
||||
@@ -33,6 +33,7 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
private readonly ITemplatesSection _templateConfig;
|
||||
private readonly ViewHelper _viewHelper;
|
||||
private readonly MasterPageHelper _masterPageHelper;
|
||||
private IRepositoryCachePolicy<ITemplate, int> _cachePolicy;
|
||||
|
||||
internal TemplateRepository(IDatabaseUnitOfWork work, CacheHelper cache, ILogger logger, ISqlSyntaxProvider sqlSyntax, IFileSystem masterpageFileSystem, IFileSystem viewFileSystem, ITemplatesSection templateConfig)
|
||||
: base(work, cache, logger, sqlSyntax)
|
||||
@@ -45,14 +46,15 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
}
|
||||
|
||||
|
||||
private FullDataSetRepositoryCachePolicyFactory<ITemplate, int> _cachePolicyFactory;
|
||||
protected override IRepositoryCachePolicyFactory<ITemplate, int> CachePolicyFactory
|
||||
protected override IRepositoryCachePolicy<ITemplate, int> CachePolicy
|
||||
{
|
||||
get
|
||||
{
|
||||
//Use a FullDataSet cache policy - this will cache the entire GetAll result in a single collection
|
||||
return _cachePolicyFactory ?? (_cachePolicyFactory = new FullDataSetRepositoryCachePolicyFactory<ITemplate, int>(
|
||||
RuntimeCache, GetEntityId, () => PerformGetAll(), false));
|
||||
if (_cachePolicy != null) return _cachePolicy;
|
||||
|
||||
_cachePolicy = new FullDataSetRepositoryCachePolicy<ITemplate, int>(RuntimeCache, GetEntityId, /*expires:*/ false);
|
||||
|
||||
return _cachePolicy;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -136,9 +136,7 @@
|
||||
<Compile Include="Cache\CacheKeys.cs" />
|
||||
<Compile Include="Cache\CacheProviderExtensions.cs" />
|
||||
<Compile Include="Cache\DefaultRepositoryCachePolicy.cs" />
|
||||
<Compile Include="Cache\DefaultRepositoryCachePolicyFactory.cs" />
|
||||
<Compile Include="Cache\FullDataSetRepositoryCachePolicy.cs" />
|
||||
<Compile Include="Cache\FullDataSetRepositoryCachePolicyFactory.cs" />
|
||||
<Compile Include="Cache\ICacheProvider.cs" />
|
||||
<Compile Include="Cache\CacheRefresherBase.cs" />
|
||||
<Compile Include="Cache\CacheRefresherEventArgs.cs" />
|
||||
@@ -146,7 +144,6 @@
|
||||
<Compile Include="Cache\HttpRequestCacheProvider.cs" />
|
||||
<Compile Include="Cache\IRepositoryCachePolicy.cs" />
|
||||
<Compile Include="Cache\IPayloadCacheRefresher.cs" />
|
||||
<Compile Include="Cache\IRepositoryCachePolicyFactory.cs" />
|
||||
<Compile Include="Cache\IsolatedRuntimeCache.cs" />
|
||||
<Compile Include="Cache\ObjectCacheRuntimeCacheProvider.cs" />
|
||||
<Compile Include="Cache\IRuntimeCacheProvider.cs" />
|
||||
@@ -156,7 +153,6 @@
|
||||
<Compile Include="Cache\NullCacheProvider.cs" />
|
||||
<Compile Include="Cache\RepositoryCachePolicyBase.cs" />
|
||||
<Compile Include="Cache\SingleItemsOnlyRepositoryCachePolicy.cs" />
|
||||
<Compile Include="Cache\OnlySingleItemsRepositoryCachePolicyFactory.cs" />
|
||||
<Compile Include="Cache\PayloadCacheRefresherBase.cs" />
|
||||
<Compile Include="Cache\RepositoryCachePolicyOptions.cs" />
|
||||
<Compile Include="Cache\StaticCacheProvider.cs" />
|
||||
|
||||
@@ -24,10 +24,8 @@ namespace Umbraco.Tests.Cache
|
||||
});
|
||||
|
||||
var defaultPolicy = new DefaultRepositoryCachePolicy<AuditItem, object>(cache.Object, new RepositoryCachePolicyOptions());
|
||||
using (defaultPolicy)
|
||||
{
|
||||
var found = defaultPolicy.Get(1, o => new AuditItem(1, "blah", AuditType.Copy, 123));
|
||||
}
|
||||
|
||||
var found = defaultPolicy.Get(1, id => new AuditItem(1, "blah", AuditType.Copy, 123), o => null);
|
||||
Assert.IsTrue(isCached);
|
||||
}
|
||||
|
||||
@@ -38,11 +36,9 @@ namespace Umbraco.Tests.Cache
|
||||
cache.Setup(x => x.GetCacheItem(It.IsAny<string>())).Returns(new AuditItem(1, "blah", AuditType.Copy, 123));
|
||||
|
||||
var defaultPolicy = new DefaultRepositoryCachePolicy<AuditItem, object>(cache.Object, new RepositoryCachePolicyOptions());
|
||||
using (defaultPolicy)
|
||||
{
|
||||
var found = defaultPolicy.Get(1, o => (AuditItem) null);
|
||||
Assert.IsNotNull(found);
|
||||
}
|
||||
|
||||
var found = defaultPolicy.Get(1, id => null, ids => null);
|
||||
Assert.IsNotNull(found);
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -59,14 +55,12 @@ namespace Umbraco.Tests.Cache
|
||||
cache.Setup(x => x.GetCacheItemsByKeySearch(It.IsAny<string>())).Returns(new AuditItem[] {});
|
||||
|
||||
var defaultPolicy = new DefaultRepositoryCachePolicy<AuditItem, object>(cache.Object, new RepositoryCachePolicyOptions());
|
||||
using (defaultPolicy)
|
||||
|
||||
var found = defaultPolicy.GetAll(new object[] { }, ids => new[]
|
||||
{
|
||||
var found = defaultPolicy.GetAll(new object[] {}, o => new[]
|
||||
{
|
||||
new AuditItem(1, "blah", AuditType.Copy, 123),
|
||||
new AuditItem(2, "blah2", AuditType.Copy, 123)
|
||||
});
|
||||
}
|
||||
new AuditItem(1, "blah", AuditType.Copy, 123),
|
||||
new AuditItem(2, "blah2", AuditType.Copy, 123)
|
||||
});
|
||||
|
||||
Assert.AreEqual(2, cached.Count);
|
||||
}
|
||||
@@ -82,11 +76,9 @@ namespace Umbraco.Tests.Cache
|
||||
});
|
||||
|
||||
var defaultPolicy = new DefaultRepositoryCachePolicy<AuditItem, object>(cache.Object, new RepositoryCachePolicyOptions());
|
||||
using (defaultPolicy)
|
||||
{
|
||||
var found = defaultPolicy.GetAll(new object[] {}, o => new[] {(AuditItem) null});
|
||||
Assert.AreEqual(2, found.Length);
|
||||
}
|
||||
|
||||
var found = defaultPolicy.GetAll(new object[] { }, ids => new[] { (AuditItem)null });
|
||||
Assert.AreEqual(2, found.Length);
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -103,13 +95,7 @@ namespace Umbraco.Tests.Cache
|
||||
var defaultPolicy = new DefaultRepositoryCachePolicy<AuditItem, object>(cache.Object, new RepositoryCachePolicyOptions());
|
||||
try
|
||||
{
|
||||
using (defaultPolicy)
|
||||
{
|
||||
defaultPolicy.CreateOrUpdate(new AuditItem(1, "blah", AuditType.Copy, 123), item =>
|
||||
{
|
||||
throw new Exception("blah!");
|
||||
});
|
||||
}
|
||||
defaultPolicy.Update(new AuditItem(1, "blah", AuditType.Copy, 123), item => { throw new Exception("blah!"); });
|
||||
}
|
||||
catch
|
||||
{
|
||||
@@ -135,13 +121,7 @@ namespace Umbraco.Tests.Cache
|
||||
var defaultPolicy = new DefaultRepositoryCachePolicy<AuditItem, object>(cache.Object, new RepositoryCachePolicyOptions());
|
||||
try
|
||||
{
|
||||
using (defaultPolicy)
|
||||
{
|
||||
defaultPolicy.Remove(new AuditItem(1, "blah", AuditType.Copy, 123), item =>
|
||||
{
|
||||
throw new Exception("blah!");
|
||||
});
|
||||
}
|
||||
defaultPolicy.Delete(new AuditItem(1, "blah", AuditType.Copy, 123), item => { throw new Exception("blah!"); });
|
||||
}
|
||||
catch
|
||||
{
|
||||
|
||||
@@ -32,11 +32,9 @@ namespace Umbraco.Tests.Cache
|
||||
isCached = true;
|
||||
});
|
||||
|
||||
var defaultPolicy = new FullDataSetRepositoryCachePolicy<AuditItem, object>(cache.Object, item => item.Id, () => getAll, false);
|
||||
using (defaultPolicy)
|
||||
{
|
||||
var found = defaultPolicy.Get(1, o => new AuditItem(1, "blah", AuditType.Copy, 123));
|
||||
}
|
||||
var policy = new FullDataSetRepositoryCachePolicy<AuditItem, object>(cache.Object, item => item.Id, false);
|
||||
|
||||
var found = policy.Get(1, id => new AuditItem(1, "blah", AuditType.Copy, 123), ids => getAll);
|
||||
Assert.IsTrue(isCached);
|
||||
}
|
||||
|
||||
@@ -52,12 +50,10 @@ namespace Umbraco.Tests.Cache
|
||||
var cache = new Mock<IRuntimeCacheProvider>();
|
||||
cache.Setup(x => x.GetCacheItem(It.IsAny<string>())).Returns(new AuditItem(1, "blah", AuditType.Copy, 123));
|
||||
|
||||
var defaultPolicy = new FullDataSetRepositoryCachePolicy<AuditItem, object>(cache.Object, item => item.Id, () => getAll, false);
|
||||
using (defaultPolicy)
|
||||
{
|
||||
var found = defaultPolicy.Get(1, o => (AuditItem)null);
|
||||
Assert.IsNotNull(found);
|
||||
}
|
||||
var defaultPolicy = new FullDataSetRepositoryCachePolicy<AuditItem, object>(cache.Object, item => item.Id, false);
|
||||
|
||||
var found = defaultPolicy.Get(1, id => null, ids => getAll);
|
||||
Assert.IsNotNull(found);
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -84,21 +80,17 @@ namespace Umbraco.Tests.Cache
|
||||
return cached.Any() ? new DeepCloneableList<AuditItem>(ListCloneBehavior.CloneOnce) : null;
|
||||
});
|
||||
|
||||
var defaultPolicy = new FullDataSetRepositoryCachePolicy<AuditItem, object>(cache.Object, item => item.Id, () => getAll, false);
|
||||
using (defaultPolicy)
|
||||
{
|
||||
var found = defaultPolicy.GetAll(new object[] {}, o => getAll);
|
||||
}
|
||||
var policy = new FullDataSetRepositoryCachePolicy<AuditItem, object>(cache.Object, item => item.Id, false);
|
||||
|
||||
var found = policy.GetAll(new object[] { }, ids => getAll);
|
||||
|
||||
Assert.AreEqual(1, cached.Count);
|
||||
Assert.IsNotNull(list);
|
||||
|
||||
//Do it again, ensure that its coming from the cache!
|
||||
defaultPolicy = new FullDataSetRepositoryCachePolicy<AuditItem, object>(cache.Object, item => item.Id, () => getAll, false);
|
||||
using (defaultPolicy)
|
||||
{
|
||||
var found = defaultPolicy.GetAll(new object[] { }, o => getAll);
|
||||
}
|
||||
policy = new FullDataSetRepositoryCachePolicy<AuditItem, object>(cache.Object, item => item.Id, false);
|
||||
|
||||
found = policy.GetAll(new object[] { }, ids => getAll);
|
||||
|
||||
Assert.AreEqual(1, cached.Count);
|
||||
Assert.IsNotNull(list);
|
||||
@@ -127,11 +119,9 @@ namespace Umbraco.Tests.Cache
|
||||
});
|
||||
cache.Setup(x => x.GetCacheItem(It.IsAny<string>())).Returns(new AuditItem[] { });
|
||||
|
||||
var defaultPolicy = new FullDataSetRepositoryCachePolicy<AuditItem, object>(cache.Object, item => item.Id, () => getAll, false);
|
||||
using (defaultPolicy)
|
||||
{
|
||||
var found = defaultPolicy.GetAll(new object[] { }, o => getAll);
|
||||
}
|
||||
var defaultPolicy = new FullDataSetRepositoryCachePolicy<AuditItem, object>(cache.Object, item => item.Id, false);
|
||||
|
||||
var found = defaultPolicy.GetAll(new object[] { }, ids => getAll);
|
||||
|
||||
Assert.AreEqual(1, cached.Count);
|
||||
Assert.IsNotNull(list);
|
||||
@@ -150,12 +140,10 @@ namespace Umbraco.Tests.Cache
|
||||
new AuditItem(2, "blah2", AuditType.Copy, 123)
|
||||
});
|
||||
|
||||
var defaultPolicy = new FullDataSetRepositoryCachePolicy<AuditItem, object>(cache.Object, item => item.Id, () => getAll, false);
|
||||
using (defaultPolicy)
|
||||
{
|
||||
var found = defaultPolicy.GetAll(new object[] { }, o => getAll);
|
||||
Assert.AreEqual(2, found.Length);
|
||||
}
|
||||
var defaultPolicy = new FullDataSetRepositoryCachePolicy<AuditItem, object>(cache.Object, item => item.Id, false);
|
||||
|
||||
var found = defaultPolicy.GetAll(new object[] { }, ids => getAll);
|
||||
Assert.AreEqual(2, found.Length);
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -175,16 +163,10 @@ namespace Umbraco.Tests.Cache
|
||||
cacheCleared = true;
|
||||
});
|
||||
|
||||
var defaultPolicy = new FullDataSetRepositoryCachePolicy<AuditItem, object>(cache.Object, item => item.Id, () => getAll, false);
|
||||
var defaultPolicy = new FullDataSetRepositoryCachePolicy<AuditItem, object>(cache.Object, item => item.Id, false);
|
||||
try
|
||||
{
|
||||
using (defaultPolicy)
|
||||
{
|
||||
defaultPolicy.CreateOrUpdate(new AuditItem(1, "blah", AuditType.Copy, 123), item =>
|
||||
{
|
||||
throw new Exception("blah!");
|
||||
});
|
||||
}
|
||||
defaultPolicy.Update(new AuditItem(1, "blah", AuditType.Copy, 123), item => { throw new Exception("blah!"); });
|
||||
}
|
||||
catch
|
||||
{
|
||||
@@ -213,16 +195,10 @@ namespace Umbraco.Tests.Cache
|
||||
cacheCleared = true;
|
||||
});
|
||||
|
||||
var defaultPolicy = new FullDataSetRepositoryCachePolicy<AuditItem, object>(cache.Object, item => item.Id, () => getAll, false);
|
||||
var defaultPolicy = new FullDataSetRepositoryCachePolicy<AuditItem, object>(cache.Object, item => item.Id, false);
|
||||
try
|
||||
{
|
||||
using (defaultPolicy)
|
||||
{
|
||||
defaultPolicy.Remove(new AuditItem(1, "blah", AuditType.Copy, 123), item =>
|
||||
{
|
||||
throw new Exception("blah!");
|
||||
});
|
||||
}
|
||||
defaultPolicy.Delete(new AuditItem(1, "blah", AuditType.Copy, 123), item => { throw new Exception("blah!"); });
|
||||
}
|
||||
catch
|
||||
{
|
||||
|
||||
@@ -25,14 +25,12 @@ namespace Umbraco.Tests.Cache
|
||||
cache.Setup(x => x.GetCacheItemsByKeySearch(It.IsAny<string>())).Returns(new AuditItem[] { });
|
||||
|
||||
var defaultPolicy = new SingleItemsOnlyRepositoryCachePolicy<AuditItem, object>(cache.Object, new RepositoryCachePolicyOptions());
|
||||
using (defaultPolicy)
|
||||
|
||||
var found = defaultPolicy.GetAll(new object[] { }, ids => new[]
|
||||
{
|
||||
var found = defaultPolicy.GetAll(new object[] { }, o => new[]
|
||||
{
|
||||
new AuditItem(1, "blah", AuditType.Copy, 123),
|
||||
new AuditItem(2, "blah2", AuditType.Copy, 123)
|
||||
});
|
||||
}
|
||||
new AuditItem(1, "blah", AuditType.Copy, 123),
|
||||
new AuditItem(2, "blah2", AuditType.Copy, 123)
|
||||
});
|
||||
|
||||
Assert.AreEqual(0, cached.Count);
|
||||
}
|
||||
@@ -50,10 +48,8 @@ namespace Umbraco.Tests.Cache
|
||||
});
|
||||
|
||||
var defaultPolicy = new SingleItemsOnlyRepositoryCachePolicy<AuditItem, object>(cache.Object, new RepositoryCachePolicyOptions());
|
||||
using (defaultPolicy)
|
||||
{
|
||||
var found = defaultPolicy.Get(1, o => new AuditItem(1, "blah", AuditType.Copy, 123));
|
||||
}
|
||||
|
||||
var found = defaultPolicy.Get(1, id => new AuditItem(1, "blah", AuditType.Copy, 123), ids => null);
|
||||
Assert.IsTrue(isCached);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user