diff --git a/src/XperienceCommunity.DataRepository/BaseRepository.cs b/src/XperienceCommunity.DataRepository/BaseRepository.cs
index e7eda92..fdfbc94 100644
--- a/src/XperienceCommunity.DataRepository/BaseRepository.cs
+++ b/src/XperienceCommunity.DataRepository/BaseRepository.cs
@@ -3,6 +3,7 @@
using CMS.Websites;
using CMS.Websites.Routing;
+using XperienceCommunity.DataRepository.Interfaces;
using XperienceCommunity.DataRepository.Models;
namespace XperienceCommunity.DataRepository;
@@ -32,6 +33,11 @@ public abstract class BaseRepository
///
protected readonly IWebsiteChannelContext WebsiteChannelContext;
+ ///
+ /// Gets the cache dependency builder instance.
+ ///
+ protected readonly ICacheDependencyBuilder CacheDependencyBuilder;
+
///
/// Initializes a new instance of the class.
///
@@ -39,12 +45,15 @@ public abstract class BaseRepository
/// The content query executor instance.
/// The website channel context instance.
/// The repository options.
+ /// The Cache Dependency Builder.
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;
}
@@ -70,7 +79,7 @@ protected ContentQueryExecutionOptions GetQueryExecutionOptions()
/// The cancellation token.
/// The parts of the cache name.
/// The result of the query.
- protected async Task> ExecutePageQuery(ContentItemQueryBuilder builder, Func dependencyFunc, CancellationToken cancellationToken = default,
+ protected async Task> ExecutePageQuery(ContentItemQueryBuilder builder, Func? dependencyFunc = null, CancellationToken cancellationToken = default,
params object[] cacheNameParts)
{
var queryOptions = GetQueryExecutionOptions();
@@ -95,7 +104,24 @@ protected async Task> ExecutePageQuery(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);
@@ -110,7 +136,7 @@ protected async Task> ExecutePageQuery(ContentItemQueryBuilder
/// The cancellation token.
/// The parts of the cache name.
/// The result of the query.
- protected async Task> ExecuteContentQuery(ContentItemQueryBuilder builder, Func dependencyFunc, CancellationToken cancellationToken = default,
+ protected async Task> ExecuteContentQuery(ContentItemQueryBuilder builder, Func? dependencyFunc = null, CancellationToken cancellationToken = default,
params object[] cacheNameParts)
{
var queryOptions = GetQueryExecutionOptions();
@@ -135,7 +161,23 @@ protected async Task> ExecuteContentQuery(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);
diff --git a/src/XperienceCommunity.DataRepository/Builders/CacheDependencyBuilder.cs b/src/XperienceCommunity.DataRepository/Builders/CacheDependencyBuilder.cs
new file mode 100644
index 0000000..e40f727
--- /dev/null
+++ b/src/XperienceCommunity.DataRepository/Builders/CacheDependencyBuilder.cs
@@ -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
+ {
+ ///
+ public CMSCacheDependency? Create(IEnumerable items)
+ {
+ var keys = ExtractCacheDependencyKeys(items);
+
+ if (keys.Count == 0)
+ {
+ return null;
+ }
+
+ return new CMSCacheDependency() { CacheKeys = [.. keys] };
+ }
+
+
+ private static void AddDependencyKeys(T value, HashSet 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 webPageFieldSources:
+ {
+ foreach (var source in webPageFieldSources)
+ {
+ dependencyKeys.Add($"webpageitem|byid|{source.SystemFields.WebPageItemID}");
+ }
+ }
+ break;
+ case IEnumerable contentItemFieldSources:
+ {
+ foreach (var source in contentItemFieldSources)
+ {
+ dependencyKeys.Add($"contentitem|byid|{source.SystemFields.ContentItemID}");
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
+ private static IReadOnlyList ExtractCacheDependencyKeys(in IEnumerable items)
+ {
+ var dependencyKeys = new HashSet(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();
+ }
+ }
+}
diff --git a/src/XperienceCommunity.DataRepository/ContentTypeRepository.cs b/src/XperienceCommunity.DataRepository/ContentTypeRepository.cs
index e38590c..bb4222a 100644
--- a/src/XperienceCommunity.DataRepository/ContentTypeRepository.cs
+++ b/src/XperienceCommunity.DataRepository/ContentTypeRepository.cs
@@ -3,7 +3,6 @@
using CMS.Websites.Routing;
using XperienceCommunity.DataRepository.Extensions;
-using XperienceCommunity.DataRepository.Helpers;
using XperienceCommunity.DataRepository.Interfaces;
using XperienceCommunity.DataRepository.Models;
@@ -16,19 +15,19 @@ public sealed class ContentTypeRepository(
IProgressiveCache cache,
IContentQueryExecutor executor,
IWebsiteChannelContext websiteChannelContext,
- RepositoryOptions options)
+ RepositoryOptions options,
+ ICacheDependencyBuilder cacheDependencyBuilder)
: BaseRepository(cache, executor,
- websiteChannelContext, options), IContentRepository
+ websiteChannelContext, options, cacheDependencyBuilder), IContentRepository
where TEntity : class, IContentItemFieldsSource
{
private readonly string contentType = typeof(TEntity)?.GetContentTypeName() ?? string.Empty;
-
public override string CachePrefix => $"data|{contentType}|{WebsiteChannelContext.WebsiteChannelName}";
///
public async Task> GetAllAsync(IEnumerable nodeGuid, string? languageName,
- int maxLinkedItems = 0,
+ int maxLinkedItems = 0, Func? dependencyFunc = null,
CancellationToken cancellationToken = default)
{
ArgumentException.ThrowIfNullOrEmpty(contentType);
@@ -47,8 +46,7 @@ public async Task> GetAllAsync(IEnumerable nodeGuid,
.Where(where => where.WhereIn(nameof(IContentItemFieldsSource.SystemFields.ContentItemGUID),
guidList))).When(!string.IsNullOrEmpty(languageName), lang => lang.InLanguage(languageName));
- var result = await ExecuteContentQuery(builder,
- () => CacheDependencyHelper.CreateContentItemGUIDCacheDependency(guidList!),
+ var result = await ExecuteContentQuery(builder, dependencyFunc,
cancellationToken, CachePrefix, nameof(GetAllAsync), guidList, maxLinkedItems);
return result;
@@ -56,7 +54,7 @@ public async Task> GetAllAsync(IEnumerable nodeGuid,
///
public async Task> GetAllAsync(IEnumerable itemIds, string? languageName,
- int maxLinkedItems = 0,
+ int maxLinkedItems = 0, Func? dependencyFunc = null,
CancellationToken cancellationToken = default)
{
ArgumentException.ThrowIfNullOrEmpty(contentType);
@@ -75,8 +73,7 @@ public async Task> GetAllAsync(IEnumerable itemIds, st
.Where(where => where.WhereIn(nameof(IContentItemFieldsSource.SystemFields.ContentItemID), idList)))
.When(!string.IsNullOrEmpty(languageName), lang => lang.InLanguage(languageName));
- var result = await ExecuteContentQuery(builder,
- () => CacheDependencyHelper.CreateContentItemIDCacheDependency(idList!),
+ var result = await ExecuteContentQuery(builder, dependencyFunc,
cancellationToken, CachePrefix, nameof(GetAllAsync), idList, maxLinkedItems);
return result;
@@ -84,7 +81,7 @@ public async Task> GetAllAsync(IEnumerable itemIds, st
///
public async Task> GetAllAsync(string? languageName, int maxLinkedItems = 0,
- CancellationToken cancellationToken = default)
+ Func? dependencyFunc = null, CancellationToken cancellationToken = default)
{
ArgumentException.ThrowIfNullOrEmpty(contentType);
@@ -94,8 +91,7 @@ public async Task> GetAllAsync(string? languageName, int ma
config.When(maxLinkedItems > 0, linkOptions => linkOptions.WithLinkedItems(maxLinkedItems)))
.When(!string.IsNullOrEmpty(languageName), lang => lang.InLanguage(languageName));
- var result = await ExecuteContentQuery(builder,
- () => CacheDependencyHelper.CreateContentItemTypeCacheDependency([contentType]),
+ var result = await ExecuteContentQuery(builder, dependencyFunc,
cancellationToken, CachePrefix, nameof(GetAllAsync), contentType, maxLinkedItems);
return result;
@@ -103,7 +99,7 @@ public async Task> GetAllAsync(string? languageName, int ma
///
public async Task> GetAllBySchema(string? languageName, int maxLinkedItems = 0,
- CancellationToken cancellationToken = default)
+ Func? dependencyFunc = null, CancellationToken cancellationToken = default)
{
string? schemaName = typeof(TSchema).GetReusableFieldSchemaName();
@@ -127,7 +123,7 @@ public async Task> GetAllBySchema(string? language
///
public async Task GetByGuidAsync(Guid itemGuid, string? languageName, int maxLinkedItems = 0,
- CancellationToken cancellationToken = default)
+ Func? dependencyFunc = null, CancellationToken cancellationToken = default)
{
ArgumentException.ThrowIfNullOrEmpty(contentType);
@@ -140,8 +136,7 @@ public async Task> GetAllBySchema(string? language
.TopN(1))
.When(!string.IsNullOrEmpty(languageName), lang => lang.InLanguage(languageName));
- var result = await ExecuteContentQuery(builder,
- () => CacheDependencyHelper.CreateContentItemGUIDCacheDependency([itemGuid]),
+ var result = await ExecuteContentQuery(builder, dependencyFunc,
cancellationToken, CachePrefix, nameof(GetByIdAsync), itemGuid, maxLinkedItems);
return result.FirstOrDefault();
@@ -149,7 +144,7 @@ public async Task> GetAllBySchema(string? language
///
public async Task GetByIdAsync(int id, string? languageName, int maxLinkedItems = 0,
- CancellationToken cancellationToken = default)
+ Func? dependencyFunc = null, CancellationToken cancellationToken = default)
{
ArgumentException.ThrowIfNullOrEmpty(contentType);
var builder = new ContentItemQueryBuilder();
@@ -161,8 +156,7 @@ public async Task> GetAllBySchema(string? language
.TopN(1))
.When(!string.IsNullOrEmpty(languageName), lang => lang.InLanguage(languageName));
- var result = await ExecuteContentQuery(builder,
- () => CacheDependencyHelper.CreateContentItemIDCacheDependency([id]),
+ var result = await ExecuteContentQuery(builder, dependencyFunc,
cancellationToken, CachePrefix, nameof(GetByIdAsync), id, maxLinkedItems);
return result.FirstOrDefault();
@@ -170,7 +164,7 @@ public async Task> GetAllBySchema(string? language
///
public async Task GetByIdentifierAsync(Guid id, string? languageName, int maxLinkedItems = 0,
- CancellationToken cancellationToken = default)
+ Func? dependencyFunc = null, CancellationToken cancellationToken = default)
{
var builder = new ContentItemQueryBuilder();
@@ -181,8 +175,7 @@ public async Task> GetAllBySchema(string? language
.TopN(1))
.When(!string.IsNullOrEmpty(languageName), lang => lang.InLanguage(languageName));
- var result = await ExecuteContentQuery(builder,
- () => CacheDependencyHelper.CreateContentItemGUIDCacheDependency([id]),
+ var result = await ExecuteContentQuery(builder, dependencyFunc,
cancellationToken, CachePrefix, nameof(GetByIdentifierAsync), id, maxLinkedItems);
return result.FirstOrDefault();
@@ -190,7 +183,7 @@ public async Task> GetAllBySchema(string? language
///
public async Task GetByNameAsync(string name, string? languageName, int maxLinkedItems = 0,
- CancellationToken cancellationToken = default)
+ Func? dependencyFunc = null, CancellationToken cancellationToken = default)
{
var builder = new ContentItemQueryBuilder();
@@ -202,8 +195,7 @@ public async Task> GetAllBySchema(string? language
.TopN(1))
.When(!string.IsNullOrEmpty(languageName), lang => lang.InLanguage(languageName));
- var result = await ExecuteContentQuery(builder,
- () => CacheDependencyHelper.CreateContentItemTypeCacheDependency([contentType]),
+ var result = await ExecuteContentQuery(builder, dependencyFunc,
cancellationToken, CachePrefix, nameof(GetByNameAsync), name, maxLinkedItems);
return result.FirstOrDefault();
@@ -211,7 +203,7 @@ public async Task> GetAllBySchema(string? language
///
public async Task> GetBySmartFolderGuidAsync(Guid smartFolderId, int maxLinkedItems = 0,
- CancellationToken cancellationToken = default)
+ Func? dependencyFunc = null, CancellationToken cancellationToken = default)
{
ArgumentException.ThrowIfNullOrEmpty(contentType);
@@ -222,8 +214,7 @@ public async Task> GetBySmartFolderGuidAsync(Guid smartFold
.OfContentType(contentType)
.InSmartFolder(smartFolderId));
- var result = await ExecuteContentQuery(builder,
- () => CacheDependencyHelper.CreateContentItemTypeCacheDependency([contentType]),
+ var result = await ExecuteContentQuery(builder, dependencyFunc,
cancellationToken, CachePrefix, nameof(GetBySmartFolderGuidAsync), smartFolderId, maxLinkedItems);
return result;
@@ -231,7 +222,7 @@ public async Task> GetBySmartFolderGuidAsync(Guid smartFold
///
public async Task> GetBySmartFolderIdAsync(int smartFolderId, int maxLinkedItems = 0,
- CancellationToken cancellationToken = default)
+ Func? dependencyFunc = null, CancellationToken cancellationToken = default)
{
ArgumentException.ThrowIfNullOrEmpty(contentType);
@@ -242,8 +233,7 @@ public async Task> GetBySmartFolderIdAsync(int smartFolderI
.OfContentType(contentType)
.InSmartFolder(smartFolderId));
- var result = await ExecuteContentQuery(builder,
- () => CacheDependencyHelper.CreateContentItemTypeCacheDependency([contentType]!),
+ var result = await ExecuteContentQuery(builder, dependencyFunc,
cancellationToken, CachePrefix, nameof(GetBySmartFolderIdAsync), contentType, smartFolderId,
maxLinkedItems);
@@ -252,7 +242,7 @@ public async Task> GetBySmartFolderIdAsync(int smartFolderI
///
public async Task> GetBySmartFolderIdAsync(int smartFolderId,
- int maxLinkedItems = 0,
+ int maxLinkedItems = 0, Func? dependencyFunc = null,
CancellationToken cancellationToken = default)
{
string?[] contentTypes = [typeof(T1).GetContentTypeName(), typeof(T2).GetContentTypeName()];
@@ -269,9 +259,7 @@ public async Task> GetBySmartFolderIdAsync
.OfContentType(contentTypes)
.InSmartFolder(smartFolderId));
-
- var result = await ExecuteContentQuery(builder,
- () => CacheDependencyHelper.CreateContentItemTypeCacheDependency(contentTypes!),
+ var result = await ExecuteContentQuery(builder, dependencyFunc,
cancellationToken, CachePrefix, nameof(GetBySmartFolderIdAsync), contentTypes, smartFolderId,
maxLinkedItems);
@@ -280,7 +268,7 @@ public async Task> GetBySmartFolderIdAsync
///
public async Task> GetBySmartFolderIdAsync(int smartFolderId,
- int maxLinkedItems = 0,
+ int maxLinkedItems = 0, Func? dependencyFunc = null,
CancellationToken cancellationToken = default)
{
string?[] contentTypes =
@@ -300,9 +288,7 @@ public async Task> GetBySmartFolderIdAsync
.OfContentType(contentTypes)
.InSmartFolder(smartFolderId));
-
- var result = await ExecuteContentQuery(builder,
- () => CacheDependencyHelper.CreateContentItemTypeCacheDependency(contentTypes!),
+ var result = await ExecuteContentQuery(builder, dependencyFunc,
cancellationToken, CachePrefix, nameof(GetBySmartFolderIdAsync), contentTypes, smartFolderId,
maxLinkedItems);
@@ -311,7 +297,7 @@ public async Task> GetBySmartFolderIdAsync
///
public async Task> GetBySmartFolderIdAsync(
- int smartFolderId, int maxLinkedItems = 0,
+ int smartFolderId, int maxLinkedItems = 0, Func? dependencyFunc = null,
CancellationToken cancellationToken = default)
{
string?[] contentTypes =
@@ -332,17 +318,16 @@ public async Task> GetBySmartFolderIdAsync
.OfContentType(contentTypes)
.InSmartFolder(smartFolderId));
-
- var result = await ExecuteContentQuery(builder,
- () => CacheDependencyHelper.CreateContentItemTypeCacheDependency(contentTypes!),
+ var result = await ExecuteContentQuery(builder, dependencyFunc,
cancellationToken, CachePrefix, nameof(GetBySmartFolderIdAsync), contentTypes, smartFolderId,
maxLinkedItems);
return result;
}
+ ///
public async Task> GetBySmartFolderIdAsync(
- int smartFolderId, int maxLinkedItems = 0,
+ int smartFolderId, int maxLinkedItems = 0, Func? dependencyFunc = null,
CancellationToken cancellationToken = default)
{
string?[] contentTypes =
@@ -363,9 +348,7 @@ public async Task> GetBySmartFolderIdAsync
.OfContentType(contentTypes)
.InSmartFolder(smartFolderId));
-
- var result = await ExecuteContentQuery(builder,
- () => CacheDependencyHelper.CreateContentItemTypeCacheDependency(contentTypes!),
+ var result = await ExecuteContentQuery(builder, dependencyFunc,
cancellationToken, CachePrefix, nameof(GetBySmartFolderIdAsync), contentTypes, smartFolderId,
maxLinkedItems);
@@ -374,7 +357,7 @@ public async Task> GetBySmartFolderIdAsync
///
public async Task> GetByTagsAsync(string columnName, IEnumerable tagIdentifiers,
- int maxLinkedItems = 0,
+ int maxLinkedItems = 0, Func? dependencyFunc = null,
CancellationToken cancellationToken = default)
{
ArgumentException.ThrowIfNullOrEmpty(contentType);
@@ -395,8 +378,7 @@ public async Task> GetByTagsAsync(string columnName, IEnume
linkOptions => linkOptions.IncludeWebPageData()))
.Where(where => where.WhereContainsTags(columnName, tagIdents)));
- var result = await ExecuteContentQuery(builder,
- () => CacheDependencyHelper.CreateContentItemTypeCacheDependency([contentType]),
+ var result = await ExecuteContentQuery(builder, dependencyFunc,
cancellationToken, CachePrefix, nameof(GetByTagsAsync), contentType, columnName,
maxLinkedItems);
diff --git a/src/XperienceCommunity.DataRepository/DependencyInjection.cs b/src/XperienceCommunity.DataRepository/DependencyInjection.cs
index d255e5f..ca7847c 100644
--- a/src/XperienceCommunity.DataRepository/DependencyInjection.cs
+++ b/src/XperienceCommunity.DataRepository/DependencyInjection.cs
@@ -35,6 +35,8 @@ public static IServiceCollection AddXperienceDataRepositories(this IServiceColle
services.TryAddScoped();
+ services.TryAddScoped();
+
return services;
}
}
diff --git a/src/XperienceCommunity.DataRepository/Interfaces/ICacheDependencyBuilder.cs b/src/XperienceCommunity.DataRepository/Interfaces/ICacheDependencyBuilder.cs
new file mode 100644
index 0000000..37bd216
--- /dev/null
+++ b/src/XperienceCommunity.DataRepository/Interfaces/ICacheDependencyBuilder.cs
@@ -0,0 +1,18 @@
+using CMS.Helpers;
+
+namespace XperienceCommunity.DataRepository.Interfaces
+{
+ ///
+ /// Defines a method to create a CMS cache dependency for a collection of items.
+ ///
+ public interface ICacheDependencyBuilder
+ {
+ ///
+ /// Creates a CMS cache dependency for the specified collection of items.
+ ///
+ /// The type of items in the collection.
+ /// The collection of items for which to create the cache dependency.
+ /// A representing the cache dependency for the specified items.
+ CMSCacheDependency? Create(IEnumerable items);
+ }
+}
diff --git a/src/XperienceCommunity.DataRepository/Interfaces/IContentRepository.cs b/src/XperienceCommunity.DataRepository/Interfaces/IContentRepository.cs
index e806059..a6b88e1 100644
--- a/src/XperienceCommunity.DataRepository/Interfaces/IContentRepository.cs
+++ b/src/XperienceCommunity.DataRepository/Interfaces/IContentRepository.cs
@@ -1,4 +1,5 @@
using CMS.ContentEngine;
+using CMS.Helpers;
namespace XperienceCommunity.DataRepository.Interfaces;
@@ -10,10 +11,11 @@ public interface IContentRepository : IRepository where TEntit
/// The name of the entities.
/// The language name.
/// The maximum number of linked items to retrieve.
+ /// The function to create a cache dependency.
/// The cancellation token.
- /// A task that represents the asynchronous operation. The task result contains the collection of entities.
+ /// A task that represents the asynchronous operation. The task result contains the entity.
/// Thrown if content type is empty, or name is Empty.
- Task GetByNameAsync(string name, string? languageName, int maxLinkedItems = 0, CancellationToken cancellationToken = default);
+ Task GetByNameAsync(string name, string? languageName, int maxLinkedItems = 0, Func? dependencyFunc = null, CancellationToken cancellationToken = default);
///
/// Gets an entity by the specified identifier asynchronously.
@@ -21,76 +23,89 @@ public interface IContentRepository : IRepository where TEntit
/// The identifier of the entity.
/// The language name.
/// The maximum number of linked items to retrieve.
+ /// The function to create a cache dependency.
/// The cancellation token.
/// A task that represents the asynchronous operation. The task result contains the entity.
/// Thrown if content type is empty.
- Task GetByIdentifierAsync(Guid id, string? languageName, int maxLinkedItems = 0, CancellationToken cancellationToken = default);
+ Task GetByIdentifierAsync(Guid id, string? languageName, int maxLinkedItems = 0, Func? dependencyFunc = null, CancellationToken cancellationToken = default);
///
/// Gets entities by the specified smart folder ID asynchronously.
///
/// The ID of the smart folder.
/// Maximum linked items to return.
+ /// The function to create a cache dependency.
/// The cancellation token.
/// A task that represents the asynchronous operation. The task result contains the collection of entities.
/// Thrown if content type is empty.
- Task> GetBySmartFolderIdAsync(int smartFolderId, int maxLinkedItems = 0,
- CancellationToken cancellationToken = default);
+ Task> GetBySmartFolderIdAsync(int smartFolderId, int maxLinkedItems = 0, Func? dependencyFunc = null, CancellationToken cancellationToken = default);
///
/// Gets entities by the specified smart folder ID asynchronously.
///
+ /// The type of the first entity.
+ /// The type of the second entity.
/// The ID of the smart folder.
/// Maximum linked items to return.
+ /// The function to create a cache dependency.
/// The cancellation token.
/// A task that represents the asynchronous operation. The task result contains the collection of entities.
/// Thrown if content type is empty.
- Task> GetBySmartFolderIdAsync(int smartFolderId, int maxLinkedItems = 0,
- CancellationToken cancellationToken = default);
+ Task> GetBySmartFolderIdAsync(int smartFolderId, int maxLinkedItems = 0, Func? dependencyFunc = null, CancellationToken cancellationToken = default);
///
/// Gets entities by the specified smart folder ID asynchronously.
///
+ /// The type of the first entity.
+ /// The type of the second entity.
+ /// The type of the third entity.
/// The ID of the smart folder.
/// Maximum linked items to return.
+ /// The function to create a cache dependency.
/// The cancellation token.
/// A task that represents the asynchronous operation. The task result contains the collection of entities.
/// Thrown if content type is empty.
- Task> GetBySmartFolderIdAsync(int smartFolderId, int maxLinkedItems = 0,
- CancellationToken cancellationToken = default);
+ Task> GetBySmartFolderIdAsync(int smartFolderId, int maxLinkedItems = 0, Func? dependencyFunc = null, CancellationToken cancellationToken = default);
///
/// Gets entities by the specified smart folder ID asynchronously.
///
+ /// The type of the first entity.
+ /// The type of the second entity.
+ /// The type of the third entity.
+ /// The type of the fourth entity.
/// The ID of the smart folder.
/// Maximum linked items to return.
+ /// The function to create a cache dependency.
/// The cancellation token.
/// A task that represents the asynchronous operation. The task result contains the collection of entities.
/// Thrown if content type is empty.
- Task> GetBySmartFolderIdAsync(int smartFolderId, int maxLinkedItems = 0,
- CancellationToken cancellationToken = default);
-
+ Task> GetBySmartFolderIdAsync(int smartFolderId, int maxLinkedItems = 0, Func? dependencyFunc = null, CancellationToken cancellationToken = default);
///
/// Gets entities by the specified smart folder ID asynchronously.
///
+ /// The type of the first entity.
+ /// The type of the second entity.
+ /// The type of the third entity.
+ /// The type of the fourth entity.
+ /// The type of the fifth entity.
/// The ID of the smart folder.
/// Maximum linked items to return.
+ /// The function to create a cache dependency.
/// The cancellation token.
/// A task that represents the asynchronous operation. The task result contains the collection of entities.
/// Thrown if content type is empty.
- Task> GetBySmartFolderIdAsync(int smartFolderId, int maxLinkedItems = 0,
- CancellationToken cancellationToken = default);
+ Task> GetBySmartFolderIdAsync(int smartFolderId, int maxLinkedItems = 0, Func? dependencyFunc = null, CancellationToken cancellationToken = default);
///
- /// Gets entities by the specified smart folder ID asynchronously.
+ /// Gets entities by the specified smart folder GUID asynchronously.
///
/// The GUID of the smart folder.
/// Maximum linked items to return.
+ /// The function to create a cache dependency.
/// The cancellation token.
/// A task that represents the asynchronous operation. The task result contains the collection of entities.
/// Thrown if content type is empty.
- Task> GetBySmartFolderGuidAsync(Guid smartFolderId, int maxLinkedItems = 0,
- CancellationToken cancellationToken = default);
-
+ Task> GetBySmartFolderGuidAsync(Guid smartFolderId, int maxLinkedItems = 0, Func? dependencyFunc = null, CancellationToken cancellationToken = default);
}
diff --git a/src/XperienceCommunity.DataRepository/Interfaces/IPageRepository.cs b/src/XperienceCommunity.DataRepository/Interfaces/IPageRepository.cs
index bf05d88..6c12f97 100644
--- a/src/XperienceCommunity.DataRepository/Interfaces/IPageRepository.cs
+++ b/src/XperienceCommunity.DataRepository/Interfaces/IPageRepository.cs
@@ -1,4 +1,5 @@
-using CMS.Websites;
+using CMS.Helpers;
+using CMS.Websites;
namespace XperienceCommunity.DataRepository.Interfaces;
@@ -8,53 +9,64 @@ namespace XperienceCommunity.DataRepository.Interfaces;
/// The type of the entity.
public interface IPageRepository : IRepository where TEntity : class, IWebPageFieldsSource
{
-
///
/// Gets entities by the specified path asynchronously.
///
/// The path.
/// The language name.
- /// Maximum Linked Items to Return
+ /// Maximum linked items to return.
+ /// The function to create a cache dependency.
/// The cancellation token.
/// A task that represents the asynchronous operation. The task result contains the collection of entities.
- /// Thrown if content type is empty, or Path is Empty.
+ /// Thrown if content type is empty, or path is empty.
Task> GetByPathAsync(string path, string? languageName, int maxLinkedItems = 0,
- CancellationToken cancellationToken = default);
-
+ Func? dependencyFunc = null, CancellationToken cancellationToken = default);
///
/// Gets entities by the specified path asynchronously.
///
+ /// The type of the first entity.
+ /// The type of the second entity.
/// The path.
/// The language name.
- /// Maximum Linked Items to Return
+ /// Maximum linked items to return.
+ /// The function to create a cache dependency.
/// The cancellation token.
/// A task that represents the asynchronous operation. The task result contains the collection of entities.
- /// Thrown if content type is empty, or Path is Empty.
+ /// Thrown if content type is empty, or path is empty.
Task> GetByPathAsync(string path, string? languageName, int maxLinkedItems = 0,
- CancellationToken cancellationToken = default);
+ Func? dependencyFunc = null, CancellationToken cancellationToken = default);
///
/// Gets entities by the specified path asynchronously.
///
+ /// The type of the first entity.
+ /// The type of the second entity.
+ /// The type of the third entity.
/// The path.
/// The language name.
- /// Maximum Linked Items to Return
+ /// Maximum linked items to return.
+ /// The function to create a cache dependency.
/// The cancellation token.
/// A task that represents the asynchronous operation. The task result contains the collection of entities.
- /// Thrown if content type is empty, or Path is Empty.
+ /// Thrown if content type is empty, or path is empty.
Task> GetByPathAsync(string path, string? languageName, int maxLinkedItems = 0,
- CancellationToken cancellationToken = default);
+ Func? dependencyFunc = null, CancellationToken cancellationToken = default);
///
/// Gets entities by the specified path asynchronously.
///
+ /// The type of the first entity.
+ /// The type of the second entity.
+ /// The type of the third entity.
+ /// The type of the fourth entity.
/// The path.
/// The language name.
- /// Maximum Linked Items to Return
+ /// Maximum linked items to return.
+ /// The function to create a cache dependency.
/// The cancellation token.
/// A task that represents the asynchronous operation. The task result contains the collection of entities.
- /// Thrown if content type is empty, or Path is Empty.
+ /// Thrown if content type is empty, or path is empty.
Task> GetByPathAsync(string path, string? languageName, int maxLinkedItems = 0,
- CancellationToken cancellationToken = default);
+ Func? dependencyFunc = null, CancellationToken cancellationToken = default);
}
diff --git a/src/XperienceCommunity.DataRepository/Interfaces/IRepository.cs b/src/XperienceCommunity.DataRepository/Interfaces/IRepository.cs
index 9530545..ea6692e 100644
--- a/src/XperienceCommunity.DataRepository/Interfaces/IRepository.cs
+++ b/src/XperienceCommunity.DataRepository/Interfaces/IRepository.cs
@@ -1,4 +1,6 @@
-namespace XperienceCommunity.DataRepository.Interfaces;
+using CMS.Helpers;
+
+namespace XperienceCommunity.DataRepository.Interfaces;
///
/// Represents a generic repository interface for accessing data.
@@ -11,46 +13,54 @@ public interface IRepository
///
/// The name of the column to filter by tags.
/// The collection of tag identifiers.
- /// Maximum Linked Items to Return
+ /// Maximum linked items to return.
+ /// The function to create a cache dependency.
/// The cancellation token.
/// A task that represents the asynchronous operation. The task result contains the collection of entities.
/// Thrown if columnName is empty.
- Task> GetByTagsAsync(string columnName, IEnumerable tagIdentifiers, int maxLinkedItems = 0,
- CancellationToken cancellationToken = default);
+ Task> GetByTagsAsync(string columnName, IEnumerable tagIdentifiers,
+ int maxLinkedItems = 0,
+ Func? dependencyFunc = null, CancellationToken cancellationToken = default);
///
/// Gets all entities asynchronously based on the specified node GUIDs.
///
/// The node GUIDs.
/// The language name.
+ /// Maximum linked items to return.
+ /// The function to create a cache dependency.
/// The cancellation token.
- /// Maximum Linked Items to Return
/// A task that represents the asynchronous operation. The task result contains the collection of entities.
/// Thrown if content type is empty.
Task> GetAllAsync(IEnumerable nodeGuid, string? languageName, int maxLinkedItems = 0,
+ Func? dependencyFunc = null,
CancellationToken cancellationToken = default);
///
- /// Gets all entities asynchronously based on the specified Item IDs.
+ /// Gets all entities asynchronously based on the specified item IDs.
///
- /// The Item IDs.
+ /// The item IDs.
/// The language name.
- /// Maximum Linked Items to Return
+ /// Maximum linked items to return.
+ /// The function to create a cache dependency.
/// The cancellation token.
/// A task that represents the asynchronous operation. The task result contains the collection of entities.
/// Thrown if content type is empty.
Task> GetAllAsync(IEnumerable itemIds, string? languageName, int maxLinkedItems = 0,
+ Func? dependencyFunc = null,
CancellationToken cancellationToken = default);
///
/// Gets all entities asynchronously.
///
/// The language name.
- /// Maximum Linked Items to Return
+ /// Maximum linked items to return.
+ /// The function to create a cache dependency.
/// The cancellation token.
/// A task that represents the asynchronous operation. The task result contains the collection of entities.
/// Thrown if content type is empty.
Task> GetAllAsync(string? languageName, int maxLinkedItems = 0,
+ Func? dependencyFunc = null,
CancellationToken cancellationToken = default);
///
@@ -58,11 +68,13 @@ Task> GetAllAsync(string? languageName, int maxLinkedItems
///
/// The ID of the entity.
/// The language name.
- /// Maximum Linked Items to Return
+ /// Maximum linked items to return.
+ /// The function to create a cache dependency.
/// The cancellation token.
/// A task that represents the asynchronous operation. The task result contains the entity.
/// Thrown if content type is empty.
Task GetByIdAsync(int id, string? languageName, int maxLinkedItems = 0,
+ Func? dependencyFunc = null,
CancellationToken cancellationToken = default);
///
@@ -70,11 +82,13 @@ Task> GetAllAsync(string? languageName, int maxLinkedItems
///
/// The item GUID of the entity.
/// The language name.
- /// Maximum Linked Items to Return
+ /// Maximum linked items to return.
+ /// The function to create a cache dependency.
/// The cancellation token.
/// A task that represents the asynchronous operation. The task result contains the entity.
/// Thrown if content type is empty.
Task GetByGuidAsync(Guid itemGuid, string? languageName, int maxLinkedItems = 0,
+ Func? dependencyFunc = null,
CancellationToken cancellationToken = default);
///
@@ -82,10 +96,12 @@ Task> GetAllAsync(string? languageName, int maxLinkedItems
///
/// The type of the schema.
/// The language name.
- /// Maximum Linked Items to Return
+ /// Maximum linked items to return.
+ /// The function to create a cache dependency.
/// The cancellation token.
/// A task that represents the asynchronous operation. The task result contains the collection of entities.
/// Thrown if content type is empty.
Task> GetAllBySchema(string? languageName, int maxLinkedItems = 0,
+ Func? dependencyFunc = null,
CancellationToken cancellationToken = default);
}
diff --git a/src/XperienceCommunity.DataRepository/PageTypeRepository.cs b/src/XperienceCommunity.DataRepository/PageTypeRepository.cs
index 5598019..f1f59bb 100644
--- a/src/XperienceCommunity.DataRepository/PageTypeRepository.cs
+++ b/src/XperienceCommunity.DataRepository/PageTypeRepository.cs
@@ -13,8 +13,8 @@
namespace XperienceCommunity.DataRepository;
public sealed class PageTypeRepository(IProgressiveCache cache, IContentQueryExecutor executor,
- IWebsiteChannelContext websiteChannelContext, RepositoryOptions options) : BaseRepository(cache, executor,
- websiteChannelContext, options), IPageRepository
+ IWebsiteChannelContext websiteChannelContext, RepositoryOptions options, ICacheDependencyBuilder cacheDependencyBuilder) : BaseRepository(cache, executor,
+ websiteChannelContext, options, cacheDependencyBuilder), IPageRepository
where TEntity : class, IWebPageFieldsSource
{
private readonly string? contentType = typeof(TEntity)?.GetContentTypeName() ?? string.Empty;
@@ -23,7 +23,7 @@ public sealed class PageTypeRepository(IProgressiveCache cache, IConten
///
public async Task> GetAllAsync(string? languageName, int maxLinkedItems = 0,
- CancellationToken cancellationToken = default)
+ Func? dependencyFunc = null, CancellationToken cancellationToken = default)
{
ArgumentException.ThrowIfNullOrEmpty(contentType);
@@ -37,7 +37,7 @@ public async Task> GetAllAsync(string? languageName, int ma
.ForWebsite(WebsiteChannelContext.WebsiteChannelName))
.When(!string.IsNullOrEmpty(languageName), lang => lang.InLanguage(languageName));
- var result = await ExecutePageQuery(builder, () => CacheDependencyHelper.CreateWebPageItemTypeCacheDependency([contentType], WebsiteChannelContext.WebsiteChannelName),
+ var result = await ExecutePageQuery(builder, dependencyFunc,
cancellationToken, CachePrefix, nameof(GetAllAsync), languageName ?? string.Empty, contentType, maxLinkedItems);
return result;
@@ -45,8 +45,7 @@ public async Task> GetAllAsync(string? languageName, int ma
///
public async Task> GetAllAsync(IEnumerable nodeGuid, string? languageName,
- int maxLinkedItems = 0,
- CancellationToken cancellationToken = default)
+ int maxLinkedItems = 0, Func? dependencyFunc = null, CancellationToken cancellationToken = default)
{
ArgumentException.ThrowIfNullOrEmpty(contentType);
@@ -69,7 +68,7 @@ public async Task> GetAllAsync(IEnumerable nodeGuid,
guidList)))
.When(!string.IsNullOrEmpty(languageName), options => options.InLanguage(languageName));
- var result = await ExecutePageQuery(builder, () => CacheDependencyHelper.CreateWebPageItemGUIDCacheDependency(guidList),
+ var result = await ExecutePageQuery(builder, dependencyFunc,
cancellationToken, CachePrefix, nameof(GetAllAsync), guidList, languageName ?? string.Empty, contentType, maxLinkedItems);
return result;
@@ -77,7 +76,7 @@ public async Task> GetAllAsync(IEnumerable nodeGuid,
///
public async Task> GetAllAsync(IEnumerable itemIds, string? languageName,
- int maxLinkedItems = 0, CancellationToken cancellationToken = default)
+ int maxLinkedItems = 0, Func? dependencyFunc = null, CancellationToken cancellationToken = default)
{
ArgumentException.ThrowIfNullOrEmpty(contentType);
@@ -99,7 +98,7 @@ public async Task> GetAllAsync(IEnumerable itemIds, st
where.WhereIn(nameof(IWebPageFieldsSource.SystemFields.WebPageItemID), itemIdList)))
.When(!string.IsNullOrEmpty(languageName), lang => lang.InLanguage(languageName));
- var result = await ExecutePageQuery(builder, () => CacheDependencyHelper.CreateWebPageItemIDCacheDependency(itemIdList),
+ var result = await ExecutePageQuery(builder, dependencyFunc,
cancellationToken, CachePrefix, nameof(GetAllAsync), itemIdList, languageName ?? string.Empty, contentType, maxLinkedItems);
return result;
@@ -107,7 +106,7 @@ public async Task> GetAllAsync(IEnumerable itemIds, st
///
public async Task> GetAllBySchema(string? languageName, int maxLinkedItems = 0,
- CancellationToken cancellationToken = default)
+ Func? dependencyFunc = null, CancellationToken cancellationToken = default)
{
string? schemaName = typeof(TSchema).GetReusableFieldSchemaName();
@@ -122,41 +121,15 @@ public async Task> GetAllBySchema(string? language
.ForWebsite(WebsiteChannelContext.WebsiteChannelName))
.When(!string.IsNullOrEmpty(languageName), lang => lang.InLanguage(languageName));
- var queryOptions = GetQueryExecutionOptions();
+ var result = await ExecuteContentQuery(builder, dependencyFunc,
+ cancellationToken, CachePrefix, nameof(GetAllBySchema), schemaName, languageName ?? string.Empty, maxLinkedItems);
- if (WebsiteChannelContext.IsPreview)
- {
- var query = await Executor.GetMappedResult(builder, queryOptions,
- cancellationToken: cancellationToken);
-
- return query;
- }
-
- var cacheSettings =
- new CacheSettings(CacheMinutes, "data", nameof(GetAllBySchema), schemaName, languageName, maxLinkedItems);
-
- return await Cache.LoadAsync(async (cs, ct) =>
- {
- var result = (await Executor.GetMappedResult(builder, queryOptions,
- cancellationToken: ct))?.ToList() ?? [];
-
- cs.BoolCondition = result.Count > 0;
-
- if (!cs.Cached)
- {
- return result;
- }
-
- cs.CacheDependency = CacheHelper.GetCacheDependency(
- $"{schemaName}|all");
-
- return result;
- }, cacheSettings, cancellationToken);
+ return result;
}
///
public async Task GetByGuidAsync(Guid itemGuid, string? languageName, int maxLinkedItems = 0,
- CancellationToken cancellationToken = default)
+ Func? dependencyFunc = null, CancellationToken cancellationToken = default)
{
ArgumentException.ThrowIfNullOrEmpty(contentType);
@@ -174,7 +147,7 @@ public async Task> GetAllBySchema(string? language
.TopN(1))
.When(!string.IsNullOrEmpty(languageName), lang => lang.InLanguage(languageName));
- var result = await ExecutePageQuery(builder, () => CacheDependencyHelper.CreateWebPageItemGUIDCacheDependency([itemGuid]),
+ var result = await ExecutePageQuery(builder, dependencyFunc,
cancellationToken, CachePrefix, nameof(GetByGuidAsync), itemGuid, contentType, maxLinkedItems);
return result.FirstOrDefault();
@@ -182,7 +155,7 @@ public async Task> GetAllBySchema