// ========================================================================== // Squidex Headless CMS // ========================================================================== // Copyright (c) Squidex UG (haftungsbeschränkt) // All rights reserved. Licensed under the MIT license. // ========================================================================== using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; using System.Threading; using System.Threading.Tasks; using Squidex.Domain.Apps.Entities.Assets; using Squidex.Infrastructure; namespace Squidex.Domain.Apps.Entities.Contents.Queries { public abstract class QueryExecutionContext : Dictionary { private readonly SemaphoreSlim maxRequests = new SemaphoreSlim(10); private readonly ConcurrentDictionary cachedContents = new ConcurrentDictionary(); private readonly ConcurrentDictionary cachedAssets = new ConcurrentDictionary(); private readonly IContentQueryService contentQuery; private readonly IAssetQueryService assetQuery; public abstract Context Context { get; } protected QueryExecutionContext(IAssetQueryService assetQuery, IContentQueryService contentQuery) { Guard.NotNull(assetQuery, nameof(assetQuery)); Guard.NotNull(contentQuery, nameof(contentQuery)); this.assetQuery = assetQuery; this.contentQuery = contentQuery; } public virtual Task FindContentAsync(string schemaIdOrName, DomainId id, long version) { return contentQuery.FindAsync(Context, schemaIdOrName, id, version); } public virtual async Task> QueryAssetsAsync(Q q) { IResultList assets; await maxRequests.WaitAsync(); try { assets = await assetQuery.QueryAsync(Context, null, q); } finally { maxRequests.Release(); } foreach (var asset in assets) { cachedAssets[asset.Id] = asset; } return assets; } public virtual async Task> QueryContentsAsync(string schemaIdOrName, Q q) { IResultList contents; await maxRequests.WaitAsync(); try { contents = await contentQuery.QueryAsync(Context, schemaIdOrName, q); } finally { maxRequests.Release(); } foreach (var content in contents) { cachedContents[content.Id] = content; } return contents; } public virtual async Task> GetReferencedAssetsAsync(ICollection ids) { Guard.NotNull(ids, nameof(ids)); var notLoadedAssets = new HashSet(ids.Where(id => !cachedAssets.ContainsKey(id))); if (notLoadedAssets.Count > 0) { IResultList assets; await maxRequests.WaitAsync(); try { assets = await assetQuery.QueryAsync(Context, null, Q.Empty.WithIds(notLoadedAssets).WithoutTotal()); } finally { maxRequests.Release(); } foreach (var asset in assets) { cachedAssets[asset.Id] = asset; } } return ids.Select(cachedAssets.GetOrDefault).NotNull().ToList(); } public virtual async Task> GetReferencedContentsAsync(ICollection ids) { Guard.NotNull(ids, nameof(ids)); var notLoadedContents = ids.Where(id => !cachedContents.ContainsKey(id)).ToList(); if (notLoadedContents.Count > 0) { IResultList contents; await maxRequests.WaitAsync(); try { contents = await contentQuery.QueryAsync(Context, Q.Empty.WithIds(notLoadedContents).WithoutTotal()); } finally { maxRequests.Release(); } foreach (var content in contents) { cachedContents[content.Id] = content; } } return ids.Select(cachedContents.GetOrDefault).NotNull().ToList(); } } }