# Conflicts: # build/NuSpecs/UmbracoCms.Web.nuspec # src/Umbraco.Core/Composing/Current.cs # src/Umbraco.Core/Persistence/NPocoDatabaseExtensions-Bulk.cs # src/Umbraco.Core/Runtime/CoreRuntime.cs # src/Umbraco.Infrastructure/Migrations/Upgrade/UmbracoPlan.cs # src/Umbraco.Infrastructure/Persistence/NPocoDatabaseExtensions.cs # src/Umbraco.Infrastructure/Persistence/UmbracoDatabase.cs # src/Umbraco.Persistence.SqlCe/SqlCeSyntaxProvider.cs # src/Umbraco.PublishedCache.NuCache/DataSource/BTree.cs # src/Umbraco.PublishedCache.NuCache/DataSource/ContentCacheDataModel.cs # src/Umbraco.PublishedCache.NuCache/DataSource/ContentCacheDataSerializationResult.cs # src/Umbraco.PublishedCache.NuCache/DataSource/ContentCacheDataSerializerEntityType.cs # src/Umbraco.PublishedCache.NuCache/DataSource/ContentData.cs # src/Umbraco.PublishedCache.NuCache/DataSource/ContentNestedData.cs # src/Umbraco.PublishedCache.NuCache/DataSource/CultureVariation.cs # src/Umbraco.PublishedCache.NuCache/DataSource/IContentCacheDataSerializer.cs # src/Umbraco.PublishedCache.NuCache/DataSource/IContentCacheDataSerializerFactory.cs # src/Umbraco.PublishedCache.NuCache/DataSource/IDictionaryOfPropertyDataSerializer.cs # src/Umbraco.PublishedCache.NuCache/DataSource/JsonContentNestedDataSerializer.cs # src/Umbraco.PublishedCache.NuCache/DataSource/JsonContentNestedDataSerializerFactory.cs # src/Umbraco.PublishedCache.NuCache/DataSource/LazyCompressedString.cs # src/Umbraco.PublishedCache.NuCache/DataSource/MsgPackContentNestedDataSerializer.cs # src/Umbraco.PublishedCache.NuCache/DataSource/MsgPackContentNestedDataSerializerFactory.cs # src/Umbraco.PublishedCache.NuCache/DataSource/PropertyData.cs # src/Umbraco.PublishedCache.NuCache/NuCacheSerializerComponent.cs # src/Umbraco.PublishedCache.NuCache/NuCacheSerializerComposer.cs # src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Services/ContentTypeServiceVariantsTests.cs # src/Umbraco.Tests/App.config # src/Umbraco.Tests/PublishedContent/NuCacheChildrenTests.cs # src/Umbraco.Tests/PublishedContent/NuCacheTests.cs # src/Umbraco.Tests/Scoping/ScopedNuCacheTests.cs # src/Umbraco.Web.UI.NetCore/umbraco/config/lang/da.xml # src/Umbraco.Web.UI/web.Template.Debug.config # src/Umbraco.Web.UI/web.Template.config # src/Umbraco.Web/Composing/ModuleInjector.cs # src/Umbraco.Web/Editors/NuCacheStatusController.cs # src/Umbraco.Web/PublishedCache/NuCache/DataSource/ContentNestedData.cs # src/Umbraco.Web/PublishedCache/NuCache/DataSource/DatabaseDataSource.cs # src/Umbraco.Web/PublishedCache/NuCache/NuCacheComposer.cs # src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs # src/Umbraco.Web/Runtime/WebRuntime.cs
156 lines
6.9 KiB
C#
156 lines
6.9 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using Umbraco.Cms.Core;
|
|
using Umbraco.Cms.Core.Events;
|
|
using Umbraco.Cms.Core.Models;
|
|
using Umbraco.Cms.Core.Notifications;
|
|
using Umbraco.Cms.Core.PublishedCache;
|
|
using Umbraco.Cms.Core.Services;
|
|
using Umbraco.Cms.Core.Services.Changes;
|
|
using Umbraco.Cms.Infrastructure.PublishedCache.Persistence;
|
|
using Umbraco.Extensions;
|
|
|
|
namespace Umbraco.Cms.Infrastructure.PublishedCache
|
|
{
|
|
/// <summary>
|
|
/// Subscribes to Umbraco events to ensure nucache remains consistent with the source data
|
|
/// </summary>
|
|
public class PublishedSnapshotServiceEventHandler :
|
|
IDisposable,
|
|
INotificationHandler<LanguageSavedNotification>,
|
|
INotificationHandler<MemberDeletingNotification>,
|
|
INotificationHandler<ContentRefreshNotification>,
|
|
INotificationHandler<MediaRefreshNotification>,
|
|
INotificationHandler<MemberRefreshNotification>,
|
|
INotificationHandler<ContentTypeRefreshedNotification>,
|
|
INotificationHandler<MediaTypeRefreshedNotification>,
|
|
INotificationHandler<MemberTypeRefreshedNotification>,
|
|
INotificationHandler<ScopedEntityRemoveNotification>
|
|
{
|
|
private readonly IRuntimeState _runtime;
|
|
private bool _disposedValue;
|
|
private readonly IPublishedSnapshotService _publishedSnapshotService;
|
|
private readonly INuCacheContentService _publishedContentService;
|
|
private readonly IContentService _contentService;
|
|
private readonly IMediaService _mediaService;
|
|
|
|
/// <summary>
|
|
/// Initializes a new instance of the <see cref="PublishedSnapshotServiceEventHandler"/> class.
|
|
/// </summary>
|
|
public PublishedSnapshotServiceEventHandler(
|
|
IRuntimeState runtime,
|
|
IPublishedSnapshotService publishedSnapshotService,
|
|
INuCacheContentService publishedContentService,
|
|
IContentService contentService,
|
|
IMediaService mediaService)
|
|
{
|
|
_runtime = runtime;
|
|
_publishedSnapshotService = publishedSnapshotService;
|
|
_publishedContentService = publishedContentService;
|
|
_contentService = contentService;
|
|
_mediaService = mediaService;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Binds to the Umbraco events
|
|
/// </summary>
|
|
/// <returns>Returns true if binding occurred</returns>
|
|
public bool Initialize()
|
|
{
|
|
// however, the cache is NOT available until we are configured, because loading
|
|
// content (and content types) from database cannot be consistent (see notes in "Handle
|
|
// Notifications" region), so
|
|
// - notifications will be ignored
|
|
// - trying to obtain a published snapshot from the service will throw
|
|
if (_runtime.Level != RuntimeLevel.Run)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
// note: if the service is not ready, ie _isReady is false, then we still handle repository events,
|
|
// because we can, we do not need a working published snapshot to do it - the only reason why it could cause an
|
|
// issue is if the database table is not ready, but that should be prevented by migrations.
|
|
|
|
// we need them to be "repository" events ie to trigger from within the repository transaction,
|
|
// because they need to be consistent with the content that is being refreshed/removed - and that
|
|
// should be guaranteed by a DB transaction
|
|
public void Handle(ScopedEntityRemoveNotification notification) =>
|
|
_publishedContentService.DeleteContentItem(notification.Entity);
|
|
|
|
public void Handle(MemberDeletingNotification notification) => _publishedContentService.DeleteContentItems(notification.DeletedEntities);
|
|
|
|
public void Handle(MemberRefreshNotification notification) => _publishedContentService.RefreshMember(notification.Entity);
|
|
|
|
public void Handle(MediaRefreshNotification notification) => _publishedContentService.RefreshMedia(notification.Entity);
|
|
|
|
public void Handle(ContentRefreshNotification notification) => _publishedContentService.RefreshContent(notification.Entity);
|
|
|
|
public void Handle(ContentTypeRefreshedNotification notification)
|
|
{
|
|
const ContentTypeChangeTypes types // only for those that have been refreshed
|
|
= ContentTypeChangeTypes.RefreshMain | ContentTypeChangeTypes.RefreshOther;
|
|
var contentTypeIds = notification.Changes.Where(x => x.ChangeTypes.HasTypesAny(types)).Select(x => x.Item.Id).ToArray();
|
|
if (contentTypeIds.Any())
|
|
{
|
|
_publishedSnapshotService.Rebuild(contentTypeIds: contentTypeIds);
|
|
}
|
|
}
|
|
|
|
public void Handle(MediaTypeRefreshedNotification notification)
|
|
{
|
|
const ContentTypeChangeTypes types // only for those that have been refreshed
|
|
= ContentTypeChangeTypes.RefreshMain | ContentTypeChangeTypes.RefreshOther;
|
|
var mediaTypeIds = notification.Changes.Where(x => x.ChangeTypes.HasTypesAny(types)).Select(x => x.Item.Id).ToArray();
|
|
if (mediaTypeIds.Any())
|
|
{
|
|
_publishedSnapshotService.Rebuild(mediaTypeIds: mediaTypeIds);
|
|
}
|
|
}
|
|
|
|
public void Handle(MemberTypeRefreshedNotification notification)
|
|
{
|
|
const ContentTypeChangeTypes types // only for those that have been refreshed
|
|
= ContentTypeChangeTypes.RefreshMain | ContentTypeChangeTypes.RefreshOther;
|
|
var memberTypeIds = notification.Changes.Where(x => x.ChangeTypes.HasTypesAny(types)).Select(x => x.Item.Id).ToArray();
|
|
if (memberTypeIds.Any())
|
|
{
|
|
_publishedSnapshotService.Rebuild(memberTypeIds: memberTypeIds);
|
|
}
|
|
}
|
|
|
|
// TODO: This should be a cache refresher call!
|
|
/// <summary>
|
|
/// If a <see cref="ILanguage"/> is ever saved with a different culture, we need to rebuild all of the content nucache database table
|
|
/// </summary>
|
|
public void Handle(LanguageSavedNotification notification)
|
|
{
|
|
// culture changed on an existing language
|
|
var cultureChanged = notification.SavedEntities.Any(x => !x.WasPropertyDirty(nameof(ILanguage.Id)) && x.WasPropertyDirty(nameof(ILanguage.IsoCode)));
|
|
if (cultureChanged)
|
|
{
|
|
// Rebuild all content for all content types
|
|
_publishedSnapshotService.Rebuild(contentTypeIds: Array.Empty<int>());
|
|
}
|
|
}
|
|
|
|
protected virtual void Dispose(bool disposing)
|
|
{
|
|
if (!_disposedValue)
|
|
{
|
|
_disposedValue = true;
|
|
}
|
|
}
|
|
|
|
public void Dispose()
|
|
{
|
|
// Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
|
|
Dispose(disposing: true);
|
|
GC.SuppressFinalize(this);
|
|
}
|
|
}
|
|
}
|