From b22c7b5350ed145ad89689f276aba76d2c8c8e44 Mon Sep 17 00:00:00 2001 From: Bjarke Berg Date: Tue, 30 Jan 2024 15:51:11 +0100 Subject: [PATCH 1/4] Bump version --- version.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version.json b/version.json index 2822aad6ef..bad154e29f 100644 --- a/version.json +++ b/version.json @@ -1,6 +1,6 @@ { "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/main/src/NerdBank.GitVersioning/version.schema.json", - "version": "13.1.0", + "version": "13.1.1", "assemblyVersion": { "precision": "build" }, From 94b8c11f0f8071c731791439c0ebdf4c73aa62f4 Mon Sep 17 00:00:00 2001 From: Bjarke Berg Date: Wed, 31 Jan 2024 15:09:12 +0100 Subject: [PATCH 2/4] Do not dispose EF Core contexts manually (#15649) * Attempt to fix #15090. Save connection string in private variable after setup. * Do not dispose ef core contexts when if the connectionstring changes. A Ef core context is protected from creations when no connection string is present. If it changes that single instance will continue with the connection string it was initialized with. * Clean up * Review Equals methods (cherry picked from commit 5146f05625d53d11c64f75459b7003ce11bcb078) --- .../UmbracoDbContext.cs | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/src/Umbraco.Cms.Persistence.EFCore/UmbracoDbContext.cs b/src/Umbraco.Cms.Persistence.EFCore/UmbracoDbContext.cs index 3df757ee15..419ecd6a28 100644 --- a/src/Umbraco.Cms.Persistence.EFCore/UmbracoDbContext.cs +++ b/src/Umbraco.Cms.Persistence.EFCore/UmbracoDbContext.cs @@ -36,19 +36,14 @@ public class UmbracoDbContext : DbContext /// /// public UmbracoDbContext(DbContextOptions options) - : base(ConfigureOptions(options, out IOptionsMonitor? connectionStringsOptionsMonitor)) + : base(ConfigureOptions(options)) { - connectionStringsOptionsMonitor.OnChange(c => - { - ILogger logger = StaticServiceProvider.Instance.GetRequiredService>(); - logger.LogWarning("Connection string changed, disposing context"); - Dispose(); - }); + } - private static DbContextOptions ConfigureOptions(DbContextOptions options, out IOptionsMonitor connectionStringsOptionsMonitor) + private static DbContextOptions ConfigureOptions(DbContextOptions options) { - connectionStringsOptionsMonitor = StaticServiceProvider.Instance.GetRequiredService>(); + IOptionsMonitor connectionStringsOptionsMonitor = StaticServiceProvider.Instance.GetRequiredService>(); ConnectionStrings connectionStrings = connectionStringsOptionsMonitor.CurrentValue; From e7c53d4768682613bab74570374fac3c0b195629 Mon Sep 17 00:00:00 2001 From: Bjarke Berg Date: Thu, 1 Feb 2024 09:58:42 +0100 Subject: [PATCH 3/4] V10+ version of https://github.com/umbraco/Umbraco-CMS/pull/15638 (#15664) --- .../Filters/ContentSaveValidationAttribute.cs | 2 +- src/Umbraco.Web.BackOffice/ModelBinders/BlueprintItemBinder.cs | 2 +- src/Umbraco.Web.BackOffice/ModelBinders/ContentItemBinder.cs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Umbraco.Web.BackOffice/Filters/ContentSaveValidationAttribute.cs b/src/Umbraco.Web.BackOffice/Filters/ContentSaveValidationAttribute.cs index f7be9d129a..c75bbd5a80 100644 --- a/src/Umbraco.Web.BackOffice/Filters/ContentSaveValidationAttribute.cs +++ b/src/Umbraco.Web.BackOffice/Filters/ContentSaveValidationAttribute.cs @@ -18,7 +18,7 @@ namespace Umbraco.Cms.Web.BackOffice.Filters; /// Validates the incoming model along with if the user is allowed to perform the /// operation /// -internal sealed class ContentSaveValidationAttribute : TypeFilterAttribute +public sealed class ContentSaveValidationAttribute : TypeFilterAttribute { public ContentSaveValidationAttribute(bool skipUserAccessValidation = false) : base(typeof(ContentSaveValidationFilter)) diff --git a/src/Umbraco.Web.BackOffice/ModelBinders/BlueprintItemBinder.cs b/src/Umbraco.Web.BackOffice/ModelBinders/BlueprintItemBinder.cs index bc07497fcd..bf8c7372bc 100644 --- a/src/Umbraco.Web.BackOffice/ModelBinders/BlueprintItemBinder.cs +++ b/src/Umbraco.Web.BackOffice/ModelBinders/BlueprintItemBinder.cs @@ -7,7 +7,7 @@ using Umbraco.Cms.Core.Services; namespace Umbraco.Cms.Web.BackOffice.ModelBinders; -internal class BlueprintItemBinder : ContentItemBinder +public class BlueprintItemBinder : ContentItemBinder { private readonly IContentService _contentService; diff --git a/src/Umbraco.Web.BackOffice/ModelBinders/ContentItemBinder.cs b/src/Umbraco.Web.BackOffice/ModelBinders/ContentItemBinder.cs index 0842ca2051..c73a45f904 100644 --- a/src/Umbraco.Web.BackOffice/ModelBinders/ContentItemBinder.cs +++ b/src/Umbraco.Web.BackOffice/ModelBinders/ContentItemBinder.cs @@ -13,7 +13,7 @@ namespace Umbraco.Cms.Web.BackOffice.ModelBinders; /// /// The model binder for /// -internal class ContentItemBinder : IModelBinder +public class ContentItemBinder : IModelBinder { private readonly IContentService _contentService; private readonly IContentTypeService _contentTypeService; From 25139dabe0ed70d799a0593cd5338b1107c035ed Mon Sep 17 00:00:00 2001 From: Ronald Barendse Date: Thu, 1 Feb 2024 09:55:09 +0100 Subject: [PATCH 4/4] Skip cache refresher operations for content blueprints (#15633) * Skip cache refresher operations for content blueprints * Fix JsonPayload deserialization error by adding a default constructor and property initializers * Obsolete JsonPayload constructor and update usages --- .../Cache/DistributedCacheExtensions.cs | 24 ++- .../Implement/ContentCacheRefresher.cs | 23 ++- .../Implement/LanguageCacheRefresher.cs | 10 +- .../IndexingNotificationHandler.Content.cs | 12 +- .../PublishedSnapshotService.cs | 17 +- .../Umbraco.Core/Cache/RefresherTests.cs | 24 ++- ...PublishedSnapshotServiceCollectionTests.cs | 179 +++++++++++++++--- 7 files changed, 239 insertions(+), 50 deletions(-) diff --git a/src/Umbraco.Core/Cache/DistributedCacheExtensions.cs b/src/Umbraco.Core/Cache/DistributedCacheExtensions.cs index 8d792a5ef7..438c66a2c1 100644 --- a/src/Umbraco.Core/Cache/DistributedCacheExtensions.cs +++ b/src/Umbraco.Core/Cache/DistributedCacheExtensions.cs @@ -118,15 +118,35 @@ public static class DistributedCacheExtensions #region ContentCacheRefresher public static void RefreshAllContentCache(this DistributedCache dc) + { + ContentCacheRefresher.JsonPayload[] payloads = new[] + { + new ContentCacheRefresher.JsonPayload() + { + ChangeTypes = TreeChangeTypes.RefreshAll + } + }; + // note: refresh all content cache does refresh content types too - => dc.RefreshByPayload(ContentCacheRefresher.UniqueId, new ContentCacheRefresher.JsonPayload(0, null, TreeChangeTypes.RefreshAll).Yield()); + dc.RefreshByPayload(ContentCacheRefresher.UniqueId, payloads); + } [Obsolete("Use the overload accepting IEnumerable instead to avoid allocating arrays. This overload will be removed in Umbraco 13.")] public static void RefreshContentCache(this DistributedCache dc, TreeChange[] changes) => dc.RefreshContentCache(changes.AsEnumerable()); public static void RefreshContentCache(this DistributedCache dc, IEnumerable> changes) - => dc.RefreshByPayload(ContentCacheRefresher.UniqueId, changes.DistinctBy(x => (x.Item.Id, x.Item.Key, x.ChangeTypes)).Select(x => new ContentCacheRefresher.JsonPayload(x.Item.Id, x.Item.Key, x.ChangeTypes))); + { + IEnumerable payloads = changes.Select(x => new ContentCacheRefresher.JsonPayload() + { + Id = x.Item.Id, + Key = x.Item.Key, + ChangeTypes = x.ChangeTypes, + Blueprint = x.Item.Blueprint + }); + + dc.RefreshByPayload(ContentCacheRefresher.UniqueId, payloads); + } #endregion diff --git a/src/Umbraco.Core/Cache/Refreshers/Implement/ContentCacheRefresher.cs b/src/Umbraco.Core/Cache/Refreshers/Implement/ContentCacheRefresher.cs index a515d5c5d1..779b22fe68 100644 --- a/src/Umbraco.Core/Cache/Refreshers/Implement/ContentCacheRefresher.cs +++ b/src/Umbraco.Core/Cache/Refreshers/Implement/ContentCacheRefresher.cs @@ -84,8 +84,8 @@ public sealed class ContentCacheRefresher : PayloadCacheRefresherBase((k, v) => v.Path?.Contains(pathid) ?? false); } - // if the item is being completely removed, we need to refresh the domains cache if any domain was assigned to the content - if (payload.ChangeTypes.HasTypesAny(TreeChangeTypes.Remove)) + // if the item is not a blueprint and is being completely removed, we need to refresh the domains cache if any domain was assigned to the content + if (payload.Blueprint is false && payload.ChangeTypes.HasTypesAny(TreeChangeTypes.Remove)) { idsRemoved.Add(payload.Id); } @@ -120,7 +120,11 @@ public sealed class ContentCacheRefresher : PayloadCacheRefresherBase x.Blueprint is false)) + { + // Only notify if the payload contains actual (non-blueprint) contents + NotifyPublishedSnapshotService(_publishedSnapshotService, AppCaches, payloads); + } base.Refresh(payloads); } @@ -157,8 +161,13 @@ public sealed class ContentCacheRefresher : PayloadCacheRefresherBase(); - } - + deleteBatch ??= new HashSet(); deleteBatch.Add(payload.Id); } else if (payload.ChangeTypes.HasType(TreeChangeTypes.RefreshAll)) diff --git a/src/Umbraco.PublishedCache.NuCache/PublishedSnapshotService.cs b/src/Umbraco.PublishedCache.NuCache/PublishedSnapshotService.cs index 175cf766c5..8aa012d11f 100644 --- a/src/Umbraco.PublishedCache.NuCache/PublishedSnapshotService.cs +++ b/src/Umbraco.PublishedCache.NuCache/PublishedSnapshotService.cs @@ -220,7 +220,16 @@ internal class PublishedSnapshotService : IPublishedSnapshotService // they require. using (_contentStore.GetScopedWriteLock(_scopeProvider)) { - NotifyLocked(new[] { new ContentCacheRefresher.JsonPayload(0, null, TreeChangeTypes.RefreshAll) }, out _, out _); + NotifyLocked( + new[] + { + new ContentCacheRefresher.JsonPayload() + { + ChangeTypes = TreeChangeTypes.RefreshAll + } + }, + out _, + out _); } using (_mediaStore.GetScopedWriteLock(_scopeProvider)) @@ -885,6 +894,12 @@ internal class PublishedSnapshotService : IPublishedSnapshotService _logger.LogDebug("Notified {ChangeTypes} for content {ContentId}", payload.ChangeTypes, payload.Id); } + if (payload.Blueprint) + { + // Skip blueprints + continue; + } + if (payload.ChangeTypes.HasType(TreeChangeTypes.RefreshAll)) { using (IScope scope = _scopeProvider.CreateScope()) diff --git a/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Cache/RefresherTests.cs b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Cache/RefresherTests.cs index 92ef2b5c9f..e509742cb9 100644 --- a/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Cache/RefresherTests.cs +++ b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Cache/RefresherTests.cs @@ -18,8 +18,10 @@ public class RefresherTests { new MediaCacheRefresher.JsonPayload(1234, Guid.NewGuid(), TreeChangeTypes.None), }; + var json = JsonConvert.SerializeObject(source); var payload = JsonConvert.DeserializeObject(json); + Assert.AreEqual(source[0].Id, payload[0].Id); Assert.AreEqual(source[0].Key, payload[0].Key); Assert.AreEqual(source[0].ChangeTypes, payload[0].ChangeTypes); @@ -30,13 +32,21 @@ public class RefresherTests { ContentCacheRefresher.JsonPayload[] source = { - new ContentCacheRefresher.JsonPayload(1234, Guid.NewGuid(), TreeChangeTypes.None), + new ContentCacheRefresher.JsonPayload() + { + Id = 1234, + Key = Guid.NewGuid(), + ChangeTypes = TreeChangeTypes.None + } }; + var json = JsonConvert.SerializeObject(source); var payload = JsonConvert.DeserializeObject(json); + Assert.AreEqual(source[0].Id, payload[0].Id); Assert.AreEqual(source[0].Key, payload[0].Key); Assert.AreEqual(source[0].ChangeTypes, payload[0].ChangeTypes); + Assert.AreEqual(source[0].Blueprint, payload[0].Blueprint); } [Test] @@ -46,8 +56,10 @@ public class RefresherTests { new ContentTypeCacheRefresher.JsonPayload("xxx", 1234, ContentTypeChangeTypes.None), }; + var json = JsonConvert.SerializeObject(source); var payload = JsonConvert.DeserializeObject(json); + Assert.AreEqual(source[0].ItemType, payload[0].ItemType); Assert.AreEqual(source[0].Id, payload[0].Id); Assert.AreEqual(source[0].ChangeTypes, payload[0].ChangeTypes); @@ -60,8 +72,10 @@ public class RefresherTests { new DataTypeCacheRefresher.JsonPayload(1234, Guid.NewGuid(), true), }; + var json = JsonConvert.SerializeObject(source); var payload = JsonConvert.DeserializeObject(json); + Assert.AreEqual(source[0].Id, payload[0].Id); Assert.AreEqual(source[0].Key, payload[0].Key); Assert.AreEqual(source[0].Removed, payload[0].Removed); @@ -70,10 +84,14 @@ public class RefresherTests [Test] public void DomainCacheRefresherCanDeserializeJsonPayload() { - DomainCacheRefresher.JsonPayload[] - source = { new DomainCacheRefresher.JsonPayload(1234, DomainChangeTypes.None) }; + DomainCacheRefresher.JsonPayload[] source = + { + new DomainCacheRefresher.JsonPayload(1234, DomainChangeTypes.None) + }; + var json = JsonConvert.SerializeObject(source); var payload = JsonConvert.DeserializeObject(json); + Assert.AreEqual(source[0].Id, payload[0].Id); Assert.AreEqual(source[0].ChangeType, payload[0].ChangeType); } diff --git a/tests/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/PublishedCache/PublishedSnapshotServiceCollectionTests.cs b/tests/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/PublishedCache/PublishedSnapshotServiceCollectionTests.cs index a007079ca9..b516b4fffb 100644 --- a/tests/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/PublishedCache/PublishedSnapshotServiceCollectionTests.cs +++ b/tests/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/PublishedCache/PublishedSnapshotServiceCollectionTests.cs @@ -358,7 +358,16 @@ public class PublishedSnapshotServiceCollectionTests : PublishedSnapshotServiceT // notify SnapshotService.Notify( - new[] { new ContentCacheRefresher.JsonPayload(10, Guid.Empty, TreeChangeTypes.RefreshBranch) }, out _, out _); + new[] + { + new ContentCacheRefresher.JsonPayload() + { + Id = 10, + ChangeTypes = TreeChangeTypes.RefreshBranch + } + }, + out _, + out _); // changes that *I* make are immediately visible on the current snapshot var documents = snapshot.Content.GetAtRoot().ToArray(); @@ -392,7 +401,16 @@ public class PublishedSnapshotServiceCollectionTests : PublishedSnapshotServiceT // notify SnapshotService.Notify( - new[] { new ContentCacheRefresher.JsonPayload(1, Guid.Empty, TreeChangeTypes.RefreshBranch) }, out _, out _); + new[] + { + new ContentCacheRefresher.JsonPayload() + { + Id = 1, + ChangeTypes = TreeChangeTypes.RefreshBranch + } + }, + out _, + out _); // changes that *I* make are immediately visible on the current snapshot var documents = snapshot.Content.GetAtRoot().ToArray(); @@ -450,7 +468,11 @@ public class PublishedSnapshotServiceCollectionTests : PublishedSnapshotServiceT SnapshotService.Notify( new[] { - new ContentCacheRefresher.JsonPayload(kit.Node.ParentContentId, Guid.Empty, TreeChangeTypes.RefreshBranch), + new ContentCacheRefresher.JsonPayload() + { + Id = kit.Node.ParentContentId, + ChangeTypes = TreeChangeTypes.RefreshBranch + } }, out _, out _); @@ -516,11 +538,19 @@ public class PublishedSnapshotServiceCollectionTests : PublishedSnapshotServiceT // notify SnapshotService.Notify( new[] - { - // removal must come first - new ContentCacheRefresher.JsonPayload(2, Guid.Empty, TreeChangeTypes.RefreshBranch), - new ContentCacheRefresher.JsonPayload(1, Guid.Empty, TreeChangeTypes.RefreshBranch), - }, + { + // removal must come first + new ContentCacheRefresher.JsonPayload() + { + Id = 2, + ChangeTypes = TreeChangeTypes.RefreshBranch + }, + new ContentCacheRefresher.JsonPayload() + { + Id = 1, + ChangeTypes = TreeChangeTypes.RefreshBranch + } + }, out _, out _); @@ -571,7 +601,16 @@ public class PublishedSnapshotServiceCollectionTests : PublishedSnapshotServiceT // notify - which ensures there are 2 generations in the cache meaning each LinkedNode has a Next value. SnapshotService.Notify( - new[] { new ContentCacheRefresher.JsonPayload(4, Guid.Empty, TreeChangeTypes.RefreshBranch) }, out _, out _); + new[] + { + new ContentCacheRefresher.JsonPayload() + { + Id = 4, + ChangeTypes = TreeChangeTypes.RefreshBranch + } + }, + out _, + out _); // refresh the branch again, this used to show the issue where a null ref exception would occur // because in the ClearBranchLocked logic, when SetValueLocked was called within a recursive call @@ -579,7 +618,16 @@ public class PublishedSnapshotServiceCollectionTests : PublishedSnapshotServiceT // this value before recursing. Assert.DoesNotThrow(() => SnapshotService.Notify( - new[] { new ContentCacheRefresher.JsonPayload(4, Guid.Empty, TreeChangeTypes.RefreshBranch) }, out _, out _)); + new[] + { + new ContentCacheRefresher.JsonPayload() + { + Id = 4, + ChangeTypes = TreeChangeTypes.RefreshBranch + } + }, + out _, + out _)); } [Test] @@ -759,11 +807,23 @@ public class PublishedSnapshotServiceCollectionTests : PublishedSnapshotServiceT // notify SnapshotService.Notify( new[] - { - new ContentCacheRefresher.JsonPayload(3, Guid.Empty, TreeChangeTypes.Remove), // remove last - new ContentCacheRefresher.JsonPayload(5, Guid.Empty, TreeChangeTypes.Remove), // remove middle - new ContentCacheRefresher.JsonPayload(9, Guid.Empty, TreeChangeTypes.Remove), // remove first - }, + { + new ContentCacheRefresher.JsonPayload() // remove last + { + Id = 3, + ChangeTypes = TreeChangeTypes.Remove + }, + new ContentCacheRefresher.JsonPayload() // remove middle + { + Id = 5, + ChangeTypes = TreeChangeTypes.Remove + }, + new ContentCacheRefresher.JsonPayload() // remove first + { + Id = 9, + ChangeTypes = TreeChangeTypes.Remove + } + }, out _, out _); @@ -779,11 +839,23 @@ public class PublishedSnapshotServiceCollectionTests : PublishedSnapshotServiceT // notify SnapshotService.Notify( new[] - { - new ContentCacheRefresher.JsonPayload(1, Guid.Empty, TreeChangeTypes.Remove), // remove first - new ContentCacheRefresher.JsonPayload(8, Guid.Empty, TreeChangeTypes.Remove), // remove - new ContentCacheRefresher.JsonPayload(7, Guid.Empty, TreeChangeTypes.Remove), // remove - }, + { + new ContentCacheRefresher.JsonPayload() // remove first + { + Id = 1, + ChangeTypes = TreeChangeTypes.Remove + }, + new ContentCacheRefresher.JsonPayload() // remove + { + Id = 8, + ChangeTypes = TreeChangeTypes.Remove + }, + new ContentCacheRefresher.JsonPayload() // remove + { + Id = 7, + ChangeTypes = TreeChangeTypes.Remove + } + }, out _, out _); @@ -823,8 +895,16 @@ public class PublishedSnapshotServiceCollectionTests : PublishedSnapshotServiceT SnapshotService.Notify( new[] { - new ContentCacheRefresher.JsonPayload(1, Guid.Empty, TreeChangeTypes.RefreshBranch), - new ContentCacheRefresher.JsonPayload(2, Guid.Empty, TreeChangeTypes.RefreshNode), + new ContentCacheRefresher.JsonPayload() + { + Id = 1, + ChangeTypes = TreeChangeTypes.RefreshBranch + }, + new ContentCacheRefresher.JsonPayload() + { + Id = 2, + ChangeTypes = TreeChangeTypes.RefreshNode + } }, out _, out _); @@ -887,7 +967,17 @@ public class PublishedSnapshotServiceCollectionTests : PublishedSnapshotServiceT var parentNode = parentNodes[0]; AssertLinkedNode(parentNode.contentNode, -1, -1, -1, 2, 2); - SnapshotService.Notify(new[] { new ContentCacheRefresher.JsonPayload(2, Guid.Empty, TreeChangeTypes.Remove) }, out _, out _); + SnapshotService.Notify( + new[] + { + new ContentCacheRefresher.JsonPayload() + { + Id = 2, + ChangeTypes = TreeChangeTypes.Remove + } + }, + out _, + out _); parentNodes = contentStore.Test.GetValues(1); parentNode = parentNodes[0]; @@ -944,9 +1034,13 @@ public class PublishedSnapshotServiceCollectionTests : PublishedSnapshotServiceT SnapshotService.Notify( new[] - { - new ContentCacheRefresher.JsonPayload(3, Guid.Empty, TreeChangeTypes.Remove), // remove middle child - }, + { + new ContentCacheRefresher.JsonPayload() // remove middle child + { + Id = 3, + ChangeTypes = TreeChangeTypes.Remove + } + }, out _, out _); @@ -1013,7 +1107,16 @@ public class PublishedSnapshotServiceCollectionTests : PublishedSnapshotServiceT Assert.IsFalse(contentStore.Test.NextGen); SnapshotService.Notify( - new[] { new ContentCacheRefresher.JsonPayload(1, Guid.Empty, TreeChangeTypes.RefreshNode) }, out _, out _); + new[] + { + new ContentCacheRefresher.JsonPayload() + { + Id = 1, + ChangeTypes = TreeChangeTypes.RefreshNode + } + }, + out _, + out _); Assert.AreEqual(2, contentStore.Test.LiveGen); Assert.IsTrue(contentStore.Test.NextGen); @@ -1084,7 +1187,17 @@ public class PublishedSnapshotServiceCollectionTests : PublishedSnapshotServiceT published ? rootKit.PublishedData : null); NuCacheContentService.ContentKits[1] = kit; - SnapshotService.Notify(new[] { new ContentCacheRefresher.JsonPayload(1, Guid.Empty, changeType) }, out _, out _); + SnapshotService.Notify( + new[] + { + new ContentCacheRefresher.JsonPayload() + { + Id = 1, + ChangeTypes = changeType + } + }, + out _, + out _); Assert.AreEqual(assertGen, contentStore.Test.LiveGen); Assert.IsTrue(contentStore.Test.NextGen); @@ -1162,9 +1275,13 @@ public class PublishedSnapshotServiceCollectionTests : PublishedSnapshotServiceT SnapshotService.Notify( new[] - { - new ContentCacheRefresher.JsonPayload(3, Guid.Empty, TreeChangeTypes.RefreshBranch), // remove middle child - }, + { + new ContentCacheRefresher.JsonPayload() // remove middle child + { + Id = 3, + ChangeTypes = TreeChangeTypes.RefreshBranch + } + }, out _, out _);