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 @@ -191,6 +191,58 @@ public override TEntity[] GetAll(TId[]? ids, Func<TId[], IEnumerable<TEntity>?>
/// <inheritdoc />
public override void ClearAll() => Cache.Clear(GetEntityTypeCacheKey());

/// <summary>
/// Gets a single entity matching a predicate from cache, cloning only the match.
/// If cache is empty, populates it first via <paramref name="performGetAll"/>.
/// </summary>
/// <param name="predicate">The predicate to match against cached entities.</param>
/// <param name="performGetAll">The repository PerformGetAll method, used to populate the cache on a miss.</param>
/// <returns>A deep-cloned copy of the matching entity, or null if not found.</returns>
internal TEntity? FindCached(Func<TEntity, bool> predicate, Func<TId[], IEnumerable<TEntity>?> performGetAll)
{
EnsureCacheIsSynced();

IEnumerable<TEntity> all = GetAllCached(performGetAll);
TEntity? entity = all.FirstOrDefault(predicate);

// 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 (TEntity?)entity?.DeepClone();
}

/// <summary>
/// Gets all entities matching a predicate from cache, cloning only the matches.
/// If cache is empty, populates it first via <paramref name="performGetAll"/>.
/// </summary>
/// <param name="predicate">The predicate to match against cached entities.</param>
/// <param name="performGetAll">The repository PerformGetAll method, used to populate the cache on a miss.</param>
/// <returns>An array of deep-cloned copies of matching entities.</returns>
internal TEntity[] FindAllCached(Func<TEntity, bool> predicate, Func<TId[], IEnumerable<TEntity>?> performGetAll)
{
EnsureCacheIsSynced();

IEnumerable<TEntity> all = GetAllCached(performGetAll);

// See note in InsertEntities - what we get here are the original
// cached entities, not clones, so we need to manually ensure they are deep-cloned.
return all.Where(predicate).Select(x => (TEntity)x.DeepClone()).ToArray();
}

/// <summary>
/// Checks whether any cached entity matches the predicate, without cloning.
/// If cache is empty, populates it first via <paramref name="performGetAll"/>.
/// </summary>
/// <param name="predicate">The predicate to match against cached entities.</param>
/// <param name="performGetAll">The repository PerformGetAll method, used to populate the cache on a miss.</param>
/// <returns>True if any entity matches the predicate; otherwise false.</returns>
internal bool ExistsCached(Func<TEntity, bool> predicate, Func<TId[], IEnumerable<TEntity>?> performGetAll)
{
EnsureCacheIsSynced();

IEnumerable<TEntity> all = GetAllCached(performGetAll);
return all.Any(predicate);
}

/// <summary>
/// Gets all cached entities, or retrieves them from the repository if not cached.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -134,37 +134,16 @@ public IEnumerable<int> GetAllContentTypeIds(string[] aliases)
protected override IRepositoryCachePolicy<IContentType, int> CreateCachePolicy() =>
new FullDataSetRepositoryCachePolicy<IContentType, int>(GlobalIsolatedCache, ScopeAccessor, _repositoryCacheVersionService, _cacheSyncService, GetEntityId, /*expires:*/ true);

// every GetExists method goes cachePolicy.GetSomething which in turns goes PerformGetAll,
// since this is a FullDataSet policy - and everything is cached
// so here,
// every PerformGet/Exists just GetMany() and then filters
// except PerformGetAll which is the one really doing the job

// TODO: the filtering is highly inefficient as we deep-clone everything
// there should be a way to GetMany(predicate) right from the cache policy!
// and ah, well, this all caching should be refactored + the cache refreshers
// should to repository.Clear() not deal with magic caches by themselves
// Note: PerformGet(int) is passed as a callback to the cache policy's Get(TId) method,
// but FullDataSetRepositoryCachePolicy.Get() never invokes it — it uses GetAllCached()
// internally and clones only the matched entity. This override exists only as a required
// implementation of the abstract base and as a fallback for non-FullDataSet policies.
protected override IContentType? PerformGet(int id)
=> GetMany().FirstOrDefault(x => x.Id == id);

protected override IContentType? PerformGet(Guid id)
=> GetMany().FirstOrDefault(x => x.Key == id);

protected override IContentType? PerformGet(string alias)
=> GetMany().FirstOrDefault(x => x.Alias.InvariantEquals(alias));

protected override bool PerformExists(Guid id)
=> GetMany().FirstOrDefault(x => x.Key == id) != null;

protected override IEnumerable<IContentType>? GetAllWithFullCachePolicy() =>
CommonRepository.GetAllTypes()?.OfType<IContentType>();

protected override IEnumerable<IContentType> PerformGetAll(params Guid[]? ids)
{
IEnumerable<IContentType> all = GetMany();
return ids?.Any() ?? false ? all.Where(x => ids.Contains(x.Key)) : all;
}

protected override IEnumerable<IContentType> PerformGetByQuery(IQuery<IContentType> query)
{
Sql<ISqlContext> baseQuery = GetBaseQuery(false);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,13 @@ protected ContentTypeRepositoryBase(

protected ILanguageRepository LanguageRepository { get; }

/// <summary>
/// Gets the cache policy as <see cref="FullDataSetRepositoryCachePolicy{TEntity, TId}"/> for predicate-based lookups.
/// Returns null if the repository uses a different cache policy type.
/// </summary>
protected FullDataSetRepositoryCachePolicy<TEntity, int>? TypedCachePolicy
=> CachePolicy as FullDataSetRepositoryCachePolicy<TEntity, int>;

protected abstract bool SupportsPublishing { get; }

/// <summary>
Expand Down Expand Up @@ -1559,7 +1566,20 @@ protected void ValidateAlias(TEntity entity)
}
}

protected abstract TEntity? PerformGet(Guid id);
/// <summary>
/// Gets a content type by its unique key (GUID), using the full-dataset cache policy
/// to clone only the matched entity rather than the entire cached collection.
/// </summary>
protected virtual TEntity? PerformGet(Guid id)
{
if (TypedCachePolicy is { } policy)
{
return policy.FindCached(x => x.Key == id, PerformGetAll);
}

// Fallback for non-FullDataSet policies.
return GetMany().FirstOrDefault(x => x.Key == id);
}

/// <summary>
/// Try to set the data type Id based on the provided key or property editor alias.
Expand Down Expand Up @@ -1615,11 +1635,65 @@ private void AssignDataTypeIdFromProvidedKeyOrPropertyEditor(IPropertyType prope
}
}

protected abstract TEntity? PerformGet(string alias);
/// <summary>
/// Gets a content type by its alias, using the full-dataset cache policy
/// to clone only the matched entity rather than the entire cached collection.
/// </summary>
protected virtual TEntity? PerformGet(string alias)
{
if (TypedCachePolicy is { } policy)
{
return policy.FindCached(x => x.Alias.InvariantEquals(alias), PerformGetAll);
}

// Fallback for non-FullDataSet policies.
return GetMany().FirstOrDefault(x => x.Alias.InvariantEquals(alias));
}

/// <summary>
/// Gets content types by their unique keys (GUIDs), using the full-dataset cache policy
/// to clone only the matched entities rather than the entire cached collection.
/// When <paramref name="ids"/> is null or empty, returns all content types.
/// </summary>
protected virtual IEnumerable<TEntity>? PerformGetAll(params Guid[]? ids)
{
if (TypedCachePolicy is { } policy)
{
if (ids?.Any() ?? false)
{
var idSet = ids.ToHashSet();
return policy.FindAllCached(x => idSet.Contains(x.Key), PerformGetAll);
}

// No filter — need all, clone everything (same as GetAll).
return policy.GetAll(null, PerformGetAll);
}

protected abstract IEnumerable<TEntity>? PerformGetAll(params Guid[]? ids);
// Fallback for non-FullDataSet policies.
IEnumerable<TEntity> all = GetMany();
if (ids?.Any() ?? false)
{
var idSet = ids.ToHashSet();
return all.Where(x => idSet.Contains(x.Key));
}

protected abstract bool PerformExists(Guid id);
return all;
}

/// <summary>
/// Checks whether a content type with the specified unique key (GUID) exists,
/// using the full-dataset cache policy to avoid any deep-cloning.
/// </summary>
protected virtual bool PerformExists(Guid id)
{
if (TypedCachePolicy is { } policy)
{
return policy.ExistsCached(x => x.Key == id, PerformGetAll);
}

// Fallback for non-FullDataSet policies.
return GetMany().Any(x => x.Key == id);
}

/// <summary>
/// Generates a unique alias for a content type by appending an incrementing number to the provided base alias if necessary.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,29 +38,46 @@ public DomainRepository(
{
}

/// <summary>
/// Gets the cache policy as <see cref="FullDataSetRepositoryCachePolicy{TEntity, TId}"/> for predicate-based lookups.
/// Returns null when caching is disabled (e.g. <see cref="AppCaches.NoCache"/>).
/// </summary>
private FullDataSetRepositoryCachePolicy<IDomain, int>? TypedCachePolicy
=> CachePolicy as FullDataSetRepositoryCachePolicy<IDomain, int>;

/// <summary>
/// Gets a domain by its name.
/// </summary>
/// <param name="domainName">The name of the domain to retrieve.</param>
/// <returns>The domain matching the specified name, or null if none found.</returns>
public IDomain? GetByName(string domainName)
=> GetMany().FirstOrDefault(x => x.DomainName.InvariantEquals(domainName));
=> TypedCachePolicy?.FindCached(x => x.DomainName.InvariantEquals(domainName), PerformGetAll)
?? GetMany().FirstOrDefault(x => x.DomainName.InvariantEquals(domainName));

/// <summary>
/// Determines whether a domain with the specified name exists.
/// </summary>
/// <param name="domainName">The name of the domain to check for existence.</param>
/// <returns>True if the domain exists; otherwise, false.</returns>
public bool Exists(string domainName)
=> GetMany().Any(x => x.DomainName.InvariantEquals(domainName));
=> TypedCachePolicy?.ExistsCached(x => x.DomainName.InvariantEquals(domainName), PerformGetAll)
?? GetMany().Any(x => x.DomainName.InvariantEquals(domainName));

/// <summary>
/// Gets all domains, optionally including wildcard domains.
/// </summary>
/// <param name="includeWildcards">If true, includes wildcard domains in the result; otherwise, excludes them.</param>
/// <returns>An enumerable collection of domains.</returns>
public IEnumerable<IDomain> GetAll(bool includeWildcards)
=> GetMany().Where(x => includeWildcards || x.IsWildcard == false);
{
if (includeWildcards)
{
return GetMany();
}

return TypedCachePolicy?.FindAllCached(x => x.IsWildcard == false, PerformGetAll)
?? GetMany().Where(x => x.IsWildcard == false);
}

/// <summary>
/// Retrieves the domains assigned to the specified content item.
Expand All @@ -69,13 +86,17 @@ public IEnumerable<IDomain> GetAll(bool includeWildcards)
/// <param name="includeWildcards">If <c>true</c>, includes wildcard domains in the results; if <c>false</c>, only non-wildcard domains are returned.</param>
/// <returns>An <see cref="IEnumerable{IDomain}"/> containing the domains assigned to the specified content item.</returns>
public IEnumerable<IDomain> GetAssignedDomains(int contentId, bool includeWildcards)
=> GetMany().Where(x => x.RootContentId == contentId).Where(x => includeWildcards || x.IsWildcard == false);
=> TypedCachePolicy?.FindAllCached(x => x.RootContentId == contentId && (includeWildcards || x.IsWildcard == false), PerformGetAll)
?? GetMany().Where(x => x.RootContentId == contentId).Where(x => includeWildcards || x.IsWildcard == false);

protected override IRepositoryCachePolicy<IDomain, int> CreateCachePolicy()
=> new FullDataSetRepositoryCachePolicy<IDomain, int>(GlobalIsolatedCache, ScopeAccessor, RepositoryCacheVersionService, CacheSyncService, GetEntityId, false);

// Note: PerformGet(int) is passed as a callback to the cache policy's Get(TId) method,
// but FullDataSetRepositoryCachePolicy.Get() never invokes it — it uses GetAllCached()
// internally and clones only the matched entity. This override exists only as a required
// implementation of the abstract base and as a fallback for non-FullDataSet policies.
protected override IDomain? PerformGet(int id)
// Use the underlying GetAll which will force cache all domains
=> GetMany().FirstOrDefault(x => x.Id == id);

protected override IEnumerable<IDomain> PerformGetAll(params int[]? ids)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,13 +37,19 @@ public LogViewerQueryRepository(
{
}

/// <summary>
/// Gets the cache policy as <see cref="FullDataSetRepositoryCachePolicy{TEntity, TId}"/> for predicate-based lookups.
/// Returns null when caching is disabled (e.g. <see cref="AppCaches.NoCache"/>).
/// </summary>
private FullDataSetRepositoryCachePolicy<ILogViewerQuery, int>? TypedCachePolicy
=> CachePolicy as FullDataSetRepositoryCachePolicy<ILogViewerQuery, int>;

/// <summary>Retrieves a log viewer query by its name.</summary>
/// <param name="name">The name of the log viewer query to retrieve.</param>
/// <returns>The log viewer query with the specified name, or null if not found.</returns>
public ILogViewerQuery? GetByName(string name) =>

// use the underlying GetAll which will force cache all log queries
GetMany().FirstOrDefault(x => x.Name == name);
TypedCachePolicy?.FindCached(x => x.Name == name, PerformGetAll)
?? GetMany().FirstOrDefault(x => x.Name == name);

protected override IRepositoryCachePolicy<ILogViewerQuery, int> CreateCachePolicy() =>
new FullDataSetRepositoryCachePolicy<ILogViewerQuery, int>(GlobalIsolatedCache, ScopeAccessor, RepositoryCacheVersionService, CacheSyncService, GetEntityId, /*expires:*/ false);
Expand Down Expand Up @@ -120,9 +126,11 @@ protected override void PersistUpdatedItem(ILogViewerQuery entity)
Database.Update(dto);
}

// Note: PerformGet(int) is passed as a callback to the cache policy's Get(TId) method,
// but FullDataSetRepositoryCachePolicy.Get() never invokes it — it uses GetAllCached()
// internally and clones only the matched entity. This override exists only as a required
// implementation of the abstract base and as a fallback for non-FullDataSet policies.
protected override ILogViewerQuery? PerformGet(int id) =>

// use the underlying GetAll which will force cache all log queries
GetMany().FirstOrDefault(x => x.Id == id);

private static ILogViewerQuery ConvertFromDto(LogViewerQueryDto dto)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,32 +66,16 @@ public MediaTypeRepository(
protected override IRepositoryCachePolicy<IMediaType, int> CreateCachePolicy() =>
new FullDataSetRepositoryCachePolicy<IMediaType, int>(GlobalIsolatedCache, ScopeAccessor, _repositoryCacheVersionService, _cacheSyncService, GetEntityId, /*expires:*/ true);

// every GetExists method goes cachePolicy.GetSomething which in turns goes PerformGetAll,
// since this is a FullDataSet policy - and everything is cached
// so here,
// every PerformGet/Exists just GetMany() and then filters
// except PerformGetAll which is the one really doing the job
// Note: PerformGet(int) is passed as a callback to the cache policy's Get(TId) method,
// but FullDataSetRepositoryCachePolicy.Get() never invokes it — it uses GetAllCached()
// internally and clones only the matched entity. This override exists only as a required
// implementation of the abstract base and as a fallback for non-FullDataSet policies.
protected override IMediaType? PerformGet(int id)
=> GetMany().FirstOrDefault(x => x.Id == id);

protected override IMediaType? PerformGet(Guid id)
=> GetMany().FirstOrDefault(x => x.Key == id);

protected override bool PerformExists(Guid id)
=> GetMany().FirstOrDefault(x => x.Key == id) != null;

protected override IMediaType? PerformGet(string alias)
=> GetMany().FirstOrDefault(x => x.Alias.InvariantEquals(alias));

protected override IEnumerable<IMediaType>? GetAllWithFullCachePolicy() =>
CommonRepository.GetAllTypes()?.OfType<IMediaType>();

protected override IEnumerable<IMediaType> PerformGetAll(params Guid[]? ids)
{
IEnumerable<IMediaType> all = GetMany();
return ids?.Any() ?? false ? all.Where(x => ids.Contains(x.Key)) : all;
}

protected override IEnumerable<IMediaType> PerformGetByQuery(IQuery<IMediaType> query)
{
Sql<ISqlContext> baseQuery = GetBaseQuery(false);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,29 +71,13 @@ public MemberTypeRepository(
protected override IRepositoryCachePolicy<IMemberType, int> CreateCachePolicy() =>
new FullDataSetRepositoryCachePolicy<IMemberType, int>(GlobalIsolatedCache, ScopeAccessor, _repositoryCacheVersionService, _cacheSyncService, GetEntityId, /*expires:*/ true);

// every GetExists method goes cachePolicy.GetSomething which in turns goes PerformGetAll,
// since this is a FullDataSet policy - and everything is cached
// so here,
// every PerformGet/Exists just GetMany() and then filters
// except PerformGetAll which is the one really doing the job
// Note: PerformGet(int) is passed as a callback to the cache policy's Get(TId) method,
// but FullDataSetRepositoryCachePolicy.Get() never invokes it — it uses GetAllCached()
// internally and clones only the matched entity. This override exists only as a required
// implementation of the abstract base and as a fallback for non-FullDataSet policies.
protected override IMemberType? PerformGet(int id)
=> GetMany().FirstOrDefault(x => x.Id == id);

protected override IMemberType? PerformGet(Guid id)
=> GetMany().FirstOrDefault(x => x.Key == id);

protected override IEnumerable<IMemberType> PerformGetAll(params Guid[]? ids)
{
IEnumerable<IMemberType> all = GetMany();
return ids?.Any() ?? false ? all.Where(x => ids.Contains(x.Key)) : all;
}

protected override bool PerformExists(Guid id)
=> GetMany().FirstOrDefault(x => x.Key == id) != null;

protected override IMemberType? PerformGet(string alias)
=> GetMany().FirstOrDefault(x => x.Alias.InvariantEquals(alias));

protected override IEnumerable<IMemberType>? GetAllWithFullCachePolicy() =>
CommonRepository.GetAllTypes()?.OfType<IMemberType>();

Expand Down
Loading
Loading