From fc821000f3ef9d6c6be2f6d7dec7be4215af7d0a Mon Sep 17 00:00:00 2001 From: Shannon Date: Mon, 16 Sep 2019 14:50:05 +1000 Subject: [PATCH] Adds ext methods for creating a flag to indicate model recompilation should occur --- .../PublishedContentExtensionsForModels.cs | 10 +--- .../PublishedModelFactoryExtensions.cs | 56 +++++++++++++++++++ .../Cache/ContentTypeCacheRefresher.cs | 3 +- .../Cache/DataTypeCacheRefresher.cs | 3 +- .../NuCache/PublishedSnapshotService.cs | 33 +++++------ 5 files changed, 78 insertions(+), 27 deletions(-) diff --git a/src/Umbraco.Core/Models/PublishedContent/PublishedContentExtensionsForModels.cs b/src/Umbraco.Core/Models/PublishedContent/PublishedContentExtensionsForModels.cs index bfc65b70d6..8e68bdae52 100644 --- a/src/Umbraco.Core/Models/PublishedContent/PublishedContentExtensionsForModels.cs +++ b/src/Umbraco.Core/Models/PublishedContent/PublishedContentExtensionsForModels.cs @@ -25,15 +25,7 @@ namespace Umbraco.Core.Models.PublishedContent // get model // if factory returns nothing, throw - var model = Current.PublishedModelFactory.CreateModel(content); - if (model == null) - throw new Exception("Factory returned null."); - - // if factory returns a different type, throw - if (!(model is IPublishedContent publishedContent)) - throw new Exception($"Factory returned model of type {model.GetType().FullName} which does not implement IPublishedContent."); - - return publishedContent; + return Current.PublishedModelFactory.CreateModelWithSafeLiveFactoryRefreshCheck(content); } } } diff --git a/src/Umbraco.Core/PublishedModelFactoryExtensions.cs b/src/Umbraco.Core/PublishedModelFactoryExtensions.cs index 70bef75192..c36643095b 100644 --- a/src/Umbraco.Core/PublishedModelFactoryExtensions.cs +++ b/src/Umbraco.Core/PublishedModelFactoryExtensions.cs @@ -47,6 +47,62 @@ namespace Umbraco.Core } } + /// + /// Creates a strongly typed model while checking if the factory is and if a refresh flag has been set, in which + /// case the models will be recompiled before model creation + /// + /// + /// + /// + internal static IPublishedContent CreateModelWithSafeLiveFactoryRefreshCheck(this IPublishedModelFactory factory, IPublishedContent content) + { + if (factory is ILivePublishedModelFactory liveFactory && _refresh) + { + lock (liveFactory.SyncRoot) + { + if (_refresh) + { + _refresh = false; + //Call refresh on the live factory to re-compile the models + liveFactory.Refresh(); + } + } + } + + var model = factory.CreateModel(content); + if (model == null) + throw new Exception("Factory returned null."); + + // if factory returns a different type, throw + if (!(model is IPublishedContent publishedContent)) + throw new Exception($"Factory returned model of type {model.GetType().FullName} which does not implement IPublishedContent."); + + return publishedContent; + } + + /// + /// Sets a flag to re-compile the models if the is + /// + /// + /// + internal static void WithSafeLiveFactoryRefreshSet(this IPublishedModelFactory factory, Action action) + { + if (factory is ILivePublishedModelFactory liveFactory) + { + lock (liveFactory.SyncRoot) + { + _refresh = true; + action(); + } + } + else + { + action(); + } + } + + private static volatile bool _refresh = false; + public static IDisposable SuspendSafeLiveFactory(this IPublishedModelFactory factory) { if (factory is ILivePublishedModelFactory liveFactory) diff --git a/src/Umbraco.Web/Cache/ContentTypeCacheRefresher.cs b/src/Umbraco.Web/Cache/ContentTypeCacheRefresher.cs index cf90b60d8a..4d339ff336 100644 --- a/src/Umbraco.Web/Cache/ContentTypeCacheRefresher.cs +++ b/src/Umbraco.Web/Cache/ContentTypeCacheRefresher.cs @@ -91,7 +91,8 @@ namespace Umbraco.Web.Cache // we are using the database to load content into caches //_publishedModelFactory.WithSafeLiveFactory(() => - _publishedSnapshotService.Notify(payloads); + _publishedModelFactory.WithSafeLiveFactoryRefreshSet(() => + _publishedSnapshotService.Notify(payloads)); // now we can trigger the event base.Refresh(payloads); diff --git a/src/Umbraco.Web/Cache/DataTypeCacheRefresher.cs b/src/Umbraco.Web/Cache/DataTypeCacheRefresher.cs index fb4f59fce4..b80542af44 100644 --- a/src/Umbraco.Web/Cache/DataTypeCacheRefresher.cs +++ b/src/Umbraco.Web/Cache/DataTypeCacheRefresher.cs @@ -67,7 +67,8 @@ namespace Umbraco.Web.Cache // we are using the database to load content into caches //_publishedModelFactory.WithSafeLiveFactory(() => - _publishedSnapshotService.Notify(payloads); + _publishedModelFactory.WithSafeLiveFactoryRefreshSet(() => + _publishedSnapshotService.Notify(payloads)); base.Refresh(payloads); } diff --git a/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs b/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs index cde45ae116..66cc5aaed0 100755 --- a/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs @@ -790,22 +790,23 @@ namespace Umbraco.Web.PublishedCache.NuCache Notify(_contentStore, payloads, RefreshContentTypesLocked); Notify(_mediaStore, payloads, RefreshMediaTypesLocked); - //TODO: I don't think this is necessary with the changes to nucache now that calls to `CreateModel` are lazy! - //if (_publishedModelFactory.IsLiveFactory()) - //{ - // //In the case of Pure Live - we actually need to refresh all of the content and the media - // //see https://github.com/umbraco/Umbraco-CMS/issues/5671 - // //The underlying issue is that in Pure Live the ILivePublishedModelFactory will re-compile all of the classes/models - // //into a new DLL for the application which includes both content types and media types. - // //Since the models in the cache are based on these actual classes, all of the objects in the cache need to be updated - // //to use the newest version of the class. - // using (_contentStore.GetScopedWriteLock(_scopeProvider)) - // using (_mediaStore.GetScopedWriteLock(_scopeProvider)) - // { - // NotifyLocked(new[] { new ContentCacheRefresher.JsonPayload(0, TreeChangeTypes.RefreshAll) }, out var draftChanged, out var publishedChanged); - // NotifyLocked(new[] { new MediaCacheRefresher.JsonPayload(0, TreeChangeTypes.RefreshAll) }, out var anythingChanged); - // } - //} + //TODO: I don't think this is necessary with the changes to nucache now that calls to `CreateModel` are lazy? + // but I may be dreaming here, if i remove this call and save a content type, then the cache doesn't render a lot of the content. hrm. + if (_publishedModelFactory.IsLiveFactory()) + { + //In the case of Pure Live - we actually need to refresh all of the content and the media + //see https://github.com/umbraco/Umbraco-CMS/issues/5671 + //The underlying issue is that in Pure Live the ILivePublishedModelFactory will re-compile all of the classes/models + //into a new DLL for the application which includes both content types and media types. + //Since the models in the cache are based on these actual classes, all of the objects in the cache need to be updated + //to use the newest version of the class. + using (_contentStore.GetScopedWriteLock(_scopeProvider)) + using (_mediaStore.GetScopedWriteLock(_scopeProvider)) + { + NotifyLocked(new[] { new ContentCacheRefresher.JsonPayload(0, TreeChangeTypes.RefreshAll) }, out var draftChanged, out var publishedChanged); + NotifyLocked(new[] { new MediaCacheRefresher.JsonPayload(0, TreeChangeTypes.RefreshAll) }, out var anythingChanged); + } + } ((PublishedSnapshot)CurrentPublishedSnapshot)?.Resync(); }