Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using Microsoft.Extensions.DependencyInjection;
using Umbraco.Cms.Core.DependencyInjection;
using Umbraco.Cms.Core.Models.Entities;
using Umbraco.Cms.Core.Persistence.Repositories;
using Umbraco.Cms.Infrastructure.Scoping;
using Umbraco.Extensions;

Expand Down Expand Up @@ -49,7 +50,7 @@ public DefaultRepositoryCachePolicy(
{
}

protected string EntityTypeCacheKey { get; } = $"uRepo_{typeof(TEntity).Name}_";
protected string EntityTypeCacheKey { get; } = RepositoryCacheKeys.GetKey<TEntity>();

/// <inheritdoc />
public override void Create(TEntity entity, Action<TEntity> persistNew)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

using Umbraco.Cms.Core.Collections;
using Umbraco.Cms.Core.Models.Entities;
using Umbraco.Cms.Core.Persistence.Repositories;
using Umbraco.Cms.Infrastructure.Scoping;
using Umbraco.Extensions;

Expand Down Expand Up @@ -53,7 +54,7 @@ public override void Create(TEntity entity, Action<TEntity> persistNew)
}
}

protected string GetEntityTypeCacheKey() => $"uRepo_{typeof(TEntity).Name}_";
protected string GetEntityTypeCacheKey() => RepositoryCacheKeys.GetKey<TEntity>();

protected void InsertEntities(TEntity[]? entities)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -635,15 +635,32 @@ private sealed class DocumentVariation

protected override Guid NodeObjectTypeId => Constants.ObjectTypes.Document;

/// <inheritdoc />
public override void Save(IContent entity)
{
base.Save(entity);

// Also populate the GUID cache so subsequent lookups by GUID don't hit the database.
_contentByGuidReadRepository.PopulateCacheByKey(entity);
}

protected override IContent? PerformGet(int id)
{
Sql<ISqlContext> sql = GetBaseQuery(QueryType.Single)
.Where<NodeDto>(x => x.NodeId == id);

DocumentDto? dto = Database.FirstOrDefault<DocumentDto>(sql);
return dto == null
? null
: MapDtoToContent(dto);
if (dto is null)
{
return null;
}

IContent content = MapDtoToContent(dto);

// Also populate the GUID cache so subsequent lookups by GUID don't hit the database.
_contentByGuidReadRepository.PopulateCacheByKey(content);

return content;
}

protected override IEnumerable<IContent> PerformGetAll(params int[]? ids)
Expand All @@ -655,7 +672,13 @@ protected override IEnumerable<IContent> PerformGetAll(params int[]? ids)
sql.WhereIn<NodeDto>(x => x.NodeId, ids);
}

return MapDtosToContent(Database.Fetch<DocumentDto>(sql));
// MapDtosToContent returns a materialized array, so this is safe to enumerate multiple times.
IEnumerable<IContent> contents = MapDtosToContent(Database.Fetch<DocumentDto>(sql));

// Also populate the GUID cache so subsequent lookups by GUID don't hit the database.
_contentByGuidReadRepository.PopulateCacheByKey(contents);

return contents;
}

protected override IEnumerable<IContent> PerformGetByQuery(IQuery<IContent> query)
Expand Down Expand Up @@ -1601,6 +1624,33 @@ IEnumerable<IContent> IReadRepository<Guid, IContent>.GetMany(params Guid[]? ids

public bool Exists(Guid id) => _contentByGuidReadRepository.Exists(id);

/// <summary>
/// Populates the int-keyed cache with the given entity.
/// This allows entities retrieved by GUID to also be cached for int ID lookups.
/// </summary>
private void PopulateCacheById(IContent entity)
{
if (entity.HasIdentity)
{
var cacheKey = GetCacheKey(entity.Id);
IsolatedCache.Insert(cacheKey, () => entity, TimeSpan.FromMinutes(5), true);
}
}

/// <summary>
/// Populates the int-keyed cache with the given entities.
/// This allows entities retrieved by GUID to also be cached for int ID lookups.
/// </summary>
private void PopulateCacheById(IEnumerable<IContent> entities)
{
foreach (IContent entity in entities)
{
PopulateCacheById(entity);
}
}

private static string GetCacheKey(int id) => RepositoryCacheKeys.GetKey<IContent>() + id;

// reading repository purely for looking up by GUID
// TODO: ugly and to fix we need to decouple the IRepositoryQueryable -> IRepository -> IReadRepository which should all be separate things!
// This sub-repository pattern is super old and totally unecessary anymore, caching can be handled in much nicer ways without this
Expand Down Expand Up @@ -1637,6 +1687,9 @@ public ContentByGuidReadRepository(

IContent content = _outerRepo.MapDtoToContent(dto);

// Also populate the int-keyed cache so subsequent lookups by int ID don't hit the database
_outerRepo.PopulateCacheById(content);

return content;
}

Expand All @@ -1648,7 +1701,13 @@ protected override IEnumerable<IContent> PerformGetAll(params Guid[]? ids)
sql.WhereIn<NodeDto>(x => x.UniqueId, ids);
}

return _outerRepo.MapDtosToContent(Database.Fetch<DocumentDto>(sql));
// MapDtosToContent returns a materialized array, so this is safe to enumerate multiple times
IEnumerable<IContent> contents = _outerRepo.MapDtosToContent(Database.Fetch<DocumentDto>(sql));

// Also populate the int-keyed cache so subsequent lookups by int ID don't hit the database
_outerRepo.PopulateCacheById(contents);

return contents;
}

protected override IEnumerable<IContent> PerformGetByQuery(IQuery<IContent> query) =>
Expand All @@ -1668,6 +1727,33 @@ protected override Sql<ISqlContext> GetBaseQuery(bool isCount) =>

protected override string GetBaseWhereClause() =>
throw new InvalidOperationException("This method won't be implemented.");

/// <summary>
/// Populates the GUID-keyed cache with the given entity.
/// This allows entities retrieved by int ID to also be cached for GUID lookups.
/// </summary>
public void PopulateCacheByKey(IContent entity)
{
if (entity.HasIdentity)
{
var cacheKey = GetCacheKey(entity.Key);
IsolatedCache.Insert(cacheKey, () => entity, TimeSpan.FromMinutes(5), true);
}
}

/// <summary>
/// Populates the GUID-keyed cache with the given entities.
/// This allows entities retrieved by int ID to also be cached for GUID lookups.
/// </summary>
public void PopulateCacheByKey(IEnumerable<IContent> entities)
{
foreach (IContent entity in entities)
{
PopulateCacheByKey(entity);
}
}

private static string GetCacheKey(Guid key) => RepositoryCacheKeys.GetKey<IContent>() + key;
}

#endregion
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
using Umbraco.Cms.Core.PropertyEditors;
using Umbraco.Cms.Core.Serialization;
using Umbraco.Cms.Core.Services;
using Umbraco.Cms.Core.Services.Navigation;
using Umbraco.Cms.Infrastructure.Persistence.Dtos;
using Umbraco.Cms.Infrastructure.Persistence.Factories;
using Umbraco.Cms.Infrastructure.Persistence.Querying;
Expand Down Expand Up @@ -226,15 +225,32 @@ private IMedia MapDtoToContent(ContentDto dto)

protected override Guid NodeObjectTypeId => Constants.ObjectTypes.Media;

/// <inheritdoc />
public override void Save(IMedia entity)
{
base.Save(entity);

// Also populate the GUID cache so subsequent lookups by GUID don't hit the database
_mediaByGuidReadRepository.PopulateCacheByKey(entity);
}

protected override IMedia? PerformGet(int id)
{
Sql<ISqlContext> sql = GetBaseQuery(QueryType.Single)
.Where<NodeDto>(x => x.NodeId == id);

ContentDto? dto = Database.FirstOrDefault<ContentDto>(sql);
return dto == null
? null
: MapDtoToContent(dto);
if (dto == null)
{
return null;
}

IMedia media = MapDtoToContent(dto);

// Also populate the GUID cache so subsequent lookups by GUID don't hit the database
_mediaByGuidReadRepository.PopulateCacheByKey(media);

return media;
}

protected override IEnumerable<IMedia> PerformGetAll(params int[]? ids)
Expand All @@ -246,7 +262,13 @@ protected override IEnumerable<IMedia> PerformGetAll(params int[]? ids)
sql.WhereIn<NodeDto>(x => x.NodeId, ids);
}

return MapDtosToContent(Database.Fetch<ContentDto>(sql));
// MapDtosToContent returns a materialized array, so this is safe to enumerate multiple times
IEnumerable<IMedia> media = MapDtosToContent(Database.Fetch<ContentDto>(sql));

// Also populate the GUID cache so subsequent lookups by GUID don't hit the database
_mediaByGuidReadRepository.PopulateCacheByKey(media);

return media;
}

protected override IEnumerable<IMedia> PerformGetByQuery(IQuery<IMedia> query)
Expand Down Expand Up @@ -581,6 +603,33 @@ IEnumerable<IMedia> IReadRepository<Guid, IMedia>.GetMany(params Guid[]? ids) =>

public bool Exists(Guid id) => _mediaByGuidReadRepository.Exists(id);

/// <summary>
/// Populates the int-keyed cache with the given entity.
/// This allows entities retrieved by GUID to also be cached for int ID lookups.
/// </summary>
private void PopulateCacheById(IMedia entity)
{
if (entity.HasIdentity)
{
var cacheKey = GetCacheKey(entity.Id);
IsolatedCache.Insert(cacheKey, () => entity, TimeSpan.FromMinutes(5), true);
}
}

/// <summary>
/// Populates the int-keyed cache with the given entities.
/// This allows entities retrieved by GUID to also be cached for int ID lookups.
/// </summary>
private void PopulateCacheById(IEnumerable<IMedia> entities)
{
foreach (IMedia entity in entities)
{
PopulateCacheById(entity);
}
}

private static string GetCacheKey(int id) => RepositoryCacheKeys.GetKey<IMedia>() + id;

// A reading repository purely for looking up by GUID
// TODO: This is ugly and to fix we need to decouple the IRepositoryQueryable -> IRepository -> IReadRepository which should all be separate things!
// This sub-repository pattern is super old and totally unecessary anymore, caching can be handled in much nicer ways without this
Expand Down Expand Up @@ -615,9 +664,12 @@ public MediaByGuidReadRepository(
return null;
}

IMedia content = _outerRepo.MapDtoToContent(dto);
IMedia media = _outerRepo.MapDtoToContent(dto);

return content;
// Also populate the int-keyed cache so subsequent lookups by int ID don't hit the database
_outerRepo.PopulateCacheById(media);

return media;
}

protected override IEnumerable<IMedia> PerformGetAll(params Guid[]? ids)
Expand All @@ -628,7 +680,13 @@ protected override IEnumerable<IMedia> PerformGetAll(params Guid[]? ids)
sql.WhereIn<NodeDto>(x => x.UniqueId, ids);
}

return _outerRepo.MapDtosToContent(Database.Fetch<ContentDto>(sql));
// MapDtosToContent returns a materialized array, so this is safe to enumerate multiple times
IEnumerable<IMedia> media = _outerRepo.MapDtosToContent(Database.Fetch<ContentDto>(sql));

// Also populate the int-keyed cache so subsequent lookups by int ID don't hit the database
_outerRepo.PopulateCacheById(media);

return media;
}

protected override IEnumerable<IMedia> PerformGetByQuery(IQuery<IMedia> query) =>
Expand All @@ -648,6 +706,33 @@ protected override Sql<ISqlContext> GetBaseQuery(bool isCount) =>

protected override string GetBaseWhereClause() =>
throw new InvalidOperationException("This method won't be implemented.");

/// <summary>
/// Populates the GUID-keyed cache with the given entity.
/// This allows entities retrieved by int ID to also be cached for GUID lookups.
/// </summary>
public void PopulateCacheByKey(IMedia entity)
{
if (entity.HasIdentity)
{
var cacheKey = GetCacheKey(entity.Key);
IsolatedCache.Insert(cacheKey, () => entity, TimeSpan.FromMinutes(5), true);
}
}

/// <summary>
/// Populates the GUID-keyed cache with the given entities.
/// This allows entities retrieved by int ID to also be cached for GUID lookups.
/// </summary>
public void PopulateCacheByKey(IEnumerable<IMedia> entities)
{
foreach (IMedia entity in entities)
{
PopulateCacheByKey(entity);
}
}

private static string GetCacheKey(Guid key) => RepositoryCacheKeys.GetKey<IMedia>() + key;
}

#endregion
Expand Down
Loading
Loading