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
@@ -1,4 +1,4 @@
using Microsoft.Extensions.DependencyInjection;

Check notice on line 1 in src/Umbraco.Core/Cache/Refreshers/Implement/ContentCacheRefresher.cs

View check run for this annotation

CodeScene Delta Analysis / CodeScene Code Health Review (v17/dev)

✅ Getting better: Overall Code Complexity

The mean cyclomatic complexity decreases from 4.64 to 4.52, threshold = 4 This file has many conditional statements (e.g. if, for, while) across its implementation, leading to lower code health. Avoid adding more conditionals.
using Umbraco.Cms.Core.DependencyInjection;
using Umbraco.Cms.Core.Events;
using Umbraco.Cms.Core.Models;
Expand Down Expand Up @@ -38,11 +38,12 @@
private readonly ICacheManager _cacheManager;
private readonly IPublishStatusManagementService _publishStatusManagementService;
private readonly IIdKeyMap _idKeyMap;
private readonly IPublishedContentTypeCache _publishedContentTypeCache;

/// <summary>
/// Initializes a new instance of the <see cref="ContentCacheRefresher"/> class.
/// </summary>
[Obsolete("Please use the constructor taking all parameters. Scheduled for removal in Umbraco 19.")]
[Obsolete("Please use the constructor also taking IPublishedContentTypeCache. Scheduled for removal in Umbraco 19.")]

Check warning on line 46 in src/Umbraco.Core/Cache/Refreshers/Implement/ContentCacheRefresher.cs

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Do not forget to remove this deprecated code someday.

See more on https://sonarcloud.io/project/issues?id=umbraco_Umbraco-CMS&issues=AaEV4kxbJwtYruktPJ27&open=AaEV4kxbJwtYruktPJ27&pullRequest=24126
public ContentCacheRefresher(
AppCaches appCaches,
IJsonSerializer serializer,
Expand Down Expand Up @@ -73,29 +74,71 @@
contentService,
publishStatusManagementService,
documentCacheService,
cacheManager)
cacheManager,
StaticServiceProvider.Instance.GetRequiredService<IPublishedContentTypeCache>())
{
}

/// <summary>
/// Initializes a new instance of the <see cref="ContentCacheRefresher"/> class.
/// </summary>
[Obsolete("Please use the constructor also taking IPublishedContentTypeCache. Scheduled for removal in Umbraco 19.")]

Check warning on line 85 in src/Umbraco.Core/Cache/Refreshers/Implement/ContentCacheRefresher.cs

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Do not forget to remove this deprecated code someday.

See more on https://sonarcloud.io/project/issues?id=umbraco_Umbraco-CMS&issues=AaEVxiUJ0O90mfPbazrr&open=AaEVxiUJ0O90mfPbazrr&pullRequest=24126
public ContentCacheRefresher(
AppCaches appCaches,

Check warning on line 87 in src/Umbraco.Core/Cache/Refreshers/Implement/ContentCacheRefresher.cs

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

The documentation for parameter 'appCaches' is missing

See more on https://sonarcloud.io/project/issues?id=umbraco_Umbraco-CMS&issues=AaEVxiUJ0O90mfPbazrs&open=AaEVxiUJ0O90mfPbazrs&pullRequest=24126
IJsonSerializer serializer,

Check warning on line 88 in src/Umbraco.Core/Cache/Refreshers/Implement/ContentCacheRefresher.cs

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

The documentation for parameter 'serializer' is missing

See more on https://sonarcloud.io/project/issues?id=umbraco_Umbraco-CMS&issues=AaEVxiUJ0O90mfPbazrt&open=AaEVxiUJ0O90mfPbazrt&pullRequest=24126
IIdKeyMap idKeyMap,

Check warning on line 89 in src/Umbraco.Core/Cache/Refreshers/Implement/ContentCacheRefresher.cs

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

The documentation for parameter 'idKeyMap' is missing

See more on https://sonarcloud.io/project/issues?id=umbraco_Umbraco-CMS&issues=AaEVxiUJ0O90mfPbazru&open=AaEVxiUJ0O90mfPbazru&pullRequest=24126
IDomainService domainService,

Check warning on line 90 in src/Umbraco.Core/Cache/Refreshers/Implement/ContentCacheRefresher.cs

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

The documentation for parameter 'domainService' is missing

See more on https://sonarcloud.io/project/issues?id=umbraco_Umbraco-CMS&issues=AaEVxiUJ0O90mfPbazrv&open=AaEVxiUJ0O90mfPbazrv&pullRequest=24126
IEventAggregator eventAggregator,

Check warning on line 91 in src/Umbraco.Core/Cache/Refreshers/Implement/ContentCacheRefresher.cs

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

The documentation for parameter 'eventAggregator' is missing

See more on https://sonarcloud.io/project/issues?id=umbraco_Umbraco-CMS&issues=AaEVxiUJ0O90mfPbazrw&open=AaEVxiUJ0O90mfPbazrw&pullRequest=24126
ICacheRefresherNotificationFactory factory,

Check warning on line 92 in src/Umbraco.Core/Cache/Refreshers/Implement/ContentCacheRefresher.cs

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

The documentation for parameter 'factory' is missing

See more on https://sonarcloud.io/project/issues?id=umbraco_Umbraco-CMS&issues=AaEVxiUJ0O90mfPbazrx&open=AaEVxiUJ0O90mfPbazrx&pullRequest=24126
IDocumentUrlService documentUrlService,

Check warning on line 93 in src/Umbraco.Core/Cache/Refreshers/Implement/ContentCacheRefresher.cs

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

The documentation for parameter 'documentUrlService' is missing

See more on https://sonarcloud.io/project/issues?id=umbraco_Umbraco-CMS&issues=AaEVxiUJ0O90mfPbazry&open=AaEVxiUJ0O90mfPbazry&pullRequest=24126
IDocumentUrlAliasService documentUrlAliasService,

Check warning on line 94 in src/Umbraco.Core/Cache/Refreshers/Implement/ContentCacheRefresher.cs

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

The documentation for parameter 'documentUrlAliasService' is missing

See more on https://sonarcloud.io/project/issues?id=umbraco_Umbraco-CMS&issues=AaEVxiUJ0O90mfPbazrz&open=AaEVxiUJ0O90mfPbazrz&pullRequest=24126
IDomainCacheService domainCacheService,

Check warning on line 95 in src/Umbraco.Core/Cache/Refreshers/Implement/ContentCacheRefresher.cs

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

The documentation for parameter 'domainCacheService' is missing

See more on https://sonarcloud.io/project/issues?id=umbraco_Umbraco-CMS&issues=AaEVxiUJ0O90mfPbazr0&open=AaEVxiUJ0O90mfPbazr0&pullRequest=24126
IDocumentNavigationQueryService documentNavigationQueryService,

Check warning on line 96 in src/Umbraco.Core/Cache/Refreshers/Implement/ContentCacheRefresher.cs

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

The documentation for parameter 'documentNavigationQueryService' is missing

See more on https://sonarcloud.io/project/issues?id=umbraco_Umbraco-CMS&issues=AaEVxiUJ0O90mfPbazr1&open=AaEVxiUJ0O90mfPbazr1&pullRequest=24126
IDocumentNavigationManagementService documentNavigationManagementService,

Check warning on line 97 in src/Umbraco.Core/Cache/Refreshers/Implement/ContentCacheRefresher.cs

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

The documentation for parameter 'documentNavigationManagementService' is missing

See more on https://sonarcloud.io/project/issues?id=umbraco_Umbraco-CMS&issues=AaEVxiUJ0O90mfPbazr2&open=AaEVxiUJ0O90mfPbazr2&pullRequest=24126
IContentService contentService,

Check warning on line 98 in src/Umbraco.Core/Cache/Refreshers/Implement/ContentCacheRefresher.cs

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

The documentation for parameter 'contentService' is missing

See more on https://sonarcloud.io/project/issues?id=umbraco_Umbraco-CMS&issues=AaEVxiUJ0O90mfPbazr3&open=AaEVxiUJ0O90mfPbazr3&pullRequest=24126
IPublishStatusManagementService publishStatusManagementService,

Check warning on line 99 in src/Umbraco.Core/Cache/Refreshers/Implement/ContentCacheRefresher.cs

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

The documentation for parameter 'publishStatusManagementService' is missing

See more on https://sonarcloud.io/project/issues?id=umbraco_Umbraco-CMS&issues=AaEVxiUJ0O90mfPbazr4&open=AaEVxiUJ0O90mfPbazr4&pullRequest=24126
IDocumentCacheService documentCacheService,

Check warning on line 100 in src/Umbraco.Core/Cache/Refreshers/Implement/ContentCacheRefresher.cs

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

The documentation for parameter 'documentCacheService' is missing

See more on https://sonarcloud.io/project/issues?id=umbraco_Umbraco-CMS&issues=AaEVxiUJ0O90mfPbazr5&open=AaEVxiUJ0O90mfPbazr5&pullRequest=24126
ICacheManager cacheManager)
: this(
appCaches,
serializer,
idKeyMap,
domainService,
eventAggregator,
factory,
documentUrlService,
documentUrlAliasService,
domainCacheService,
documentNavigationQueryService,
documentNavigationManagementService,
contentService,
publishStatusManagementService,
documentCacheService,
cacheManager,
StaticServiceProvider.Instance.GetRequiredService<IPublishedContentTypeCache>())
{
}

/// <summary>
/// Initializes a new instance of the <see cref="ContentCacheRefresher"/> class.
/// </summary>
public ContentCacheRefresher(
AppCaches appCaches,
IJsonSerializer serializer,
IIdKeyMap idKeyMap,
IDomainService domainService,
IEventAggregator eventAggregator,
ICacheRefresherNotificationFactory factory,
IDocumentUrlService documentUrlService,
IDocumentUrlAliasService documentUrlAliasService,
IDomainCacheService domainCacheService,
IDocumentNavigationQueryService documentNavigationQueryService,
IDocumentNavigationManagementService documentNavigationManagementService,
IContentService contentService,
IPublishStatusManagementService publishStatusManagementService,
IDocumentCacheService documentCacheService,
ICacheManager cacheManager,

Check warning on line 140 in src/Umbraco.Core/Cache/Refreshers/Implement/ContentCacheRefresher.cs

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

The documentation for parameter 'cacheManager' is missing

See more on https://sonarcloud.io/project/issues?id=umbraco_Umbraco-CMS&issues=AaEVxiUJ0O90mfPbazr6&open=AaEVxiUJ0O90mfPbazr6&pullRequest=24126
IPublishedContentTypeCache publishedContentTypeCache)

Check warning on line 141 in src/Umbraco.Core/Cache/Refreshers/Implement/ContentCacheRefresher.cs

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

The documentation for parameter 'publishedContentTypeCache' is missing

See more on https://sonarcloud.io/project/issues?id=umbraco_Umbraco-CMS&issues=AaEVxiUJ0O90mfPbazr7&open=AaEVxiUJ0O90mfPbazr7&pullRequest=24126
: base(appCaches, serializer, eventAggregator, factory)
{
_idKeyMap = idKeyMap;
Expand All @@ -108,6 +151,7 @@
_contentService = contentService;
_documentCacheService = documentCacheService;
_publishStatusManagementService = publishStatusManagementService;
_publishedContentTypeCache = publishedContentTypeCache;

// TODO: Ideally we should inject IElementsCache
// this interface is in infrastructure, and changing this is very breaking
Expand Down Expand Up @@ -193,6 +237,15 @@
/// <inheritdoc/>
public override void Refresh(JsonPayload[] payloads)
{
// A "refresh all" is the signal that the in-memory published caches are being rebuilt from the database
// cache. Published content types are cached independently of the content they describe, so they have to
// be dropped here too - otherwise the rebuilt content is projected through the same definitions as
// before and the reload has no effect on anything the content type governs.
if (payloads.Any(x => x.ChangeTypes.HasType(TreeChangeTypes.RefreshAll)))
{
_publishedContentTypeCache.ClearAll();
}

var idsRemoved = new HashSet<int>();

foreach (JsonPayload payload in payloads)
Expand Down
144 changes: 113 additions & 31 deletions src/Umbraco.Core/Services/ContentTypeServiceBase{TRepository,TItem}.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using System.Globalization;

Check notice on line 1 in src/Umbraco.Core/Services/ContentTypeServiceBase{TRepository,TItem}.cs

View check run for this annotation

CodeScene Delta Analysis / CodeScene Code Health Review (v17/dev)

✅ Getting better: Primitive Obsession

The ratio of primitive types in function arguments decreases from 56.91% to 56.80%, threshold = 30.0% The functions in this file have too many primitive types (e.g. int, double, float) in their function argument lists. Using many primitive types lead to the code smell Primitive Obsession. Avoid adding more primitive arguments.
using System.Runtime.InteropServices;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
Expand Down Expand Up @@ -344,129 +344,154 @@
{
// find all content types impacted by the changes,
// - content type alias changed
// - content type property removed, or alias changed
// - content type composition removed (not testing if composition had properties...)
// - content type property removed, added, or alias changed
// - content type composition removed or added (not testing if composition had properties...)
// - content type variation changed
// - property type variation changed
//
// because these are the changes that would impact the raw content data
// because these are the changes that alter the set of property types a content type exposes,
// and so the set exposed by every content type that derives from it

// note
// this is meant to run *after* uow.Commit() so must use WasPropertyDirty() everywhere
// instead of IsPropertyDirty() since dirty properties have been reset already

var changes = new List<ContentTypeChange<TItem>>();

// Shared by every content type in the batch, so a deriving type reached from more than one of them is
// the same instance each time, and AddChange merges its changes into a single entry.
ILookup<int, TItem>? directReferencingTypes = null;

// Track which types genuinely need a raw cmsContentNu rebuild vs. which only had a property removed.
// A type can appear via more than one path (e.g. a batch save touching a composition, where the same
// type is both saved directly and returned by GetComposedOf as a different instance), so we key these
// by Id — not entity reference — and resolve the RawDataUnaffected flag once at the end: it is only
// type is both saved directly and returned by the composition traversal as a different instance), so we
// key these by Id — not entity reference — and resolve the RawDataUnaffected flag once at the end: it is only
// safe when *nothing* required a rebuild for that Id.
var rebuildRequiredIds = new HashSet<int>();
var rawDataUnaffectedCandidateIds = new HashSet<int>();

foreach (TItem contentType in contentTypes)
{
var dirty = (IRememberBeingDirty)contentType;

// skip new content types
// TODO: This used to be WasPropertyDirty("HasIdentity") but i don't think that actually worked for detecting new entities this does seem to work properly
var isNewContentType = dirty.WasPropertyDirty("Id");
if (isNewContentType)
{
AddChange(changes, contentType, ContentTypeChangeTypes.Create);
continue;
}

// alias change?
var hasAliasChanged = dirty.WasPropertyDirty("Alias");

// existing property alias change?
var hasAnyPropertyChangedAlias = contentType.PropertyTypes.Any(propertyType =>
{
// skip new properties
// TODO: This used to be WasPropertyDirty("HasIdentity") but i don't think that actually worked for detecting new entities this does seem to work properly
var isNewProperty = propertyType.WasPropertyDirty("Id");
if (isNewProperty)
{
return false;
}

// alias change?
return propertyType.WasPropertyDirty("Alias");
});

// removed properties?
var hasAnyPropertyBeenRemoved = dirty.WasPropertyDirty("HasPropertyTypeBeenRemoved");

// removed compositions?
var hasAnyCompositionBeenRemoved = dirty.WasPropertyDirty("HasCompositionTypeBeenRemoved");

// variation changed?
var hasContentTypeVariationChanged = dirty.WasPropertyDirty("Variations");

// property variation change?
var hasAnyPropertyVariationChanged = contentType.WasPropertyTypeVariationChanged();

// A composition change dirties the composition collection (add or remove). Both directions change the
// set of properties this content type - and every type composed of or inheriting from it - exposes, so
// it is a main-impact change that must refresh this type and its descendants. Whether it also needs a
// raw cmsContentNu rebuild is decided separately below: an addition on its own does not (its aliases
// have no stored values yet), but a removal - or an addition that reintroduces a just-removed alias -
// does. See the rawDataAffected calculation.
var hasCompositionChanged = dirty.WasPropertyDirty("ContentTypeComposition");

// added properties?
var hasAnyPropertyBeenAdded = contentType.PropertyTypes.Any(propertyType => propertyType.WasPropertyDirty("Id"));

// main impact on properties?
var hasPropertyMainImpact = hasContentTypeVariationChanged || hasAnyPropertyVariationChanged
|| hasAnyCompositionBeenRemoved || hasAnyPropertyBeenRemoved || hasAnyPropertyChangedAlias
|| hasCompositionChanged;

// The raw cmsContentNu blob is keyed by property alias, so a structural change only requires a
// rebuild when it re-keys existing stored values or lets a stale value resolve to a different
// property. It does NOT when the only structural change is:
// - a property removal - the orphaned value simply stops resolving and is never read again, or
// - a composition being added - its new aliases have no stored value yet.
// In those cases clearing the converted content cache is enough (the content type cache is
// refreshed regardless). An alias or variation change, a composition removal, or a property
// removal combined with a composition change (which can reintroduce the removed alias behind a
// different property type) all re-key or revive stored values and therefore need a rebuild.
var rawDataAffected =
hasAliasChanged ||
hasAnyPropertyChangedAlias ||
hasContentTypeVariationChanged ||
hasAnyPropertyVariationChanged ||
hasAnyCompositionBeenRemoved ||
(hasAnyPropertyBeenRemoved && hasCompositionChanged);
var rawDataUnaffected = rawDataAffected is false;

if (hasAliasChanged || hasPropertyMainImpact)
{
// The raw cmsContentNu blob is keyed by property alias, so a structural change only requires a
// rebuild when it re-keys existing stored values or lets a stale value resolve to a different
// property. It does NOT when the only structural change is:
// - a property removal - the orphaned value simply stops resolving and is never read again, or
// - a composition being added - its new aliases have no stored value yet.
// In those cases clearing the converted content cache is enough (the content type cache is
// refreshed regardless). An alias or variation change, a composition removal, or a property
// removal combined with a composition change (which can reintroduce the removed alias behind a
// different property type) all re-key or revive stored values and therefore need a rebuild.
var rawDataAffected =
hasAliasChanged ||
hasAnyPropertyChangedAlias ||
hasContentTypeVariationChanged ||
hasAnyPropertyVariationChanged ||
hasAnyCompositionBeenRemoved ||
(hasAnyPropertyBeenRemoved && hasCompositionChanged);
var rawDataUnaffected = rawDataAffected is false;

// add that one, as a main change
AddChange(changes, contentType, ContentTypeChangeTypes.RefreshMain);
(rawDataUnaffected ? rawDataUnaffectedCandidateIds : rebuildRequiredIds).Add(contentType.Id);

// Add VariationChanged flag if content type variation changed.
// This is used by DocumentUrlService to rebuild URL cache with correct languageId.
if (hasContentTypeVariationChanged)
{
AddChange(changes, contentType, ContentTypeChangeTypes.VariationChanged);
}
}
else
{
// add that one, as an other change
AddChange(changes, contentType, ContentTypeChangeTypes.RefreshOther);
}

if (hasPropertyMainImpact)
// A content type that derives from this one - through composition or inheritance, at any depth -
// resolves its published content type from this one's alias and property types, so any change to
// either has to refresh the whole closure beneath it. Left stale, a deriving type keeps projecting
// content through a property set the content type no longer has, and keeps reporting the old alias
// from its composition aliases (which IPublishedContent.IsComposedOf reads).
if (hasPropertyMainImpact || hasAnyPropertyBeenAdded || hasAliasChanged)
{
directReferencingTypes ??= GetDirectReferencingTypes();
foreach (TItem c in GetComposedOfTransitive(contentType.Id, directReferencingTypes))
{
foreach (TItem c in GetComposedOf(contentType.Id))
if (hasPropertyMainImpact)
{
// Composing types inherit the same property change, so they share its rebuild requirement.
// Deriving types inherit the same property change, so they share its rebuild requirement.
AddChange(changes, c, ContentTypeChangeTypes.RefreshMain);
(rawDataUnaffected ? rawDataUnaffectedCandidateIds : rebuildRequiredIds).Add(c.Id);
}
else
{
// Neither a property addition nor a content type alias change re-keys anything the
// deriving types have stored - the added alias has no value yet, and the stored blob is
// keyed by property alias, not content type alias. They only need their content type and
// converted content caches cleared, not a rebuild.
AddChange(changes, c, ContentTypeChangeTypes.RefreshOther);
}
}
}

Check warning on line 494 in src/Umbraco.Core/Services/ContentTypeServiceBase{TRepository,TItem}.cs

View check run for this annotation

CodeScene Delta Analysis / CodeScene Code Health Review (v17/dev)

❌ Getting worse: Complex Method

ComposeContentTypeChanges increases in cyclomatic complexity from 27 to 31, threshold = 9 This function has many conditional statements (e.g. if, for, while), leading to lower code health. Avoid adding more conditionals and code to it without refactoring.

Check warning on line 494 in src/Umbraco.Core/Services/ContentTypeServiceBase{TRepository,TItem}.cs

View check run for this annotation

CodeScene Delta Analysis / CodeScene Code Health Review (v17/dev)

❌ Getting worse: Complex Conditional

ComposeContentTypeChanges increases from 1 complex conditionals with 2 branches to 2 complex conditionals with 4 branches, threshold = 2 A complex conditional is an expression inside a branch (e.g. if, for, while) which consists of multiple, logical operators such as AND/OR. The more logical operators in an expression, the more severe the code smell.
else
{
// add that one, as an other change
AddChange(changes, contentType, ContentTypeChangeTypes.RefreshOther);
}
}

// Flag the raw data as unaffected only for types that were never independently marked as needing a
Expand Down Expand Up @@ -705,8 +730,65 @@
return GetComposedOf(id, allContentTypes);
}

/// <summary>
/// Builds a lookup from each content type id to the content types that directly reference it as a composition
/// (or as the content type they inherit from).
/// </summary>
/// <returns>The lookup of directly referencing content types, keyed by referenced content type id.</returns>
/// <remarks>
/// Every call takes a fresh snapshot of all content types, and the full dataset cache policy deep clones each
/// of them, so build this once per unit of work and share it.
/// </remarks>
private ILookup<int, TItem> GetDirectReferencingTypes() =>
GetAll()
.SelectMany(
contentType => contentType.ContentTypeComposition,
(contentType, referenced) => (ReferencedId: referenced.Id, ContentType: contentType))
.ToLookup(x => x.ReferencedId, x => x.ContentType);

/// <summary>
/// Gets every content type whose effective property set derives from the specified content type, directly
/// or indirectly.
/// </summary>
/// <param name="id">The identifier of the content type.</param>
/// <param name="directReferencingTypes">
/// The content types that directly reference each content type, as built by
/// <see cref="GetDirectReferencingTypes" />.
/// </param>
/// <returns>The content types deriving from the specified content type.</returns>
/// <remarks>
/// Inheritance is stored as composition, so a single traversal of the composition graph covers both axes.
/// It has to be transitive because a content type resolves both its property types and its composition
/// aliases recursively through its compositions (see
/// <see cref="IContentTypeComposition.CompositionPropertyTypes" /> and
/// <see cref="IContentTypeComposition.CompositionAliases()" />), so a change to one content type is a change
/// to everything deriving from it, not just to its direct consumers.
/// </remarks>
private static TItem[] GetComposedOfTransitive(int id, ILookup<int, TItem> directReferencingTypes)

Check warning on line 767 in src/Umbraco.Core/Services/ContentTypeServiceBase{TRepository,TItem}.cs

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Static members should appear before non-static members

See more on https://sonarcloud.io/project/issues?id=umbraco_Umbraco-CMS&issues=AaEgc2LIChmKjQIEditk&open=AaEgc2LIChmKjQIEditk&pullRequest=24126
{
var composedOf = new Dictionary<int, TItem>();
var remaining = new Stack<int>();
remaining.Push(id);

while (remaining.Count > 0)
{
foreach (TItem contentType in directReferencingTypes[remaining.Pop()])
{
// TryAdd doubles as the guard against cycles in the composition graph
if (composedOf.TryAdd(contentType.Id, contentType) is false)

Check warning on line 778 in src/Umbraco.Core/Services/ContentTypeServiceBase{TRepository,TItem}.cs

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Remove the unnecessary Boolean literal(s).

See more on https://sonarcloud.io/project/issues?id=umbraco_Umbraco-CMS&issues=AaEV4kngJwtYruktPJ26&open=AaEV4kngJwtYruktPJ26&pullRequest=24126
{
continue;
}

remaining.Push(contentType.Id);
}
}

return [.. composedOf.Values];
}

/// <inheritdoc />
public int Count()

Check warning on line 791 in src/Umbraco.Core/Services/ContentTypeServiceBase{TRepository,TItem}.cs

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

'public' members should come before 'private' members

See more on https://sonarcloud.io/project/issues?id=umbraco_Umbraco-CMS&issues=AaEVxiIb0O90mfPbazrp&open=AaEVxiIb0O90mfPbazrp&pullRequest=24126
{
using ICoreScope scope = ScopeProvider.CreateCoreScope(autoComplete: true);
scope.ReadLock(ReadLockIds);
Expand Down
Loading
Loading