diff --git a/src/Umbraco.Core/Collections/CompositeStringStringKey.cs b/src/Umbraco.Core/Collections/CompositeStringStringKey.cs index b0b7ca2b7b..01f94bf149 100644 --- a/src/Umbraco.Core/Collections/CompositeStringStringKey.cs +++ b/src/Umbraco.Core/Collections/CompositeStringStringKey.cs @@ -17,7 +17,7 @@ namespace Umbraco.Cms.Core.Collections /// /// Initializes a new instance of the struct. /// - public CompositeStringStringKey(string key1, string key2) + public CompositeStringStringKey(string? key1, string? key2) { _key1 = key1?.ToLowerInvariant() ?? throw new ArgumentNullException(nameof(key1)); _key2 = key2?.ToLowerInvariant() ?? throw new ArgumentNullException(nameof(key2)); diff --git a/src/Umbraco.Core/Extensions/EnumerableExtensions.cs b/src/Umbraco.Core/Extensions/EnumerableExtensions.cs index 5f5598f226..ba420003e5 100644 --- a/src/Umbraco.Core/Extensions/EnumerableExtensions.cs +++ b/src/Umbraco.Core/Extensions/EnumerableExtensions.cs @@ -13,7 +13,7 @@ namespace Umbraco.Extensions /// public static class EnumerableExtensions { - public static bool IsCollectionEmpty(this IReadOnlyCollection list) => list == null || list.Count == 0; + public static bool IsCollectionEmpty(this IReadOnlyCollection? list) => list == null || list.Count == 0; internal static bool HasDuplicates(this IEnumerable items, bool includeNull) { diff --git a/src/Umbraco.Core/Extensions/PublishedContentExtensions.cs b/src/Umbraco.Core/Extensions/PublishedContentExtensions.cs index 63dfd5b884..280f25ad23 100644 --- a/src/Umbraco.Core/Extensions/PublishedContentExtensions.cs +++ b/src/Umbraco.Core/Extensions/PublishedContentExtensions.cs @@ -25,7 +25,7 @@ namespace Umbraco.Extensions /// The content item. /// /// The specific culture to get the name for. If null is used the current culture is used (Default is null). - public static string? Name(this IPublishedContent content, IVariationContextAccessor variationContextAccessor, string? culture = null) + public static string? Name(this IPublishedContent content, IVariationContextAccessor? variationContextAccessor, string? culture = null) { if (content == null) { @@ -54,7 +54,7 @@ namespace Umbraco.Extensions /// The content item. /// /// The specific culture to get the URL segment for. If null is used the current culture is used (Default is null). - public static string? UrlSegment(this IPublishedContent content, IVariationContextAccessor variationContextAccessor, string? culture = null) + public static string? UrlSegment(this IPublishedContent content, IVariationContextAccessor? variationContextAccessor, string? culture = null) { if (content == null) { @@ -927,7 +927,7 @@ namespace Umbraco.Extensions /// However, if an empty string is specified only invariant children are returned. /// /// - public static IEnumerable? Children(this IPublishedContent content, IVariationContextAccessor variationContextAccessor, string? culture = null) + public static IEnumerable? Children(this IPublishedContent content, IVariationContextAccessor? variationContextAccessor, string? culture = null) { // handle context culture for variant if (culture == null) @@ -1106,7 +1106,7 @@ namespace Umbraco.Extensions { return content.Parent != null ? content.Parent.Children(variationContextAccessor, culture) - : publishedSnapshot?.Content.GetAtRoot().WhereIsInvariantOrHasCulture(variationContextAccessor, culture); + : publishedSnapshot?.Content?.GetAtRoot().WhereIsInvariantOrHasCulture(variationContextAccessor, culture); } /// @@ -1122,7 +1122,7 @@ namespace Umbraco.Extensions { return content.Parent != null ? content.Parent.ChildrenOfType(variationContextAccessor, contentTypeAlias, culture) - : publishedSnapshot?.Content.GetAtRoot().OfTypes(contentTypeAlias).WhereIsInvariantOrHasCulture(variationContextAccessor, culture); + : publishedSnapshot?.Content?.GetAtRoot().OfTypes(contentTypeAlias).WhereIsInvariantOrHasCulture(variationContextAccessor, culture); } /// @@ -1139,7 +1139,7 @@ namespace Umbraco.Extensions { return content.Parent != null ? content.Parent.Children(variationContextAccessor, culture) - : publishedSnapshot?.Content.GetAtRoot().OfType().WhereIsInvariantOrHasCulture(variationContextAccessor, culture); + : publishedSnapshot?.Content?.GetAtRoot().OfType().WhereIsInvariantOrHasCulture(variationContextAccessor, culture); } #endregion diff --git a/src/Umbraco.Core/Extensions/PublishedSnapshotAccessorExtensions.cs b/src/Umbraco.Core/Extensions/PublishedSnapshotAccessorExtensions.cs index fa7e4555e6..9fd7da4640 100644 --- a/src/Umbraco.Core/Extensions/PublishedSnapshotAccessorExtensions.cs +++ b/src/Umbraco.Core/Extensions/PublishedSnapshotAccessorExtensions.cs @@ -11,6 +11,7 @@ namespace Umbraco.Extensions { return publishedSnapshot!; } + throw new InvalidOperationException("Wasn't possible to a get a valid Snapshot"); } } diff --git a/src/Umbraco.Core/Models/PublishedContent/IPublishedContentType.cs b/src/Umbraco.Core/Models/PublishedContent/IPublishedContentType.cs index ba060fa5d0..bd3f77152d 100644 --- a/src/Umbraco.Core/Models/PublishedContent/IPublishedContentType.cs +++ b/src/Umbraco.Core/Models/PublishedContent/IPublishedContentType.cs @@ -48,7 +48,7 @@ namespace Umbraco.Cms.Core.Models.PublishedContent /// /// Gets the content type properties. /// - IEnumerable? PropertyTypes { get; } + IEnumerable PropertyTypes { get; } /// /// Gets a property type index. diff --git a/src/Umbraco.Core/Models/PublishedContent/IPublishedMemberCache.cs b/src/Umbraco.Core/Models/PublishedContent/IPublishedMemberCache.cs index bafe04f3e4..ba8bdc43d4 100644 --- a/src/Umbraco.Core/Models/PublishedContent/IPublishedMemberCache.cs +++ b/src/Umbraco.Core/Models/PublishedContent/IPublishedMemberCache.cs @@ -11,7 +11,7 @@ namespace Umbraco.Cms.Core.PublishedCache /// /// /// - IPublishedContent Get(IMember member); + IPublishedContent? Get(IMember member); /// /// Gets a content type identified by its unique identifier. diff --git a/src/Umbraco.Core/Models/PublishedContent/PublishedContentBase.cs b/src/Umbraco.Core/Models/PublishedContent/PublishedContentBase.cs index 0ece44a828..60d3cd4a02 100644 --- a/src/Umbraco.Core/Models/PublishedContent/PublishedContentBase.cs +++ b/src/Umbraco.Core/Models/PublishedContent/PublishedContentBase.cs @@ -13,9 +13,9 @@ namespace Umbraco.Cms.Core.Models.PublishedContent [DebuggerDisplay("Content Id: {Id}")] public abstract class PublishedContentBase : IPublishedContent { - private readonly IVariationContextAccessor _variationContextAccessor; + private readonly IVariationContextAccessor? _variationContextAccessor; - protected PublishedContentBase(IVariationContextAccessor variationContextAccessor) + protected PublishedContentBase(IVariationContextAccessor? variationContextAccessor) { _variationContextAccessor = variationContextAccessor; } @@ -85,7 +85,7 @@ namespace Umbraco.Cms.Core.Models.PublishedContent #region Tree /// - public abstract IPublishedContent Parent { get; } + public abstract IPublishedContent? Parent { get; } /// public virtual IEnumerable? Children => this.Children(_variationContextAccessor); @@ -101,7 +101,7 @@ namespace Umbraco.Cms.Core.Models.PublishedContent public abstract IEnumerable Properties { get; } /// - public abstract IPublishedProperty GetProperty(string alias); + public abstract IPublishedProperty? GetProperty(string alias); #endregion } diff --git a/src/Umbraco.Core/Models/PublishedContent/PublishedContentExtensionsForModels.cs b/src/Umbraco.Core/Models/PublishedContent/PublishedContentExtensionsForModels.cs index 731d670184..f1d348d2ff 100644 --- a/src/Umbraco.Core/Models/PublishedContent/PublishedContentExtensionsForModels.cs +++ b/src/Umbraco.Core/Models/PublishedContent/PublishedContentExtensionsForModels.cs @@ -13,7 +13,7 @@ namespace Umbraco.Extensions /// /// The internal published content. /// The strongly typed published content model. - public static IPublishedContent? CreateModel(this IPublishedContent content, IPublishedModelFactory publishedModelFactory) + public static IPublishedContent? CreateModel(this IPublishedContent content, IPublishedModelFactory? publishedModelFactory) { if (publishedModelFactory == null) throw new ArgumentNullException(nameof(publishedModelFactory)); if (content == null) diff --git a/src/Umbraco.Core/Models/PublishedContent/PublishedContentType.cs b/src/Umbraco.Core/Models/PublishedContent/PublishedContentType.cs index fbf29ac235..592c2eff5e 100644 --- a/src/Umbraco.Core/Models/PublishedContent/PublishedContentType.cs +++ b/src/Umbraco.Core/Models/PublishedContent/PublishedContentType.cs @@ -14,7 +14,7 @@ namespace Umbraco.Cms.Core.Models.PublishedContent [DebuggerDisplay("{Alias}")] public class PublishedContentType : IPublishedContentType { - private readonly IPublishedPropertyType[]? _propertyTypes; + private readonly IPublishedPropertyType[] _propertyTypes = null!; // fast alias-to-index xref containing both the raw alias and its lowercase version private readonly Dictionary _indexes = new Dictionary(); @@ -169,7 +169,7 @@ namespace Umbraco.Cms.Core.Models.PublishedContent #region Properties /// - public IEnumerable? PropertyTypes => _propertyTypes; + public IEnumerable PropertyTypes => _propertyTypes; /// public int GetPropertyIndex(string alias) diff --git a/src/Umbraco.Core/PropertyEditors/ValueConverters/ContentPickerValueConverter.cs b/src/Umbraco.Core/PropertyEditors/ValueConverters/ContentPickerValueConverter.cs index 3392c504e5..126b4516d1 100644 --- a/src/Umbraco.Core/PropertyEditors/ValueConverters/ContentPickerValueConverter.cs +++ b/src/Umbraco.Core/PropertyEditors/ValueConverters/ContentPickerValueConverter.cs @@ -65,7 +65,7 @@ namespace Umbraco.Cms.Core.PropertyEditors.ValueConverters var publishedSnapshot = _publishedSnapshotAccessor.GetRequiredPublishedSnapshot(); if (inter is int id) { - content = publishedSnapshot?.Content.GetById(id); + content = publishedSnapshot.Content?.GetById(id); if (content != null) return content; } @@ -74,7 +74,7 @@ namespace Umbraco.Cms.Core.PropertyEditors.ValueConverters var udi = inter as GuidUdi; if (udi is null) return null; - content = publishedSnapshot?.Content.GetById(udi.Guid); + content = publishedSnapshot.Content?.GetById(udi.Guid); if (content != null && content.ContentType.ItemType == PublishedItemType.Content) return content; } diff --git a/src/Umbraco.Core/PropertyEditors/ValueConverters/MediaPickerValueConverter.cs b/src/Umbraco.Core/PropertyEditors/ValueConverters/MediaPickerValueConverter.cs index ea244cf5a0..2df96fc310 100644 --- a/src/Umbraco.Core/PropertyEditors/ValueConverters/MediaPickerValueConverter.cs +++ b/src/Umbraco.Core/PropertyEditors/ValueConverters/MediaPickerValueConverter.cs @@ -76,7 +76,7 @@ namespace Umbraco.Cms.Core.PropertyEditors.ValueConverters { var guidUdi = udi as GuidUdi; if (guidUdi is null) continue; - var item = publishedSnapshot?.Media.GetById(guidUdi.Guid); + var item = publishedSnapshot?.Media?.GetById(guidUdi.Guid); if (item != null) mediaItems.Add(item); } diff --git a/src/Umbraco.Core/PropertyEditors/ValueConverters/MultiNodeTreePickerValueConverter.cs b/src/Umbraco.Core/PropertyEditors/ValueConverters/MultiNodeTreePickerValueConverter.cs index 8211be362f..faab6e712e 100644 --- a/src/Umbraco.Core/PropertyEditors/ValueConverters/MultiNodeTreePickerValueConverter.cs +++ b/src/Umbraco.Core/PropertyEditors/ValueConverters/MultiNodeTreePickerValueConverter.cs @@ -96,10 +96,10 @@ namespace Umbraco.Cms.Core.PropertyEditors.ValueConverters switch (udi.EntityType) { case Constants.UdiEntityType.Document: - multiNodeTreePickerItem = GetPublishedContent(udi, ref objectType, UmbracoObjectTypes.Document, id => publishedSnapshot?.Content.GetById(guidUdi.Guid)); + multiNodeTreePickerItem = GetPublishedContent(udi, ref objectType, UmbracoObjectTypes.Document, id => publishedSnapshot.Content?.GetById(guidUdi.Guid)); break; case Constants.UdiEntityType.Media: - multiNodeTreePickerItem = GetPublishedContent(udi, ref objectType, UmbracoObjectTypes.Media, id => publishedSnapshot?.Media.GetById(guidUdi.Guid)); + multiNodeTreePickerItem = GetPublishedContent(udi, ref objectType, UmbracoObjectTypes.Media, id => publishedSnapshot.Media?.GetById(guidUdi.Guid)); break; case Constants.UdiEntityType.Member: multiNodeTreePickerItem = GetPublishedContent(udi, ref objectType, UmbracoObjectTypes.Member, id => diff --git a/src/Umbraco.Core/PublishedCache/IPublishedCache.cs b/src/Umbraco.Core/PublishedCache/IPublishedCache.cs index 5cf7f58411..5a06d88ee5 100644 --- a/src/Umbraco.Core/PublishedCache/IPublishedCache.cs +++ b/src/Umbraco.Core/PublishedCache/IPublishedCache.cs @@ -27,7 +27,7 @@ namespace Umbraco.Cms.Core.PublishedCache /// The content unique identifier. /// The content, or null. /// The value of overrides defaults. - IPublishedContent GetById(bool preview, Guid contentId); + IPublishedContent? GetById(bool preview, Guid contentId); /// /// Gets a content identified by its Udi identifier. @@ -36,7 +36,7 @@ namespace Umbraco.Cms.Core.PublishedCache /// The content Udi identifier. /// The content, or null. /// The value of overrides defaults. - IPublishedContent GetById(bool preview, Udi contentId); + IPublishedContent? GetById(bool preview, Udi contentId); /// /// Gets a content identified by its unique identifier. @@ -52,7 +52,7 @@ namespace Umbraco.Cms.Core.PublishedCache /// The content unique identifier. /// The content, or null. /// Considers published or unpublished content depending on defaults. - IPublishedContent GetById(Guid contentId); + IPublishedContent? GetById(Guid contentId); /// /// Gets a content identified by its unique identifier. @@ -60,7 +60,7 @@ namespace Umbraco.Cms.Core.PublishedCache /// The content unique identifier. /// The content, or null. /// Considers published or unpublished content depending on defaults. - IPublishedContent GetById(Udi contentId); + IPublishedContent? GetById(Udi contentId); /// /// Gets a value indicating whether the cache contains a specified content. @@ -104,7 +104,7 @@ namespace Umbraco.Cms.Core.PublishedCache /// Optional XPath variables. /// The content, or null. /// The value of overrides defaults. - IPublishedContent GetSingleByXPath(bool preview, string xpath, params XPathVariable[] vars); + IPublishedContent? GetSingleByXPath(bool preview, string xpath, params XPathVariable[] vars); /// /// Gets a content resulting from an XPath query. @@ -113,7 +113,7 @@ namespace Umbraco.Cms.Core.PublishedCache /// Optional XPath variables. /// The content, or null. /// Considers published or unpublished content depending on defaults. - IPublishedContent GetSingleByXPath(string xpath, params XPathVariable[] vars); + IPublishedContent? GetSingleByXPath(string xpath, params XPathVariable[] vars); /// /// Gets a content resulting from an XPath query. @@ -123,7 +123,7 @@ namespace Umbraco.Cms.Core.PublishedCache /// Optional XPath variables. /// The content, or null. /// The value of overrides defaults. - IPublishedContent GetSingleByXPath(bool preview, XPathExpression xpath, params XPathVariable[] vars); + IPublishedContent? GetSingleByXPath(bool preview, XPathExpression xpath, params XPathVariable[] vars); /// /// Gets a content resulting from an XPath query. @@ -132,7 +132,7 @@ namespace Umbraco.Cms.Core.PublishedCache /// Optional XPath variables. /// The content, or null. /// Considers published or unpublished content depending on defaults. - IPublishedContent GetSingleByXPath(XPathExpression xpath, params XPathVariable[] vars); + IPublishedContent? GetSingleByXPath(XPathExpression xpath, params XPathVariable[] vars); /// /// Gets contents resulting from an XPath query. @@ -196,7 +196,7 @@ namespace Umbraco.Cms.Core.PublishedCache /// compatibility + transition reasons, we should obsolete that one as soon as possible. /// If the node does not exist, returns null. /// - XPathNavigator CreateNodeNavigator(int id, bool preview); + XPathNavigator? CreateNodeNavigator(int id, bool preview); /// /// Gets a value indicating whether the cache contains published content. @@ -218,7 +218,7 @@ namespace Umbraco.Cms.Core.PublishedCache /// /// The content type unique identifier. /// The content type, or null. - IPublishedContentType GetContentType(int id); + IPublishedContentType? GetContentType(int id); /// /// Gets a content type identified by its alias. @@ -226,7 +226,7 @@ namespace Umbraco.Cms.Core.PublishedCache /// The content type alias. /// The content type, or null. /// The alias is case-insensitive. - IPublishedContentType GetContentType(string alias); + IPublishedContentType? GetContentType(string alias); /// /// Gets contents of a given content type. @@ -240,6 +240,6 @@ namespace Umbraco.Cms.Core.PublishedCache /// /// The content type key. /// The content type, or null. - IPublishedContentType GetContentType(Guid key); + IPublishedContentType? GetContentType(Guid key); } } diff --git a/src/Umbraco.Core/PublishedCache/IPublishedContentCache.cs b/src/Umbraco.Core/PublishedCache/IPublishedContentCache.cs index 2958e57f92..4621adcb82 100644 --- a/src/Umbraco.Core/PublishedCache/IPublishedContentCache.cs +++ b/src/Umbraco.Core/PublishedCache/IPublishedContentCache.cs @@ -16,7 +16,7 @@ namespace Umbraco.Cms.Core.PublishedCache /// If is null then the settings value is used. /// The value of overrides defaults. /// - IPublishedContent GetByRoute(bool preview, string route, bool? hideTopLevelNode = null, string? culture = null); + IPublishedContent? GetByRoute(bool preview, string route, bool? hideTopLevelNode = null, string? culture = null); /// /// Gets content identified by a route. @@ -29,7 +29,7 @@ namespace Umbraco.Cms.Core.PublishedCache /// If is null then the settings value is used. /// Considers published or unpublished content depending on defaults. /// - IPublishedContent GetByRoute(string route, bool? hideTopLevelNode = null, string? culture = null); + IPublishedContent? GetByRoute(string route, bool? hideTopLevelNode = null, string? culture = null); /// /// Gets the route for a content identified by its unique identifier. @@ -44,7 +44,7 @@ namespace Umbraco.Cms.Core.PublishedCache /// /// The value of overrides defaults. /// - string GetRouteById(bool preview, int contentId, string? culture = null); + string? GetRouteById(bool preview, int contentId, string? culture = null); /// /// Gets the route for a content identified by its unique identifier. @@ -56,6 +56,6 @@ namespace Umbraco.Cms.Core.PublishedCache /// The resulting string is a special encoded route string that may contain the domain ID /// for the current route. If a domain is present the string will be prefixed with the domain ID integer, example: {domainId}/route-path-of-item /// - string GetRouteById(int contentId, string? culture = null); + string? GetRouteById(int contentId, string? culture = null); } } diff --git a/src/Umbraco.Core/PublishedCache/IPublishedSnapshot.cs b/src/Umbraco.Core/PublishedCache/IPublishedSnapshot.cs index 3c55b9d699..1f5344df4c 100644 --- a/src/Umbraco.Core/PublishedCache/IPublishedSnapshot.cs +++ b/src/Umbraco.Core/PublishedCache/IPublishedSnapshot.cs @@ -13,12 +13,12 @@ namespace Umbraco.Cms.Core.PublishedCache /// /// Gets the . /// - IPublishedContentCache Content { get; } + IPublishedContentCache? Content { get; } /// /// Gets the . /// - IPublishedMediaCache Media { get; } + IPublishedMediaCache? Media { get; } /// /// Gets the . diff --git a/src/Umbraco.Core/PublishedCache/PublishedCacheBase.cs b/src/Umbraco.Core/PublishedCache/PublishedCacheBase.cs index 379668a832..b374424b8b 100644 --- a/src/Umbraco.Core/PublishedCache/PublishedCacheBase.cs +++ b/src/Umbraco.Core/PublishedCache/PublishedCacheBase.cs @@ -29,14 +29,14 @@ namespace Umbraco.Cms.Core.PublishedCache public IPublishedContent? GetById(int contentId) => GetById(PreviewDefault, contentId); - public abstract IPublishedContent GetById(bool preview, Guid contentId); + public abstract IPublishedContent? GetById(bool preview, Guid contentId); - public IPublishedContent GetById(Guid contentId) + public IPublishedContent? GetById(Guid contentId) => GetById(PreviewDefault, contentId); - public abstract IPublishedContent GetById(bool preview, Udi contentId); + public abstract IPublishedContent? GetById(bool preview, Udi contentId); - public IPublishedContent GetById(Udi contentId) + public IPublishedContent? GetById(Udi contentId) => GetById(PreviewDefault, contentId); public abstract bool HasById(bool preview, int contentId); @@ -51,16 +51,16 @@ namespace Umbraco.Cms.Core.PublishedCache return GetAtRoot(PreviewDefault, culture); } - public abstract IPublishedContent GetSingleByXPath(bool preview, string xpath, XPathVariable[] vars); + public abstract IPublishedContent? GetSingleByXPath(bool preview, string xpath, XPathVariable[] vars); - public IPublishedContent GetSingleByXPath(string xpath, XPathVariable[] vars) + public IPublishedContent? GetSingleByXPath(string xpath, XPathVariable[] vars) { return GetSingleByXPath(PreviewDefault, xpath, vars); } - public abstract IPublishedContent GetSingleByXPath(bool preview, XPathExpression xpath, XPathVariable[] vars); + public abstract IPublishedContent? GetSingleByXPath(bool preview, XPathExpression xpath, XPathVariable[] vars); - public IPublishedContent GetSingleByXPath(XPathExpression xpath, XPathVariable[] vars) + public IPublishedContent? GetSingleByXPath(XPathExpression xpath, XPathVariable[] vars) { return GetSingleByXPath(PreviewDefault, xpath, vars); } @@ -86,7 +86,7 @@ namespace Umbraco.Cms.Core.PublishedCache return CreateNavigator(PreviewDefault); } - public abstract XPathNavigator CreateNodeNavigator(int id, bool preview); + public abstract XPathNavigator? CreateNodeNavigator(int id, bool preview); public abstract bool HasContent(bool preview); @@ -95,9 +95,9 @@ namespace Umbraco.Cms.Core.PublishedCache return HasContent(PreviewDefault); } - public abstract IPublishedContentType GetContentType(int id); - public abstract IPublishedContentType GetContentType(string alias); - public abstract IPublishedContentType GetContentType(Guid key); + public abstract IPublishedContentType? GetContentType(int id); + public abstract IPublishedContentType? GetContentType(string alias); + public abstract IPublishedContentType? GetContentType(Guid key); public virtual IEnumerable GetByContentType(IPublishedContentType contentType) { diff --git a/src/Umbraco.Core/Routing/AliasUrlProvider.cs b/src/Umbraco.Core/Routing/AliasUrlProvider.cs index 98160aab1a..21fb3e9832 100644 --- a/src/Umbraco.Core/Routing/AliasUrlProvider.cs +++ b/src/Umbraco.Core/Routing/AliasUrlProvider.cs @@ -61,7 +61,7 @@ namespace Umbraco.Cms.Core.Routing public IEnumerable GetOtherUrls(int id, Uri current) { var umbracoContext = _umbracoContextAccessor.GetRequiredUmbracoContext(); - var node = umbracoContext.Content.GetById(id); + var node = umbracoContext.Content?.GetById(id); if (node == null) yield break; diff --git a/src/Umbraco.Core/Routing/ContentFinderByIdPath.cs b/src/Umbraco.Core/Routing/ContentFinderByIdPath.cs index daaf84ebe8..6e7238890f 100644 --- a/src/Umbraco.Core/Routing/ContentFinderByIdPath.cs +++ b/src/Umbraco.Core/Routing/ContentFinderByIdPath.cs @@ -71,7 +71,7 @@ namespace Umbraco.Cms.Core.Routing if (nodeId > 0) { _logger.LogDebug("Id={NodeId}", nodeId); - node = umbracoContext?.Content.GetById(nodeId); + node = umbracoContext?.Content?.GetById(nodeId); if (node != null) { diff --git a/src/Umbraco.Core/Routing/ContentFinderByPageIdQuery.cs b/src/Umbraco.Core/Routing/ContentFinderByPageIdQuery.cs index 912381f6c4..95b9faef2a 100644 --- a/src/Umbraco.Core/Routing/ContentFinderByPageIdQuery.cs +++ b/src/Umbraco.Core/Routing/ContentFinderByPageIdQuery.cs @@ -35,7 +35,7 @@ namespace Umbraco.Cms.Core.Routing } if (int.TryParse(_requestAccessor.GetRequestValue("umbPageID"), NumberStyles.Integer, CultureInfo.InvariantCulture, out int pageId)) { - IPublishedContent? doc = umbracoContext?.Content.GetById(pageId); + IPublishedContent? doc = umbracoContext.Content?.GetById(pageId); if (doc != null) { diff --git a/src/Umbraco.Core/Routing/ContentFinderByRedirectUrl.cs b/src/Umbraco.Core/Routing/ContentFinderByRedirectUrl.cs index 4259feacca..d1d4873d55 100644 --- a/src/Umbraco.Core/Routing/ContentFinderByRedirectUrl.cs +++ b/src/Umbraco.Core/Routing/ContentFinderByRedirectUrl.cs @@ -62,7 +62,7 @@ namespace Umbraco.Cms.Core.Routing return false; } - IPublishedContent? content = umbracoContext.Content.GetById(redirectUrl.ContentId); + IPublishedContent? content = umbracoContext.Content?.GetById(redirectUrl.ContentId); var url = content == null ? "#" : content.Url(_publishedUrlProvider, redirectUrl.Culture); if (url.StartsWith("#")) { diff --git a/src/Umbraco.Core/Routing/ContentFinderByUrl.cs b/src/Umbraco.Core/Routing/ContentFinderByUrl.cs index b338e03bbf..3a753563df 100644 --- a/src/Umbraco.Core/Routing/ContentFinderByUrl.cs +++ b/src/Umbraco.Core/Routing/ContentFinderByUrl.cs @@ -72,7 +72,7 @@ namespace Umbraco.Cms.Core.Routing _logger.LogDebug("Test route {Route}", route); - IPublishedContent? node = umbracoContext?.Content.GetByRoute(umbracoContext.InPreviewMode, route, culture: docreq.Culture); + IPublishedContent? node = umbracoContext.Content?.GetByRoute(umbracoContext.InPreviewMode, route, culture: docreq.Culture); if (node != null) { docreq.SetPublishedContent(node); diff --git a/src/Umbraco.Core/Routing/ContentFinderByUrlAlias.cs b/src/Umbraco.Core/Routing/ContentFinderByUrlAlias.cs index 5d9ac79c81..7e51f63aa4 100644 --- a/src/Umbraco.Core/Routing/ContentFinderByUrlAlias.cs +++ b/src/Umbraco.Core/Routing/ContentFinderByUrlAlias.cs @@ -69,7 +69,7 @@ namespace Umbraco.Cms.Core.Routing return node != null; } - private IPublishedContent? FindContentByAlias(IPublishedContentCache cache, int rootNodeId, string? culture, string alias) + private IPublishedContent? FindContentByAlias(IPublishedContentCache? cache, int rootNodeId, string? culture, string alias) { if (alias == null) { @@ -131,16 +131,19 @@ namespace Umbraco.Cms.Core.Routing // but the only solution is to entirely refactor URL providers to stop being dynamic if (rootNodeId > 0) { - IPublishedContent? rootNode = cache.GetById(rootNodeId); + IPublishedContent? rootNode = cache?.GetById(rootNodeId); return rootNode?.Descendants(_variationContextAccessor).FirstOrDefault(x => IsMatch(x, test1, test2)); } - foreach (IPublishedContent rootContent in cache.GetAtRoot()) + if (cache is not null) { - IPublishedContent? c = rootContent.DescendantsOrSelf(_variationContextAccessor).FirstOrDefault(x => IsMatch(x, test1, test2)); - if (c != null) + foreach (IPublishedContent rootContent in cache.GetAtRoot()) { - return c; + IPublishedContent? c = rootContent.DescendantsOrSelf(_variationContextAccessor).FirstOrDefault(x => IsMatch(x, test1, test2)); + if (c != null) + { + return c; + } } } diff --git a/src/Umbraco.Core/Routing/DefaultUrlProvider.cs b/src/Umbraco.Core/Routing/DefaultUrlProvider.cs index 7e3a46f7a8..25e0764349 100644 --- a/src/Umbraco.Core/Routing/DefaultUrlProvider.cs +++ b/src/Umbraco.Core/Routing/DefaultUrlProvider.cs @@ -70,7 +70,7 @@ namespace Umbraco.Cms.Core.Routing public virtual IEnumerable GetOtherUrls(int id, Uri current) { IUmbracoContext umbracoContext = _umbracoContextAccessor.GetRequiredUmbracoContext(); - IPublishedContent? node = umbracoContext.Content.GetById(id); + IPublishedContent? node = umbracoContext.Content?.GetById(id); if (node == null) { yield break; @@ -101,7 +101,7 @@ namespace Umbraco.Cms.Core.Routing var culture = d.Culture; // although we are passing in culture here, if any node in this path is invariant, it ignores the culture anyways so this is ok - var route = umbracoContext.Content.GetRouteById(id, culture); + var route = umbracoContext.Content?.GetRouteById(id, culture); if (route == null) { continue; @@ -131,7 +131,7 @@ namespace Umbraco.Cms.Core.Routing IUmbracoContext umbracoContext = _umbracoContextAccessor.GetRequiredUmbracoContext(); // will not use cache if previewing - var route = umbracoContext.Content.GetRouteById(content.Id, culture); + var route = umbracoContext.Content?.GetRouteById(content.Id, culture); return GetUrlFromRoute(route, umbracoContext, content.Id, current, mode, culture); } diff --git a/src/Umbraco.Core/Routing/Domain.cs b/src/Umbraco.Core/Routing/Domain.cs index cf09687dae..ecefb07e8b 100644 --- a/src/Umbraco.Core/Routing/Domain.cs +++ b/src/Umbraco.Core/Routing/Domain.cs @@ -13,7 +13,7 @@ namespace Umbraco.Cms.Core.Routing /// The identifier of the content which supports the domain. /// The culture of the domain. /// A value indicating whether the domain is a wildcard domain. - public Domain(int id, string name, int contentId, string culture, bool isWildcard) + public Domain(int id, string name, int contentId, string? culture, bool isWildcard) { Id = id; Name = name; @@ -53,7 +53,7 @@ namespace Umbraco.Cms.Core.Routing /// /// Gets the culture of the domain. /// - public string Culture { get; } + public string? Culture { get; } /// /// Gets a value indicating whether the domain is a wildcard domain. diff --git a/src/Umbraco.Core/Routing/DomainUtilities.cs b/src/Umbraco.Core/Routing/DomainUtilities.cs index 61b27c3723..9e762a600e 100644 --- a/src/Umbraco.Core/Routing/DomainUtilities.cs +++ b/src/Umbraco.Core/Routing/DomainUtilities.cs @@ -39,8 +39,8 @@ namespace Umbraco.Cms.Core.Routing // get the published route, else the preview route // if both are null then the content does not exist - var route = umbracoContext.Content.GetRouteById(contentId) ?? - umbracoContext.Content.GetRouteById(true, contentId); + var route = umbracoContext.Content?.GetRouteById(contentId) ?? + umbracoContext.Content?.GetRouteById(true, contentId); if (route == null) return null; diff --git a/src/Umbraco.Core/Routing/PublishedRouter.cs b/src/Umbraco.Core/Routing/PublishedRouter.cs index 2850164a3a..aa852c10d7 100644 --- a/src/Umbraco.Core/Routing/PublishedRouter.cs +++ b/src/Umbraco.Core/Routing/PublishedRouter.cs @@ -282,7 +282,7 @@ namespace Umbraco.Cms.Core.Routing bool IsPublishedContentDomain(Domain domain) { // just get it from content cache - optimize there, not here - IPublishedContent? domainDocument = umbracoContext.PublishedSnapshot.Content.GetById(domain.ContentId); + IPublishedContent? domainDocument = umbracoContext.PublishedSnapshot.Content?.GetById(domain.ContentId); // not published - at all if (domainDocument == null) @@ -297,7 +297,7 @@ namespace Umbraco.Cms.Core.Routing } // variant, ensure that the culture corresponding to the domain's language is published - return domainDocument.Cultures.ContainsKey(domain.Culture); + return domain.Culture is not null && domainDocument.Cultures.ContainsKey(domain.Culture); } domains = domains?.Where(IsPublishedContentDomain).ToList(); @@ -510,7 +510,7 @@ namespace Umbraco.Cms.Core.Routing { // try and get the redirect node from a legacy integer ID valid = true; - internalRedirectNode = umbracoContext.Content.GetById(internalRedirectId); + internalRedirectNode = umbracoContext.Content?.GetById(internalRedirectId); } else { @@ -519,7 +519,7 @@ namespace Umbraco.Cms.Core.Routing { // try and get the redirect node from a UDI Guid valid = true; - internalRedirectNode = umbracoContext.Content.GetById(udiInternalRedirectId.Guid); + internalRedirectNode = umbracoContext.Content?.GetById(udiInternalRedirectId.Guid); } } diff --git a/src/Umbraco.Core/Routing/UrlProvider.cs b/src/Umbraco.Core/Routing/UrlProvider.cs index c53d75fc31..11597159f7 100644 --- a/src/Umbraco.Core/Routing/UrlProvider.cs +++ b/src/Umbraco.Core/Routing/UrlProvider.cs @@ -52,17 +52,17 @@ namespace Umbraco.Cms.Core.Routing private IPublishedContent? GetDocument(int id) { var umbracoContext = _umbracoContextAccessor.GetRequiredUmbracoContext(); - return umbracoContext.Content.GetById(id); + return umbracoContext.Content?.GetById(id); } - private IPublishedContent GetDocument(Guid id) + private IPublishedContent? GetDocument(Guid id) { var umbracoContext = _umbracoContextAccessor.GetRequiredUmbracoContext(); - return umbracoContext.Content.GetById(id); + return umbracoContext.Content?.GetById(id); } - private IPublishedContent GetMedia(Guid id) + private IPublishedContent? GetMedia(Guid id) { var umbracoContext = _umbracoContextAccessor.GetRequiredUmbracoContext(); - return umbracoContext.Media.GetById(id); + return umbracoContext.Media?.GetById(id); } /// @@ -189,7 +189,7 @@ namespace Umbraco.Cms.Core.Routing /// /// public string GetMediaUrl(Guid id, UrlMode mode = UrlMode.Default, string? culture = null, string propertyAlias = Constants.Conventions.Media.File, Uri? current = null) - => GetMediaUrl(GetMedia(id), mode, culture, propertyAlias, current); + => GetMediaUrl( GetMedia(id), mode, culture, propertyAlias, current); /// /// Gets the URL of a media item. @@ -206,7 +206,7 @@ namespace Umbraco.Cms.Core.Routing /// when no culture is specified, the current culture. /// If the provider is unable to provide a URL, it returns . /// - public string GetMediaUrl(IPublishedContent content, UrlMode mode = UrlMode.Default, string? culture = null, string propertyAlias = Constants.Conventions.Media.File, Uri? current = null) + public string GetMediaUrl(IPublishedContent? content, UrlMode mode = UrlMode.Default, string? culture = null, string propertyAlias = Constants.Conventions.Media.File, Uri? current = null) { if (propertyAlias == null) throw new ArgumentNullException(nameof(propertyAlias)); diff --git a/src/Umbraco.Core/Templates/UmbracoComponentRenderer.cs b/src/Umbraco.Core/Templates/UmbracoComponentRenderer.cs index 4aee8036a3..45a44d4edb 100644 --- a/src/Umbraco.Core/Templates/UmbracoComponentRenderer.cs +++ b/src/Umbraco.Core/Templates/UmbracoComponentRenderer.cs @@ -67,7 +67,7 @@ namespace Umbraco.Cms.Core.Templates throw new ArgumentException("Invalid content id " + contentId); } var umbracoContext = _umbracoContextAccessor.GetRequiredUmbracoContext(); - var content = umbracoContext.Content.GetById(contentId); + var content = umbracoContext.Content?.GetById(contentId); if (content == null) { diff --git a/src/Umbraco.Core/UdiParser.cs b/src/Umbraco.Core/UdiParser.cs index 4a542e9352..907880db13 100644 --- a/src/Umbraco.Core/UdiParser.cs +++ b/src/Umbraco.Core/UdiParser.cs @@ -102,7 +102,7 @@ namespace Umbraco.Cms.Core /// If is true, assemblies are not scanned for types, /// and therefore only builtin types may be known. Unless scanning already took place. /// - public static bool TryParse(string s, bool knownTypes, [MaybeNullWhen(returnValue: false)] out Udi udi) + public static bool TryParse(string? s, bool knownTypes, [MaybeNullWhen(returnValue: false)] out Udi udi) { return ParseInternal(s, true, knownTypes, out udi); } diff --git a/src/Umbraco.Core/Web/IUmbracoContext.cs b/src/Umbraco.Core/Web/IUmbracoContext.cs index d591d23e70..7cfa3876c0 100644 --- a/src/Umbraco.Core/Web/IUmbracoContext.cs +++ b/src/Umbraco.Core/Web/IUmbracoContext.cs @@ -35,12 +35,12 @@ namespace Umbraco.Cms.Core.Web /// /// Gets the published content cache. /// - IPublishedContentCache Content { get; } + IPublishedContentCache? Content { get; } /// /// Gets the published media cache. /// - IPublishedMediaCache Media { get; } + IPublishedMediaCache? Media { get; } /// /// Gets the domains cache. diff --git a/src/Umbraco.Core/Xml/XPath/INavigableContent.cs b/src/Umbraco.Core/Xml/XPath/INavigableContent.cs index 0e0848088a..c1a4e6c3e4 100644 --- a/src/Umbraco.Core/Xml/XPath/INavigableContent.cs +++ b/src/Umbraco.Core/Xml/XPath/INavigableContent.cs @@ -28,7 +28,7 @@ namespace Umbraco.Cms.Core.Xml.XPath /// /// Gets the unique identifiers of the children of the navigable content. /// - IList ChildIds { get; } + IList? ChildIds { get; } /// /// Gets the value of a field of the navigable content for XPath navigation use. @@ -43,7 +43,7 @@ namespace Umbraco.Cms.Core.Xml.XPath /// and has content (is not empty), null to indicate that the element is empty, or a string /// which can be empty, whitespace... depending on what the data type wants to expose. /// - object Value(int index); + object? Value(int index); // TODO: implement the following one diff --git a/src/Umbraco.Core/Xml/XPath/INavigableContentType.cs b/src/Umbraco.Core/Xml/XPath/INavigableContentType.cs index 420cd6a487..2e214d5e9a 100644 --- a/src/Umbraco.Core/Xml/XPath/INavigableContentType.cs +++ b/src/Umbraco.Core/Xml/XPath/INavigableContentType.cs @@ -8,7 +8,7 @@ /// /// Gets the name of the content type. /// - string Name { get; } + string? Name { get; } /// /// Gets the field types of the content type. diff --git a/src/Umbraco.Core/Xml/XPath/INavigableFieldType.cs b/src/Umbraco.Core/Xml/XPath/INavigableFieldType.cs index a9bbef3821..0b66cc0626 100644 --- a/src/Umbraco.Core/Xml/XPath/INavigableFieldType.cs +++ b/src/Umbraco.Core/Xml/XPath/INavigableFieldType.cs @@ -18,6 +18,6 @@ namespace Umbraco.Cms.Core.Xml.XPath /// /// This is for built-in properties, ie attributes. User-defined properties have their /// own way to convert their value for XPath. - Func XmlStringConverter { get; } + Func? XmlStringConverter { get; } } } diff --git a/src/Umbraco.Core/Xml/XPath/INavigableSource.cs b/src/Umbraco.Core/Xml/XPath/INavigableSource.cs index 60243142d7..76b43b618c 100644 --- a/src/Umbraco.Core/Xml/XPath/INavigableSource.cs +++ b/src/Umbraco.Core/Xml/XPath/INavigableSource.cs @@ -11,7 +11,7 @@ /// The unique identifier. /// The content identified by the unique identifier, or null. /// When id is -1 (root content) implementations should return null. - INavigableContent Get(int id); + INavigableContent? Get(int id); /// /// Gets the index of the last attribute in the fields collections. diff --git a/src/Umbraco.Core/Xml/XPath/NavigableNavigator.cs b/src/Umbraco.Core/Xml/XPath/NavigableNavigator.cs index c21ffda281..a575ee86f8 100644 --- a/src/Umbraco.Core/Xml/XPath/NavigableNavigator.cs +++ b/src/Umbraco.Core/Xml/XPath/NavigableNavigator.cs @@ -98,7 +98,7 @@ namespace Umbraco.Cms.Core.Xml.XPath throw new ArgumentException("Not the identifier of a content within the source.", nameof(rootId)); _state = new State(content, null, null, 0, StatePosition.Root); - _contents = new ConcurrentDictionary(); + _contents = new ConcurrentDictionary(); } ///// @@ -261,9 +261,9 @@ namespace Umbraco.Cms.Core.Xml.XPath #region Source management - private readonly ConcurrentDictionary _contents; + private readonly ConcurrentDictionary _contents; - private INavigableContent SourceGet(int id) + private INavigableContent? SourceGet(int id) { // original version, would keep creating INavigableContent objects //return _source.Get(id); @@ -704,7 +704,7 @@ namespace Umbraco.Cms.Core.Xml.XPath _state = new State(state.Content, null, null, 0, StatePosition.Element); while (content != null) { - _state = new State(content, _state, _state.Content?.ChildIds, _state.Content?.ChildIds.IndexOf(content.Id) ?? -1, StatePosition.Element); + _state = new State(content, _state, _state.Content?.ChildIds, _state.Content?.ChildIds?.IndexOf(content.Id) ?? -1, StatePosition.Element); content = s.Count == 0 ? null : s.Pop(); } DebugState(); diff --git a/src/Umbraco.Examine.Lucene/BackOfficeExamineSearcher.cs b/src/Umbraco.Examine.Lucene/BackOfficeExamineSearcher.cs index b8a942edc5..9cb667ad44 100644 --- a/src/Umbraco.Examine.Lucene/BackOfficeExamineSearcher.cs +++ b/src/Umbraco.Examine.Lucene/BackOfficeExamineSearcher.cs @@ -55,7 +55,7 @@ namespace Umbraco.Cms.Infrastructure.Examine _publishedUrlProvider = publishedUrlProvider; } - public IEnumerable Search(string query, UmbracoEntityTypes entityType, int pageSize, long pageIndex, out long totalFound, string searchFrom = null, bool ignoreUserStartNodes = false) + public IEnumerable Search(string query, UmbracoEntityTypes entityType, int pageSize, long pageIndex, out long totalFound, string? searchFrom = null, bool ignoreUserStartNodes = false) { var sb = new StringBuilder(); @@ -148,7 +148,7 @@ namespace Umbraco.Cms.Infrastructure.Examine return result; } - private bool BuildQuery(StringBuilder sb, string query, string searchFrom, List fields, string type) + private bool BuildQuery(StringBuilder sb, string query, string? searchFrom, List fields, string type) { //build a lucene query: // the nodeName will be boosted 10x without wildcards @@ -326,7 +326,7 @@ namespace Umbraco.Cms.Infrastructure.Examine } } - private void AppendPath(StringBuilder sb, UmbracoObjectTypes objectType, int[] startNodeIds, string searchFrom, bool ignoreUserStartNodes, IEntityService entityService) + private void AppendPath(StringBuilder sb, UmbracoObjectTypes objectType, int[]? startNodeIds, string? searchFrom, bool ignoreUserStartNodes, IEntityService entityService) { if (sb == null) throw new ArgumentNullException(nameof(sb)); if (entityService == null) throw new ArgumentNullException(nameof(entityService)); @@ -344,12 +344,12 @@ namespace Umbraco.Cms.Infrastructure.Examine AppendPath(sb, entityPath.Path, false); sb.Append(" "); } - else if (startNodeIds.Length == 0) + else if (startNodeIds?.Length == 0) { // make sure we don't find anything sb.Append("+__Path:none "); } - else if (startNodeIds.Contains(-1) == false && ignoreUserStartNodes == false) // -1 = no restriction + else if (startNodeIds?.Contains(-1) == false && ignoreUserStartNodes == false) // -1 = no restriction { var entityPaths = entityService.GetAllPaths(objectType, startNodeIds); diff --git a/src/Umbraco.Examine.Lucene/ConfigurationEnabledDirectoryFactory.cs b/src/Umbraco.Examine.Lucene/ConfigurationEnabledDirectoryFactory.cs index dfed005470..108aad06bc 100644 --- a/src/Umbraco.Examine.Lucene/ConfigurationEnabledDirectoryFactory.cs +++ b/src/Umbraco.Examine.Lucene/ConfigurationEnabledDirectoryFactory.cs @@ -20,7 +20,7 @@ namespace Umbraco.Cms.Infrastructure.Examine private readonly IServiceProvider _services; private readonly IApplicationRoot _applicationRoot; private readonly IndexCreatorSettings _settings; - private IDirectoryFactory _directoryFactory; + private IDirectoryFactory? _directoryFactory; public ConfigurationEnabledDirectoryFactory( IServiceProvider services, @@ -39,8 +39,8 @@ namespace Umbraco.Cms.Infrastructure.Examine } /// - /// Creates a directory factory based on the configured value and ensures that - /// + /// Creates a directory factory based on the configured value and ensures that + /// private IDirectoryFactory CreateFactory() { DirectoryInfo dirInfo = _applicationRoot.ApplicationRoot; @@ -51,12 +51,12 @@ namespace Umbraco.Cms.Infrastructure.Examine } switch (_settings.LuceneDirectoryFactory) - { + { case LuceneDirectoryFactory.SyncedTempFileSystemDirectoryFactory: return _services.GetRequiredService(); case LuceneDirectoryFactory.TempFileSystemDirectoryFactory: return _services.GetRequiredService(); - case LuceneDirectoryFactory.Default: + case LuceneDirectoryFactory.Default: default: return _services.GetRequiredService(); } diff --git a/src/Umbraco.Examine.Lucene/Extensions/ExamineExtensions.cs b/src/Umbraco.Examine.Lucene/Extensions/ExamineExtensions.cs index 82b43eaa20..02a9ea85e0 100644 --- a/src/Umbraco.Examine.Lucene/Extensions/ExamineExtensions.cs +++ b/src/Umbraco.Examine.Lucene/Extensions/ExamineExtensions.cs @@ -2,6 +2,7 @@ // See LICENSE for more details. using System; +using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Threading; using Examine; @@ -53,7 +54,7 @@ namespace Umbraco.Extensions /// /// The exception returned if there was an error /// - public static bool IsHealthy(this LuceneIndex indexer, out Exception ex) + public static bool IsHealthy(this LuceneIndex indexer, [MaybeNullWhen(true)] out Exception ex) { try { diff --git a/src/Umbraco.Examine.Lucene/LuceneIndexDiagnostics.cs b/src/Umbraco.Examine.Lucene/LuceneIndexDiagnostics.cs index 33230a296d..d04c38ebaa 100644 --- a/src/Umbraco.Examine.Lucene/LuceneIndexDiagnostics.cs +++ b/src/Umbraco.Examine.Lucene/LuceneIndexDiagnostics.cs @@ -18,13 +18,13 @@ namespace Umbraco.Cms.Infrastructure.Examine public class LuceneIndexDiagnostics : IIndexDiagnostics { private readonly IHostingEnvironment _hostingEnvironment; - private readonly LuceneDirectoryIndexOptions _indexOptions; + private readonly LuceneDirectoryIndexOptions? _indexOptions; public LuceneIndexDiagnostics( LuceneIndex index, ILogger logger, IHostingEnvironment hostingEnvironment, - IOptionsMonitor indexOptions) + IOptionsMonitor? indexOptions) { _hostingEnvironment = hostingEnvironment; if (indexOptions != null) @@ -41,22 +41,22 @@ namespace Umbraco.Cms.Infrastructure.Examine - public Attempt IsHealthy() + public Attempt IsHealthy() { var isHealthy = Index.IsHealthy(out var indexError); - return isHealthy ? Attempt.Succeed() : Attempt.Fail(indexError.Message); + return isHealthy ? Attempt.Succeed() : Attempt.Fail(indexError?.Message); } public long GetDocumentCount() => Index.GetDocumentCount(); public IEnumerable GetFieldNames() => Index.GetFieldNames(); - public virtual IReadOnlyDictionary Metadata + public virtual IReadOnlyDictionary Metadata { get { Directory luceneDir = Index.GetLuceneDirectory(); - var d = new Dictionary + var d = new Dictionary { [nameof(UmbracoExamineIndex.CommitCount)] = Index.CommitCount, [nameof(UmbracoExamineIndex.DefaultAnalyzer)] = Index.DefaultAnalyzer.GetType().Name, diff --git a/src/Umbraco.Examine.Lucene/Umbraco.Examine.Lucene.csproj b/src/Umbraco.Examine.Lucene/Umbraco.Examine.Lucene.csproj index bba9068dfe..31657ba776 100644 --- a/src/Umbraco.Examine.Lucene/Umbraco.Examine.Lucene.csproj +++ b/src/Umbraco.Examine.Lucene/Umbraco.Examine.Lucene.csproj @@ -6,6 +6,7 @@ Umbraco.Examine.Lucene Umbraco.Cms.Examine.Lucene + enable true diff --git a/src/Umbraco.Examine.Lucene/UmbracoContentIndex.cs b/src/Umbraco.Examine.Lucene/UmbracoContentIndex.cs index 3bb3eb006c..f2c08a16b8 100644 --- a/src/Umbraco.Examine.Lucene/UmbracoContentIndex.cs +++ b/src/Umbraco.Examine.Lucene/UmbracoContentIndex.cs @@ -26,7 +26,7 @@ namespace Umbraco.Cms.Infrastructure.Examine IOptionsMonitor indexOptions, IHostingEnvironment hostingEnvironment, IRuntimeState runtimeState, - ILocalizationService languageService = null) + ILocalizationService? languageService = null) : base(loggerFactory, name, indexOptions, hostingEnvironment, runtimeState) { LanguageService = languageService; @@ -44,7 +44,7 @@ namespace Umbraco.Cms.Infrastructure.Examine } } - protected ILocalizationService LanguageService { get; } + protected ILocalizationService? LanguageService { get; } /// /// Explicitly override because we need to do validation differently than the underlying logic @@ -64,7 +64,7 @@ namespace Umbraco.Cms.Infrastructure.Examine // Then we'll index the Value group all together. var invalidOrValid = values.GroupBy(v => { - if (!v.Values.TryGetValue("path", out List paths) || paths.Count <= 0 || paths[0] == null) + if (!v.Values.TryGetValue("path", out List? paths) || paths.Count <= 0 || paths[0] == null) { return ValueSetValidationResult.Failed; } @@ -120,7 +120,7 @@ namespace Umbraco.Cms.Infrastructure.Examine /// /// ID of the node to delete /// - protected override void PerformDeleteFromIndex(IEnumerable itemIds, Action onComplete) + protected override void PerformDeleteFromIndex(IEnumerable itemIds, Action? onComplete) { var idsAsList = itemIds.ToList(); diff --git a/src/Umbraco.Examine.Lucene/UmbracoExamineIndex.cs b/src/Umbraco.Examine.Lucene/UmbracoExamineIndex.cs index 5121c32d32..105a74823c 100644 --- a/src/Umbraco.Examine.Lucene/UmbracoExamineIndex.cs +++ b/src/Umbraco.Examine.Lucene/UmbracoExamineIndex.cs @@ -53,7 +53,7 @@ namespace Umbraco.Cms.Infrastructure.Examine /// /// This check is required since the base examine lib will try to rebuild on startup /// - protected override void PerformDeleteFromIndex(IEnumerable itemIds, Action onComplete) + protected override void PerformDeleteFromIndex(IEnumerable itemIds, Action? onComplete) { if (CanInitialize()) { @@ -126,7 +126,7 @@ namespace Umbraco.Cms.Infrastructure.Examine } } - public Attempt IsHealthy() => _diagnostics.IsHealthy(); - public virtual IReadOnlyDictionary Metadata => _diagnostics.Metadata; + public Attempt IsHealthy() => _diagnostics.IsHealthy(); + public virtual IReadOnlyDictionary Metadata => _diagnostics.Metadata; } } diff --git a/src/Umbraco.Examine.Lucene/UmbracoExamineIndexDiagnostics.cs b/src/Umbraco.Examine.Lucene/UmbracoExamineIndexDiagnostics.cs index 877b9b97f0..6a18884dc2 100644 --- a/src/Umbraco.Examine.Lucene/UmbracoExamineIndexDiagnostics.cs +++ b/src/Umbraco.Examine.Lucene/UmbracoExamineIndexDiagnostics.cs @@ -24,7 +24,7 @@ namespace Umbraco.Cms.Infrastructure.Examine _index = index; } - public override IReadOnlyDictionary Metadata + public override IReadOnlyDictionary Metadata { get { diff --git a/src/Umbraco.Infrastructure/Examine/ExamineExtensions.cs b/src/Umbraco.Infrastructure/Examine/ExamineExtensions.cs index 8782d58835..076353a990 100644 --- a/src/Umbraco.Infrastructure/Examine/ExamineExtensions.cs +++ b/src/Umbraco.Infrastructure/Examine/ExamineExtensions.cs @@ -25,7 +25,7 @@ namespace Umbraco.Extensions /// /// Search results are skipped if it can't be fetched from the by its integer id. /// - public static IEnumerable ToPublishedSearchResults(this IEnumerable results, IPublishedCache cache) + public static IEnumerable ToPublishedSearchResults(this IEnumerable results, IPublishedCache? cache) { if (cache == null) throw new ArgumentNullException(nameof(cache)); @@ -70,10 +70,10 @@ namespace Umbraco.Extensions switch (indexType) { case IndexTypes.Content: - content = snapshot.Content.GetById(contentId); + content = snapshot.Content?.GetById(contentId); break; case IndexTypes.Media: - content = snapshot.Media.GetById(contentId); + content = snapshot.Media?.GetById(contentId); break; case IndexTypes.Member: throw new NotSupportedException("Cannot convert search results to member instances"); diff --git a/src/Umbraco.Infrastructure/Examine/GenericIndexDiagnostics.cs b/src/Umbraco.Infrastructure/Examine/GenericIndexDiagnostics.cs index 7bcfa2b2da..c2bf6b002d 100644 --- a/src/Umbraco.Infrastructure/Examine/GenericIndexDiagnostics.cs +++ b/src/Umbraco.Infrastructure/Examine/GenericIndexDiagnostics.cs @@ -46,11 +46,11 @@ namespace Umbraco.Cms.Infrastructure.Examine public IEnumerable GetFieldNames() => Enumerable.Empty(); - public IReadOnlyDictionary Metadata + public IReadOnlyDictionary Metadata { get { - var result = new Dictionary(); + var result = new Dictionary(); var props = TypeHelper.CachedDiscoverableProperties(_index.GetType(), mustWrite: false) .Where(x => s_ignoreProperties.InvariantContains(x.Name) == false) diff --git a/src/Umbraco.Infrastructure/Examine/IIndexDiagnostics.cs b/src/Umbraco.Infrastructure/Examine/IIndexDiagnostics.cs index 2eb6ca4595..dd9ee63239 100644 --- a/src/Umbraco.Infrastructure/Examine/IIndexDiagnostics.cs +++ b/src/Umbraco.Infrastructure/Examine/IIndexDiagnostics.cs @@ -25,6 +25,6 @@ namespace Umbraco.Cms.Infrastructure.Examine /// /// Used to display in the UI /// - IReadOnlyDictionary Metadata { get; } + IReadOnlyDictionary Metadata { get; } } } diff --git a/src/Umbraco.Infrastructure/IPublishedContentQuery.cs b/src/Umbraco.Infrastructure/IPublishedContentQuery.cs index b244cb93de..ab71edf650 100644 --- a/src/Umbraco.Infrastructure/IPublishedContentQuery.cs +++ b/src/Umbraco.Infrastructure/IPublishedContentQuery.cs @@ -14,13 +14,13 @@ namespace Umbraco.Cms.Core { IPublishedContent? Content(int id); - IPublishedContent Content(Guid id); + IPublishedContent? Content(Guid id); IPublishedContent? Content(Udi id); IPublishedContent? Content(object id); - IPublishedContent ContentSingleAtXPath(string xpath, params XPathVariable[] vars); + IPublishedContent? ContentSingleAtXPath(string xpath, params XPathVariable[] vars); IEnumerable Content(IEnumerable ids); @@ -36,7 +36,7 @@ namespace Umbraco.Cms.Core IPublishedContent? Media(int id); - IPublishedContent Media(Guid id); + IPublishedContent? Media(Guid id); IPublishedContent? Media(Udi id); diff --git a/src/Umbraco.Infrastructure/ModelsBuilder/PublishedModelUtility.cs b/src/Umbraco.Infrastructure/ModelsBuilder/PublishedModelUtility.cs index ebce9dd0aa..b782751dd8 100644 --- a/src/Umbraco.Infrastructure/ModelsBuilder/PublishedModelUtility.cs +++ b/src/Umbraco.Infrastructure/ModelsBuilder/PublishedModelUtility.cs @@ -37,9 +37,9 @@ namespace Umbraco.Cms.Infrastructure.ModelsBuilder switch (itemType) { case PublishedItemType.Content: - return publishedSnapshot.Content.GetContentType(alias); + return publishedSnapshot.Content?.GetContentType(alias); case PublishedItemType.Media: - return publishedSnapshot.Media.GetContentType(alias); + return publishedSnapshot.Media?.GetContentType(alias); case PublishedItemType.Member: return publishedSnapshot.Members?.GetContentType(alias); default: diff --git a/src/Umbraco.Infrastructure/PropertyEditors/MultiUrlPickerValueEditor.cs b/src/Umbraco.Infrastructure/PropertyEditors/MultiUrlPickerValueEditor.cs index 3b40e78ecd..cf80f547d4 100644 --- a/src/Umbraco.Infrastructure/PropertyEditors/MultiUrlPickerValueEditor.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/MultiUrlPickerValueEditor.cs @@ -104,7 +104,7 @@ namespace Umbraco.Cms.Core.PropertyEditors icon = documentEntity.ContentTypeIcon; published = culture == null ? documentEntity.Published : documentEntity.PublishedCultures.Contains(culture); udi = new GuidUdi(Constants.UdiEntityType.Document, documentEntity.Key); - url = publishedSnapshot.Content.GetById(entity.Key)?.Url(_publishedUrlProvider) ?? "#"; + url = publishedSnapshot.Content?.GetById(entity.Key)?.Url(_publishedUrlProvider) ?? "#"; trashed = documentEntity.Trashed; } else if(entity is IContentEntitySlim contentEntity) @@ -112,7 +112,7 @@ namespace Umbraco.Cms.Core.PropertyEditors icon = contentEntity.ContentTypeIcon; published = !contentEntity.Trashed; udi = new GuidUdi(Constants.UdiEntityType.Media, contentEntity.Key); - url = publishedSnapshot.Media.GetById(entity.Key)?.Url(_publishedUrlProvider) ?? "#"; + url = publishedSnapshot.Media?.GetById(entity.Key)?.Url(_publishedUrlProvider) ?? "#"; trashed = contentEntity.Trashed; } else diff --git a/src/Umbraco.Infrastructure/PropertyEditors/RichTextEditorPastedImages.cs b/src/Umbraco.Infrastructure/PropertyEditors/RichTextEditorPastedImages.cs index 6bba32c113..7556e24524 100644 --- a/src/Umbraco.Infrastructure/PropertyEditors/RichTextEditorPastedImages.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/RichTextEditorPastedImages.cs @@ -124,7 +124,7 @@ namespace Umbraco.Cms.Core.PropertyEditors // Get the new persisted image URL _umbracoContextAccessor.TryGetUmbracoContext(out var umbracoContext); - var mediaTyped = umbracoContext?.Media.GetById(udi.Guid); + var mediaTyped = umbracoContext?.Media?.GetById(udi.Guid); if (mediaTyped == null) throw new PanicException($"Could not find media by id {udi.Guid} or there was no UmbracoContext available."); diff --git a/src/Umbraco.Infrastructure/PropertyEditors/ValueConverters/BlockEditorConverter.cs b/src/Umbraco.Infrastructure/PropertyEditors/ValueConverters/BlockEditorConverter.cs index 8132e5fefc..8f68baeb40 100644 --- a/src/Umbraco.Infrastructure/PropertyEditors/ValueConverters/BlockEditorConverter.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/ValueConverters/BlockEditorConverter.cs @@ -28,7 +28,7 @@ namespace Umbraco.Cms.Core.PropertyEditors.ValueConverters var publishedContentCache = _publishedSnapshotAccessor.GetRequiredPublishedSnapshot().Content; // Only convert element types - content types will cause an exception when PublishedModelFactory creates the model - var publishedContentType = publishedContentCache.GetContentType(data.ContentTypeKey); + var publishedContentType = publishedContentCache?.GetContentType(data.ContentTypeKey); if (publishedContentType == null || publishedContentType.IsElement == false) { return null; @@ -52,7 +52,7 @@ namespace Umbraco.Cms.Core.PropertyEditors.ValueConverters public Type GetModelType(Guid contentTypeKey) { var publishedContentCache = _publishedSnapshotAccessor.GetRequiredPublishedSnapshot().Content; - var publishedContentType = publishedContentCache.GetContentType(contentTypeKey); + var publishedContentType = publishedContentCache?.GetContentType(contentTypeKey); if (publishedContentType != null) { var modelType = ModelType.For(publishedContentType.Alias); diff --git a/src/Umbraco.Infrastructure/PropertyEditors/ValueConverters/MediaPickerWithCropsValueConverter.cs b/src/Umbraco.Infrastructure/PropertyEditors/ValueConverters/MediaPickerWithCropsValueConverter.cs index 3d96edd6df..f7afea0715 100644 --- a/src/Umbraco.Infrastructure/PropertyEditors/ValueConverters/MediaPickerWithCropsValueConverter.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/ValueConverters/MediaPickerWithCropsValueConverter.cs @@ -66,7 +66,7 @@ namespace Umbraco.Cms.Core.PropertyEditors.ValueConverters var publishedSnapshot = _publishedSnapshotAccessor.GetRequiredPublishedSnapshot(); foreach (var dto in dtos) { - var mediaItem = publishedSnapshot.Media.GetById(preview, dto.MediaKey); + var mediaItem = publishedSnapshot.Media?.GetById(preview, dto.MediaKey); if (mediaItem != null) { var localCrops = new ImageCropperValue diff --git a/src/Umbraco.Infrastructure/PropertyEditors/ValueConverters/MultiUrlPickerValueConverter.cs b/src/Umbraco.Infrastructure/PropertyEditors/ValueConverters/MultiUrlPickerValueConverter.cs index 17a423923d..6ba63b5765 100644 --- a/src/Umbraco.Infrastructure/PropertyEditors/ValueConverters/MultiUrlPickerValueConverter.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/ValueConverters/MultiUrlPickerValueConverter.cs @@ -75,8 +75,8 @@ namespace Umbraco.Cms.Core.PropertyEditors.ValueConverters : LinkType.Content; var content = type == LinkType.Media ? - publishedSnapshot.Media.GetById(preview, dto.Udi.Guid) : - publishedSnapshot.Content.GetById(preview, dto.Udi.Guid); + publishedSnapshot.Media?.GetById(preview, dto.Udi.Guid) : + publishedSnapshot.Content?.GetById(preview, dto.Udi.Guid); if (content == null || content.ContentType.ItemType == PublishedItemType.Element) { diff --git a/src/Umbraco.Infrastructure/PropertyEditors/ValueConverters/NestedContentValueConverterBase.cs b/src/Umbraco.Infrastructure/PropertyEditors/ValueConverters/NestedContentValueConverterBase.cs index 09832c8fbb..580cd22930 100644 --- a/src/Umbraco.Infrastructure/PropertyEditors/ValueConverters/NestedContentValueConverterBase.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/ValueConverters/NestedContentValueConverterBase.cs @@ -48,7 +48,7 @@ namespace Umbraco.Cms.Core.PropertyEditors.ValueConverters return null; var publishedSnapshot = _publishedSnapshotAccessor.GetRequiredPublishedSnapshot(); // only convert element types - content types will cause an exception when PublishedModelFactory creates the model - var publishedContentType = publishedSnapshot.Content.GetContentType(elementTypeAlias); + var publishedContentType = publishedSnapshot.Content?.GetContentType(elementTypeAlias); if (publishedContentType == null || publishedContentType.IsElement == false) return null; diff --git a/src/Umbraco.Infrastructure/PublishedCache/PublishedContentTypeCache.cs b/src/Umbraco.Infrastructure/PublishedCache/PublishedContentTypeCache.cs index 4065628ee9..d26d4da617 100644 --- a/src/Umbraco.Infrastructure/PublishedCache/PublishedContentTypeCache.cs +++ b/src/Umbraco.Infrastructure/PublishedCache/PublishedContentTypeCache.cs @@ -19,16 +19,16 @@ namespace Umbraco.Cms.Core.PublishedCache private readonly Dictionary _typesByAlias = new Dictionary(); private readonly Dictionary _typesById = new Dictionary(); private readonly Dictionary _keyToIdMap = new Dictionary(); - private readonly IContentTypeService _contentTypeService; - private readonly IMediaTypeService _mediaTypeService; - private readonly IMemberTypeService _memberTypeService; + private readonly IContentTypeService? _contentTypeService; + private readonly IMediaTypeService? _mediaTypeService; + private readonly IMemberTypeService? _memberTypeService; private readonly IPublishedContentTypeFactory _publishedContentTypeFactory; private readonly ILogger _logger; private readonly ReaderWriterLockSlim _lock = new ReaderWriterLockSlim(); private bool _disposedValue; // default ctor - public PublishedContentTypeCache(IContentTypeService contentTypeService, IMediaTypeService mediaTypeService, IMemberTypeService memberTypeService, IPublishedContentTypeFactory publishedContentTypeFactory, ILogger logger) + public PublishedContentTypeCache(IContentTypeService? contentTypeService, IMediaTypeService? mediaTypeService, IMemberTypeService? memberTypeService, IPublishedContentTypeFactory publishedContentTypeFactory, ILogger logger) { _contentTypeService = contentTypeService; _mediaTypeService = mediaTypeService; @@ -249,9 +249,9 @@ namespace Umbraco.Cms.Core.PublishedCache { IContentTypeComposition? contentType = itemType switch { - PublishedItemType.Content => _contentTypeService.Get(key), - PublishedItemType.Media => _mediaTypeService.Get(key), - PublishedItemType.Member => _memberTypeService.Get(key), + PublishedItemType.Content => _contentTypeService?.Get(key), + PublishedItemType.Media => _mediaTypeService?.Get(key), + PublishedItemType.Member => _memberTypeService?.Get(key), _ => throw new ArgumentOutOfRangeException(nameof(itemType)), }; if (contentType == null) @@ -264,9 +264,9 @@ namespace Umbraco.Cms.Core.PublishedCache { IContentTypeComposition? contentType = itemType switch { - PublishedItemType.Content => _contentTypeService.Get(alias), - PublishedItemType.Media => _mediaTypeService.Get(alias), - PublishedItemType.Member => _memberTypeService.Get(alias), + PublishedItemType.Content => _contentTypeService?.Get(alias), + PublishedItemType.Media => _mediaTypeService?.Get(alias), + PublishedItemType.Member => _memberTypeService?.Get(alias), _ => throw new ArgumentOutOfRangeException(nameof(itemType)), }; if (contentType == null) @@ -279,9 +279,9 @@ namespace Umbraco.Cms.Core.PublishedCache { IContentTypeComposition? contentType = itemType switch { - PublishedItemType.Content => _contentTypeService.Get(id), - PublishedItemType.Media => _mediaTypeService.Get(id), - PublishedItemType.Member => _memberTypeService.Get(id), + PublishedItemType.Content => _contentTypeService?.Get(id), + PublishedItemType.Media => _mediaTypeService?.Get(id), + PublishedItemType.Member => _memberTypeService?.Get(id), _ => throw new ArgumentOutOfRangeException(nameof(itemType)), }; if (contentType == null) diff --git a/src/Umbraco.Infrastructure/PublishedContentQuery.cs b/src/Umbraco.Infrastructure/PublishedContentQuery.cs index 0c8a31d829..ec37152e3c 100644 --- a/src/Umbraco.Infrastructure/PublishedContentQuery.cs +++ b/src/Umbraco.Infrastructure/PublishedContentQuery.cs @@ -99,7 +99,7 @@ namespace Umbraco.Cms.Infrastructure public IPublishedContent? Content(int id) => ItemById(id, _publishedSnapshot.Content); - public IPublishedContent Content(Guid id) + public IPublishedContent? Content(Guid id) => ItemById(id, _publishedSnapshot.Content); public IPublishedContent? Content(Udi? id) @@ -132,7 +132,7 @@ namespace Umbraco.Cms.Infrastructure return null; } - public IPublishedContent ContentSingleAtXPath(string xpath, params XPathVariable[] vars) + public IPublishedContent? ContentSingleAtXPath(string xpath, params XPathVariable[] vars) => ItemByXPath(xpath, vars, _publishedSnapshot.Content); public IEnumerable Content(IEnumerable ids) @@ -160,7 +160,7 @@ namespace Umbraco.Cms.Infrastructure public IPublishedContent? Media(int id) => ItemById(id, _publishedSnapshot.Media); - public IPublishedContent Media(Guid id) + public IPublishedContent? Media(Guid id) => ItemById(id, _publishedSnapshot.Media); public IPublishedContent? Media(Udi? id) @@ -209,29 +209,29 @@ namespace Umbraco.Cms.Infrastructure #region Used by Content/Media - private static IPublishedContent? ItemById(int id, IPublishedCache cache) - => cache.GetById(id); + private static IPublishedContent? ItemById(int id, IPublishedCache? cache) + => cache?.GetById(id); - private static IPublishedContent ItemById(Guid id, IPublishedCache cache) - => cache.GetById(id); + private static IPublishedContent? ItemById(Guid id, IPublishedCache? cache) + => cache?.GetById(id); - private static IPublishedContent ItemByXPath(string xpath, XPathVariable[] vars, IPublishedCache cache) - => cache.GetSingleByXPath(xpath, vars); + private static IPublishedContent? ItemByXPath(string xpath, XPathVariable[] vars, IPublishedCache? cache) + => cache?.GetSingleByXPath(xpath, vars); - private static IEnumerable ItemsByIds(IPublishedCache cache, IEnumerable ids) + private static IEnumerable ItemsByIds(IPublishedCache? cache, IEnumerable ids) => ids.Select(eachId => ItemById(eachId, cache)).WhereNotNull(); - private IEnumerable ItemsByIds(IPublishedCache cache, IEnumerable ids) + private IEnumerable ItemsByIds(IPublishedCache? cache, IEnumerable ids) => ids.Select(eachId => ItemById(eachId, cache)).WhereNotNull(); - private static IEnumerable ItemsByXPath(string xpath, XPathVariable[] vars, IPublishedCache cache) - => cache.GetByXPath(xpath, vars); + private static IEnumerable ItemsByXPath(string xpath, XPathVariable[] vars, IPublishedCache? cache) + => cache?.GetByXPath(xpath, vars) ?? Array.Empty(); - private static IEnumerable ItemsByXPath(XPathExpression xpath, XPathVariable[] vars, IPublishedCache cache) - => cache.GetByXPath(xpath, vars); + private static IEnumerable ItemsByXPath(XPathExpression xpath, XPathVariable[] vars, IPublishedCache? cache) + => cache?.GetByXPath(xpath, vars) ?? Array.Empty(); - private static IEnumerable ItemsAtRoot(IPublishedCache cache) - => cache.GetAtRoot(); + private static IEnumerable ItemsAtRoot(IPublishedCache? cache) + => cache?.GetAtRoot() ?? Array.Empty(); #endregion diff --git a/src/Umbraco.Infrastructure/Routing/ContentFinderByConfigured404.cs b/src/Umbraco.Infrastructure/Routing/ContentFinderByConfigured404.cs index 4ce4a0252f..3bcb8c525f 100644 --- a/src/Umbraco.Infrastructure/Routing/ContentFinderByConfigured404.cs +++ b/src/Umbraco.Infrastructure/Routing/ContentFinderByConfigured404.cs @@ -63,7 +63,7 @@ namespace Umbraco.Cms.Core.Routing int? domainContentId = null; // try to find a culture as best as we can - string errorCulture = CultureInfo.CurrentUICulture.Name; + string? errorCulture = CultureInfo.CurrentUICulture.Name; if (frequest.Domain != null) { errorCulture = frequest.Domain.Culture; @@ -77,7 +77,7 @@ namespace Umbraco.Cms.Core.Routing while (pos > 1) { route = route.Substring(0, pos); - node = umbracoContext.Content.GetByRoute(route, culture: frequest?.Culture); + node = umbracoContext.Content?.GetByRoute(route, culture: frequest?.Culture); if (node != null) { break; @@ -109,7 +109,7 @@ namespace Umbraco.Cms.Core.Routing { _logger.LogDebug("Got id={ErrorNodeId}.", error404.Value); - content = umbracoContext.Content.GetById(error404.Value); + content = umbracoContext.Content?.GetById(error404.Value); _logger.LogDebug(content == null ? "Could not find content with that id." diff --git a/src/Umbraco.Infrastructure/Routing/NotFoundHandlerHelper.cs b/src/Umbraco.Infrastructure/Routing/NotFoundHandlerHelper.cs index f85e66cf1a..b2fd624177 100644 --- a/src/Umbraco.Infrastructure/Routing/NotFoundHandlerHelper.cs +++ b/src/Umbraco.Infrastructure/Routing/NotFoundHandlerHelper.cs @@ -20,7 +20,7 @@ namespace Umbraco.Cms.Core.Routing ContentErrorPage[] error404Collection, IEntityService entityService, IPublishedContentQuery publishedContentQuery, - string errorCulture, + string? errorCulture, int? domainContentId) { if (error404Collection.Length > 1) @@ -87,7 +87,7 @@ namespace Umbraco.Cms.Core.Routing publishedContentExists: i => publishedContentQuery.Content(i) != null); // now we'll try to execute the expression - IPublishedContent nodeResult = publishedContentQuery.ContentSingleAtXPath(xpathResult); + IPublishedContent? nodeResult = publishedContentQuery.ContentSingleAtXPath(xpathResult); if (nodeResult != null) { return nodeResult.Id; diff --git a/src/Umbraco.PublishedCache.NuCache/CacheKeys.cs b/src/Umbraco.PublishedCache.NuCache/CacheKeys.cs index 0ec6f0b7cb..3a268c49c6 100644 --- a/src/Umbraco.PublishedCache.NuCache/CacheKeys.cs +++ b/src/Umbraco.PublishedCache.NuCache/CacheKeys.cs @@ -12,7 +12,7 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static string LangId(string culture) + private static string LangId(string? culture) => string.IsNullOrEmpty(culture) ? string.Empty : ("-L:" + culture); public static string PublishedContentChildren(Guid contentUid, bool previewing) @@ -49,12 +49,12 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache // a valid ID in the database at that point, whereas content and properties // may be virtual (and not in umbracoNode). - public static string ContentCacheRouteByContent(int id, bool previewing, string culture) + public static string ContentCacheRouteByContent(int id, bool previewing, string? culture) { return "NuCache.ContentCache.RouteByContent[" + DraftOrPub(previewing) + id + LangId(culture) + "]"; } - public static string ContentCacheContentByRoute(string route, bool previewing, string culture) + public static string ContentCacheContentByRoute(string route, bool previewing, string? culture) { return "NuCache.ContentCache.ContentByRoute[" + DraftOrPub(previewing) + route + LangId(culture) + "]"; } diff --git a/src/Umbraco.PublishedCache.NuCache/ContentCache.cs b/src/Umbraco.PublishedCache.NuCache/ContentCache.cs index 55362612b8..180d24a4cf 100644 --- a/src/Umbraco.PublishedCache.NuCache/ContentCache.cs +++ b/src/Umbraco.PublishedCache.NuCache/ContentCache.cs @@ -19,7 +19,7 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache public class ContentCache : PublishedCacheBase, IPublishedContentCache, INavigableData, IDisposable { private readonly IDomainCache _domainCache; - private readonly IAppCache _elementsCache; + private readonly IAppCache? _elementsCache; private readonly GlobalSettings _globalSettings; private readonly ContentStore.Snapshot _snapshot; private readonly IAppCache _snapshotCache; @@ -39,7 +39,7 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache // but, no, UmbracoContext returns snapshot.Content which comes from elements SO a resync should create a new cache public ContentCache(bool previewDefault, ContentStore.Snapshot snapshot, IAppCache snapshotCache, - IAppCache elementsCache, IDomainCache domainCache, IOptions globalSettings, + IAppCache? elementsCache, IDomainCache domainCache, IOptions globalSettings, IVariationContextAccessor variationContextAccessor) : base(previewDefault) { @@ -66,11 +66,11 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache // at the moment we try our best to be backward compatible, but really, // should get rid of hideTopLevelNode and other oddities entirely, eventually - public IPublishedContent GetByRoute(string route, bool? hideTopLevelNode = null, string? culture = null) => + public IPublishedContent? GetByRoute(string route, bool? hideTopLevelNode = null, string? culture = null) => GetByRoute(PreviewDefault, route, hideTopLevelNode, culture); - public IPublishedContent GetByRoute(bool preview, string route, bool? hideTopLevelNode = null, - string culture = null) + public IPublishedContent? GetByRoute(bool preview, string route, bool? hideTopLevelNode = null, + string? culture = null) { if (route == null) { @@ -78,14 +78,14 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache } - IAppCache cache = preview == false || PublishedSnapshotService.FullCacheWhenPreviewing + IAppCache? cache = preview == false || PublishedSnapshotService.FullCacheWhenPreviewing ? _elementsCache : _snapshotCache; var key = CacheKeys.ContentCacheContentByRoute(route, preview, culture); - return cache.GetCacheItem(key, () => GetByRouteInternal(preview, route, hideTopLevelNode, culture)); + return cache?.GetCacheItem(key, () => GetByRouteInternal(preview, route, hideTopLevelNode, culture)); } - private IPublishedContent GetByRouteInternal(bool preview, string route, bool? hideTopLevelNode, string culture) + private IPublishedContent? GetByRouteInternal(bool preview, string route, bool? hideTopLevelNode, string? culture) { hideTopLevelNode = hideTopLevelNode ?? HideTopLevelNodeFromPath; // default = settings @@ -97,7 +97,7 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache var startNodeId = pos == 0 ? 0 : int.Parse(route.Substring(0, pos), CultureInfo.InvariantCulture); var parts = path.Split(Constants.CharArrays.ForwardSlash, StringSplitOptions.RemoveEmptyEntries); - IPublishedContent content; + IPublishedContent? content; if (startNodeId > 0) { @@ -120,7 +120,7 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache // hideTopLevelNode = support legacy stuff, look for /*/path/to/node // else normal, look for /path/to/node content = hideTopLevelNode.Value - ? GetAtRoot(preview).SelectMany(x => x.Children(_variationContextAccessor, culture)) + ? GetAtRoot(preview).SelectMany(x => x.Children(_variationContextAccessor, culture)!) // Do we suppress here? .FirstOrDefault(x => x.UrlSegment(_variationContextAccessor, culture) == parts[0]) : GetAtRoot(preview) .FirstOrDefault(x => x.UrlSegment(_variationContextAccessor, culture) == parts[0]); @@ -139,21 +139,21 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache return content; } - public string GetRouteById(int contentId, string? culture = null) => + public string? GetRouteById(int contentId, string? culture = null) => GetRouteById(PreviewDefault, contentId, culture); - public string GetRouteById(bool preview, int contentId, string? culture = null) + public string? GetRouteById(bool preview, int contentId, string? culture = null) { - IAppCache cache = preview == false || PublishedSnapshotService.FullCacheWhenPreviewing + IAppCache? cache = preview == false || PublishedSnapshotService.FullCacheWhenPreviewing ? _elementsCache : _snapshotCache; var key = CacheKeys.ContentCacheRouteByContent(contentId, preview, culture); - return cache.GetCacheItem(key, () => GetRouteByIdInternal(preview, contentId, null, culture)); + return cache?.GetCacheItem(key, () => GetRouteByIdInternal(preview, contentId, null, culture)); } - private string GetRouteByIdInternal(bool preview, int contentId, bool? hideTopLevelNode, string culture) + private string? GetRouteByIdInternal(bool preview, int contentId, bool? hideTopLevelNode, string? culture) { - IPublishedContent node = GetById(preview, contentId); + IPublishedContent? node = GetById(preview, contentId); if (node == null) { return null; @@ -164,7 +164,7 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache // walk up from that node until we hit a node with a domain, // or we reach the content root, collecting URLs in the way var pathParts = new List(); - IPublishedContent n = node; + IPublishedContent? n = node; var urlSegment = n.UrlSegment(_variationContextAccessor, culture); var hasDomains = _domainCache.GetAssignedWithCulture(culture, n.Id); while (hasDomains == false && n != null) // n is null at root @@ -175,7 +175,7 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache return null; } - pathParts.Add(urlSegment); + pathParts.Add(urlSegment!); // move to parent node n = n.Parent; @@ -209,14 +209,14 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache return route; } - private IPublishedContent FollowRoute(IPublishedContent content, IReadOnlyList parts, int start, - string culture) + private IPublishedContent? FollowRoute(IPublishedContent? content, IReadOnlyList parts, int start, + string? culture) { var i = start; while (content != null && i < parts.Count) { var part = parts[i++]; - content = content.Children(_variationContextAccessor, culture).FirstOrDefault(x => + content = content.Children(_variationContextAccessor, culture)?.FirstOrDefault(x => { var urlSegment = x.UrlSegment(_variationContextAccessor, culture); return urlSegment == part; @@ -238,7 +238,7 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache // that's the way it works pre-4.10 and we try to be backward compat for the time being if (content.Parent == null) { - IPublishedContent rootNode = GetByRoute(preview, "/", true); + IPublishedContent? rootNode = GetByRoute(preview, "/", true); if (rootNode == null) { throw new Exception("Failed to get node at /."); @@ -259,19 +259,19 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache #region Get, Has - public override IPublishedContent GetById(bool preview, int contentId) + public override IPublishedContent? GetById(bool preview, int contentId) { - ContentNode node = _snapshot.Get(contentId); + ContentNode? node = _snapshot.Get(contentId); return GetNodePublishedContent(node, preview); } - public override IPublishedContent GetById(bool preview, Guid contentId) + public override IPublishedContent? GetById(bool preview, Guid contentId) { - ContentNode node = _snapshot.Get(contentId); + ContentNode? node = _snapshot.Get(contentId); return GetNodePublishedContent(node, preview); } - public override IPublishedContent GetById(bool preview, Udi contentId) + public override IPublishedContent? GetById(bool preview, Udi contentId) { var guidUdi = contentId as GuidUdi; if (guidUdi == null) @@ -290,7 +290,7 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache public override bool HasById(bool preview, int contentId) { - ContentNode n = _snapshot.Get(contentId); + ContentNode? n = _snapshot.Get(contentId); if (n == null) { return false; @@ -329,7 +329,7 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache return atRoot; } - private static IPublishedContent GetNodePublishedContent(ContentNode node, bool preview) + private static IPublishedContent? GetNodePublishedContent(ContentNode? node, bool preview) { if (node == null) { @@ -345,7 +345,7 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache // gets a published content as a previewing draft, if preview is true // this is for published content when previewing - private static IPublishedContent GetPublishedContentAsDraft(IPublishedContent content /*, bool preview*/) + private static IPublishedContent? GetPublishedContentAsDraft(IPublishedContent? content /*, bool preview*/) { if (content == null /*|| preview == false*/) { @@ -369,21 +369,21 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache #region XPath - public override IPublishedContent GetSingleByXPath(bool preview, string xpath, XPathVariable[] vars) + public override IPublishedContent? GetSingleByXPath(bool preview, string xpath, XPathVariable[] vars) { XPathNavigator navigator = CreateNavigator(preview); XPathNodeIterator iterator = navigator.Select(xpath, vars); return GetSingleByXPath(iterator); } - public override IPublishedContent GetSingleByXPath(bool preview, XPathExpression xpath, XPathVariable[] vars) + public override IPublishedContent? GetSingleByXPath(bool preview, XPathExpression xpath, XPathVariable[] vars) { XPathNavigator navigator = CreateNavigator(preview); XPathNodeIterator iterator = navigator.Select(xpath, vars); return GetSingleByXPath(iterator); } - private static IPublishedContent GetSingleByXPath(XPathNodeIterator iterator) + private static IPublishedContent? GetSingleByXPath(XPathNodeIterator iterator) { if (iterator.MoveNext() == false) { @@ -433,7 +433,7 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache return navigator; } - public override XPathNavigator CreateNodeNavigator(int id, bool preview) + public override XPathNavigator? CreateNodeNavigator(int id, bool preview) { var source = new Source(this, preview); var navigator = new NavigableNavigator(source); @@ -444,11 +444,11 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache #region Content types - public override IPublishedContentType GetContentType(int id) => _snapshot.GetContentType(id); + public override IPublishedContentType? GetContentType(int id) => _snapshot.GetContentType(id); - public override IPublishedContentType GetContentType(string alias) => _snapshot.GetContentType(alias); + public override IPublishedContentType? GetContentType(string alias) => _snapshot.GetContentType(alias); - public override IPublishedContentType GetContentType(Guid key) => _snapshot.GetContentType(key); + public override IPublishedContentType? GetContentType(Guid key) => _snapshot.GetContentType(key); #endregion } diff --git a/src/Umbraco.PublishedCache.NuCache/ContentNode.cs b/src/Umbraco.PublishedCache.NuCache/ContentNode.cs index beb5986861..e5a766a653 100644 --- a/src/Umbraco.PublishedCache.NuCache/ContentNode.cs +++ b/src/Umbraco.PublishedCache.NuCache/ContentNode.cs @@ -19,6 +19,7 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache LastChildContentId = -1; NextSiblingContentId = -1; PreviousSiblingContentId = -1; + Path = string.Empty; } // special ctor with no content data - for members @@ -73,7 +74,7 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache } // two-phase ctor, phase 2 - public void SetContentTypeAndData(IPublishedContentType contentType, ContentData draftData, ContentData publishedData, IPublishedSnapshotAccessor publishedSnapshotAccessor, IVariationContextAccessor variationContextAccessor, IPublishedModelFactory publishedModelFactory) + public void SetContentTypeAndData(IPublishedContentType contentType, ContentData? draftData, ContentData? publishedData, IPublishedSnapshotAccessor publishedSnapshotAccessor, IVariationContextAccessor variationContextAccessor, IPublishedModelFactory publishedModelFactory) { ContentType = contentType; @@ -89,7 +90,7 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache } // clone - public ContentNode(ContentNode origin, IPublishedModelFactory publishedModelFactory, IPublishedContentType contentType = null) + public ContentNode(ContentNode origin, IPublishedModelFactory publishedModelFactory, IPublishedContentType? contentType = null) { _publishedModelFactory = publishedModelFactory; Id = origin.Id; @@ -118,7 +119,7 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache #pragma warning disable IDE1006 // Naming Styles public readonly int Id; public readonly Guid Uid; - public IPublishedContentType ContentType; + public IPublishedContentType ContentType = null!; public readonly int Level; public readonly string Path; public readonly int SortOrder; @@ -137,21 +138,21 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache public readonly int CreatorId; #pragma warning restore IDE1006 // Naming Styles - private ContentData _draftData; - private ContentData _publishedData; - private IVariationContextAccessor _variationContextAccessor; - private IPublishedSnapshotAccessor _publishedSnapshotAccessor; + private ContentData? _draftData; + private ContentData? _publishedData; + private IVariationContextAccessor? _variationContextAccessor; + private IPublishedSnapshotAccessor? _publishedSnapshotAccessor; public bool HasPublished => _publishedData != null; - public bool HasPublishedCulture(string culture) => _publishedData != null && _publishedData.CultureInfos.ContainsKey(culture); + public bool HasPublishedCulture(string culture) => _publishedData != null && (_publishedData.CultureInfos?.ContainsKey(culture) ?? false); // draft and published version (either can be null, but not both) // are models not direct PublishedContent instances - private IPublishedContent _draftModel; - private IPublishedContent _publishedModel; - private IPublishedModelFactory _publishedModelFactory; + private IPublishedContent? _draftModel; + private IPublishedContent? _publishedModel; + private IPublishedModelFactory? _publishedModelFactory; - private IPublishedContent GetModel(ref IPublishedContent model, ContentData contentData) + private IPublishedContent? GetModel(ref IPublishedContent? model, ContentData? contentData) { if (model != null) return model; if (contentData == null) return null; @@ -166,13 +167,13 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache // we know what we do, so it is fine here and avoids allocating an object lock (this) { - return model = model ?? m; + return model ??= m; } } - public IPublishedContent DraftModel => GetModel(ref _draftModel, _draftData); + public IPublishedContent? DraftModel => GetModel(ref _draftModel, _draftData); - public IPublishedContent PublishedModel => GetModel(ref _publishedModel, _publishedData); + public IPublishedContent? PublishedModel => GetModel(ref _publishedModel, _publishedData); public ContentNodeKit ToKit() => new ContentNodeKit(this, ContentType.Id, _draftData, _publishedData); diff --git a/src/Umbraco.PublishedCache.NuCache/ContentNodeKit.cs b/src/Umbraco.PublishedCache.NuCache/ContentNodeKit.cs index 3f230925fe..d845621db9 100644 --- a/src/Umbraco.PublishedCache.NuCache/ContentNodeKit.cs +++ b/src/Umbraco.PublishedCache.NuCache/ContentNodeKit.cs @@ -8,18 +8,18 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache public struct ContentNodeKit { [Obsolete("This will be changed to a property in future versions")] - public ContentNode Node; + public ContentNode Node = null!; [Obsolete("This will be changed to a property in future versions")] public int ContentTypeId; [Obsolete("This will be changed to a property in future versions")] - public ContentData DraftData; + public ContentData? DraftData; [Obsolete("This will be changed to a property in future versions")] - public ContentData PublishedData; + public ContentData? PublishedData; - public ContentNodeKit(ContentNode node, int contentTypeId, ContentData draftData, ContentData publishedData) + public ContentNodeKit(ContentNode node, int contentTypeId, ContentData? draftData, ContentData? publishedData) { Node = node; ContentTypeId = contentTypeId; @@ -33,7 +33,7 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache public bool IsNull => ContentTypeId < 0; public static ContentNodeKit Empty { get; } = new ContentNodeKit(); - public static ContentNodeKit Null { get; } = new ContentNodeKit(null, -1, null, null); + public static ContentNodeKit Null { get; } = new ContentNodeKit(null!, -1, null, null); public void Build( IPublishedContentType contentType, @@ -53,7 +53,7 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache if (draftData == null && !canBePublished) draftData = PublishedData; - Node.SetContentTypeAndData(contentType, draftData, publishedData, publishedSnapshotAccessor, variationContextAccessor, publishedModelFactory); + Node?.SetContentTypeAndData(contentType, draftData, publishedData, publishedSnapshotAccessor, variationContextAccessor, publishedModelFactory); } public ContentNodeKit Clone(IPublishedModelFactory publishedModelFactory) diff --git a/src/Umbraco.PublishedCache.NuCache/ContentStore.cs b/src/Umbraco.PublishedCache.NuCache/ContentStore.cs index 98fc4a3ffe..f5492cf0ab 100644 --- a/src/Umbraco.PublishedCache.NuCache/ContentStore.cs +++ b/src/Umbraco.PublishedCache.NuCache/ContentStore.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Concurrent; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Threading; using System.Threading.Tasks; @@ -11,6 +12,7 @@ using Umbraco.Cms.Core.Models.PublishedContent; using Umbraco.Cms.Core.PublishedCache; using Umbraco.Cms.Core.Scoping; using Umbraco.Cms.Infrastructure.PublishedCache.Snap; +using Umbraco.Extensions; namespace Umbraco.Cms.Infrastructure.PublishedCache { @@ -37,27 +39,27 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache private readonly IVariationContextAccessor _variationContextAccessor; private readonly ILogger _logger; private readonly ILoggerFactory _loggerFactory; - private readonly ConcurrentDictionary> _contentNodes; + private readonly ConcurrentDictionary> _contentNodes; private LinkedNode _root; // We must keep separate dictionaries for by id and by alias because we track these in snapshot/layers // and it is possible that the alias of a content type can be different for the same id in another layer // whereas the GUID -> INT cross reference can never be different - private readonly ConcurrentDictionary> _contentTypesById; - private readonly ConcurrentDictionary> _contentTypesByAlias; + private readonly ConcurrentDictionary> _contentTypesById; + private readonly ConcurrentDictionary> _contentTypesByAlias; private readonly ConcurrentDictionary _contentTypeKeyToIdMap; private readonly ConcurrentDictionary _contentKeyToIdMap; private readonly IPublishedModelFactory _publishedModelFactory; - private BPlusTree _localDb; + private BPlusTree? _localDb; private readonly ConcurrentQueue _genObjs; - private GenObj _genObj; + private GenObj? _genObj; private readonly object _wlocko = new object(); private readonly object _rlocko = new object(); private long _liveGen, _floorGen; private bool _nextGen, _collectAuto; - private Task _collectTask; - private List> _wchanges; + private Task? _collectTask; + private List>? _wchanges; // TODO: collection trigger (ok for now) // see SnapDictionary notes @@ -71,7 +73,7 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache ILogger logger, ILoggerFactory loggerFactory, IPublishedModelFactory publishedModelFactory, - BPlusTree localDb = null) + BPlusTree? localDb = null) { _publishedSnapshotAccessor = publishedSnapshotAccessor; _variationContextAccessor = variationContextAccessor; @@ -80,10 +82,10 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache _publishedModelFactory = publishedModelFactory; _localDb = localDb; - _contentNodes = new ConcurrentDictionary>(); + _contentNodes = new ConcurrentDictionary>(); _root = new LinkedNode(new ContentNode(), 0); - _contentTypesById = new ConcurrentDictionary>(); - _contentTypesByAlias = new ConcurrentDictionary>(StringComparer.InvariantCultureIgnoreCase); + _contentTypesById = new ConcurrentDictionary>(); + _contentTypesByAlias = new ConcurrentDictionary>(StringComparer.InvariantCultureIgnoreCase); _contentTypeKeyToIdMap = new ConcurrentDictionary(); _contentKeyToIdMap = new ConcurrentDictionary(); @@ -134,7 +136,7 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache // gets a scope contextual representing a locked writer to the dictionary // TODO: GetScopedWriter? should the dict have a ref onto the scope provider? - public IDisposable GetScopedWriteLock(IScopeProvider scopeProvider) + public IDisposable? GetScopedWriteLock(IScopeProvider scopeProvider) { return ScopeContextualBase.Get(scopeProvider, _instanceId, scoped => new ScopedWriteLock(this, scoped)); } @@ -222,7 +224,8 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache } private void Rollback(ConcurrentDictionary> dictionary) - where TValue : class + where TValue : class? + where TKey : notnull { foreach (var item in dictionary) { @@ -340,8 +343,8 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache { var node = link.Value; if (node == null) continue; - var contentTypeId = node.ContentType.Id; - if (index.TryGetValue(contentTypeId, out var contentType) == false) continue; + var contentTypeId = node.ContentType?.Id; + if (contentTypeId is null || index.TryGetValue(contentTypeId.Value, out var contentType) == false) continue; SetValueLocked(_contentNodes, node.Id, new ContentNode(node, _publishedModelFactory, contentType)); } } @@ -357,7 +360,7 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache /// /// Thrown if this method is not called within a write lock /// - public void SetAllContentTypesLocked(IEnumerable types) + public void SetAllContentTypesLocked(IEnumerable? types) { EnsureLocked(); @@ -365,12 +368,16 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache ClearLocked(_contentTypesById); ClearLocked(_contentTypesByAlias); - // set all new content types - foreach (var type in types) + if (types is not null) { - SetContentTypeLocked(type); + // set all new content types + foreach (var type in types) + { + SetContentTypeLocked(type); + } } + // beware! at that point the cache is inconsistent, // assuming we are going to SetAll content items! } @@ -388,7 +395,7 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache /// /// Thrown if this method is not called within a write lock /// - public void UpdateContentTypesLocked(IReadOnlyCollection removedIds, IReadOnlyCollection refreshedTypes, IReadOnlyCollection kits) + public void UpdateContentTypesLocked(IReadOnlyCollection? removedIds, IReadOnlyCollection refreshedTypes, IReadOnlyCollection kits) { EnsureLocked(); @@ -472,7 +479,7 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache /// /// Thrown if this method is not called within a write lock /// - public void UpdateDataTypesLocked(IEnumerable dataTypeIds, Func getContentType) + public void UpdateDataTypesLocked(IEnumerable dataTypeIds, Func getContentType) { EnsureLocked(); @@ -481,11 +488,11 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache kvp.Value.Value != null && kvp.Value.Value.PropertyTypes.Any(p => dataTypeIds.Contains(p.DataType.Id))) .Select(kvp => kvp.Value.Value) - .Select(x => getContentType(x.Id)) + .Select(x => getContentType(x!.Id)) .Where(x => x != null) // poof, gone, very unlikely and probably an anomaly .ToArray(); - var contentTypeIdsA = contentTypes.Select(x => x.Id).ToArray(); + var contentTypeIdsA = contentTypes.Select(x => x!.Id).ToArray(); var contentTypeNodes = new Dictionary>(); foreach (var id in contentTypeIdsA) contentTypeNodes[id] = new List(); @@ -496,7 +503,7 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache contentTypeNodes[node.ContentType.Id].Add(node.Id); } - foreach (var contentType in contentTypes) + foreach (var contentType in contentTypes.WhereNotNull()) { // again, weird situation if (contentTypeNodes.ContainsKey(contentType.Id) == false) @@ -522,7 +529,7 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache /// /// Returns false if the parent was not found or if the kit validation failed /// - private bool BuildKit(ContentNodeKit kit, out LinkedNode parent) + private bool BuildKit(ContentNodeKit kit, [MaybeNullWhen(false)] out LinkedNode parent) { // make sure parent exists parent = GetParentLink(kit.Node, null); @@ -578,8 +585,9 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache /// /// /// - private static LinkedNode GetHead(ConcurrentDictionary> dict, TKey key) - where TValue : class + private static LinkedNode? GetHead(ConcurrentDictionary> dict, TKey key) + where TValue : class? + where TKey : notnull { dict.TryGetValue(key, out var link); // else null return link; @@ -662,8 +670,10 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache { if (_root.Gen != _liveGen) _root = new LinkedNode(new ContentNode(), _liveGen, _root); - else + else if(_root.Value is not null) + { _root.Value.FirstChildContentId = -1; + } } /// @@ -703,8 +713,8 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache // NextSiblingContentId // PreviousSiblingContentId - ContentNode previousNode = null; - ContentNode parent = null; + ContentNode? previousNode = null; + ContentNode? parent = null; foreach (var kit in kits) { @@ -720,9 +730,9 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache { // first parent parent = parentLink.Value; - parent.FirstChildContentId = thisNode.Id; // this node is the first node + parent!.FirstChildContentId = thisNode.Id; // this node is the first node } - else if (parent.Id != parentLink.Value.Id) + else if (parent.Id != parentLink.Value!.Id) { // new parent parent = parentLink.Value; @@ -908,7 +918,7 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache ClearBranchLocked(link.Value); } - private void ClearBranchLocked(ContentNode content) + private void ClearBranchLocked(ContentNode? content) { // This should never be null, all code that calls this method is null checking but we've seen // issues of null ref exceptions in issue reports so we'll double check here @@ -926,7 +936,7 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache var link = GetRequiredLinkedNode(id, "child", null); var linkValue = link.Value; // capture local since clearing in recurse can clear it ClearBranchLocked(linkValue); // recurse - id = linkValue.NextSiblingContentId; + id = linkValue?.NextSiblingContentId ?? 0; } } @@ -942,8 +952,10 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache if (_contentNodes.TryGetValue(id, out var link)) { link = GetLinkedNodeGen(link, gen); - if (link != null && link.Value != null) - return link; + if (link is not null && link.Value is not null) + { + return link!; + } } throw new PanicException($"failed to get {description} with id={id}"); @@ -953,7 +965,7 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache /// Gets the parent link node, may be null or root if ParentContentId is less than 0 /// /// the generation requested, null for the latest stored - private LinkedNode GetParentLink(ContentNode content, long? gen) + private LinkedNode? GetParentLink(ContentNode content, long? gen) { if (content.ParentContentId < 0) { @@ -964,7 +976,11 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache if (_contentNodes.TryGetValue(content.ParentContentId, out var link)) link = GetLinkedNodeGen(link, gen); - return link; + if (link is not null && link.Value is not null) + { + return link!; + } + return null; } /// @@ -984,8 +1000,8 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache /// /// The generation requested, use null to avoid the lookup /// - private LinkedNode GetLinkedNodeGen(LinkedNode link, long? gen) - where TValue : class + private LinkedNode? GetLinkedNodeGen(LinkedNode? link, long? gen) + where TValue : class? { if (!gen.HasValue) return link; @@ -1016,7 +1032,7 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache var parent = parentLink.Value; // must have children - if (parent.FirstChildContentId < 0) + if (parent!.FirstChildContentId < 0) throw new PanicException("no children"); // if first/last, clone parent, then remove @@ -1024,11 +1040,18 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache if (parent.FirstChildContentId == content.Id || parent.LastChildContentId == content.Id) parent = GenCloneLocked(parentLink); - if (parent.FirstChildContentId == content.Id) - parent.FirstChildContentId = content.NextSiblingContentId; + if (parent is not null) + { + if (parent.FirstChildContentId == content.Id) + { + parent.FirstChildContentId = content.NextSiblingContentId; + } - if (parent.LastChildContentId == content.Id) - parent.LastChildContentId = content.PreviousSiblingContentId; + if (parent.LastChildContentId == content.Id) + { + parent.LastChildContentId = content.PreviousSiblingContentId; + } + } // maintain linked list @@ -1036,14 +1059,22 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache { var nextLink = GetRequiredLinkedNode(content.NextSiblingContentId, "next sibling", null); var next = GenCloneLocked(nextLink); - next.PreviousSiblingContentId = content.PreviousSiblingContentId; + + if (next is not null) + { + next.PreviousSiblingContentId = content.PreviousSiblingContentId; + } } if (content.PreviousSiblingContentId > 0) { var prevLink = GetRequiredLinkedNode(content.PreviousSiblingContentId, "previous sibling", null); var prev = GenCloneLocked(prevLink); - prev.NextSiblingContentId = content.NextSiblingContentId; + + if (prev is not null) + { + prev.NextSiblingContentId = content.NextSiblingContentId; + } } } @@ -1056,13 +1087,13 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache return node != null && node.HasPublished; } - private ContentNode GenCloneLocked(LinkedNode link) + private ContentNode? GenCloneLocked(LinkedNode link) { var node = link.Value; if (node != null && link.Gen != _liveGen) { - node = new ContentNode(link.Value, _publishedModelFactory); + node = new ContentNode(node, _publishedModelFactory); if (link == _root) SetRootLocked(node); else @@ -1075,7 +1106,7 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache /// /// Adds a node to the tree structure. /// - private void AddTreeNodeLocked(ContentNode content, LinkedNode parentLink = null) + private void AddTreeNodeLocked(ContentNode content, LinkedNode? parentLink = null) { parentLink = parentLink ?? GetRequiredParentLink(content, null); @@ -1091,8 +1122,12 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache if (parent.FirstChildContentId < 0) { parent = GenCloneLocked(parentLink); - parent.FirstChildContentId = content.Id; - parent.LastChildContentId = content.Id; + if (parent is not null) + { + parent.FirstChildContentId = content.Id; + parent.LastChildContentId = content.Id; + } + return; } @@ -1102,16 +1137,23 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache // if first, clone parent + insert as first child // NOTE: Don't perform this check if loading from local DB since we know it's already sorted - if (child.SortOrder > content.SortOrder) + if (child?.SortOrder > content.SortOrder) { content.NextSiblingContentId = parent.FirstChildContentId; content.PreviousSiblingContentId = -1; parent = GenCloneLocked(parentLink); - parent.FirstChildContentId = content.Id; + if (parent is not null) + { + parent.FirstChildContentId = content.Id; + } child = GenCloneLocked(childLink); - child.PreviousSiblingContentId = content.Id; + + if (child is not null) + { + child.PreviousSiblingContentId = content.Id; + } return; } @@ -1121,16 +1163,23 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache var lastChild = lastChildLink.Value; // if last, clone parent + append as last child - if (lastChild.SortOrder <= content.SortOrder) + if (lastChild?.SortOrder <= content.SortOrder) { content.PreviousSiblingContentId = parent.LastChildContentId; content.NextSiblingContentId = -1; parent = GenCloneLocked(parentLink); - parent.LastChildContentId = content.Id; + if (parent is not null) + { + parent.LastChildContentId = content.Id; + } lastChild = GenCloneLocked(lastChildLink); - lastChild.NextSiblingContentId = content.Id; + + if (lastChild is not null) + { + lastChild.NextSiblingContentId = content.Id; + } return; } @@ -1139,7 +1188,7 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache // TODO: There was a note about performance when this occurs and that this only happens when moving and not very often, but that is not true, // this also happens anytime a middle node is unpublished or republished (which causes a branch update), i'm unsure if this has perf impacts, // i think this used to but it doesn't seem bad anymore that I can see... - while (child.NextSiblingContentId > 0) + while (child?.NextSiblingContentId > 0) { // get next child var nextChildLink = GetRequiredLinkedNode(child.NextSiblingContentId, "next child", null); @@ -1147,16 +1196,24 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache // if here, clone previous + append/insert // NOTE: Don't perform this check if loading from local DB since we know it's already sorted - if (nextChild.SortOrder > content.SortOrder) + if (nextChild?.SortOrder > content.SortOrder) { content.NextSiblingContentId = nextChild.Id; content.PreviousSiblingContentId = nextChild.PreviousSiblingContentId; child = GenCloneLocked(childLink); - child.NextSiblingContentId = content.Id; + + if (child is not null) + { + child.NextSiblingContentId = content.Id; + } var nnext = GenCloneLocked(nextChildLink); - nnext.PreviousSiblingContentId = content.Id; + + if (nnext is not null) + { + nnext.PreviousSiblingContentId = content.Id; + } return; } @@ -1192,7 +1249,8 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache // set a node (just the node, not the tree) private void SetValueLocked(ConcurrentDictionary> dict, TKey key, TValue value) - where TValue : class + where TValue : class? + where TKey : notnull { // this is safe only because we're write-locked var link = GetHead(dict, key); @@ -1223,7 +1281,8 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache } private void ClearLocked(ConcurrentDictionary> dict) - where TValue : class + where TValue : class? + where TKey : notnull { // this is safe only because we're write-locked foreach (var kvp in dict.Where(x => x.Value != null)) @@ -1240,36 +1299,37 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache } } - public ContentNode Get(int id, long gen) + public ContentNode? Get(int id, long gen) { return GetValue(_contentNodes, id, gen); } - public ContentNode Get(Guid uid, long gen) + public ContentNode? Get(Guid uid, long gen) { return _contentKeyToIdMap.TryGetValue(uid, out var id) ? GetValue(_contentNodes, id, gen) : null; } - public IEnumerable GetAtRoot(long gen) + public IEnumerable GetAtRoot(long gen) { var root = GetLinkedNodeGen(_root, gen); if (root == null) yield break; - var id = root.Value.FirstChildContentId; + var id = root.Value?.FirstChildContentId; while (id > 0) { - var link = GetRequiredLinkedNode(id, "root", gen); + var link = GetRequiredLinkedNode(id.Value, "root", gen); yield return link.Value; - id = link.Value.NextSiblingContentId; + id = link.Value?.NextSiblingContentId; } } - private TValue GetValue(ConcurrentDictionary> dict, TKey key, long gen) - where TValue : class + private TValue? GetValue(ConcurrentDictionary> dict, TKey key, long gen) + where TValue : class? + where TKey : notnull { // look ma, no lock! var link = GetHead(dict, key); @@ -1300,17 +1360,17 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache return has == false; } - public IPublishedContentType GetContentType(int id, long gen) + public IPublishedContentType? GetContentType(int id, long gen) { return GetValue(_contentTypesById, id, gen); } - public IPublishedContentType GetContentType(string alias, long gen) + public IPublishedContentType? GetContentType(string alias, long gen) { return GetValue(_contentTypesByAlias, alias, gen); } - public IPublishedContentType GetContentType(Guid key, long gen) + public IPublishedContentType? GetContentType(Guid key, long gen) { if (!_contentTypeKeyToIdMap.TryGetValue(key, out var id)) return null; @@ -1435,7 +1495,7 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache while (_genObjs.TryPeek(out var genObj) && (genObj.Count == 0 || genObj.WeakGenRef.IsAlive == false)) { _genObjs.TryDequeue(out genObj); // cannot fail since TryPeek has succeeded - _floorGen = genObj.Gen; + _floorGen = genObj!.Gen; #if DEBUG //_logger.LogDebug("_floorGen=" + _floorGen + ", _liveGen=" + _liveGen); #endif @@ -1456,7 +1516,8 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache } private void Collect(ConcurrentDictionary> dict) - where TValue : class + where TValue : class? + where TKey : notnull { // it is OK to enumerate a concurrent dictionary and it does not lock // it - and here it's not an issue if we skip some items, they will be @@ -1528,7 +1589,7 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache #region Internals/Unit testing - private TestHelper _unitTesting; + private TestHelper? _unitTesting; // note: nothing here is thread-safe internal class TestHelper @@ -1554,14 +1615,14 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache /// /// /// - public (long gen, ContentNode contentNode)[] GetValues(int id) + public (long gen, ContentNode? contentNode)[] GetValues(int id) { - _store._contentNodes.TryGetValue(id, out LinkedNode link); // else null + _store._contentNodes.TryGetValue(id, out LinkedNode? link); // else null if (link == null) - return Array.Empty<(long, ContentNode)>(); + return Array.Empty<(long, ContentNode?)>(); - var tuples = new List<(long, ContentNode)>(); + var tuples = new List<(long, ContentNode?)>(); do { tuples.Add((link.Gen, link.Value)); @@ -1580,7 +1641,7 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache public class Snapshot : IDisposable { private readonly ContentStore _store; - private readonly GenRef _genRef; + private readonly GenRef? _genRef; private long _gen; #if DEBUG private readonly ILogger _logger; @@ -1622,14 +1683,14 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache #endif } - public ContentNode Get(int id) + public ContentNode? Get(int id) { if (_gen < 0) throw new ObjectDisposedException("snapshot" /*+ " (" + _thisCount + ")"*/); return _store.Get(id, _gen); } - public ContentNode Get(Guid id) + public ContentNode? Get(Guid id) { if (_gen < 0) throw new ObjectDisposedException("snapshot" /*+ " (" + _thisCount + ")"*/); @@ -1640,7 +1701,7 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache { if (_gen < 0) throw new ObjectDisposedException("snapshot" /*+ " (" + _thisCount + ")"*/); - return _store.GetAtRoot(_gen); + return _store.GetAtRoot(_gen).WhereNotNull(); } public IEnumerable GetAll() @@ -1650,21 +1711,21 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache return _store.GetAll(_gen); } - public IPublishedContentType GetContentType(int id) + public IPublishedContentType? GetContentType(int id) { if (_gen < 0) throw new ObjectDisposedException("snapshot" /*+ " (" + _thisCount + ")"*/); return _store.GetContentType(id, _gen); } - public IPublishedContentType GetContentType(string alias) + public IPublishedContentType? GetContentType(string alias) { if (_gen < 0) throw new ObjectDisposedException("snapshot" /*+ " (" + _thisCount + ")"*/); return _store.GetContentType(alias, _gen); } - public IPublishedContentType GetContentType(Guid key) + public IPublishedContentType? GetContentType(Guid key) { if (_gen < 0) throw new ObjectDisposedException("snapshot" /*+ " (" + _thisCount + ")"*/); diff --git a/src/Umbraco.PublishedCache.NuCache/DataSource/BTree.ContentDataSerializer.cs b/src/Umbraco.PublishedCache.NuCache/DataSource/BTree.ContentDataSerializer.cs index 261e884fca..ee4c1dac1f 100644 --- a/src/Umbraco.PublishedCache.NuCache/DataSource/BTree.ContentDataSerializer.cs +++ b/src/Umbraco.PublishedCache.NuCache/DataSource/BTree.ContentDataSerializer.cs @@ -8,7 +8,7 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache.DataSource /// public class ContentDataSerializer : ISerializer { - public ContentDataSerializer(IDictionaryOfPropertyDataSerializer dictionaryOfPropertyDataSerializer = null) + public ContentDataSerializer(IDictionaryOfPropertyDataSerializer? dictionaryOfPropertyDataSerializer = null) { _dictionaryOfPropertyDataSerializer = dictionaryOfPropertyDataSerializer; if(_dictionaryOfPropertyDataSerializer == null) @@ -18,7 +18,7 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache.DataSource } private static readonly DictionaryOfPropertyDataSerializer s_defaultPropertiesSerializer = new DictionaryOfPropertyDataSerializer(); private static readonly DictionaryOfCultureVariationSerializer s_defaultCultureVariationsSerializer = new DictionaryOfCultureVariationSerializer(); - private readonly IDictionaryOfPropertyDataSerializer _dictionaryOfPropertyDataSerializer; + private readonly IDictionaryOfPropertyDataSerializer? _dictionaryOfPropertyDataSerializer; public ContentData ReadFrom(Stream stream) { @@ -29,7 +29,7 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache.DataSource var versionDate = PrimitiveSerializer.DateTime.ReadFrom(stream); var writerId = PrimitiveSerializer.Int32.ReadFrom(stream); var templateId = PrimitiveSerializer.Int32.ReadFrom(stream); - var properties = _dictionaryOfPropertyDataSerializer.ReadFrom(stream); // TODO: We don't want to allocate empty arrays + var properties = _dictionaryOfPropertyDataSerializer?.ReadFrom(stream); // TODO: We don't want to allocate empty arrays var cultureInfos = s_defaultCultureVariationsSerializer.ReadFrom(stream); // TODO: We don't want to allocate empty arrays return new ContentData(name, urlSegment, versionId, versionDate, writerId, templateId, published, properties, cultureInfos); } @@ -43,7 +43,7 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache.DataSource PrimitiveSerializer.DateTime.WriteTo(value.VersionDate, stream); PrimitiveSerializer.Int32.WriteTo(value.WriterId, stream); PrimitiveSerializer.Int32.WriteTo(value.TemplateId ?? 0, stream); - _dictionaryOfPropertyDataSerializer.WriteTo(value.Properties, stream); + _dictionaryOfPropertyDataSerializer?.WriteTo(value.Properties, stream); s_defaultCultureVariationsSerializer.WriteTo(value.CultureInfos, stream); } } diff --git a/src/Umbraco.PublishedCache.NuCache/DataSource/BTree.ContentNodeKitSerializer.cs b/src/Umbraco.PublishedCache.NuCache/DataSource/BTree.ContentNodeKitSerializer.cs index 42da3e601b..da50a49f72 100644 --- a/src/Umbraco.PublishedCache.NuCache/DataSource/BTree.ContentNodeKitSerializer.cs +++ b/src/Umbraco.PublishedCache.NuCache/DataSource/BTree.ContentNodeKitSerializer.cs @@ -5,7 +5,7 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache.DataSource { internal class ContentNodeKitSerializer : ISerializer { - public ContentNodeKitSerializer(ContentDataSerializer contentDataSerializer = null) + public ContentNodeKitSerializer(ContentDataSerializer? contentDataSerializer = null) { _contentDataSerializer = contentDataSerializer; if(_contentDataSerializer == null) @@ -14,7 +14,7 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache.DataSource } } static readonly ContentDataSerializer s_defaultDataSerializer = new ContentDataSerializer(); - private readonly ContentDataSerializer _contentDataSerializer; + private readonly ContentDataSerializer? _contentDataSerializer; //static readonly ListOfIntSerializer ChildContentIdsSerializer = new ListOfIntSerializer(); @@ -33,16 +33,16 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache.DataSource int contentTypeId = PrimitiveSerializer.Int32.ReadFrom(stream); var hasDraft = PrimitiveSerializer.Boolean.ReadFrom(stream); - ContentData draftData = null; - ContentData publishedData = null; + ContentData? draftData = null; + ContentData? publishedData = null; if (hasDraft) { - draftData = _contentDataSerializer.ReadFrom(stream); + draftData = _contentDataSerializer?.ReadFrom(stream); } var hasPublished = PrimitiveSerializer.Boolean.ReadFrom(stream); if (hasPublished) { - publishedData = _contentDataSerializer.ReadFrom(stream); + publishedData = _contentDataSerializer?.ReadFrom(stream); } var kit = new ContentNodeKit( contentNode, @@ -55,23 +55,26 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache.DataSource public void WriteTo(ContentNodeKit value, Stream stream) { - PrimitiveSerializer.Int32.WriteTo(value.Node.Id, stream); - PrimitiveSerializer.Guid.WriteTo(value.Node.Uid, stream); - PrimitiveSerializer.Int32.WriteTo(value.Node.Level, stream); - PrimitiveSerializer.String.WriteTo(value.Node.Path, stream); - PrimitiveSerializer.Int32.WriteTo(value.Node.SortOrder, stream); - PrimitiveSerializer.Int32.WriteTo(value.Node.ParentContentId, stream); - PrimitiveSerializer.DateTime.WriteTo(value.Node.CreateDate, stream); - PrimitiveSerializer.Int32.WriteTo(value.Node.CreatorId, stream); - PrimitiveSerializer.Int32.WriteTo(value.ContentTypeId, stream); + if (value.Node is not null) + { + PrimitiveSerializer.Int32.WriteTo(value.Node.Id, stream); + PrimitiveSerializer.Guid.WriteTo(value.Node.Uid, stream); + PrimitiveSerializer.Int32.WriteTo(value.Node.Level, stream); + PrimitiveSerializer.String.WriteTo(value.Node.Path, stream); + PrimitiveSerializer.Int32.WriteTo(value.Node.SortOrder, stream); + PrimitiveSerializer.Int32.WriteTo(value.Node.ParentContentId, stream); + PrimitiveSerializer.DateTime.WriteTo(value.Node.CreateDate, stream); + PrimitiveSerializer.Int32.WriteTo(value.Node.CreatorId, stream); + PrimitiveSerializer.Int32.WriteTo(value.ContentTypeId, stream); + } PrimitiveSerializer.Boolean.WriteTo(value.DraftData != null, stream); if (value.DraftData != null) - _contentDataSerializer.WriteTo(value.DraftData, stream); + _contentDataSerializer?.WriteTo(value.DraftData, stream); PrimitiveSerializer.Boolean.WriteTo(value.PublishedData != null, stream); if (value.PublishedData != null) - _contentDataSerializer.WriteTo(value.PublishedData, stream); + _contentDataSerializer?.WriteTo(value.PublishedData, stream); } } } diff --git a/src/Umbraco.PublishedCache.NuCache/DataSource/BTree.DictionaryOfCultureVariationSerializer.cs b/src/Umbraco.PublishedCache.NuCache/DataSource/BTree.DictionaryOfCultureVariationSerializer.cs index 1ed603d003..451d730318 100644 --- a/src/Umbraco.PublishedCache.NuCache/DataSource/BTree.DictionaryOfCultureVariationSerializer.cs +++ b/src/Umbraco.PublishedCache.NuCache/DataSource/BTree.DictionaryOfCultureVariationSerializer.cs @@ -35,7 +35,7 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache.DataSource private static readonly IReadOnlyDictionary Empty = new Dictionary(); - public void WriteTo(IReadOnlyDictionary value, Stream stream) + public void WriteTo(IReadOnlyDictionary? value, Stream stream) { var variations = value ?? Empty; diff --git a/src/Umbraco.PublishedCache.NuCache/DataSource/BTree.cs b/src/Umbraco.PublishedCache.NuCache/DataSource/BTree.cs index 2d276fbaf4..29b20866c0 100644 --- a/src/Umbraco.PublishedCache.NuCache/DataSource/BTree.cs +++ b/src/Umbraco.PublishedCache.NuCache/DataSource/BTree.cs @@ -7,7 +7,7 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache.DataSource { public class BTree { - public static BPlusTree GetTree(string filepath, bool exists, NuCacheSettings settings, ContentDataSerializer contentDataSerializer = null) + public static BPlusTree GetTree(string filepath, bool exists, NuCacheSettings settings, ContentDataSerializer? contentDataSerializer = null) { var keySerializer = new PrimitiveSerializer(); var valueSerializer = new ContentNodeKitSerializer(contentDataSerializer); diff --git a/src/Umbraco.PublishedCache.NuCache/DataSource/ContentCacheDataModel.cs b/src/Umbraco.PublishedCache.NuCache/DataSource/ContentCacheDataModel.cs index 34df43d87c..bc47e1aa58 100644 --- a/src/Umbraco.PublishedCache.NuCache/DataSource/ContentCacheDataModel.cs +++ b/src/Umbraco.PublishedCache.NuCache/DataSource/ContentCacheDataModel.cs @@ -18,17 +18,17 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache.DataSource [JsonProperty("pd")] [JsonConverter(typeof(AutoInterningStringKeyCaseInsensitiveDictionaryConverter))] [MessagePackFormatter(typeof(MessagePackAutoInterningStringKeyCaseInsensitiveDictionaryFormatter))] - public Dictionary PropertyData { get; set; } + public Dictionary? PropertyData { get; set; } [DataMember(Order = 1)] [JsonProperty("cd")] [JsonConverter(typeof(AutoInterningStringKeyCaseInsensitiveDictionaryConverter))] [MessagePackFormatter(typeof(MessagePackAutoInterningStringKeyCaseInsensitiveDictionaryFormatter))] - public Dictionary CultureData { get; set; } + public Dictionary? CultureData { get; set; } [DataMember(Order = 2)] [JsonProperty("us")] - public string UrlSegment { get; set; } + public string? UrlSegment { get; set; } //Legacy properties used to deserialize existing nucache db entries [IgnoreDataMember] diff --git a/src/Umbraco.PublishedCache.NuCache/DataSource/ContentCacheDataSerializationResult.cs b/src/Umbraco.PublishedCache.NuCache/DataSource/ContentCacheDataSerializationResult.cs index 5938927243..d48d422073 100644 --- a/src/Umbraco.PublishedCache.NuCache/DataSource/ContentCacheDataSerializationResult.cs +++ b/src/Umbraco.PublishedCache.NuCache/DataSource/ContentCacheDataSerializationResult.cs @@ -9,16 +9,16 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache.DataSource /// public struct ContentCacheDataSerializationResult : IEquatable { - public ContentCacheDataSerializationResult(string stringData, byte[] byteData) + public ContentCacheDataSerializationResult(string? stringData, byte[]? byteData) { StringData = stringData; ByteData = byteData; } - public string StringData { get; } - public byte[] ByteData { get; } + public string? StringData { get; } + public byte[]? ByteData { get; } - public override bool Equals(object obj) + public override bool Equals(object? obj) => obj is ContentCacheDataSerializationResult result && Equals(result); public bool Equals(ContentCacheDataSerializationResult other) @@ -28,8 +28,16 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache.DataSource public override int GetHashCode() { var hashCode = 1910544615; - hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(StringData); - hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(ByteData); + if (StringData is not null) + { + hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(StringData); + } + + if (ByteData is not null) + { + hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(ByteData); + } + return hashCode; } diff --git a/src/Umbraco.PublishedCache.NuCache/DataSource/ContentData.cs b/src/Umbraco.PublishedCache.NuCache/DataSource/ContentData.cs index a461cda437..cc53e4d047 100644 --- a/src/Umbraco.PublishedCache.NuCache/DataSource/ContentData.cs +++ b/src/Umbraco.PublishedCache.NuCache/DataSource/ContentData.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Collections.ObjectModel; namespace Umbraco.Cms.Infrastructure.PublishedCache.DataSource { @@ -8,13 +9,17 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache.DataSource /// public class ContentData { + // Scheduled for removal in V11 [Obsolete("Use ctor with all params, as the pros should be immutable")] public ContentData() { - + Name = string.Empty; + UrlSegment = string.Empty; + Properties = null!; + CultureInfos = null!; } - public ContentData(string name, string urlSegment, int versionId, DateTime versionDate, int writerId, int? templateId, bool published, IDictionary properties, IReadOnlyDictionary cultureInfos) + public ContentData(string? name, string? urlSegment, int versionId, DateTime versionDate, int writerId, int? templateId, bool published, IDictionary? properties, IReadOnlyDictionary? cultureInfos) { Name = name ?? throw new ArgumentNullException(nameof(name)); UrlSegment = urlSegment; @@ -28,7 +33,7 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache.DataSource } public string Name { get; [Obsolete("Do not change this, use ctor with params and have this object immutable.")] set; } - public string UrlSegment { get; [Obsolete("Do not change this, use ctor with params and have this object immutable.")] set; } + public string? UrlSegment { get; [Obsolete("Do not change this, use ctor with params and have this object immutable.")] set; } public int VersionId { get; [Obsolete("Do not change this, use ctor with params and have this object immutable.")] set; } public DateTime VersionDate { get; [Obsolete("Do not change this, use ctor with params and have this object immutable.")] set; } public int WriterId { get; [Obsolete("Do not change this, use ctor with params and have this object immutable.")] set; } @@ -40,6 +45,6 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache.DataSource /// /// The collection of language Id to name for the content item /// - public IReadOnlyDictionary CultureInfos { get; [Obsolete("Do not change this, use ctor with params and have this object immutable.")] set; } + public IReadOnlyDictionary? CultureInfos { get; [Obsolete("Do not change this, use ctor with params and have this object immutable.")] set; } } } diff --git a/src/Umbraco.PublishedCache.NuCache/DataSource/ContentSourceDto.cs b/src/Umbraco.PublishedCache.NuCache/DataSource/ContentSourceDto.cs index 78a8ea6e81..5bdcefcf94 100644 --- a/src/Umbraco.PublishedCache.NuCache/DataSource/ContentSourceDto.cs +++ b/src/Umbraco.PublishedCache.NuCache/DataSource/ContentSourceDto.cs @@ -11,7 +11,7 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache.DataSource public int ContentTypeId { get; set; } public int Level { get; set; } - public string Path { get; set; } + public string Path { get; set; } = string.Empty; public int SortOrder { get; set; } public int ParentId { get; set; } @@ -23,25 +23,25 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache.DataSource // edited data public int VersionId { get; set; } - public string EditName { get; set; } + public string? EditName { get; set; } public DateTime EditVersionDate { get; set; } public int EditWriterId { get; set; } public int EditTemplateId { get; set; } - public string EditData { get; set; } - public byte[] EditDataRaw { get; set; } + public string? EditData { get; set; } + public byte[]? EditDataRaw { get; set; } // published data public int PublishedVersionId { get; set; } - public string PubName { get; set; } + public string? PubName { get; set; } public DateTime PubVersionDate { get; set; } public int PubWriterId { get; set; } public int PubTemplateId { get; set; } - public string PubData { get; set; } - public byte[] PubDataRaw { get; set; } + public string? PubData { get; set; } + public byte[]? PubDataRaw { get; set; } // Explicit implementation DateTime IReadOnlyContentBase.UpdateDate => EditVersionDate; - string IReadOnlyContentBase.Name => EditName; + string? IReadOnlyContentBase.Name => EditName; int IReadOnlyContentBase.WriterId => EditWriterId; } } diff --git a/src/Umbraco.PublishedCache.NuCache/DataSource/CultureVariation.cs b/src/Umbraco.PublishedCache.NuCache/DataSource/CultureVariation.cs index 17a3b3e4dc..2424793a0c 100644 --- a/src/Umbraco.PublishedCache.NuCache/DataSource/CultureVariation.cs +++ b/src/Umbraco.PublishedCache.NuCache/DataSource/CultureVariation.cs @@ -12,11 +12,11 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache.DataSource { [DataMember(Order = 0)] [JsonProperty("nm")] - public string Name { get; set; } + public string? Name { get; set; } [DataMember(Order = 1)] [JsonProperty("us")] - public string UrlSegment { get; set; } + public string? UrlSegment { get; set; } [DataMember(Order = 2)] [JsonProperty("dt")] diff --git a/src/Umbraco.PublishedCache.NuCache/DataSource/IContentCacheDataSerializer.cs b/src/Umbraco.PublishedCache.NuCache/DataSource/IContentCacheDataSerializer.cs index cbc40a9630..d1a5ab1fe3 100644 --- a/src/Umbraco.PublishedCache.NuCache/DataSource/IContentCacheDataSerializer.cs +++ b/src/Umbraco.PublishedCache.NuCache/DataSource/IContentCacheDataSerializer.cs @@ -14,7 +14,7 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache.DataSource /// /// Deserialize the data into a /// - ContentCacheDataModel Deserialize(IReadOnlyContentBase content, string stringData, byte[] byteData, bool published); + ContentCacheDataModel? Deserialize(IReadOnlyContentBase content, string? stringData, byte[]? byteData, bool published); /// /// Serializes the diff --git a/src/Umbraco.PublishedCache.NuCache/DataSource/JsonContentNestedDataSerializer.cs b/src/Umbraco.PublishedCache.NuCache/DataSource/JsonContentNestedDataSerializer.cs index 9f1bfd39e2..06da8ccec1 100644 --- a/src/Umbraco.PublishedCache.NuCache/DataSource/JsonContentNestedDataSerializer.cs +++ b/src/Umbraco.PublishedCache.NuCache/DataSource/JsonContentNestedDataSerializer.cs @@ -24,13 +24,13 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache.DataSource DateFormatString = "o" }; private readonly JsonNameTable _propertyNameTable = new DefaultJsonNameTable(); - public ContentCacheDataModel Deserialize(IReadOnlyContentBase content, string stringData, byte[] byteData, bool published) + public ContentCacheDataModel? Deserialize(IReadOnlyContentBase content, string? stringData, byte[]? byteData, bool published) { if (stringData == null && byteData != null) throw new NotSupportedException($"{typeof(JsonContentNestedDataSerializer)} does not support byte[] serialization"); JsonSerializer serializer = JsonSerializer.Create(_jsonSerializerSettings); - using (JsonTextReader reader = new JsonTextReader(new StringReader(stringData))) + using (JsonTextReader reader = new JsonTextReader(new StringReader(stringData!))) { // reader will get buffer from array pool reader.ArrayPool = JsonArrayPool.Instance; @@ -58,12 +58,16 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache.DataSource return ArrayPool.Shared.Rent(minimumLength); } - public void Return(char[] array) + public void Return(char[]? array) { // return char array to System.Buffers shared pool - ArrayPool.Shared.Return(array); + if (array is not null) + { + ArrayPool.Shared.Return(array); + } } } + public class AutomaticJsonNameTable : DefaultJsonNameTable { int nAutoAdded = 0; @@ -74,7 +78,7 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache.DataSource this.maxToAutoAdd = maxToAdd; } - public override string Get(char[] key, int start, int length) + public override string? Get(char[] key, int start, int length) { var s = base.Get(key, start, length); diff --git a/src/Umbraco.PublishedCache.NuCache/DataSource/LazyCompressedString.cs b/src/Umbraco.PublishedCache.NuCache/DataSource/LazyCompressedString.cs index a5edbc2512..c6105f7af6 100644 --- a/src/Umbraco.PublishedCache.NuCache/DataSource/LazyCompressedString.cs +++ b/src/Umbraco.PublishedCache.NuCache/DataSource/LazyCompressedString.cs @@ -12,8 +12,8 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache.DataSource [DebuggerDisplay("{Display}")] internal struct LazyCompressedString { - private byte[] _bytes; - private string _str; + private byte[]? _bytes; + private string? _str; private readonly object _locker; /// diff --git a/src/Umbraco.PublishedCache.NuCache/DataSource/MsgPackContentNestedDataSerializer.cs b/src/Umbraco.PublishedCache.NuCache/DataSource/MsgPackContentNestedDataSerializer.cs index ba45dec9a8..ff65271e44 100644 --- a/src/Umbraco.PublishedCache.NuCache/DataSource/MsgPackContentNestedDataSerializer.cs +++ b/src/Umbraco.PublishedCache.NuCache/DataSource/MsgPackContentNestedDataSerializer.cs @@ -49,7 +49,7 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache.DataSource return json; } - public ContentCacheDataModel Deserialize(IReadOnlyContentBase content, string stringData, byte[] byteData, bool published) + public ContentCacheDataModel? Deserialize(IReadOnlyContentBase content, string? stringData, byte[]? byteData, bool published) { if (byteData != null) { @@ -93,17 +93,26 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache.DataSource /// private void Compress(IReadOnlyContentBase content, ContentCacheDataModel model, bool published) { + if (model.PropertyData is null) + { + return; + } + foreach(var propertyAliasToData in model.PropertyData) { if (_propertyOptions.IsCompressed(content, propertyAliasToData.Key, published)) { foreach(var property in propertyAliasToData.Value.Where(x => x.Value != null && x.Value is string)) { - property.Value = LZ4Pickler.Pickle(Encoding.UTF8.GetBytes((string)property.Value), LZ4Level.L00_FAST); + if (property.Value is string propertyValue) + { + property.Value = LZ4Pickler.Pickle(Encoding.UTF8.GetBytes(propertyValue), LZ4Level.L00_FAST); + } } + foreach (var property in propertyAliasToData.Value.Where(x => x.Value != null && x.Value is int intVal)) { - property.Value = Convert.ToBoolean((int)property.Value); + property.Value = Convert.ToBoolean((int?)property.Value); } } } @@ -117,6 +126,11 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache.DataSource /// private void Expand(IReadOnlyContentBase content, ContentCacheDataModel nestedData, bool published) { + if (nestedData.PropertyData is null) + { + return; + } + foreach (var propertyAliasToData in nestedData.PropertyData) { if (_propertyOptions.IsCompressed(content, propertyAliasToData.Key,published)) diff --git a/src/Umbraco.PublishedCache.NuCache/DataSource/PropertyData.cs b/src/Umbraco.PublishedCache.NuCache/DataSource/PropertyData.cs index befd1698de..fdef5029cd 100644 --- a/src/Umbraco.PublishedCache.NuCache/DataSource/PropertyData.cs +++ b/src/Umbraco.PublishedCache.NuCache/DataSource/PropertyData.cs @@ -10,14 +10,14 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache.DataSource [DataContract] // NOTE: Use DataContract annotations here to control how MessagePack serializes/deserializes the data to use INT keys public class PropertyData { - private string _culture; - private string _segment; + private string? _culture; + private string? _segment; [DataMember(Order = 0)] [JsonConverter(typeof(AutoInterningStringConverter))] [DefaultValue("")] [JsonProperty(DefaultValueHandling = DefaultValueHandling.IgnoreAndPopulate, PropertyName = "c")] - public string Culture + public string? Culture { get => _culture; set => _culture = value ?? throw new ArgumentNullException(nameof(value)); // TODO: or fallback to string.Empty? CANNOT be null @@ -27,7 +27,7 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache.DataSource [JsonConverter(typeof(AutoInterningStringConverter))] [DefaultValue("")] [JsonProperty(DefaultValueHandling = DefaultValueHandling.IgnoreAndPopulate, PropertyName = "s")] - public string Segment + public string? Segment { get => _segment; set => _segment = value ?? throw new ArgumentNullException(nameof(value)); // TODO: or fallback to string.Empty? CANNOT be null @@ -35,7 +35,7 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache.DataSource [DataMember(Order = 2)] [JsonProperty("v")] - public object Value { get; set; } + public object? Value { get; set; } // Legacy properties used to deserialize existing nucache db entries [IgnoreDataMember] diff --git a/src/Umbraco.PublishedCache.NuCache/DataSource/SerializerBase.cs b/src/Umbraco.PublishedCache.NuCache/DataSource/SerializerBase.cs index fb2f39f711..620e6bb75c 100644 --- a/src/Umbraco.PublishedCache.NuCache/DataSource/SerializerBase.cs +++ b/src/Umbraco.PublishedCache.NuCache/DataSource/SerializerBase.cs @@ -43,7 +43,7 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache.DataSource return read(stream); } - protected string ReadStringObject(Stream stream, bool intern = false) // required 'cos string is not a struct + protected string? ReadStringObject(Stream stream, bool intern = false) // required 'cos string is not a struct { var type = PrimitiveSerializer.Char.ReadFrom(stream); if (type == PrefixNull) return null; @@ -60,7 +60,7 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache.DataSource protected double? ReadDoubleObject(Stream stream) => ReadStruct(stream, PrefixDouble, ReadDouble); protected DateTime? ReadDateTimeObject(Stream stream) => ReadStruct(stream, PrefixDateTime, ReadDateTime); - protected object ReadObject(Stream stream) + protected object? ReadObject(Stream stream) => ReadObject(PrimitiveSerializer.Char.ReadFrom(stream), stream); /// @@ -73,7 +73,7 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache.DataSource /// This will incur boxing because the result is an object but in most cases the value will be a struct. /// When the type is known use the specific methods like instead /// - protected object ReadObject(char type, Stream stream) + protected object? ReadObject(char type, Stream stream) { switch (type) { @@ -127,7 +127,7 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache.DataSource /// This method will incur boxing if the value is a struct. When the type is known use the /// to write the value directly. /// - protected void WriteObject(object value, Stream stream) + protected void WriteObject(object? value, Stream stream) { if (value == null) { diff --git a/src/Umbraco.PublishedCache.NuCache/DomainCacheExtensions.cs b/src/Umbraco.PublishedCache.NuCache/DomainCacheExtensions.cs index 47cc427217..ff4808b7e6 100644 --- a/src/Umbraco.PublishedCache.NuCache/DomainCacheExtensions.cs +++ b/src/Umbraco.PublishedCache.NuCache/DomainCacheExtensions.cs @@ -6,12 +6,12 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache { public static class DomainCacheExtensions { - public static bool GetAssignedWithCulture(this IDomainCache domainCache, string culture, int documentId, bool includeWildcards = false) + public static bool GetAssignedWithCulture(this IDomainCache domainCache, string? culture, int documentId, bool includeWildcards = false) { var assigned = domainCache.GetAssigned(documentId, includeWildcards); // It's super important that we always compare cultures with ignore case, since we can't be sure of the casing! - return culture is null ? assigned.Any() : assigned.Any(x => x.Culture.Equals(culture, StringComparison.InvariantCultureIgnoreCase)); + return culture is null ? assigned.Any() : assigned.Any(x => x.Culture?.Equals(culture, StringComparison.InvariantCultureIgnoreCase) ?? false); } } } diff --git a/src/Umbraco.PublishedCache.NuCache/MediaCache.cs b/src/Umbraco.PublishedCache.NuCache/MediaCache.cs index 78e5050da5..a037c56541 100644 --- a/src/Umbraco.PublishedCache.NuCache/MediaCache.cs +++ b/src/Umbraco.PublishedCache.NuCache/MediaCache.cs @@ -31,21 +31,21 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache #region Get, Has - public override IPublishedContent GetById(bool preview, int contentId) + public override IPublishedContent? GetById(bool preview, int contentId) { // ignore preview, there's only draft for media var n = _snapshot.Get(contentId); return n?.PublishedModel; } - public override IPublishedContent GetById(bool preview, Guid contentId) + public override IPublishedContent? GetById(bool preview, Guid contentId) { // ignore preview, there's only draft for media var n = _snapshot.Get(contentId); return n?.PublishedModel; } - public override IPublishedContent GetById(bool preview, Udi contentId) + public override IPublishedContent? GetById(bool preview, Udi contentId) { var guidUdi = contentId as GuidUdi; if (guidUdi == null) @@ -74,7 +74,7 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache culture = _variationContextAccessor?.VariationContext?.Culture ?? ""; var atRoot = _snapshot.GetAtRoot().Select(x => x.PublishedModel); - return culture == "*" ? atRoot : atRoot.Where(x => x.IsInvariantOrHasCulture(culture)); + return culture == "*" ? atRoot.WhereNotNull() : atRoot.Where(x => x?.IsInvariantOrHasCulture(culture) ?? false).WhereNotNull(); } public override bool HasContent(bool preview) @@ -86,21 +86,21 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache #region XPath - public override IPublishedContent GetSingleByXPath(bool preview, string xpath, XPathVariable[] vars) + public override IPublishedContent? GetSingleByXPath(bool preview, string xpath, XPathVariable[] vars) { var navigator = CreateNavigator(preview); var iterator = navigator.Select(xpath, vars); return GetSingleByXPath(iterator); } - public override IPublishedContent GetSingleByXPath(bool preview, XPathExpression xpath, XPathVariable[] vars) + public override IPublishedContent? GetSingleByXPath(bool preview, XPathExpression xpath, XPathVariable[] vars) { var navigator = CreateNavigator(preview); var iterator = navigator.Select(xpath, vars); return GetSingleByXPath(iterator); } - private static IPublishedContent GetSingleByXPath(XPathNodeIterator iterator) + private static IPublishedContent? GetSingleByXPath(XPathNodeIterator iterator) { if (iterator.MoveNext() == false) return null; @@ -146,7 +146,7 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache return navigator; } - public override XPathNavigator CreateNodeNavigator(int id, bool preview) + public override XPathNavigator? CreateNodeNavigator(int id, bool preview) { var source = new Source(this, preview); var navigator = new NavigableNavigator(source); @@ -157,11 +157,11 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache #region Content types - public override IPublishedContentType GetContentType(int id) => _snapshot.GetContentType(id); + public override IPublishedContentType? GetContentType(int id) => _snapshot.GetContentType(id); - public override IPublishedContentType GetContentType(string alias) => _snapshot.GetContentType(alias); + public override IPublishedContentType? GetContentType(string alias) => _snapshot.GetContentType(alias); - public override IPublishedContentType GetContentType(Guid key) => _snapshot.GetContentType(key); + public override IPublishedContentType? GetContentType(Guid key) => _snapshot.GetContentType(key); #endregion diff --git a/src/Umbraco.PublishedCache.NuCache/MemberCache.cs b/src/Umbraco.PublishedCache.NuCache/MemberCache.cs index ade291eacf..84c2838ee6 100644 --- a/src/Umbraco.PublishedCache.NuCache/MemberCache.cs +++ b/src/Umbraco.PublishedCache.NuCache/MemberCache.cs @@ -33,7 +33,7 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache public IPublishedContentType GetContentType(string alias) => _contentTypeCache.Get(PublishedItemType.Member, alias); - public IPublishedContent Get(IMember member) + public IPublishedContent? Get(IMember member) => PublishedMember.Create(member, GetContentType(member.ContentTypeId), _previewDefault, _publishedSnapshotAccessor, _variationContextAccessor, _publishedModelFactory); #endregion diff --git a/src/Umbraco.PublishedCache.NuCache/Navigable/INavigableData.cs b/src/Umbraco.PublishedCache.NuCache/Navigable/INavigableData.cs index 2dc238d1e4..f629b1e8b7 100644 --- a/src/Umbraco.PublishedCache.NuCache/Navigable/INavigableData.cs +++ b/src/Umbraco.PublishedCache.NuCache/Navigable/INavigableData.cs @@ -5,7 +5,7 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache.Navigable { internal interface INavigableData { - IPublishedContent GetById(bool preview, int contentId); + IPublishedContent? GetById(bool preview, int contentId); IEnumerable GetAtRoot(bool preview); } } diff --git a/src/Umbraco.PublishedCache.NuCache/Navigable/NavigableContent.cs b/src/Umbraco.PublishedCache.NuCache/Navigable/NavigableContent.cs index 9d2b2bbbe5..fefb75b6b1 100644 --- a/src/Umbraco.PublishedCache.NuCache/Navigable/NavigableContent.cs +++ b/src/Umbraco.PublishedCache.NuCache/Navigable/NavigableContent.cs @@ -8,7 +8,7 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache.Navigable internal class NavigableContent : INavigableContent { private readonly PublishedContent _content; - private readonly string[] _builtInValues; + private readonly string?[] _builtInValues; public NavigableContent(IPublishedContent content) { @@ -33,7 +33,7 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache.Navigable }; } - private string XmlString(int index, object value) + private string? XmlString(int index, object? value) { if (value == null) return string.Empty; var field = Type.FieldTypes[index]; @@ -51,9 +51,9 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache.Navigable public INavigableContentType Type => NavigableContentType.GetContentType(_content.ContentType); // returns all child ids, will be filtered by the source - public IList ChildIds => _content.ChildIds; + public IList? ChildIds => _content.ChildIds; - public object Value(int index) + public object? Value(int index) { if (index < 0) throw new ArgumentOutOfRangeException(nameof(index)); diff --git a/src/Umbraco.PublishedCache.NuCache/Navigable/NavigableContentType.cs b/src/Umbraco.PublishedCache.NuCache/Navigable/NavigableContentType.cs index 9062f66b92..3b40851fbf 100644 --- a/src/Umbraco.PublishedCache.NuCache/Navigable/NavigableContentType.cs +++ b/src/Umbraco.PublishedCache.NuCache/Navigable/NavigableContentType.cs @@ -14,7 +14,9 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache.Navigable // called by the conditional weak table -- must be public // ReSharper disable EmptyConstructor +#pragma warning disable CS8618 public NavigableContentType() +#pragma warning restore CS8618 // ReSharper restore EmptyConstructor { } diff --git a/src/Umbraco.PublishedCache.NuCache/Navigable/NavigablePropertyType.cs b/src/Umbraco.PublishedCache.NuCache/Navigable/NavigablePropertyType.cs index eed3d1a08d..ddfb08dbe9 100644 --- a/src/Umbraco.PublishedCache.NuCache/Navigable/NavigablePropertyType.cs +++ b/src/Umbraco.PublishedCache.NuCache/Navigable/NavigablePropertyType.cs @@ -5,13 +5,13 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache.Navigable { internal class NavigablePropertyType : INavigableFieldType { - public NavigablePropertyType(string name, Func xmlStringConverter = null) + public NavigablePropertyType(string name, Func? xmlStringConverter = null) { Name = name; XmlStringConverter = xmlStringConverter; } public string Name { get; } - public Func XmlStringConverter { get; } + public Func? XmlStringConverter { get; } } } diff --git a/src/Umbraco.PublishedCache.NuCache/Navigable/RootContent.cs b/src/Umbraco.PublishedCache.NuCache/Navigable/RootContent.cs index 68eb41a66a..5f2cd8c165 100644 --- a/src/Umbraco.PublishedCache.NuCache/Navigable/RootContent.cs +++ b/src/Umbraco.PublishedCache.NuCache/Navigable/RootContent.cs @@ -22,7 +22,7 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache.Navigable public IList ChildIds => _childIds; - public object Value(int index) + public object? Value(int index) { // only id has a value return index == 0 ? "-1" : null; diff --git a/src/Umbraco.PublishedCache.NuCache/Navigable/Source.cs b/src/Umbraco.PublishedCache.NuCache/Navigable/Source.cs index ad4f1a2b13..86c92d0fe9 100644 --- a/src/Umbraco.PublishedCache.NuCache/Navigable/Source.cs +++ b/src/Umbraco.PublishedCache.NuCache/Navigable/Source.cs @@ -18,7 +18,7 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache.Navigable _root = new RootContent(contentAtRoot.Select(x => x.Id)); } - public INavigableContent Get(int id) + public INavigableContent? Get(int id) { // wrap in a navigable content diff --git a/src/Umbraco.PublishedCache.NuCache/Persistence/INuCacheContentRepository.cs b/src/Umbraco.PublishedCache.NuCache/Persistence/INuCacheContentRepository.cs index dd04a63a66..ea63d11480 100644 --- a/src/Umbraco.PublishedCache.NuCache/Persistence/INuCacheContentRepository.cs +++ b/src/Umbraco.PublishedCache.NuCache/Persistence/INuCacheContentRepository.cs @@ -12,7 +12,7 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache.Persistence IEnumerable GetBranchMediaSources(int id); ContentNodeKit GetContentSource(int id); ContentNodeKit GetMediaSource(int id); - IEnumerable GetTypeContentSources(IEnumerable ids); + IEnumerable GetTypeContentSources(IEnumerable? ids); IEnumerable GetTypeMediaSources(IEnumerable ids); /// @@ -37,9 +37,9 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache.Persistence /// If not null will process content for the matching media types, if empty will process all media /// If not null will process content for the matching members types, if empty will process all members void Rebuild( - IReadOnlyCollection contentTypeIds = null, - IReadOnlyCollection mediaTypeIds = null, - IReadOnlyCollection memberTypeIds = null); + IReadOnlyCollection? contentTypeIds = null, + IReadOnlyCollection? mediaTypeIds = null, + IReadOnlyCollection? memberTypeIds = null); bool VerifyContentDbCache(); bool VerifyMediaDbCache(); diff --git a/src/Umbraco.PublishedCache.NuCache/Persistence/INuCacheContentService.cs b/src/Umbraco.PublishedCache.NuCache/Persistence/INuCacheContentService.cs index 78ab94fddb..1ae3f94ac2 100644 --- a/src/Umbraco.PublishedCache.NuCache/Persistence/INuCacheContentService.cs +++ b/src/Umbraco.PublishedCache.NuCache/Persistence/INuCacheContentService.cs @@ -39,7 +39,7 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache.Persistence /// /// MUST be ordered by level + parentId + sortOrder! /// - IEnumerable GetTypeContentSources(IEnumerable ids); + IEnumerable GetTypeContentSources(IEnumerable? ids); ContentNodeKit GetMediaSource(int id); @@ -93,9 +93,9 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache.Persistence /// If not null will process content for the matching media types, if empty will process all media /// If not null will process content for the matching members types, if empty will process all members void Rebuild( - IReadOnlyCollection contentTypeIds = null, - IReadOnlyCollection mediaTypeIds = null, - IReadOnlyCollection memberTypeIds = null); + IReadOnlyCollection? contentTypeIds = null, + IReadOnlyCollection? mediaTypeIds = null, + IReadOnlyCollection? memberTypeIds = null); bool VerifyContentDbCache(); diff --git a/src/Umbraco.PublishedCache.NuCache/Persistence/NuCacheContentRepository.cs b/src/Umbraco.PublishedCache.NuCache/Persistence/NuCacheContentRepository.cs index 9964195f34..583ffa2799 100644 --- a/src/Umbraco.PublishedCache.NuCache/Persistence/NuCacheContentRepository.cs +++ b/src/Umbraco.PublishedCache.NuCache/Persistence/NuCacheContentRepository.cs @@ -117,9 +117,9 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache.Persistence } public void Rebuild( - IReadOnlyCollection contentTypeIds = null, - IReadOnlyCollection mediaTypeIds = null, - IReadOnlyCollection memberTypeIds = null) + IReadOnlyCollection? contentTypeIds = null, + IReadOnlyCollection? mediaTypeIds = null, + IReadOnlyCollection? memberTypeIds = null) { IContentCacheDataSerializer serializer = _contentCacheDataSerializerFactory.Create( ContentCacheDataSerializerEntityType.Document @@ -134,7 +134,7 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache.Persistence } // assumes content tree lock - private void RebuildContentDbCache(IContentCacheDataSerializer serializer, int groupSize, IReadOnlyCollection contentTypeIds) + private void RebuildContentDbCache(IContentCacheDataSerializer serializer, int groupSize, IReadOnlyCollection? contentTypeIds) { Guid contentObjectType = Constants.ObjectTypes.Document; @@ -200,7 +200,7 @@ WHERE cmsContentNu.nodeId IN ( } // assumes media tree lock - private void RebuildMediaDbCache(IContentCacheDataSerializer serializer, int groupSize, IReadOnlyCollection contentTypeIds) + private void RebuildMediaDbCache(IContentCacheDataSerializer serializer, int groupSize, IReadOnlyCollection? contentTypeIds) { var mediaObjectType = Constants.ObjectTypes.Media; @@ -251,7 +251,7 @@ WHERE cmsContentNu.nodeId IN ( } // assumes member tree lock - private void RebuildMemberDbCache(IContentCacheDataSerializer serializer, int groupSize, IReadOnlyCollection contentTypeIds) + private void RebuildMemberDbCache(IContentCacheDataSerializer serializer, int groupSize, IReadOnlyCollection? contentTypeIds) { Guid memberObjectType = Constants.ObjectTypes.Member; @@ -388,23 +388,26 @@ AND cmsContentNu.nodeId IS NULL // sanitize - names should be ok but ... never knows if (content.ContentType.VariesByCulture()) { - ContentCultureInfosCollection infos = content is IContent document + ContentCultureInfosCollection? infos = content is IContent document ? published ? document.PublishCultureInfos : document.CultureInfos : content.CultureInfos; // ReSharper disable once UseDeconstruction - foreach (ContentCultureInfos cultureInfo in infos) + if (infos is not null) { - var cultureIsDraft = !published && content is IContent d && d.IsCultureEdited(cultureInfo.Culture); - cultureData[cultureInfo.Culture] = new CultureVariation + foreach (ContentCultureInfos cultureInfo in infos) { - Name = cultureInfo.Name, - UrlSegment = content.GetUrlSegment(_shortStringHelper, _urlSegmentProviders, cultureInfo.Culture), - Date = content.GetUpdateDate(cultureInfo.Culture) ?? DateTime.MinValue, - IsDraft = cultureIsDraft - }; + var cultureIsDraft = !published && content is IContent d && d.IsCultureEdited(cultureInfo.Culture); + cultureData[cultureInfo.Culture] = new CultureVariation + { + Name = cultureInfo.Name, + UrlSegment = content.GetUrlSegment(_shortStringHelper, _urlSegmentProviders, cultureInfo.Culture), + Date = content.GetUpdateDate(cultureInfo.Culture) ?? DateTime.MinValue, + IsDraft = cultureIsDraft + }; + } } } @@ -430,7 +433,7 @@ AND cmsContentNu.nodeId IS NULL } // we want arrays, we want them all loaded, not an enumerable - private Sql SqlContentSourcesSelect(Func> joins = null) + private Sql SqlContentSourcesSelect(Func>? joins = null) { var sqlTemplate = SqlContext.Templates.Get(Constants.SqlTemplates.NuCacheDatabaseDataSource.ContentSourcesSelect, tsql => tsql.Select(x => Alias(x.NodeId, "Id"), x => Alias(x.UniqueId, "Key"), @@ -538,7 +541,7 @@ AND cmsContentNu.nodeId IS NULL /// /// /// - private Sql SqlContentSourcesCount(Func> joins = null) + private Sql SqlContentSourcesCount(Func>? joins = null) { var sqlTemplate = SqlContext.Templates.Get(Constants.SqlTemplates.NuCacheDatabaseDataSource.ContentSourcesCount, tsql => tsql.Select(x => Alias(x.NodeId, "Id")) @@ -562,7 +565,7 @@ AND cmsContentNu.nodeId IS NULL return sql; } - private Sql SqlMediaSourcesSelect(Func> joins = null) + private Sql SqlMediaSourcesSelect(Func>? joins = null) { var sqlTemplate = SqlContext.Templates.Get(Constants.SqlTemplates.NuCacheDatabaseDataSource.MediaSourcesSelect, tsql => tsql.Select(x => Alias(x.NodeId, "Id"), x => Alias(x.UniqueId, "Key"), @@ -588,7 +591,7 @@ AND cmsContentNu.nodeId IS NULL return sql; } - private Sql SqlMediaSourcesCount(Func> joins = null) + private Sql SqlMediaSourcesCount(Func>? joins = null) { var sqlTemplate = SqlContext.Templates.Get(Constants.SqlTemplates.NuCacheDatabaseDataSource.MediaSourcesCount, tsql => tsql.Select(x => Alias(x.NodeId, "Id")).From()); @@ -668,9 +671,9 @@ AND cmsContentNu.nodeId IS NULL } } - public IEnumerable GetTypeContentSources(IEnumerable ids) + public IEnumerable GetTypeContentSources(IEnumerable? ids) { - if (!ids.Any()) + if (!ids?.Any() ?? false) yield break; var sql = SqlContentSourcesSelect() @@ -802,8 +805,8 @@ AND cmsContentNu.nodeId IS NULL private ContentNodeKit CreateContentNodeKit(ContentSourceDto dto, IContentCacheDataSerializer serializer) { - ContentData d = null; - ContentData p = null; + ContentData? d = null; + ContentData? p = null; if (dto.Edited) { @@ -823,14 +826,14 @@ AND cmsContentNu.nodeId IS NULL d = new ContentData( dto.EditName, - deserializedContent.UrlSegment, + deserializedContent?.UrlSegment, dto.VersionId, dto.EditVersionDate, dto.EditWriterId, dto.EditTemplateId, published, - deserializedContent.PropertyData, - deserializedContent.CultureData); + deserializedContent?.PropertyData, + deserializedContent?.CultureData); } } @@ -852,14 +855,14 @@ AND cmsContentNu.nodeId IS NULL p = new ContentData( dto.PubName, - deserializedContent.UrlSegment, + deserializedContent?.UrlSegment, dto.VersionId, dto.PubVersionDate, dto.PubWriterId, dto.PubTemplateId, published, - deserializedContent.PropertyData, - deserializedContent.CultureData); + deserializedContent?.PropertyData, + deserializedContent?.CultureData); } } @@ -887,8 +890,8 @@ AND cmsContentNu.nodeId IS NULL dto.CreatorId, -1, published, - deserializedMedia.PropertyData, - deserializedMedia.CultureData); + deserializedMedia?.PropertyData, + deserializedMedia?.CultureData); var n = new ContentNode(dto.Id, dto.Key, dto.Level, dto.Path, dto.SortOrder, dto.ParentId, dto.CreateDate, dto.CreatorId); diff --git a/src/Umbraco.PublishedCache.NuCache/Persistence/NuCacheContentService.cs b/src/Umbraco.PublishedCache.NuCache/Persistence/NuCacheContentService.cs index a86d412d13..18aa7f1658 100644 --- a/src/Umbraco.PublishedCache.NuCache/Persistence/NuCacheContentService.cs +++ b/src/Umbraco.PublishedCache.NuCache/Persistence/NuCacheContentService.cs @@ -82,7 +82,7 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache.Persistence => _repository.GetMediaSource(id); /// - public IEnumerable GetTypeContentSources(IEnumerable ids) + public IEnumerable GetTypeContentSources(IEnumerable? ids) => _repository.GetTypeContentSources(ids); /// @@ -115,9 +115,9 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache.Persistence /// public void Rebuild( - IReadOnlyCollection contentTypeIds = null, - IReadOnlyCollection mediaTypeIds = null, - IReadOnlyCollection memberTypeIds = null) + IReadOnlyCollection? contentTypeIds = null, + IReadOnlyCollection? mediaTypeIds = null, + IReadOnlyCollection? memberTypeIds = null) { using (IScope scope = ScopeProvider.CreateScope(repositoryCacheMode: RepositoryCacheMode.Scoped)) { diff --git a/src/Umbraco.PublishedCache.NuCache/Property.cs b/src/Umbraco.PublishedCache.NuCache/Property.cs index 0762b5791e..06aea62607 100644 --- a/src/Umbraco.PublishedCache.NuCache/Property.cs +++ b/src/Umbraco.PublishedCache.NuCache/Property.cs @@ -26,17 +26,17 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache private readonly object _locko = new object(); // the invariant-neutral source and inter values - private readonly object _sourceValue; + private readonly object? _sourceValue; private bool _interInitialized; - private object _interValue; + private object? _interValue; // the variant source and inter values - private Dictionary _sourceValues; + private Dictionary? _sourceValues; // the variant and non-variant object values - private CacheValues _cacheValues; + private CacheValues? _cacheValues; - private string _valuesCacheKey; + private string? _valuesCacheKey; // initializes a published content property with no value public Property(IPublishedPropertyType propertyType, PublishedContent content, IPublishedSnapshotAccessor publishedSnapshotAccessor, PropertyCacheLevel referenceCacheLevel = PropertyCacheLevel.Element) @@ -44,7 +44,7 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache { } // initializes a published content property with a value - public Property(IPublishedPropertyType propertyType, PublishedContent content, PropertyData[] sourceValues, IPublishedSnapshotAccessor publishedSnapshotAccessor, PropertyCacheLevel referenceCacheLevel = PropertyCacheLevel.Element) + public Property(IPublishedPropertyType propertyType, PublishedContent content, PropertyData[]? sourceValues, IPublishedSnapshotAccessor publishedSnapshotAccessor, PropertyCacheLevel referenceCacheLevel = PropertyCacheLevel.Element) : base(propertyType, referenceCacheLevel) { if (sourceValues != null) @@ -89,7 +89,7 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache } // determines whether a property has value - public override bool HasValue(string culture = null, string? segment = null) + public override bool HasValue(string? culture = null, string? segment = null) { _content.VariationContextAccessor.ContextualizeVariation(_variations, _content.Id, ref culture, ref segment); @@ -126,7 +126,7 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache { CacheValues cacheValues; IPublishedSnapshot publishedSnapshot; - IAppCache cache; + IAppCache? cache; switch (cacheLevel) { case PropertyCacheLevel.None: @@ -163,15 +163,15 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache return cacheValues; } - private CacheValues GetCacheValues(IAppCache cache) + private CacheValues GetCacheValues(IAppCache? cache) { if (cache == null) // no cache, don't cache return new CacheValues(); - return (CacheValues) cache.Get(ValuesCacheKey, () => new CacheValues()); + return (CacheValues) cache.Get(ValuesCacheKey, () => new CacheValues())!; } // this is always invoked from within a lock, so does not require its own lock - private object GetInterValue(string culture, string segment) + private object? GetInterValue(string? culture, string? segment) { if (culture == "" && segment == "") { @@ -194,7 +194,7 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache return vvalue.InterValue; } - public override object GetSourceValue(string culture = null, string? segment = null) + public override object? GetSourceValue(string? culture = null, string? segment = null) { _content.VariationContextAccessor.ContextualizeVariation(_variations, _content.Id, ref culture, ref segment); @@ -208,11 +208,11 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache } } - public override object GetValue(string culture = null, string? segment = null) + public override object? GetValue(string? culture = null, string? segment = null) { _content.VariationContextAccessor.ContextualizeVariation(_variations, _content.Id, ref culture, ref segment); - object value; + object? value; lock (_locko) { var cacheValues = GetCacheValues(PropertyType.CacheLevel).For(culture, segment); @@ -229,7 +229,7 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache return value; } - public override object GetXPathValue(string culture = null, string? segment = null) + public override object? GetXPathValue(string? culture = null, string? segment = null) { _content.VariationContextAccessor.ContextualizeVariation(_variations, _content.Id, ref culture, ref segment); @@ -252,17 +252,17 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache private class CacheValue { public bool ObjectInitialized { get; set; } - public object ObjectValue { get; set; } + public object? ObjectValue { get; set; } public bool XPathInitialized { get; set; } - public object XPathValue { get; set; } + public object? XPathValue { get; set; } } private class CacheValues : CacheValue { - private Dictionary _values; + private Dictionary? _values; // this is always invoked from within a lock, so does not require its own lock - public CacheValue For(string culture, string segment) + public CacheValue For(string? culture, string? segment) { if (culture == "" && segment == "") return this; @@ -280,22 +280,22 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache private class SourceInterValue { - private string _culture; - private string _segment; + private string? _culture; + private string? _segment; - public string Culture + public string? Culture { get => _culture; internal set => _culture = value?.ToLowerInvariant(); } - public string Segment + public string? Segment { get => _segment; internal set => _segment = value?.ToLowerInvariant(); } - public object SourceValue { get; set; } + public object? SourceValue { get; set; } public bool InterInitialized { get; set; } - public object InterValue { get; set; } + public object? InterValue { get; set; } } #endregion diff --git a/src/Umbraco.PublishedCache.NuCache/PublishedContent.cs b/src/Umbraco.PublishedCache.NuCache/PublishedContent.cs index 596c97a670..0579f3eb28 100644 --- a/src/Umbraco.PublishedCache.NuCache/PublishedContent.cs +++ b/src/Umbraco.PublishedCache.NuCache/PublishedContent.cs @@ -13,18 +13,18 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache internal class PublishedContent : PublishedContentBase { private readonly IPublishedSnapshotAccessor _publishedSnapshotAccessor; - private readonly IPublishedModelFactory _publishedModelFactory; + private readonly IPublishedModelFactory? _publishedModelFactory; private readonly ContentNode _contentNode; - private readonly string _urlSegment; + private readonly string? _urlSegment; #region Constructors public PublishedContent( ContentNode contentNode, ContentData contentData, - IPublishedSnapshotAccessor publishedSnapshotAccessor, - IVariationContextAccessor variationContextAccessor, - IPublishedModelFactory publishedModelFactory) : base(variationContextAccessor) + IPublishedSnapshotAccessor? publishedSnapshotAccessor, + IVariationContextAccessor? variationContextAccessor, + IPublishedModelFactory? publishedModelFactory) : base(variationContextAccessor) { _contentNode = contentNode ?? throw new ArgumentNullException(nameof(contentNode)); ContentData = contentData ?? throw new ArgumentNullException(nameof(contentData)); @@ -90,13 +90,13 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache // this is for tests purposes // args are: current published snapshot (may be null), previewing, content id - returns: content - internal static Func GetContentByIdFunc { get; set; } - = (publishedShapshot, previewing, id) => publishedShapshot.Content.GetById(previewing, id); + internal static Func GetContentByIdFunc { get; set; } + = (publishedShapshot, previewing, id) => publishedShapshot.Content?.GetById(previewing, id); - internal static Func GetMediaByIdFunc { get; set; } - = (publishedShapshot, previewing, id) => publishedShapshot.Media.GetById(previewing, id); + internal static Func GetMediaByIdFunc { get; set; } + = (publishedShapshot, previewing, id) => publishedShapshot.Media?.GetById(previewing, id); - private Func GetGetterById() + private Func GetGetterById() { switch (ContentType.ItemType) { @@ -158,7 +158,7 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache // ReSharper disable once CollectionNeverUpdated.Local private static readonly IReadOnlyDictionary EmptyCultures = new Dictionary(); - private IReadOnlyDictionary _cultures; + private IReadOnlyDictionary? _cultures; /// public override IReadOnlyDictionary Cultures @@ -185,7 +185,7 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache public override PublishedItemType ItemType => _contentNode.ContentType.ItemType; /// - public override bool IsDraft(string culture = null) + public override bool IsDraft(string? culture = null) { // if this is the 'published' published content, nothing can be draft if (ContentData.Published) @@ -201,11 +201,11 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache // not the 'published' published content, and varies // = depends on the culture - return ContentData.CultureInfos.TryGetValue(culture, out var cvar) && cvar.IsDraft; + return ContentData.CultureInfos is not null && ContentData.CultureInfos.TryGetValue(culture, out var cvar) && cvar.IsDraft; } /// - public override bool IsPublished(string culture = null) + public override bool IsPublished(string? culture = null) { // whether we are the 'draft' or 'published' content, need to determine whether // there is a 'published' version for the specified culture (or at all, for @@ -233,7 +233,7 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache #region Tree /// - public override IPublishedContent Parent + public override IPublishedContent? Parent { get { @@ -299,7 +299,7 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache public override IEnumerable Properties => PropertiesArray; /// - public override IPublishedProperty GetProperty(string alias) + public override IPublishedProperty? GetProperty(string alias) { var index = _contentNode.ContentType.GetPropertyIndex(alias); if (index < 0) return null; // happens when 'alias' does not match a content type property alias @@ -314,7 +314,7 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache #region Caching // beware what you use that one for - you don't want to cache its result - private IAppCache GetAppropriateCache() + private IAppCache? GetAppropriateCache() { var publishedSnapshot = _publishedSnapshotAccessor.GetRequiredPublishedSnapshot(); var cache = publishedSnapshot == null @@ -325,7 +325,7 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache return cache; } - private IAppCache GetCurrentSnapshotCache() + private IAppCache? GetCurrentSnapshotCache() { var publishedSnapshot = _publishedSnapshotAccessor.GetRequiredPublishedSnapshot(); return publishedSnapshot?.SnapshotCache; @@ -348,7 +348,7 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache // includes all children, published or unpublished // NavigableNavigator takes care of selecting those it wants // note: this is not efficient - we do not try to be (would require a double-linked list) - internal IList ChildIds => Children.Select(x => x.Id).ToList(); + internal IList? ChildIds => Children?.Select(x => x.Id).ToList(); // used by Property // gets a value indicating whether the content or media exists in @@ -356,19 +356,19 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache // properties should refer to published, or draft content internal bool IsPreviewing { get; } - private string _asPreviewingCacheKey; + private string? _asPreviewingCacheKey; private string AsPreviewingCacheKey => _asPreviewingCacheKey ?? (_asPreviewingCacheKey = CacheKeys.PublishedContentAsPreviewing(Key)); // used by ContentCache - internal IPublishedContent AsDraft() + internal IPublishedContent? AsDraft() { if (IsPreviewing) return this; var cache = GetAppropriateCache(); if (cache == null) return new PublishedContent(this).CreateModel(_publishedModelFactory); - return (IPublishedContent)cache.Get(AsPreviewingCacheKey, () => new PublishedContent(this).CreateModel(_publishedModelFactory)); + return (IPublishedContent?)cache.Get(AsPreviewingCacheKey, () => new PublishedContent(this).CreateModel(_publishedModelFactory)); } // used by Navigable.Source,... diff --git a/src/Umbraco.PublishedCache.NuCache/PublishedMember.cs b/src/Umbraco.PublishedCache.NuCache/PublishedMember.cs index 53cc597cf5..5670877499 100644 --- a/src/Umbraco.PublishedCache.NuCache/PublishedMember.cs +++ b/src/Umbraco.PublishedCache.NuCache/PublishedMember.cs @@ -26,7 +26,7 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache Member = member; } - public static IPublishedContent Create( + public static IPublishedContent? Create( IMember member, IPublishedContentType contentType, bool previewing, @@ -88,7 +88,7 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache return properties; } - private static void AddIf(IPublishedContentType contentType, IDictionary properties, string alias, object value) + private static void AddIf(IPublishedContentType contentType, IDictionary properties, string alias, object? value) { var propertyType = contentType.GetPropertyType(alias); if (propertyType == null || propertyType.IsUserProperty) return; @@ -103,7 +103,7 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache public string UserName => Member.Username; - public string Comments => Member.Comments; + public string? Comments => Member.Comments; public bool IsApproved => Member.IsApproved; diff --git a/src/Umbraco.PublishedCache.NuCache/PublishedSnapshot.cs b/src/Umbraco.PublishedCache.NuCache/PublishedSnapshot.cs index 68446b1dcc..74239f42dc 100644 --- a/src/Umbraco.PublishedCache.NuCache/PublishedSnapshot.cs +++ b/src/Umbraco.PublishedCache.NuCache/PublishedSnapshot.cs @@ -8,9 +8,9 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache // implements published snapshot public class PublishedSnapshot : IPublishedSnapshot, IDisposable { - private readonly PublishedSnapshotService _service; + private readonly PublishedSnapshotService? _service; private bool _defaultPreview; - private PublishedSnapshotElements _elements; + private PublishedSnapshotElements? _elements; #region Constructors @@ -23,19 +23,19 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache public class PublishedSnapshotElements : IDisposable { #pragma warning disable IDE1006 // Naming Styles - public ContentCache ContentCache; - public MediaCache MediaCache; - public MemberCache MemberCache; - public DomainCache DomainCache; - public IAppCache SnapshotCache; - public IAppCache ElementsCache; + public ContentCache? ContentCache; + public MediaCache? MediaCache; + public MemberCache? MemberCache; + public DomainCache? DomainCache; + public IAppCache? SnapshotCache; + public IAppCache? ElementsCache; #pragma warning restore IDE1006 // Naming Styles public void Dispose() { - ContentCache.Dispose(); - MediaCache.Dispose(); - MemberCache.Dispose(); + ContentCache?.Dispose(); + MediaCache?.Dispose(); + MemberCache?.Dispose(); } } @@ -62,23 +62,23 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache #region Caches - public IAppCache SnapshotCache => Elements.SnapshotCache; + public IAppCache? SnapshotCache => Elements.SnapshotCache; - public IAppCache ElementsCache => Elements.ElementsCache; + public IAppCache? ElementsCache => Elements.ElementsCache; #endregion #region IPublishedSnapshot - public IPublishedContentCache Content => Elements.ContentCache; + public IPublishedContentCache? Content => Elements.ContentCache; - public IPublishedMediaCache Media => Elements.MediaCache; + public IPublishedMediaCache? Media => Elements.MediaCache; - public IPublishedMemberCache Members => Elements.MemberCache; + public IPublishedMemberCache? Members => Elements.MemberCache; - public IDomainCache Domains => Elements.DomainCache; + public IDomainCache? Domains => Elements.DomainCache; - public IDisposable ForcedPreview(bool preview, Action callback = null) + public IDisposable ForcedPreview(bool preview, Action? callback = null) { return new ForcedPreviewObject(this, preview, callback); } @@ -87,9 +87,9 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache { private readonly PublishedSnapshot _publishedSnapshot; private readonly bool _origPreview; - private readonly Action _callback; + private readonly Action? _callback; - public ForcedPreviewObject(PublishedSnapshot publishedShapshot, bool preview, Action callback) + public ForcedPreviewObject(PublishedSnapshot publishedShapshot, bool preview, Action? callback) { _publishedSnapshot = publishedShapshot; _callback = callback; diff --git a/src/Umbraco.PublishedCache.NuCache/PublishedSnapshotService.cs b/src/Umbraco.PublishedCache.NuCache/PublishedSnapshotService.cs index 80dfc8b73d..dee5231031 100644 --- a/src/Umbraco.PublishedCache.NuCache/PublishedSnapshotService.cs +++ b/src/Umbraco.PublishedCache.NuCache/PublishedSnapshotService.cs @@ -51,23 +51,23 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache private bool _isReady; private bool _isReadSet; - private object _isReadyLock; + private object? _isReadyLock; - private ContentStore _contentStore; - private ContentStore _mediaStore; - private SnapDictionary _domainStore; + private ContentStore _contentStore = null!; + private ContentStore _mediaStore = null!; + private SnapDictionary _domainStore = null!; private readonly object _storesLock = new object(); private readonly object _elementsLock = new object(); - private BPlusTree _localContentDb; - private BPlusTree _localMediaDb; + private BPlusTree? _localContentDb; + private BPlusTree? _localMediaDb; private bool _localContentDbExists; private bool _localMediaDbExists; private long _contentGen; private long _mediaGen; private long _domainGen; - private IAppCache _elementsCache; + private IAppCache? _elementsCache; // define constant - determines whether to use cache when previewing // to store eg routes, property converted values, anything - caching @@ -114,12 +114,12 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache _publishedModelFactory = publishedModelFactory; } - protected PublishedSnapshot CurrentPublishedSnapshot + protected PublishedSnapshot? CurrentPublishedSnapshot { get { _publishedSnapshotAccessor.TryGetPublishedSnapshot(out var publishedSnapshot); - return (PublishedSnapshot)publishedSnapshot; + return (PublishedSnapshot?)publishedSnapshot; } } @@ -150,9 +150,9 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache return GetUid(_mediaStore, id); } - private int GetId(ContentStore store, Guid uid) => store.LiveSnapshot.Get(uid)?.Id ?? 0; + private int GetId(ContentStore? store, Guid uid) => store?.LiveSnapshot.Get(uid)?.Id ?? 0; - private Guid GetUid(ContentStore store, int id) => store.LiveSnapshot.Get(id)?.Uid ?? Guid.Empty; + private Guid GetUid(ContentStore? store, int id) => store?.LiveSnapshot.Get(id)?.Uid ?? Guid.Empty; /// /// Install phase of @@ -309,7 +309,7 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache // first get a writer, then a scope // if there already is a scope, the writer will attach to it // otherwise, it will only exist here - cheap - using (_contentStore.GetScopedWriteLock(_scopeProvider)) + using (_contentStore?.GetScopedWriteLock(_scopeProvider)) using (var scope = _scopeProvider.CreateScope()) { scope.ReadLock(Constants.Locks.ContentTree); @@ -325,9 +325,9 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache // contentStore is wlocked (1 thread) // content (and types) are read-locked - var contentTypes = _serviceContext.ContentTypeService.GetAll().ToList(); + var contentTypes = _serviceContext.ContentTypeService?.GetAll().ToList(); - _contentStore.SetAllContentTypesLocked(contentTypes.Select(x => _publishedContentTypeFactory.CreateContentType(x))); + _contentStore.SetAllContentTypesLocked(contentTypes?.Select(x => _publishedContentTypeFactory.CreateContentType(x))); using (_profilingLogger.TraceDuration("Loading content from database")) { @@ -344,7 +344,7 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache private bool LoadContentFromLocalDbLocked(bool onStartup) { - var contentTypes = _serviceContext.ContentTypeService.GetAll() + var contentTypes = _serviceContext.ContentTypeService?.GetAll() .Select(x => _publishedContentTypeFactory.CreateContentType(x)); _contentStore.SetAllContentTypesLocked(contentTypes); @@ -360,7 +360,7 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache private bool LockAndLoadMedia(Func action) { // see note in LockAndLoadContent - using (_mediaStore.GetScopedWriteLock(_scopeProvider)) + using (_mediaStore?.GetScopedWriteLock(_scopeProvider)) using (var scope = _scopeProvider.CreateScope()) { scope.ReadLock(Constants.Locks.MediaTree); @@ -373,7 +373,7 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache private bool LoadMediaFromDatabaseLocked(bool onStartup) { // locks & notes: see content - var mediaTypes = _serviceContext.MediaTypeService.GetAll() + var mediaTypes = _serviceContext.MediaTypeService?.GetAll() .Select(x => _publishedContentTypeFactory.CreateContentType(x)); _mediaStore.SetAllContentTypesLocked(mediaTypes); @@ -392,7 +392,7 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache private bool LoadMediaFromLocalDbLocked(bool onStartup) { - var mediaTypes = _serviceContext.MediaTypeService.GetAll() + var mediaTypes = _serviceContext.MediaTypeService?.GetAll() .Select(x => _publishedContentTypeFactory.CreateContentType(x)); _mediaStore.SetAllContentTypesLocked(mediaTypes); @@ -405,15 +405,15 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache } } - private bool LoadEntitiesFromLocalDbLocked(bool onStartup, BPlusTree localDb, ContentStore store, string entityType) + private bool LoadEntitiesFromLocalDbLocked(bool onStartup, BPlusTree? localDb, ContentStore store, string entityType) { - var kits = localDb.Select(x => x.Value) + var kits = localDb?.Select(x => x.Value) .OrderBy(x => x.Node.Level) .ThenBy(x => x.Node.ParentContentId) .ThenBy(x => x.Node.SortOrder) // IMPORTANT sort by level + parentId + sortOrder .ToList(); - if (kits.Count == 0) + if (kits is null || kits.Count == 0) { // If there's nothing in the local cache file, we should return false? YES even though the site legitately might be empty. // Is it possible that the cache file is empty but the database is not? YES... (well, it used to be possible) @@ -440,7 +440,7 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache private void LockAndLoadDomains() { // see note in LockAndLoadContent - using (_domainStore.GetScopedWriteLock(_scopeProvider)) + using (_domainStore?.GetScopedWriteLock(_scopeProvider)) using (var scope = _scopeProvider.CreateScope()) { scope.ReadLock(Constants.Locks.Domains); @@ -451,12 +451,15 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache private void LoadDomainsLocked() { - var domains = _serviceContext.DomainService.GetAll(true); - foreach (var domain in domains - .Where(x => x.RootContentId.HasValue && x.LanguageIsoCode.IsNullOrWhiteSpace() == false) - .Select(x => new Domain(x.Id, x.DomainName, x.RootContentId.Value, x.LanguageIsoCode, x.IsWildcard))) + var domains = _serviceContext.DomainService?.GetAll(true); + if (domains is not null) { - _domainStore.SetLocked(domain.Id, domain); + foreach (var domain in domains + .Where(x => x.RootContentId.HasValue && x.LanguageIsoCode.IsNullOrWhiteSpace() == false) + .Select(x => new Domain(x.Id, x.DomainName, x.RootContentId!.Value, x.LanguageIsoCode!, x.IsWildcard))) + { + _domainStore.SetLocked(domain.Id, domain); + } } } @@ -718,7 +721,7 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache CurrentPublishedSnapshot?.Resync(); } - private void Notify(ContentStore store, ContentTypeCacheRefresher.JsonPayload[] payloads, Action, List, List, List> action) + private void Notify(ContentStore store, ContentTypeCacheRefresher.JsonPayload[] payloads, Action?, List?, List?, List?> action) where T : IContentTypeComposition { if (payloads.Length == 0) @@ -728,7 +731,7 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache var nameOfT = typeof(T).Name; - List removedIds = null, refreshedIds = null, otherIds = null, newIds = null; + List? removedIds = null, refreshedIds = null, otherIds = null, newIds = null; foreach (var payload in payloads) { @@ -829,7 +832,7 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache _domainStore.ClearLocked(payload.Id); break; case DomainChangeTypes.Refresh: - var domain = _serviceContext.DomainService.GetById(payload.Id); + var domain = _serviceContext.DomainService?.GetById(payload.Id); if (domain == null) continue; if (domain.RootContentId.HasValue == false) @@ -845,51 +848,56 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache } // Methods used to prevent allocations of lists - private void AddToList(ref List list, int val) => GetOrCreateList(ref list).Add(val); + private void AddToList(ref List? list, int val) => GetOrCreateList(ref list).Add(val); - private List GetOrCreateList(ref List list) => list ?? (list = new List()); + private List GetOrCreateList(ref List? list) => list ?? (list = new List()); - private IReadOnlyCollection CreateContentTypes(PublishedItemType itemType, int[] ids) + private IReadOnlyCollection CreateContentTypes(PublishedItemType itemType, int[]? ids) { // XxxTypeService.GetAll(empty) returns everything! - if (ids.Length == 0) + if (ids is null || ids.Length == 0) { return Array.Empty(); } - IEnumerable contentTypes; + IEnumerable? contentTypes; switch (itemType) { case PublishedItemType.Content: - contentTypes = _serviceContext.ContentTypeService.GetAll(ids); + contentTypes = _serviceContext.ContentTypeService?.GetAll(ids); break; case PublishedItemType.Media: - contentTypes = _serviceContext.MediaTypeService.GetAll(ids); + contentTypes = _serviceContext.MediaTypeService?.GetAll(ids); break; case PublishedItemType.Member: - contentTypes = _serviceContext.MemberTypeService.GetAll(ids); + contentTypes = _serviceContext.MemberTypeService?.GetAll(ids); break; default: throw new ArgumentOutOfRangeException(nameof(itemType)); } + if (contentTypes is null) + { + return Array.Empty(); + } + // some may be missing - not checking here return contentTypes.Select(x => _publishedContentTypeFactory.CreateContentType(x)).ToList(); } - private IPublishedContentType CreateContentType(PublishedItemType itemType, int id) + private IPublishedContentType? CreateContentType(PublishedItemType itemType, int id) { - IContentTypeComposition contentType; + IContentTypeComposition? contentType; switch (itemType) { case PublishedItemType.Content: - contentType = _serviceContext.ContentTypeService.Get(id); + contentType = _serviceContext.ContentTypeService?.Get(id); break; case PublishedItemType.Media: - contentType = _serviceContext.MediaTypeService.Get(id); + contentType = _serviceContext.MediaTypeService?.Get(id); break; case PublishedItemType.Member: - contentType = _serviceContext.MemberTypeService.Get(id); + contentType = _serviceContext.MemberTypeService?.Get(id); break; default: throw new ArgumentOutOfRangeException(nameof(itemType)); @@ -898,7 +906,7 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache return contentType == null ? null : _publishedContentTypeFactory.CreateContentType(contentType); } - private void RefreshContentTypesLocked(List removedIds, List refreshedIds, List otherIds, List newIds) + private void RefreshContentTypesLocked(List? removedIds, List? refreshedIds, List? otherIds, List? newIds) { if (removedIds.IsCollectionEmpty() && refreshedIds.IsCollectionEmpty() && otherIds.IsCollectionEmpty() && newIds.IsCollectionEmpty()) { @@ -915,7 +923,7 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache var typesA = refreshedIds.IsCollectionEmpty() ? Array.Empty() - : CreateContentTypes(PublishedItemType.Content, refreshedIds.ToArray()).ToArray(); + : CreateContentTypes(PublishedItemType.Content, refreshedIds?.ToArray()).ToArray(); var kits = refreshedIds.IsCollectionEmpty() ? Array.Empty() @@ -924,19 +932,19 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache _contentStore.UpdateContentTypesLocked(removedIds, typesA, kits); if (!otherIds.IsCollectionEmpty()) { - _contentStore.UpdateContentTypesLocked(CreateContentTypes(PublishedItemType.Content, otherIds.ToArray())); + _contentStore.UpdateContentTypesLocked(CreateContentTypes(PublishedItemType.Content, otherIds?.ToArray())); } if (!newIds.IsCollectionEmpty()) { - _contentStore.NewContentTypesLocked(CreateContentTypes(PublishedItemType.Content, newIds.ToArray())); + _contentStore.NewContentTypesLocked(CreateContentTypes(PublishedItemType.Content, newIds?.ToArray())); } scope.Complete(); } } - private void RefreshMediaTypesLocked(List removedIds, List refreshedIds, List otherIds, List newIds) + private void RefreshMediaTypesLocked(List? removedIds, List? refreshedIds, List? otherIds, List? newIds) { if (removedIds.IsCollectionEmpty() && refreshedIds.IsCollectionEmpty() && otherIds.IsCollectionEmpty() && newIds.IsCollectionEmpty()) { @@ -962,12 +970,12 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache _mediaStore.UpdateContentTypesLocked(removedIds, typesA, kits); if (!otherIds.IsCollectionEmpty()) { - _mediaStore.UpdateContentTypesLocked(CreateContentTypes(PublishedItemType.Media, otherIds.ToArray()).ToArray()); + _mediaStore.UpdateContentTypesLocked(CreateContentTypes(PublishedItemType.Media, otherIds?.ToArray()).ToArray()); } if (!newIds.IsCollectionEmpty()) { - _mediaStore.NewContentTypesLocked(CreateContentTypes(PublishedItemType.Media, newIds.ToArray()).ToArray()); + _mediaStore.NewContentTypesLocked(CreateContentTypes(PublishedItemType.Media, newIds?.ToArray()).ToArray()); } scope.Complete(); @@ -1006,7 +1014,7 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache // ie FastDictionaryAppCache (thread safe and all) ContentStore.Snapshot contentSnap, mediaSnap; SnapDictionary.Snapshot domainSnap; - IAppCache elementsCache; + IAppCache? elementsCache; // Here we are reading/writing to shared objects so we need to lock (can't be _storesLock which manages the actual nucache files // and would result in a deadlock). Even though we are locking around underlying readlocks (within CreateSnapshot) it's because @@ -1015,7 +1023,7 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache lock (_elementsLock) { - IScopeContext scopeContext = _scopeProvider.Context; + IScopeContext? scopeContext = _scopeProvider.Context; if (scopeContext == null) { @@ -1041,7 +1049,7 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache scopeContext.Enlist("Umbraco.Web.PublishedCache.NuCache.PublishedSnapshotService.Resync", () => this, (completed, svc) => { - svc.CurrentPublishedSnapshot?.Resync(); + svc?.CurrentPublishedSnapshot?.Resync(); }, int.MaxValue); } @@ -1075,9 +1083,9 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache /// public void Rebuild( - IReadOnlyCollection contentTypeIds = null, - IReadOnlyCollection mediaTypeIds = null, - IReadOnlyCollection memberTypeIds = null) + IReadOnlyCollection? contentTypeIds = null, + IReadOnlyCollection? mediaTypeIds = null, + IReadOnlyCollection? memberTypeIds = null) => _publishedContentService.Rebuild(contentTypeIds, mediaTypeIds, memberTypeIds); public async Task CollectAsync() @@ -1094,7 +1102,7 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache return _contentStore; } - internal ContentStore GetMediaStore() + internal ContentStore? GetMediaStore() { EnsureCaches(); return _mediaStore; diff --git a/src/Umbraco.PublishedCache.NuCache/PublishedSnapshotStatus.cs b/src/Umbraco.PublishedCache.NuCache/PublishedSnapshotStatus.cs index 6a75e3e021..3c1d2cabda 100644 --- a/src/Umbraco.PublishedCache.NuCache/PublishedSnapshotStatus.cs +++ b/src/Umbraco.PublishedCache.NuCache/PublishedSnapshotStatus.cs @@ -8,10 +8,10 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache /// internal class PublishedSnapshotStatus : IPublishedSnapshotStatus { - private readonly PublishedSnapshotService _service; + private readonly PublishedSnapshotService? _service; private readonly INuCacheContentService _publishedContentService; - public PublishedSnapshotStatus(IPublishedSnapshotService service, INuCacheContentService publishedContentService) + public PublishedSnapshotStatus(IPublishedSnapshotService? service, INuCacheContentService publishedContentService) { _service = service as PublishedSnapshotService; _publishedContentService = publishedContentService; @@ -26,7 +26,7 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache if (_service == null) { return $"The current {typeof(IPublishedSnapshotService)} is not the default type. A status cannot be determined."; - } + } // TODO: This should be private _service.EnsureCaches(); @@ -37,15 +37,15 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache ? "ok" : "NOT ok (rebuild?)"; - ContentStore contentStore = _service.GetContentStore(); - ContentStore mediaStore = _service.GetMediaStore(); + ContentStore? contentStore = _service.GetContentStore(); + ContentStore? mediaStore = _service.GetMediaStore(); - var contentStoreGen = contentStore.GenCount; - var mediaStoreGen = mediaStore.GenCount; - var contentStoreSnap = contentStore.SnapCount; - var mediaStoreSnap = mediaStore.SnapCount; - var contentStoreCount = contentStore.Count; - var mediaStoreCount = mediaStore.Count; + var contentStoreGen = contentStore?.GenCount; + var mediaStoreGen = mediaStore?.GenCount; + var contentStoreSnap = contentStore?.SnapCount; + var mediaStoreSnap = mediaStore?.SnapCount; + var contentStoreCount = contentStore?.Count; + var mediaStoreCount = mediaStore?.Count; string contentStoreCountPlural = contentStoreCount > 1 ? "s" : string.Empty; string contentStoreGenPlural = contentStoreGen > 1 ? "s" : string.Empty; diff --git a/src/Umbraco.PublishedCache.NuCache/Snap/GenObj.cs b/src/Umbraco.PublishedCache.NuCache/Snap/GenObj.cs index a5dc6ae06b..25a0435f65 100644 --- a/src/Umbraco.PublishedCache.NuCache/Snap/GenObj.cs +++ b/src/Umbraco.PublishedCache.NuCache/Snap/GenObj.cs @@ -14,7 +14,7 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache.Snap public GenRef GetGenRef() { // not thread-safe but always invoked from within a lock - var genRef = (GenRef)WeakGenRef.Target; + var genRef = (GenRef?)WeakGenRef.Target; if (genRef == null) WeakGenRef.Target = genRef = new GenRef(this); return genRef; diff --git a/src/Umbraco.PublishedCache.NuCache/Snap/LinkedNode.cs b/src/Umbraco.PublishedCache.NuCache/Snap/LinkedNode.cs index df82b47e94..a9fd07f88f 100644 --- a/src/Umbraco.PublishedCache.NuCache/Snap/LinkedNode.cs +++ b/src/Umbraco.PublishedCache.NuCache/Snap/LinkedNode.cs @@ -7,9 +7,9 @@ /// /// internal class LinkedNode - where TValue : class + where TValue : class? { - public LinkedNode(TValue value, long gen, LinkedNode next = null) + public LinkedNode(TValue? value, long gen, LinkedNode? next = null) { Value = value; // This is allowed to be null, we actually explicitly set this to null in ClearLocked Gen = gen; @@ -20,7 +20,7 @@ // reading & writing references is thread-safe on all .NET platforms // mark as volatile to ensure we always read the correct value - public volatile TValue Value; - public volatile LinkedNode Next; + public volatile TValue? Value; + public volatile LinkedNode? Next; } } diff --git a/src/Umbraco.PublishedCache.NuCache/SnapDictionary.cs b/src/Umbraco.PublishedCache.NuCache/SnapDictionary.cs index bd250027bb..7437527d2a 100644 --- a/src/Umbraco.PublishedCache.NuCache/SnapDictionary.cs +++ b/src/Umbraco.PublishedCache.NuCache/SnapDictionary.cs @@ -12,6 +12,7 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache { public class SnapDictionary where TValue : class + where TKey : notnull { // read // http://www.codeproject.com/Articles/548406/Dictionary-plus-Locking-versus-ConcurrentDictionar @@ -29,12 +30,12 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache private readonly ConcurrentDictionary> _items; private readonly ConcurrentQueue _genObjs; - private GenObj _genObj; + private GenObj? _genObj; private readonly object _wlocko = new object(); private readonly object _rlocko = new object(); private long _liveGen, _floorGen; private bool _nextGen, _collectAuto; - private Task _collectTask; + private Task? _collectTask; // minGenDelta to be adjusted // we may want to throttle collects even if delta is reached @@ -107,7 +108,7 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache // the dict is write-locked until the write-lock is released // which happens when it is disposed (non-scoped) // or when the scope context exits (scoped) - public IDisposable GetScopedWriteLock(IScopeProvider scopeProvider) + public IDisposable? GetScopedWriteLock(IScopeProvider scopeProvider) { return ScopeContextualBase.Get(scopeProvider, _instanceId, scoped => new ScopedWriteLock(this, scoped)); } @@ -191,13 +192,13 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache public int Count => _items.Count; - private LinkedNode GetHead(TKey key) + private LinkedNode? GetHead(TKey key) { _items.TryGetValue(key, out var link); // else null return link; } - public void SetLocked(TKey key, TValue value) + public void SetLocked(TKey key, TValue? value) { EnsureLocked(); @@ -253,7 +254,7 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache } } - public TValue Get(TKey key, long gen) + public TValue? Get(TKey key, long gen) { // look ma, no lock! var link = GetHead(key); @@ -396,7 +397,7 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache while (_genObjs.TryPeek(out var genObj) && (genObj.Count == 0 || genObj.WeakGenRef.IsAlive == false)) { _genObjs.TryDequeue(out genObj); // cannot fail since TryPeek has succeeded - _floorGen = genObj.Gen; + _floorGen = genObj!.Gen; } Collect(_items); @@ -476,7 +477,7 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache #region Unit testing - private TestHelper _unitTesting; + private TestHelper? _unitTesting; // note: nothing here is thread-safe internal class TestHelper @@ -499,7 +500,7 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache set => _dict._collectAuto = value; } - public GenObj GenObj => _dict._genObj; + public GenObj? GenObj => _dict._genObj; public ConcurrentQueue GenObjs => _dict._genObjs; @@ -523,14 +524,14 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache public class GenVal { - public GenVal(long gen, TValue value) + public GenVal(long gen, TValue? value) { Gen = gen; Value = value; } public long Gen { get; } - public TValue Value { get; } + public TValue? Value { get; } } } @@ -543,7 +544,7 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache public class Snapshot : IDisposable { private readonly SnapDictionary _store; - private readonly GenRef _genRef; + private readonly GenRef? _genRef; private readonly long _gen; // copied for perfs private int _disposed; @@ -571,7 +572,7 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache throw new ObjectDisposedException("snapshot" /*+ " (" + _thisCount + ")"*/); } - public TValue Get(TKey key) + public TValue? Get(TKey key) { EnsureNotDisposed(); return _store.Get(key, _gen); diff --git a/src/Umbraco.PublishedCache.NuCache/Umbraco.PublishedCache.NuCache.csproj b/src/Umbraco.PublishedCache.NuCache/Umbraco.PublishedCache.NuCache.csproj index 0caba0b128..3d4926b44d 100644 --- a/src/Umbraco.PublishedCache.NuCache/Umbraco.PublishedCache.NuCache.csproj +++ b/src/Umbraco.PublishedCache.NuCache/Umbraco.PublishedCache.NuCache.csproj @@ -3,10 +3,10 @@ net6.0 Umbraco.Cms.Infrastructure.PublishedCache - 8 Umbraco.Cms.PublishedCache.NuCache Umbraco CMS Published Cache Contains the Published Cache assembly needed to run Umbraco Cms. This package only contains the assembly, and can be used for package development. Use the template in the Umbraco.Templates package to setup Umbraco + enable diff --git a/src/Umbraco.Web.Common/Templates/TemplateRenderer.cs b/src/Umbraco.Web.Common/Templates/TemplateRenderer.cs index 46d5856c9d..982ec53ed9 100644 --- a/src/Umbraco.Web.Common/Templates/TemplateRenderer.cs +++ b/src/Umbraco.Web.Common/Templates/TemplateRenderer.cs @@ -76,7 +76,7 @@ namespace Umbraco.Cms.Web.Common.Templates // terribly much for this implementation since we are just creating a doc content request to modify it's properties manually. var requestBuilder = await _publishedRouter.CreateRequestAsync(umbracoContext.CleanedUmbracoUrl); - var doc = umbracoContext.Content.GetById(pageId); + var doc = umbracoContext.Content?.GetById(pageId); if (doc == null) { diff --git a/src/Umbraco.Web.Common/Umbraco.Web.Common.csproj b/src/Umbraco.Web.Common/Umbraco.Web.Common.csproj index b023b5ecdc..a2f7bfdc32 100644 --- a/src/Umbraco.Web.Common/Umbraco.Web.Common.csproj +++ b/src/Umbraco.Web.Common/Umbraco.Web.Common.csproj @@ -7,6 +7,7 @@ Umbraco.Cms.Web.Common Umbraco CMS Web Contains the Web assembly needed to run Umbraco Cms. This package only contains the assembly, and can be used for package development. Use the template in the Umbraco.Templates package to setup Umbraco + enable diff --git a/src/Umbraco.Web.Common/UmbracoContext/UmbracoContext.cs b/src/Umbraco.Web.Common/UmbracoContext/UmbracoContext.cs index 98f3db5483..c24db1502b 100644 --- a/src/Umbraco.Web.Common/UmbracoContext/UmbracoContext.cs +++ b/src/Umbraco.Web.Common/UmbracoContext/UmbracoContext.cs @@ -88,10 +88,10 @@ namespace Umbraco.Cms.Web.Common.UmbracoContext public IPublishedSnapshot PublishedSnapshot => _publishedSnapshot.Value; /// - public IPublishedContentCache Content => PublishedSnapshot.Content; + public IPublishedContentCache? Content => PublishedSnapshot.Content; /// - public IPublishedMediaCache Media => PublishedSnapshot.Media; + public IPublishedMediaCache? Media => PublishedSnapshot.Media; /// public IDomainCache? Domains => PublishedSnapshot.Domains; diff --git a/src/Umbraco.Web.Common/UmbracoHelper.cs b/src/Umbraco.Web.Common/UmbracoHelper.cs index c7e29a7302..1b41a6b2fd 100644 --- a/src/Umbraco.Web.Common/UmbracoHelper.cs +++ b/src/Umbraco.Web.Common/UmbracoHelper.cs @@ -183,7 +183,7 @@ namespace Umbraco.Cms.Web.Common private IPublishedContent? ContentForObject(object id) => _publishedContentQuery.Content(id); - public IPublishedContent ContentSingleAtXPath(string xpath, params XPathVariable[] vars) + public IPublishedContent? ContentSingleAtXPath(string xpath, params XPathVariable[] vars) { return _publishedContentQuery.ContentSingleAtXPath(xpath, vars); } @@ -200,7 +200,7 @@ namespace Umbraco.Cms.Web.Common /// /// The key of the content item. /// The content, or null of the content item is not in the cache. - public IPublishedContent Content(Guid id) => _publishedContentQuery.Content(id); + public IPublishedContent? Content(Guid id) => _publishedContentQuery.Content(id); /// /// Gets a content item from the cache. @@ -322,7 +322,7 @@ namespace Umbraco.Cms.Web.Common public IPublishedContent? Media(Udi id) => _publishedContentQuery.Media(id); - public IPublishedContent Media(Guid id) => _publishedContentQuery.Media(id); + public IPublishedContent? Media(Guid id) => _publishedContentQuery.Media(id); /// /// Overloaded method accepting an 'object' type