Last active
October 10, 2024 07:14
-
-
Save MilanLund/7920e50e724ef23bf9b108d9c3311a47 to your computer and use it in GitHub Desktop.
Repository pattern with a Generic type for a Xperience by Kentico project
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This gist represents an implementation of repository pattern for Xperiece by Kentico. | |
The details about this gist could be found on https://www.milanlund.com/knowledge-base/guide-to-repository-pattern-with-a-generic-type-in-xperience-by-kentico |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using CMS.ContentEngine; | |
using CMS.Helpers; | |
using CMS.Websites; | |
using CMS.Websites.Routing; | |
namespace YourProject.Models; | |
public abstract class ContentRepositoryBase | |
{ | |
protected IWebsiteChannelContext WebsiteChannelContext { get; init; } | |
private readonly IContentQueryExecutor executor; | |
private readonly IProgressiveCache cache; | |
public ContentRepositoryBase(IWebsiteChannelContext websiteChannelContext, IContentQueryExecutor executor, IProgressiveCache cache) | |
{ | |
WebsiteChannelContext = websiteChannelContext; | |
this.executor = executor; | |
this.cache = cache; | |
} | |
public Task<IEnumerable<T>> GetCachedQueryResult<T>(GetCachedQueryResultParameters<T> parameters) | |
{ | |
if (parameters.QueryBuilder is null) | |
{ | |
throw new ArgumentNullException(nameof(parameters.QueryBuilder)); | |
} | |
if (parameters.CacheSettings is null) | |
{ | |
throw new ArgumentNullException(nameof(parameters.CacheSettings)); | |
} | |
if (parameters.BuildCacheItemNames is null) | |
{ | |
throw new ArgumentNullException(nameof(parameters.BuildCacheItemNames)); | |
} | |
return GetCachedQueryResultInternal(parameters); | |
} | |
private async Task<IEnumerable<T>> GetCachedQueryResultInternal<T>(GetCachedQueryResultParameters<T> parameters) | |
{ | |
if (WebsiteChannelContext.IsPreview) | |
{ | |
var queryOptions = new ContentQueryExecutionOptions() | |
{ | |
ForPreview = true | |
}; | |
return await GetMappedResultByContentItemType(parameters, queryOptions); | |
} | |
return await cache.LoadAsync(async (cacheSettings) => | |
{ | |
var result = await GetMappedResultByContentItemType(parameters); | |
if (cacheSettings.Cached = result != null && result.Any()) | |
{ | |
cacheSettings.CacheDependency = CacheHelper.GetCacheDependency(await parameters.BuildCacheItemNames(new GetDependencyCacheKeysParameters<T> | |
{ | |
CancellationToken = parameters.CancellationToken, | |
Items = result | |
})); | |
} | |
return result; | |
}, parameters.CacheSettings); | |
} | |
private async Task<List<T>> GetMappedResultByContentItemType<T>(CachedQueryResultParameters<T> parameters, ContentQueryExecutionOptions queryOptions = null) | |
{ | |
switch (parameters.ContentItemType) | |
{ | |
case ContentItemType.ReusableContent: | |
return (await executor.GetMappedResult<T>(parameters.QueryBuilder, queryOptions, cancellationToken: parameters.CancellationToken)).ToList(); | |
case ContentItemType.WebPage: | |
return (await executor.GetMappedWebPageResult<T>(parameters.QueryBuilder, queryOptions, cancellationToken: parameters.CancellationToken)).ToList(); | |
default: | |
throw new NotSupportedException($"ContentItemType '{parameters.ContentItemType}' is not supported. Use 'ReusableContent' or 'WebPage' instead."); | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using CMS.ContentEngine; | |
using CMS.Helpers; | |
namespace YourProject.Models; | |
public abstract class RepositoryParameters | |
{ | |
public CancellationToken CancellationToken { get; set; } | |
public int LinkedItemsMaxLevel { get; set; } = 4; | |
} | |
public class GetByGuidsRepositoryParameters : RepositoryParameters | |
{ | |
public ICollection<Guid> Guids { get; set; } | |
} | |
public enum ContentItemType | |
{ | |
WebPage, | |
ReusableContent | |
} | |
public class GetCachedQueryResultParameters<T> | |
{ | |
public ContentItemQueryBuilder QueryBuilder { get; set; } | |
public CacheSettings CacheSettings { get; set; } | |
public Func<GetDependencyCacheKeysParameters<T>, Task<ISet<string>>> BuildCacheItemNames { get; set; } | |
public ContentItemType ContentItemType { get; set; } | |
public CancellationToken CancellationToken { get; set; } | |
} | |
public class GetDependencyCacheKeysParameters<T> : RepositoryParameters | |
{ | |
public IEnumerable<T> Items { get; set; } | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using YourProject; | |
using YourProject.Controllers; | |
using YourProject.Models; | |
using Kentico.Content.Web.Mvc; | |
using Kentico.Content.Web.Mvc.Routing; | |
using Microsoft.AspNetCore.Mvc; | |
[assembly: RegisterWebPageRoute(Page.CONTENT_TYPE_NAME, typeof(PageController), WebsiteChannelNames = [WebsiteConstants.WEBSITE_CHANNEL_NAME])] | |
namespace YourProject.Controllers; | |
public class PageController : Controller | |
{ | |
private readonly WebPageRepository webPageRepository; | |
private readonly IWebPageDataContextRetriever webPageDataContextRetriever; | |
public PageController(WebPageRepository webPageRepository, IWebPageDataContextRetriever webPageDataContextRetriever) | |
{ | |
this.webPageRepository = webPageRepository; | |
this.webPageDataContextRetriever = webPageDataContextRetriever; | |
} | |
public async Task<IActionResult> Index() | |
{ | |
var webPage = webPageDataContextRetriever.Retrieve().WebPage; | |
var pages = await webPageRepository.GetPages<Page>(new GetByGuidsRepositoryParameters() | |
{ | |
Guids = new List<Guid> { webPage.WebPageItemGUID }, | |
CancellationToken = HttpContext.RequestAborted | |
}); | |
if (pages == null || !pages.Any()) | |
{ | |
return NotFound(); | |
} | |
var page = pages.First(); | |
var model = PageViewModel.GetViewModel(page); | |
return View(model); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using CMS.ContentEngine; | |
using CMS.Helpers; | |
using CMS.Websites.Routing; | |
namespace YourProject.Models; | |
public class ReusableContentRepository : ContentRepositoryBase | |
{ | |
private readonly ILinkedItemsDependencyAsyncRetriever linkedItemsDependencyRetriever; | |
public ReusableContentRepository( | |
IWebsiteChannelContext websiteChannelContext, | |
IContentQueryExecutor executor, | |
IProgressiveCache cache, | |
ILinkedItemsDependencyAsyncRetriever linkedItemsDependencyRetriever) | |
: base(websiteChannelContext, executor, cache) | |
{ | |
this.linkedItemsDependencyRetriever = linkedItemsDependencyRetriever; | |
} | |
public async Task<IEnumerable<T>> GetItems<T>(GetByGuidsRepositoryParameters parameters = null) where T : IContentItemFieldsSource | |
{ | |
if (parameters == null) | |
{ | |
parameters = new GetByGuidsRepositoryParameters(); | |
} | |
var typeName = typeof(T).FullName; | |
var query = new ContentItemQueryBuilder() | |
.ForContentType(typeName, | |
config => config | |
.WithLinkedItems(parameters.LinkedItemsMaxLevel) | |
.Where(parameters.Guids != null ? where => where.WhereIn(nameof(IContentQueryDataContainer.ContentItemGUID), parameters.Guids) : null)) | |
.InLanguage(WebsiteConstants.LANGUAGE_DEFAULT); | |
var cacheSettings = new CacheSettings(WebsiteConstants.CACHE_MINUTES, WebsiteChannelContext.WebsiteChannelName, typeName, WebsiteConstants.LANGUAGE_DEFAULT, parameters.Guids != null ? parameters.Guids.Select(guid => guid.ToString()).Join("|") : "all"); | |
return await GetCachedQueryResult<T>(new GetCachedQueryResultParameters<T> | |
{ | |
QueryBuilder = query, | |
CacheSettings = cacheSettings, | |
BuildCacheItemNames = GetDependencyCacheKeys, | |
ContentItemType = ContentItemType.ReusableContent, | |
CancellationToken = parameters.CancellationToken | |
}); | |
} | |
private async Task<ISet<string>> GetDependencyCacheKeys<T>(GetDependencyCacheKeysParameters<T> parameters) where T : IContentItemFieldsSource | |
{ | |
var dependencyCacheKeys = | |
(await linkedItemsDependencyRetriever | |
.Get(parameters.Items.Select(item => item.SystemFields.ContentItemID), maxLevel: parameters.LinkedItemsMaxLevel)) | |
.ToHashSet(StringComparer.InvariantCultureIgnoreCase); | |
foreach (var item in parameters.Items) | |
{ | |
dependencyCacheKeys.Add(CacheHelper.BuildCacheItemName(new[] { "contentitem", "bychannel", WebsiteChannelContext.WebsiteChannelName, "bycontenttype", typeof(T).FullName }, false)); | |
} | |
return dependencyCacheKeys; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
... | |
await reusableContentRepository.GetItems<ReusableContent>(new GetByGuidsRepositoryParameters() | |
{ | |
Guids = guidsList, | |
CancellationToken = HttpContext.RequestAborted | |
}); | |
... |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using CMS.ContentEngine; | |
using CMS.DataEngine; | |
using CMS.Helpers; | |
using CMS.Websites; | |
using CMS.Websites.Routing; | |
namespace YourProject.Models; | |
public class WebPageRepository : ContentRepositoryBase | |
{ | |
private readonly IWebPageLinkedItemsDependencyAsyncRetriever webPageLinkedItemsDependencyAsyncRetriever; | |
public WebPageRepository(IWebsiteChannelContext websiteChannelContext, IContentQueryExecutor executor, IProgressiveCache cache, IWebPageLinkedItemsDependencyAsyncRetriever webPageLinkedItemsDependencyAsyncRetriever) | |
: base(websiteChannelContext, executor, cache) | |
{ | |
this.webPageLinkedItemsDependencyAsyncRetriever = webPageLinkedItemsDependencyAsyncRetriever; | |
} | |
public async Task<IEnumerable<T>> GetPages<T>(GetByGuidsRepositoryParameters parameters = null) where T : IWebPageFieldsSource | |
{ | |
if (parameters == null) | |
{ | |
parameters = new GetByGuidsRepositoryParameters(); | |
} | |
var typeName = typeof(T).FullName; | |
var query = new ContentItemQueryBuilder() | |
.ForContentType(typeName, | |
config => config | |
.WithLinkedItems(parameters.LinkedItemsMaxLevel) | |
.OrderBy(OrderByColumn.Asc(nameof(IWebPageFieldsSource.SystemFields.WebPageItemOrder))) | |
.ForWebsite(WebsiteChannelContext.WebsiteChannelName) | |
.Where(parameters?.Guids != null ? where => where.WhereIn(nameof(IWebPageContentQueryDataContainer.WebPageItemGUID), parameters.Guids) : null)) | |
.InLanguage(WebsiteConstants.LANGUAGE_DEFAULT); | |
var cacheSettings = new CacheSettings(WebsiteConstants.CACHE_MINUTES, WebsiteChannelContext.WebsiteChannelName, typeName, WebsiteConstants.LANGUAGE_DEFAULT, parameters.Guids != null ? parameters.Guids.Select(guid => guid.ToString()).Join("|") : "all"); | |
return await GetCachedQueryResult(new GetCachedQueryResultParameters<T> | |
{ | |
QueryBuilder = query, | |
CacheSettings = cacheSettings, | |
BuildCacheItemNames = GetDependencyCacheKeys, | |
ContentItemType = ContentItemType.WebPage, | |
CancellationToken = parameters.CancellationToken | |
}); | |
} | |
private async Task<ISet<string>> GetDependencyCacheKeys<T>(GetDependencyCacheKeysParameters<T> parameters) where T : IWebPageFieldsSource | |
{ | |
var dependencyCacheKeys = | |
(await webPageLinkedItemsDependencyAsyncRetriever | |
.Get(parameters.Items.Select(item => item.SystemFields.WebPageItemID), maxLevel: parameters.LinkedItemsMaxLevel)) | |
.ToHashSet(StringComparer.InvariantCultureIgnoreCase); | |
foreach (var item in parameters.Items) | |
{ | |
dependencyCacheKeys.Add(CacheHelper.BuildCacheItemName(new[] { "webpageitem", "bychannel", WebsiteChannelContext.WebsiteChannelName, "bycontenttype", typeof(T).FullName }, false)); | |
} | |
return dependencyCacheKeys; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment