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
52 changes: 47 additions & 5 deletions src/XperienceCommunity.DataRepository/BaseRepository.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using CMS.Websites;
using CMS.Websites.Routing;

using XperienceCommunity.DataRepository.Interfaces;
using XperienceCommunity.DataRepository.Models;

namespace XperienceCommunity.DataRepository;
Expand Down Expand Up @@ -32,19 +33,27 @@ public abstract class BaseRepository
/// </summary>
protected readonly IWebsiteChannelContext WebsiteChannelContext;

/// <summary>
/// Gets the cache dependency builder instance.
/// </summary>
protected readonly ICacheDependencyBuilder CacheDependencyBuilder;

/// <summary>
/// Initializes a new instance of the <see cref="BaseRepository"/> class.
/// </summary>
/// <param name="cache">The progressive cache instance.</param>
/// <param name="executor">The content query executor instance.</param>
/// <param name="websiteChannelContext">The website channel context instance.</param>
/// <param name="options">The repository options.</param>
/// <param name="cacheDependencyBuilder">The Cache Dependency Builder.</param>
protected BaseRepository(IProgressiveCache cache,
IContentQueryExecutor executor, IWebsiteChannelContext websiteChannelContext, RepositoryOptions options)
IContentQueryExecutor executor, IWebsiteChannelContext websiteChannelContext, RepositoryOptions options, ICacheDependencyBuilder cacheDependencyBuilder)
{
Cache = cache ?? throw new ArgumentNullException(nameof(cache));
Executor = executor ?? throw new ArgumentNullException(nameof(executor));
WebsiteChannelContext = websiteChannelContext ?? throw new ArgumentNullException(nameof(websiteChannelContext));
CacheDependencyBuilder =
cacheDependencyBuilder ?? throw new ArgumentNullException(nameof(cacheDependencyBuilder));
CacheMinutes = options?.CacheMinutes ?? 10;
}

Expand All @@ -70,7 +79,7 @@ protected ContentQueryExecutionOptions GetQueryExecutionOptions()
/// <param name="cancellationToken">The cancellation token.</param>
/// <param name="cacheNameParts">The parts of the cache name.</param>
/// <returns>The result of the query.</returns>
protected async Task<IEnumerable<T>> ExecutePageQuery<T>(ContentItemQueryBuilder builder, Func<CMSCacheDependency> dependencyFunc, CancellationToken cancellationToken = default,
protected async Task<IEnumerable<T>> ExecutePageQuery<T>(ContentItemQueryBuilder builder, Func<CMSCacheDependency>? dependencyFunc = null, CancellationToken cancellationToken = default,
params object[] cacheNameParts)
{
var queryOptions = GetQueryExecutionOptions();
Expand All @@ -95,7 +104,24 @@ protected async Task<IEnumerable<T>> ExecutePageQuery<T>(ContentItemQueryBuilder
return result;
}

cs.CacheDependency = dependencyFunc.Invoke();

if (dependencyFunc is not null)
{
cs.CacheDependency = dependencyFunc.Invoke();
}
else
{
var dependency = CacheDependencyBuilder.Create(result);

if (dependency is not null)
{
cs.CacheDependency = dependency;
}
else
{
cs.BoolCondition = false;
}
}

return result;
}, cacheSettings, cancellationToken);
Expand All @@ -110,7 +136,7 @@ protected async Task<IEnumerable<T>> ExecutePageQuery<T>(ContentItemQueryBuilder
/// <param name="cancellationToken">The cancellation token.</param>
/// <param name="cacheNameParts">The parts of the cache name.</param>
/// <returns>The result of the query.</returns>
protected async Task<IEnumerable<T>> ExecuteContentQuery<T>(ContentItemQueryBuilder builder, Func<CMSCacheDependency> dependencyFunc, CancellationToken cancellationToken = default,
protected async Task<IEnumerable<T>> ExecuteContentQuery<T>(ContentItemQueryBuilder builder, Func<CMSCacheDependency>? dependencyFunc = null, CancellationToken cancellationToken = default,
params object[] cacheNameParts)
{
var queryOptions = GetQueryExecutionOptions();
Expand All @@ -135,7 +161,23 @@ protected async Task<IEnumerable<T>> ExecuteContentQuery<T>(ContentItemQueryBuil
return result;
}

cs.CacheDependency = dependencyFunc.Invoke();
if (dependencyFunc is not null)
{
cs.CacheDependency = dependencyFunc.Invoke();
}
else
{
var dependency = CacheDependencyBuilder.Create(result);

if (dependency is not null)
{
cs.CacheDependency = dependency;
}
else
{
cs.BoolCondition = false;
}
}

return result;
}, cacheSettings, cancellationToken);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
using System.Reflection;

using CMS.ContentEngine;
using CMS.Helpers;
using CMS.Websites;

using XperienceCommunity.DataRepository.Interfaces;

namespace XperienceCommunity.DataRepository.Builders
{
public sealed class CacheDependencyBuilder : ICacheDependencyBuilder
{
/// <inheritdoc />
public CMSCacheDependency? Create<T>(IEnumerable<T> items)
{
var keys = ExtractCacheDependencyKeys(items);

if (keys.Count == 0)
{
return null;
}

return new CMSCacheDependency() { CacheKeys = [.. keys] };
}


private static void AddDependencyKeys<T>(T value, HashSet<string> dependencyKeys)
{
switch (value)
{
case null:
return;
case ContentItemReference reference:
dependencyKeys.Add($"contentitem|byguid|{reference.Identifier}");
break;
case WebPageRelatedItem webPageReference:
dependencyKeys.Add($"webpageitem|byguid|{webPageReference.WebPageGuid}");
break;
case IWebPageFieldsSource webPageFieldSource:
dependencyKeys.Add($"webpageitem|byid|{webPageFieldSource.SystemFields.WebPageItemID}");
break;
case IContentItemFieldsSource contentItemFieldSource:
dependencyKeys.Add($"contentitem|byid|{contentItemFieldSource.SystemFields.ContentItemID}");
break;
case IEnumerable<IWebPageFieldsSource> webPageFieldSources:
{
foreach (var source in webPageFieldSources)
{
dependencyKeys.Add($"webpageitem|byid|{source.SystemFields.WebPageItemID}");
}
}
break;
case IEnumerable<IContentItemFieldsSource> contentItemFieldSources:
{
foreach (var source in contentItemFieldSources)
{
dependencyKeys.Add($"contentitem|byid|{source.SystemFields.ContentItemID}");
}
}
break;
default:
break;
}
}

private static IReadOnlyList<string> ExtractCacheDependencyKeys<T>(in IEnumerable<T> items)
{
var dependencyKeys = new HashSet<string>(StringComparer.OrdinalIgnoreCase);

foreach (var item in items)
{
if (item is null)
{
continue;
}

AddDependencyKeys(item, dependencyKeys);

var properties = item
.GetType()
.GetProperties(BindingFlags.Public | BindingFlags.Instance);

foreach (var property in properties)
{
object? value = property.GetValue(item);

if (value is null)
{
continue;
}

AddDependencyKeys(value, dependencyKeys);
}
}

return dependencyKeys.ToList();
}
}
}
Loading
Loading