Initialize important services before unattended installs (#17366)
* Added new notification to hook in after the premigrations and use this to init different services. * Force MaxDegreeOfParallelism to 1, while investigating scopes * Tried some more workarounds * Updated scopes and changed parallel to non parallel to ensure migration works * Missing scope * Make it parallel again - The secret is, the SuppressFlow needs to be when you create the task, but not on the await!. * Fixed issue when DEBUG_SCOPES is not added to tests. * Remove test exception * Try build on ubuntu again, even that we know it can be stuck. Just a test to see if all tests pass * Updated comment --------- Co-authored-by: kjac <kja@umbraco.dk>
This commit is contained in:
@@ -71,7 +71,7 @@ stages:
|
||||
- job: A
|
||||
displayName: Build Umbraco CMS
|
||||
pool:
|
||||
vmImage: 'windows-latest'
|
||||
vmImage: 'ubuntu-latest'
|
||||
steps:
|
||||
- checkout: self
|
||||
submodules: true
|
||||
|
||||
@@ -5,36 +5,45 @@ namespace Umbraco.Cms.Persistence.EFCore.Scoping;
|
||||
|
||||
public class AmbientEFCoreScopeStack<TDbContext> : IAmbientEFCoreScopeStack<TDbContext> where TDbContext : DbContext
|
||||
{
|
||||
|
||||
private static Lock _lock = new();
|
||||
private static AsyncLocal<ConcurrentStack<IEfCoreScope<TDbContext>>> _stack = new();
|
||||
|
||||
public IEfCoreScope<TDbContext>? AmbientScope
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_stack.Value?.TryPeek(out IEfCoreScope<TDbContext>? ambientScope) ?? false)
|
||||
lock (_lock)
|
||||
{
|
||||
return ambientScope;
|
||||
}
|
||||
if (_stack.Value?.TryPeek(out IEfCoreScope<TDbContext>? ambientScope) ?? false)
|
||||
{
|
||||
return ambientScope;
|
||||
}
|
||||
|
||||
return null;
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public IEfCoreScope<TDbContext> Pop()
|
||||
{
|
||||
if (_stack.Value?.TryPop(out IEfCoreScope<TDbContext>? ambientScope) ?? false)
|
||||
lock (_lock)
|
||||
{
|
||||
return ambientScope;
|
||||
}
|
||||
if (_stack.Value?.TryPop(out IEfCoreScope<TDbContext>? ambientScope) ?? false)
|
||||
{
|
||||
return ambientScope;
|
||||
}
|
||||
|
||||
throw new InvalidOperationException("No AmbientScope was found.");
|
||||
throw new InvalidOperationException("No AmbientScope was found.");
|
||||
}
|
||||
}
|
||||
|
||||
public void Push(IEfCoreScope<TDbContext> scope)
|
||||
{
|
||||
_stack.Value ??= new ConcurrentStack<IEfCoreScope<TDbContext>>();
|
||||
lock (_lock)
|
||||
{
|
||||
_stack.Value ??= new ConcurrentStack<IEfCoreScope<TDbContext>>();
|
||||
|
||||
_stack.Value.Push(scope);
|
||||
_stack.Value.Push(scope);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ public static partial class Constants
|
||||
{
|
||||
public static readonly string[] UmbracoCoreAssemblyNames =
|
||||
{
|
||||
"Umbraco.Core", "Umbraco.Infrastructure", "Umbraco.PublishedCache.NuCache", "Umbraco.Examine.Lucene",
|
||||
"Umbraco.Core", "Umbraco.Infrastructure", "Umbraco.Examine.Lucene",
|
||||
"Umbraco.Web.Common", "Umbraco.Cms.Api.Common","Umbraco.Cms.Api.Delivery","Umbraco.Cms.Api.Management", "Umbraco.Web.Website",
|
||||
};
|
||||
}
|
||||
|
||||
@@ -416,7 +416,8 @@ namespace Umbraco.Cms.Core.DependencyInjection
|
||||
|
||||
// Routing
|
||||
Services.AddUnique<IDocumentUrlService, DocumentUrlService>();
|
||||
Services.AddHostedService<DocumentUrlServiceInitializer>();
|
||||
Services.AddNotificationAsyncHandler<UmbracoApplicationStartingNotification, DocumentUrlServiceInitializerNotificationHandler>();
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
namespace Umbraco.Cms.Core.Notifications;
|
||||
|
||||
public class PostRuntimePremigrationsUpgradeNotification : INotification
|
||||
{
|
||||
|
||||
}
|
||||
@@ -1,53 +0,0 @@
|
||||
using Microsoft.Extensions.Hosting;
|
||||
|
||||
namespace Umbraco.Cms.Core.Services;
|
||||
|
||||
public class DocumentUrlServiceInitializer : IHostedLifecycleService
|
||||
{
|
||||
private readonly IDocumentUrlService _documentUrlService;
|
||||
private readonly IRuntimeState _runtimeState;
|
||||
|
||||
public DocumentUrlServiceInitializer(IDocumentUrlService documentUrlService, IRuntimeState runtimeState)
|
||||
{
|
||||
_documentUrlService = documentUrlService;
|
||||
_runtimeState = runtimeState;
|
||||
}
|
||||
|
||||
public async Task StartAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
if (_runtimeState.Level == RuntimeLevel.Upgrade)
|
||||
{
|
||||
//Special case on the first upgrade, as the database is not ready yet.
|
||||
return;
|
||||
}
|
||||
|
||||
await _documentUrlService.InitAsync(
|
||||
_runtimeState.Level <= RuntimeLevel.Install,
|
||||
cancellationToken);
|
||||
}
|
||||
|
||||
public Task StopAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public Task StartingAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public Task StartedAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public Task StoppingAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public Task StoppedAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
using Umbraco.Cms.Core.Events;
|
||||
using Umbraco.Cms.Core.Notifications;
|
||||
|
||||
namespace Umbraco.Cms.Core.Services;
|
||||
|
||||
public class DocumentUrlServiceInitializerNotificationHandler : INotificationAsyncHandler<UmbracoApplicationStartingNotification>
|
||||
{
|
||||
private readonly IDocumentUrlService _documentUrlService;
|
||||
private readonly IRuntimeState _runtimeState;
|
||||
|
||||
public DocumentUrlServiceInitializerNotificationHandler(IDocumentUrlService documentUrlService, IRuntimeState runtimeState)
|
||||
{
|
||||
_documentUrlService = documentUrlService;
|
||||
_runtimeState = runtimeState;
|
||||
}
|
||||
|
||||
public async Task HandleAsync(UmbracoApplicationStartingNotification notification, CancellationToken cancellationToken)
|
||||
{
|
||||
if (_runtimeState.Level == RuntimeLevel.Upgrade)
|
||||
{
|
||||
//Special case on the first upgrade, as the database is not ready yet.
|
||||
return;
|
||||
}
|
||||
|
||||
await _documentUrlService.InitAsync(
|
||||
_runtimeState.Level <= RuntimeLevel.Install,
|
||||
cancellationToken);
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,6 @@
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Umbraco.Cms.Core.Events;
|
||||
using Umbraco.Cms.Core.Notifications;
|
||||
|
||||
namespace Umbraco.Cms.Core.Services.Navigation;
|
||||
|
||||
@@ -6,13 +8,13 @@ namespace Umbraco.Cms.Core.Services.Navigation;
|
||||
/// Responsible for seeding the in-memory navigation structures at application's startup
|
||||
/// by rebuild the navigation structures.
|
||||
/// </summary>
|
||||
public sealed class NavigationInitializationHostedService : IHostedLifecycleService
|
||||
public sealed class NavigationInitializationNotificationHandler : INotificationAsyncHandler<PostRuntimePremigrationsUpgradeNotification>
|
||||
{
|
||||
private readonly IRuntimeState _runtimeState;
|
||||
private readonly IDocumentNavigationManagementService _documentNavigationManagementService;
|
||||
private readonly IMediaNavigationManagementService _mediaNavigationManagementService;
|
||||
|
||||
public NavigationInitializationHostedService(
|
||||
public NavigationInitializationNotificationHandler(
|
||||
IRuntimeState runtimeState,
|
||||
IDocumentNavigationManagementService documentNavigationManagementService,
|
||||
IMediaNavigationManagementService mediaNavigationManagementService)
|
||||
@@ -22,7 +24,7 @@ public sealed class NavigationInitializationHostedService : IHostedLifecycleServ
|
||||
_mediaNavigationManagementService = mediaNavigationManagementService;
|
||||
}
|
||||
|
||||
public async Task StartingAsync(CancellationToken cancellationToken)
|
||||
public async Task HandleAsync(PostRuntimePremigrationsUpgradeNotification notification, CancellationToken cancellationToken)
|
||||
{
|
||||
if(_runtimeState.Level < RuntimeLevel.Upgrade)
|
||||
{
|
||||
@@ -34,14 +36,4 @@ public sealed class NavigationInitializationHostedService : IHostedLifecycleServ
|
||||
await _mediaNavigationManagementService.RebuildAsync();
|
||||
await _mediaNavigationManagementService.RebuildBinAsync();
|
||||
}
|
||||
|
||||
public Task StartAsync(CancellationToken cancellationToken) => Task.CompletedTask;
|
||||
|
||||
public Task StartedAsync(CancellationToken cancellationToken) => Task.CompletedTask;
|
||||
|
||||
public Task StoppingAsync(CancellationToken cancellationToken) => Task.CompletedTask;
|
||||
|
||||
public Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask;
|
||||
|
||||
public Task StoppedAsync(CancellationToken cancellationToken) => Task.CompletedTask;
|
||||
}
|
||||
@@ -1,4 +1,6 @@
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Umbraco.Cms.Core.Events;
|
||||
using Umbraco.Cms.Core.Notifications;
|
||||
|
||||
namespace Umbraco.Cms.Core.Services.Navigation;
|
||||
|
||||
@@ -6,12 +8,12 @@ namespace Umbraco.Cms.Core.Services.Navigation;
|
||||
/// Responsible for seeding the in-memory publish status cache at application's startup
|
||||
/// by loading all data from the database.
|
||||
/// </summary>
|
||||
public sealed class PublishStatusInitializationHostedService : IHostedLifecycleService
|
||||
public sealed class PublishStatusInitializationNotificationHandler : INotificationAsyncHandler<PostRuntimePremigrationsUpgradeNotification>
|
||||
{
|
||||
private readonly IRuntimeState _runtimeState;
|
||||
private readonly IPublishStatusManagementService _publishStatusManagementService;
|
||||
|
||||
public PublishStatusInitializationHostedService(
|
||||
public PublishStatusInitializationNotificationHandler(
|
||||
IRuntimeState runtimeState,
|
||||
IPublishStatusManagementService publishStatusManagementService
|
||||
)
|
||||
@@ -20,7 +22,7 @@ public sealed class PublishStatusInitializationHostedService : IHostedLifecycleS
|
||||
_publishStatusManagementService = publishStatusManagementService;
|
||||
}
|
||||
|
||||
public async Task StartingAsync(CancellationToken cancellationToken)
|
||||
public async Task HandleAsync(PostRuntimePremigrationsUpgradeNotification notification, CancellationToken cancellationToken)
|
||||
{
|
||||
if(_runtimeState.Level < RuntimeLevel.Upgrade)
|
||||
{
|
||||
@@ -29,14 +31,4 @@ public sealed class PublishStatusInitializationHostedService : IHostedLifecycleS
|
||||
|
||||
await _publishStatusManagementService.InitializeAsync(cancellationToken);
|
||||
}
|
||||
|
||||
public Task StartAsync(CancellationToken cancellationToken) => Task.CompletedTask;
|
||||
|
||||
public Task StartedAsync(CancellationToken cancellationToken) => Task.CompletedTask;
|
||||
|
||||
public Task StoppingAsync(CancellationToken cancellationToken) => Task.CompletedTask;
|
||||
|
||||
public Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask;
|
||||
|
||||
public Task StoppedAsync(CancellationToken cancellationToken) => Task.CompletedTask;
|
||||
}
|
||||
@@ -11,7 +11,7 @@ using Umbraco.Cms.Infrastructure.Persistence;
|
||||
namespace Umbraco.Cms.Infrastructure.Install;
|
||||
|
||||
/// <summary>
|
||||
/// Handles <see cref="RuntimeUnattendedUpgradeNotification" /> to execute the unattended Umbraco upgrader
|
||||
/// Handles <see cref="RuntimePremigrationsUpgradeNotification" /> to execute the unattended Umbraco upgrader
|
||||
/// or the unattended Package migrations runner.
|
||||
/// </summary>
|
||||
public class PremigrationUpgrader : INotificationAsyncHandler<RuntimePremigrationsUpgradeNotification>
|
||||
|
||||
@@ -66,14 +66,14 @@ public class UmbracoPlan : MigrationPlan
|
||||
To<V_13_5_0.ChangeRedirectUrlToNvarcharMax>("{CC47C751-A81B-489A-A2BC-0240245DB687}");
|
||||
|
||||
// To 14.0.0
|
||||
To<V_14_0_0.AddPropertyEditorUiAliasColumn>("{419827A0-4FCE-464B-A8F3-247C6092AF55}");
|
||||
To<NoopMigration>("{419827A0-4FCE-464B-A8F3-247C6092AF55}");
|
||||
To<NoopMigration>("{69E12556-D9B3-493A-8E8A-65EC89FB658D}");
|
||||
To<NoopMigration>("{F2B16CD4-F181-4BEE-81C9-11CF384E6025}");
|
||||
To<NoopMigration>("{A8E01644-9F2E-4988-8341-587EF5B7EA69}");
|
||||
To<V_14_0_0.UpdateDefaultGuidsOfCreatedPackages>("{E073DBC0-9E8E-4C92-8210-9CB18364F46E}");
|
||||
To<V_14_0_0.RenameTechnologyLeakingPropertyEditorAliases>("{80D282A4-5497-47FF-991F-BC0BCE603121}");
|
||||
To<V_14_0_0.MigrateSchduledPublishesToUtc>("{96525697-E9DC-4198-B136-25AD033442B8}");
|
||||
To<V_14_0_0.AddListViewKeysToDocumentTypes>("{7FC5AC9B-6F56-415B-913E-4A900629B853}");
|
||||
To<NoopMigration>("{7FC5AC9B-6F56-415B-913E-4A900629B853}");
|
||||
To<V_14_0_0.MigrateDataTypeConfigurations>("{1539A010-2EB5-4163-8518-4AE2AA98AFC6}");
|
||||
To<NoopMigration>("{C567DE81-DF92-4B99-BEA8-CD34EF99DA5D}");
|
||||
To<V_14_0_0.DeleteMacroTables>("{0D82C836-96DD-480D-A924-7964E458BD34}");
|
||||
|
||||
@@ -65,5 +65,7 @@ public class UmbracoPremigrationPlan : MigrationPlan
|
||||
To<V_14_0_0.MigrateTours>("{A08254B6-D9E7-4207-A496-2ED0A87FB4FD}");
|
||||
To<V_15_0_0.AddKindToUser>("{69AA6889-8B67-42B4-AA4F-114704487A45}");
|
||||
To<V_15_0_0.AddDocumentUrl>("{B9133686-B758-404D-AF12-708AA80C7E44}");
|
||||
To<V_14_0_0.AddPropertyEditorUiAliasColumn>("{EEB1F012-B44D-4AB4-8756-F7FB547345B4}");
|
||||
To<V_14_0_0.AddListViewKeysToDocumentTypes>("{0F49E1A4-AFD8-4673-A91B-F64E78C48174}");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ using Umbraco.Cms.Core;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
using Umbraco.Cms.Core.Models.Blocks;
|
||||
using Umbraco.Cms.Core.Models.Editors;
|
||||
using Umbraco.Cms.Core.Scoping;
|
||||
using Umbraco.Cms.Core.Serialization;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
using Umbraco.Cms.Core.Web;
|
||||
@@ -23,6 +24,7 @@ public abstract class ConvertBlockEditorPropertiesBase : MigrationBase
|
||||
private readonly IJsonSerializer _jsonSerializer;
|
||||
private readonly IUmbracoContextFactory _umbracoContextFactory;
|
||||
private readonly ILanguageService _languageService;
|
||||
private readonly ICoreScopeProvider _coreScopeProvider;
|
||||
|
||||
protected abstract IEnumerable<string> PropertyEditorAliases { get; }
|
||||
|
||||
@@ -44,7 +46,8 @@ public abstract class ConvertBlockEditorPropertiesBase : MigrationBase
|
||||
IDataTypeService dataTypeService,
|
||||
IJsonSerializer jsonSerializer,
|
||||
IUmbracoContextFactory umbracoContextFactory,
|
||||
ILanguageService languageService)
|
||||
ILanguageService languageService,
|
||||
ICoreScopeProvider coreScopeProvider)
|
||||
: base(context)
|
||||
{
|
||||
_logger = logger;
|
||||
@@ -53,6 +56,7 @@ public abstract class ConvertBlockEditorPropertiesBase : MigrationBase
|
||||
_jsonSerializer = jsonSerializer;
|
||||
_umbracoContextFactory = umbracoContextFactory;
|
||||
_languageService = languageService;
|
||||
_coreScopeProvider = coreScopeProvider;
|
||||
}
|
||||
|
||||
protected override void Migrate()
|
||||
@@ -71,6 +75,7 @@ public abstract class ConvertBlockEditorPropertiesBase : MigrationBase
|
||||
.GroupBy(pt => pt.PropertyEditorAlias)
|
||||
.ToDictionary(group => group.Key, group => group.ToArray());
|
||||
|
||||
|
||||
foreach (var propertyEditorAlias in PropertyEditorAliases)
|
||||
{
|
||||
if (allPropertyTypesByEditor.TryGetValue(propertyEditorAlias, out IPropertyType[]? propertyTypes) is false)
|
||||
@@ -98,11 +103,17 @@ public abstract class ConvertBlockEditorPropertiesBase : MigrationBase
|
||||
{
|
||||
var success = true;
|
||||
|
||||
foreach (IPropertyType propertyType in propertyTypes)
|
||||
var propertyTypeCount = propertyTypes.Length;
|
||||
for (var propertyTypeIndex = 0; propertyTypeIndex < propertyTypeCount; propertyTypeIndex++)
|
||||
{
|
||||
IPropertyType propertyType = propertyTypes[propertyTypeIndex];
|
||||
try
|
||||
{
|
||||
_logger.LogInformation("- starting property type: {propertyTypeName} (id: {propertyTypeId}, alias: {propertyTypeAlias})...", propertyType.Name, propertyType.Id, propertyType.Alias);
|
||||
_logger.LogInformation(
|
||||
"- starting property type {propertyTypeIndex}/{propertyTypeCount} : {propertyTypeName} (id: {propertyTypeId}, alias: {propertyTypeAlias})...",
|
||||
propertyTypeIndex+1,
|
||||
propertyTypeCount,
|
||||
propertyType.Name, propertyType.Id, propertyType.Alias);
|
||||
IDataType dataType = _dataTypeService.GetAsync(propertyType.DataTypeKey).GetAwaiter().GetResult()
|
||||
?? throw new InvalidOperationException("The data type could not be fetched.");
|
||||
|
||||
@@ -113,7 +124,8 @@ public abstract class ConvertBlockEditorPropertiesBase : MigrationBase
|
||||
}
|
||||
|
||||
IDataValueEditor valueEditor = dataType.Editor?.GetValueEditor()
|
||||
?? throw new InvalidOperationException("The data type value editor could not be fetched.");
|
||||
?? throw new InvalidOperationException(
|
||||
"The data type value editor could not be fetched.");
|
||||
|
||||
Sql<ISqlContext> sql = Sql()
|
||||
.Select<PropertyDataDto>()
|
||||
@@ -132,15 +144,24 @@ public abstract class ConvertBlockEditorPropertiesBase : MigrationBase
|
||||
|
||||
var progress = 0;
|
||||
|
||||
ExecutionContext.SuppressFlow();
|
||||
Parallel.ForEach(updateBatch, update =>
|
||||
Parallel.ForEachAsync(updateBatch, async (update, token) =>
|
||||
{
|
||||
using UmbracoContextReference umbracoContextReference = _umbracoContextFactory.EnsureUmbracoContext();
|
||||
//Foreach here, but we need to suppress the flow before each task, but not the actuall await of the task
|
||||
Task task;
|
||||
using (ExecutionContext.SuppressFlow())
|
||||
{
|
||||
task = Task.Run(() =>
|
||||
{
|
||||
using ICoreScope scope = _coreScopeProvider.CreateCoreScope();
|
||||
scope.Complete();
|
||||
using UmbracoContextReference umbracoContextReference =
|
||||
_umbracoContextFactory.EnsureUmbracoContext();
|
||||
|
||||
progress++;
|
||||
if (progress % 100 == 0)
|
||||
{
|
||||
_logger.LogInformation(" - finíshed {progress} of {total} properties", progress, updateBatch.Count);
|
||||
_logger.LogInformation(" - finíshed {progress} of {total} properties", progress,
|
||||
updateBatch.Count);
|
||||
}
|
||||
|
||||
PropertyDataDto propertyDataDto = update.Poco;
|
||||
@@ -148,7 +169,8 @@ public abstract class ConvertBlockEditorPropertiesBase : MigrationBase
|
||||
// NOTE: some old property data DTOs can have variance defined, even if the property type no longer varies
|
||||
var culture = propertyType.VariesByCulture()
|
||||
&& propertyDataDto.LanguageId.HasValue
|
||||
&& languagesById.TryGetValue(propertyDataDto.LanguageId.Value, out ILanguage? language)
|
||||
&& languagesById.TryGetValue(propertyDataDto.LanguageId.Value,
|
||||
out ILanguage? language)
|
||||
? language.IsoCode
|
||||
: null;
|
||||
|
||||
@@ -211,6 +233,7 @@ public abstract class ConvertBlockEditorPropertiesBase : MigrationBase
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException();
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -233,8 +256,11 @@ public abstract class ConvertBlockEditorPropertiesBase : MigrationBase
|
||||
stringValue = UpdateDatabaseValue(stringValue);
|
||||
|
||||
propertyDataDto.TextValue = stringValue;
|
||||
});
|
||||
ExecutionContext.RestoreFlow();
|
||||
}, token);
|
||||
}
|
||||
|
||||
await task;
|
||||
}).GetAwaiter().GetResult();
|
||||
|
||||
updateBatch.RemoveAll(updatesToSkip.Contains);
|
||||
|
||||
@@ -248,7 +274,8 @@ public abstract class ConvertBlockEditorPropertiesBase : MigrationBase
|
||||
var result = Database.UpdateBatch(updateBatch, new BatchOptions { BatchSize = 100 });
|
||||
if (result != updateBatch.Count)
|
||||
{
|
||||
throw new InvalidOperationException($"The database batch update was supposed to update {updateBatch.Count} property DTO entries, but it updated {result} entries.");
|
||||
throw new InvalidOperationException(
|
||||
$"The database batch update was supposed to update {updateBatch.Count} property DTO entries, but it updated {result} entries.");
|
||||
}
|
||||
|
||||
_logger.LogDebug(
|
||||
|
||||
@@ -2,6 +2,7 @@ using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Umbraco.Cms.Core;
|
||||
using Umbraco.Cms.Core.Models.Blocks;
|
||||
using Umbraco.Cms.Core.Scoping;
|
||||
using Umbraco.Cms.Core.Serialization;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
using Umbraco.Cms.Core.Web;
|
||||
@@ -19,8 +20,9 @@ public class ConvertBlockGridEditorProperties : ConvertBlockEditorPropertiesBase
|
||||
IJsonSerializer jsonSerializer,
|
||||
IUmbracoContextFactory umbracoContextFactory,
|
||||
ILanguageService languageService,
|
||||
IOptions<ConvertBlockEditorPropertiesOptions> options)
|
||||
: base(context, logger, contentTypeService, dataTypeService, jsonSerializer, umbracoContextFactory, languageService)
|
||||
IOptions<ConvertBlockEditorPropertiesOptions> options,
|
||||
ICoreScopeProvider coreScopeProvider)
|
||||
: base(context, logger, contentTypeService, dataTypeService, jsonSerializer, umbracoContextFactory, languageService, coreScopeProvider)
|
||||
=> SkipMigration = options.Value.SkipBlockGridEditors;
|
||||
|
||||
protected override IEnumerable<string> PropertyEditorAliases
|
||||
@@ -40,8 +42,9 @@ public class ConvertBlockGridEditorProperties : ConvertBlockEditorPropertiesBase
|
||||
IDataTypeService dataTypeService,
|
||||
IJsonSerializer jsonSerializer,
|
||||
IUmbracoContextFactory umbracoContextFactory,
|
||||
ILanguageService languageService)
|
||||
: base(context, logger, contentTypeService, dataTypeService, jsonSerializer, umbracoContextFactory, languageService)
|
||||
ILanguageService languageService,
|
||||
ICoreScopeProvider coreScopeProvider)
|
||||
: base(context, logger, contentTypeService, dataTypeService, jsonSerializer, umbracoContextFactory, languageService, coreScopeProvider)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Umbraco.Cms.Core;
|
||||
using Umbraco.Cms.Core.Models.Blocks;
|
||||
using Umbraco.Cms.Core.Scoping;
|
||||
using Umbraco.Cms.Core.Serialization;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
using Umbraco.Cms.Core.Web;
|
||||
@@ -19,8 +20,9 @@ public class ConvertBlockListEditorProperties : ConvertBlockEditorPropertiesBase
|
||||
IJsonSerializer jsonSerializer,
|
||||
IUmbracoContextFactory umbracoContextFactory,
|
||||
ILanguageService languageService,
|
||||
IOptions<ConvertBlockEditorPropertiesOptions> options)
|
||||
: base(context, logger, contentTypeService, dataTypeService, jsonSerializer, umbracoContextFactory, languageService)
|
||||
IOptions<ConvertBlockEditorPropertiesOptions> options,
|
||||
ICoreScopeProvider coreScopeProvider)
|
||||
: base(context, logger, contentTypeService, dataTypeService, jsonSerializer, umbracoContextFactory, languageService, coreScopeProvider)
|
||||
=> SkipMigration = options.Value.SkipBlockListEditors;
|
||||
|
||||
protected override IEnumerable<string> PropertyEditorAliases
|
||||
@@ -40,8 +42,9 @@ public class ConvertBlockListEditorProperties : ConvertBlockEditorPropertiesBase
|
||||
IDataTypeService dataTypeService,
|
||||
IJsonSerializer jsonSerializer,
|
||||
IUmbracoContextFactory umbracoContextFactory,
|
||||
ILanguageService languageService)
|
||||
: base(context, logger, contentTypeService, dataTypeService, jsonSerializer, umbracoContextFactory, languageService)
|
||||
ILanguageService languageService,
|
||||
ICoreScopeProvider coreScopeProvider)
|
||||
: base(context, logger, contentTypeService, dataTypeService, jsonSerializer, umbracoContextFactory, languageService, coreScopeProvider)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ using Microsoft.Extensions.Options;
|
||||
using Umbraco.Cms.Core;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
using Umbraco.Cms.Core.PropertyEditors;
|
||||
using Umbraco.Cms.Core.Scoping;
|
||||
using Umbraco.Cms.Core.Serialization;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
using Umbraco.Cms.Core.Web;
|
||||
@@ -21,8 +22,9 @@ public partial class ConvertRichTextEditorProperties : ConvertBlockEditorPropert
|
||||
IJsonSerializer jsonSerializer,
|
||||
IUmbracoContextFactory umbracoContextFactory,
|
||||
ILanguageService languageService,
|
||||
IOptions<ConvertBlockEditorPropertiesOptions> options)
|
||||
: base(context, logger, contentTypeService, dataTypeService, jsonSerializer, umbracoContextFactory, languageService)
|
||||
IOptions<ConvertBlockEditorPropertiesOptions> options,
|
||||
ICoreScopeProvider coreScopeProvider)
|
||||
: base(context, logger, contentTypeService, dataTypeService, jsonSerializer, umbracoContextFactory, languageService, coreScopeProvider)
|
||||
=> SkipMigration = options.Value.SkipRichTextEditors;
|
||||
|
||||
protected override IEnumerable<string> PropertyEditorAliases
|
||||
@@ -60,8 +62,9 @@ public partial class ConvertRichTextEditorProperties : ConvertBlockEditorPropert
|
||||
IDataTypeService dataTypeService,
|
||||
IJsonSerializer jsonSerializer,
|
||||
IUmbracoContextFactory umbracoContextFactory,
|
||||
ILanguageService languageService)
|
||||
: base(context, logger, contentTypeService, dataTypeService, jsonSerializer, umbracoContextFactory, languageService)
|
||||
ILanguageService languageService,
|
||||
ICoreScopeProvider coreScopeProvider)
|
||||
: base(context, logger, contentTypeService, dataTypeService, jsonSerializer, umbracoContextFactory, languageService, coreScopeProvider)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
@@ -199,6 +199,10 @@ public class CoreRuntime : IRuntime
|
||||
break;
|
||||
}
|
||||
|
||||
//
|
||||
var postRuntimePremigrationsUpgradeNotification = new PostRuntimePremigrationsUpgradeNotification();
|
||||
await _eventAggregator.PublishAsync(postRuntimePremigrationsUpgradeNotification, cancellationToken);
|
||||
|
||||
// If level is Run and reason is UpgradeMigrations, that means we need to perform an unattended upgrade
|
||||
var unattendedUpgradeNotification = new RuntimeUnattendedUpgradeNotification();
|
||||
await _eventAggregator.PublishAsync(unattendedUpgradeNotification, cancellationToken);
|
||||
|
||||
@@ -5,35 +5,46 @@ namespace Umbraco.Cms.Infrastructure.Scoping;
|
||||
|
||||
internal class AmbientScopeContextStack : IAmbientScopeContextStack
|
||||
{
|
||||
private static Lock _lock = new();
|
||||
private static AsyncLocal<ConcurrentStack<IScopeContext>> _stack = new();
|
||||
|
||||
public IScopeContext? AmbientContext
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_stack.Value?.TryPeek(out IScopeContext? ambientContext) ?? false)
|
||||
lock (_lock)
|
||||
{
|
||||
return ambientContext;
|
||||
if (_stack.Value?.TryPeek(out IScopeContext? ambientContext) ?? false)
|
||||
{
|
||||
return ambientContext;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public IScopeContext Pop()
|
||||
{
|
||||
if (_stack.Value?.TryPop(out IScopeContext? ambientContext) ?? false)
|
||||
lock (_lock)
|
||||
{
|
||||
return ambientContext;
|
||||
}
|
||||
if (_stack.Value?.TryPop(out IScopeContext? ambientContext) ?? false)
|
||||
{
|
||||
return ambientContext;
|
||||
}
|
||||
|
||||
throw new InvalidOperationException("No AmbientContext was found.");
|
||||
throw new InvalidOperationException("No AmbientContext was found.");
|
||||
}
|
||||
}
|
||||
|
||||
public void Push(IScopeContext scope)
|
||||
{
|
||||
_stack.Value ??= new ConcurrentStack<IScopeContext>();
|
||||
lock (_lock)
|
||||
{
|
||||
_stack.Value ??= new ConcurrentStack<IScopeContext>();
|
||||
|
||||
_stack.Value.Push(scope);
|
||||
_stack.Value.Push(scope);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,36 +4,46 @@ namespace Umbraco.Cms.Infrastructure.Scoping
|
||||
{
|
||||
internal class AmbientScopeStack : IAmbientScopeStack
|
||||
{
|
||||
private static Lock _lock = new();
|
||||
private static AsyncLocal<ConcurrentStack<IScope>> _stack = new ();
|
||||
|
||||
public IScope? AmbientScope
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_stack.Value?.TryPeek(out IScope? ambientScope) ?? false)
|
||||
lock (_lock)
|
||||
{
|
||||
return ambientScope;
|
||||
}
|
||||
if (_stack.Value?.TryPeek(out IScope? ambientScope) ?? false)
|
||||
{
|
||||
return ambientScope;
|
||||
}
|
||||
|
||||
return null;
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public IScope Pop()
|
||||
{
|
||||
if (_stack.Value?.TryPop(out IScope? ambientScope) ?? false)
|
||||
lock (_lock)
|
||||
{
|
||||
return ambientScope;
|
||||
}
|
||||
|
||||
throw new InvalidOperationException("No AmbientScope was found.");
|
||||
|
||||
if (_stack.Value?.TryPop(out IScope? ambientScope) ?? false)
|
||||
{
|
||||
return ambientScope;
|
||||
}
|
||||
|
||||
throw new InvalidOperationException("No AmbientScope was found.");
|
||||
}
|
||||
}
|
||||
|
||||
public void Push(IScope scope)
|
||||
{
|
||||
_stack.Value ??= new ConcurrentStack<IScope>();
|
||||
|
||||
_stack.Value.Push(scope);
|
||||
lock (_lock)
|
||||
{
|
||||
(_stack.Value ??= new ConcurrentStack<IScope>()).Push(scope);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -367,7 +367,7 @@ namespace Umbraco.Cms.Infrastructure.Scoping
|
||||
$"The {nameof(Scope)} {InstanceId} being disposed is not the Ambient {nameof(Scope)} {_scopeProvider.AmbientScope?.InstanceId.ToString() ?? "NULL"}. This typically indicates that a child {nameof(Scope)} was not disposed, or flowed to a child thread that was not awaited, or concurrent threads are accessing the same {nameof(Scope)} (Ambient context) which is not supported. If using Task.Run (or similar) as a fire and forget tasks or to run threads in parallel you must suppress execution context flow with ExecutionContext.SuppressFlow() and ExecutionContext.RestoreFlow().";
|
||||
|
||||
#if DEBUG_SCOPES
|
||||
Scope ambient = _scopeProvider.AmbientScope;
|
||||
Scope? ambient = _scopeProvider.AmbientScope;
|
||||
_logger.LogWarning("Dispose error (" + (ambient == null ? "no" : "other") + " ambient)");
|
||||
if (ambient == null)
|
||||
{
|
||||
@@ -377,8 +377,8 @@ namespace Umbraco.Cms.Infrastructure.Scoping
|
||||
ScopeInfo ambientInfos = _scopeProvider.GetScopeInfo(ambient);
|
||||
ScopeInfo disposeInfos = _scopeProvider.GetScopeInfo(this);
|
||||
throw new InvalidOperationException($"{failedMessage} (see ctor stack traces).\r\n"
|
||||
+ "- ambient ->\r\n" + ambientInfos.ToString() + "\r\n"
|
||||
+ "- dispose ->\r\n" + disposeInfos.ToString() + "\r\n");
|
||||
+ "- ambient ->\r\n" + ambientInfos + "\r\n"
|
||||
+ "- dispose ->\r\n" + disposeInfos + "\r\n");
|
||||
#else
|
||||
throw new InvalidOperationException(failedMessage);
|
||||
#endif
|
||||
|
||||
@@ -267,7 +267,7 @@ namespace Umbraco.Cms.Infrastructure.Scoping
|
||||
{
|
||||
lock (s_staticScopeInfosLock)
|
||||
{
|
||||
return s_staticScopeInfos.TryGetValue(scope, out ScopeInfo scopeInfo) ? scopeInfo : null;
|
||||
return s_staticScopeInfos.TryGetValue(scope, out ScopeInfo? scopeInfo) ? scopeInfo : null!;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -302,7 +302,7 @@ namespace Umbraco.Cms.Infrastructure.Scoping
|
||||
|
||||
lock (s_staticScopeInfosLock)
|
||||
{
|
||||
if (s_staticScopeInfos.TryGetValue(scope, out ScopeInfo info) == false)
|
||||
if (s_staticScopeInfos.TryGetValue(scope, out ScopeInfo? info) == false)
|
||||
{
|
||||
info = null;
|
||||
}
|
||||
@@ -327,7 +327,7 @@ namespace Umbraco.Cms.Infrastructure.Scoping
|
||||
|
||||
sb.Append(s.InstanceId.ToString("N").Substring(0, 8));
|
||||
var ss = s as Scope;
|
||||
s = ss?.ParentScope;
|
||||
s = ss?.ParentScope!;
|
||||
}
|
||||
|
||||
_logger.LogTrace("Register " + (context ?? "null") + " context " + sb);
|
||||
@@ -339,7 +339,7 @@ namespace Umbraco.Cms.Infrastructure.Scoping
|
||||
|
||||
_logger.LogTrace("At:\r\n" + Head(Environment.StackTrace, 16));
|
||||
|
||||
info.Context = context;
|
||||
info.Context = context!;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -431,15 +431,15 @@ namespace Umbraco.Cms.Infrastructure.Scoping
|
||||
public IScope Scope { get; } // the scope itself
|
||||
|
||||
// the scope's parent identifier
|
||||
public Guid Parent => ((Scope)Scope).ParentScope == null ? Guid.Empty : ((Scope)Scope).ParentScope.InstanceId;
|
||||
public Guid Parent => ((Scope)Scope).ParentScope == null ? Guid.Empty : ((Scope)Scope).ParentScope!.InstanceId;
|
||||
|
||||
public DateTime Created { get; } // the date time the scope was created
|
||||
public bool Disposed { get; set; } // whether the scope has been disposed already
|
||||
public string Context { get; set; } // the current 'context' that contains the scope (null, "http" or "lcc")
|
||||
public string Context { get; set; }= string.Empty; // the current 'context' that contains the scope (null, "http" or "lcc")
|
||||
|
||||
public string CtorStack { get; } // the stacktrace of the scope ctor
|
||||
//public string DisposedStack { get; set; } // the stacktrace when disposed
|
||||
public string NullStack { get; set; } // the stacktrace when the 'context' that contains the scope went null
|
||||
public string NullStack { get; set; } = string.Empty; // the stacktrace when the 'context' that contains the scope went null
|
||||
|
||||
public override string ToString() => new StringBuilder()
|
||||
.AppendLine("ScopeInfo:")
|
||||
|
||||
@@ -32,9 +32,7 @@ internal class SeedingNotificationHandler : INotificationAsyncHandler<UmbracoApp
|
||||
return;
|
||||
}
|
||||
|
||||
await Task.WhenAll(
|
||||
_documentCacheService.SeedAsync(cancellationToken),
|
||||
_mediaCacheService.SeedAsync(cancellationToken)
|
||||
);
|
||||
await _documentCacheService.SeedAsync(cancellationToken);
|
||||
await _mediaCacheService.SeedAsync(cancellationToken);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -72,15 +72,18 @@ internal sealed class DocumentCacheService : IDocumentCacheService
|
||||
|
||||
public async Task<IPublishedContent?> GetByKeyAsync(Guid key, bool? preview = null)
|
||||
{
|
||||
using ICoreScope scope = _scopeProvider.CreateCoreScope();
|
||||
|
||||
bool calculatedPreview = preview ?? GetPreview();
|
||||
|
||||
ContentCacheNode? contentCacheNode = await _hybridCache.GetOrCreateAsync(
|
||||
GetCacheKey(key, calculatedPreview), // Unique key to the cache entry
|
||||
async cancel => await _databaseCacheRepository.GetContentSourceAsync(key, calculatedPreview));
|
||||
async cancel =>
|
||||
{
|
||||
using ICoreScope scope = _scopeProvider.CreateCoreScope();
|
||||
ContentCacheNode? contentCacheNode = await _databaseCacheRepository.GetContentSourceAsync(key, calculatedPreview);
|
||||
scope.Complete();
|
||||
return contentCacheNode;
|
||||
});
|
||||
|
||||
scope.Complete();
|
||||
return contentCacheNode is null ? null : _publishedContentFactory.ToIPublishedContent(contentCacheNode, calculatedPreview).CreateModel(_publishedModelFactory);
|
||||
}
|
||||
|
||||
@@ -99,11 +102,16 @@ internal sealed class DocumentCacheService : IDocumentCacheService
|
||||
|
||||
bool calculatedPreview = preview ?? GetPreview();
|
||||
|
||||
using ICoreScope scope = _scopeProvider.CreateCoreScope();
|
||||
ContentCacheNode? contentCacheNode = await _hybridCache.GetOrCreateAsync(
|
||||
GetCacheKey(keyAttempt.Result, calculatedPreview), // Unique key to the cache entry
|
||||
async cancel => await _databaseCacheRepository.GetContentSourceAsync(id, calculatedPreview));
|
||||
scope.Complete();
|
||||
async cancel =>
|
||||
{
|
||||
using ICoreScope scope = _scopeProvider.CreateCoreScope();
|
||||
ContentCacheNode? contentCacheNode = await _databaseCacheRepository.GetContentSourceAsync(id, calculatedPreview);
|
||||
scope.Complete();
|
||||
return contentCacheNode;
|
||||
});
|
||||
|
||||
return contentCacheNode is null ? null : _publishedContentFactory.ToIPublishedContent(contentCacheNode, calculatedPreview).CreateModel(_publishedModelFactory);;
|
||||
}
|
||||
|
||||
@@ -120,8 +128,6 @@ internal sealed class DocumentCacheService : IDocumentCacheService
|
||||
|
||||
public async Task SeedAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
using ICoreScope scope = _scopeProvider.CreateCoreScope();
|
||||
|
||||
foreach (Guid key in SeedKeys)
|
||||
{
|
||||
if(cancellationToken.IsCancellationRequested)
|
||||
@@ -131,31 +137,32 @@ internal sealed class DocumentCacheService : IDocumentCacheService
|
||||
|
||||
var cacheKey = GetCacheKey(key, false);
|
||||
|
||||
// We'll use GetOrCreateAsync because it may be in the second level cache, in which case we don't have to re-seed.
|
||||
ContentCacheNode? cachedValue = await _hybridCache.GetOrCreateAsync<ContentCacheNode?>(
|
||||
cacheKey,
|
||||
async cancel =>
|
||||
{
|
||||
ContentCacheNode? cacheNode = await _databaseCacheRepository.GetContentSourceAsync(key, false);
|
||||
|
||||
// We don't want to seed drafts
|
||||
if (cacheNode is null || cacheNode.IsDraft)
|
||||
// We'll use GetOrCreateAsync because it may be in the second level cache, in which case we don't have to re-seed.
|
||||
ContentCacheNode? cachedValue = await _hybridCache.GetOrCreateAsync<ContentCacheNode?>(
|
||||
cacheKey,
|
||||
async cancel =>
|
||||
{
|
||||
return null;
|
||||
}
|
||||
using ICoreScope scope = _scopeProvider.CreateCoreScope();
|
||||
|
||||
return cacheNode;
|
||||
},
|
||||
GetSeedEntryOptions());
|
||||
ContentCacheNode? cacheNode = await _databaseCacheRepository.GetContentSourceAsync(key, false);
|
||||
|
||||
// If the value is null, it's likely because
|
||||
if (cachedValue is null)
|
||||
scope.Complete();
|
||||
// We don't want to seed drafts
|
||||
if (cacheNode is null || cacheNode.IsDraft)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return cacheNode;
|
||||
},
|
||||
GetSeedEntryOptions());
|
||||
|
||||
// If the value is null, it's likely because
|
||||
if (cachedValue is null)
|
||||
{
|
||||
await _hybridCache.RemoveAsync(cacheKey);
|
||||
}
|
||||
}
|
||||
|
||||
scope.Complete();
|
||||
}
|
||||
|
||||
private HybridCacheEntryOptions GetSeedEntryOptions() => new()
|
||||
@@ -257,6 +264,7 @@ internal sealed class DocumentCacheService : IDocumentCacheService
|
||||
using ICoreScope scope = _scopeProvider.CreateCoreScope();
|
||||
_databaseCacheRepository.Rebuild(contentTypeIds.ToList());
|
||||
IEnumerable<ContentCacheNode> contentByContentTypeKey = _databaseCacheRepository.GetContentByContentTypeKey(contentTypeIds.Select(x => _idKeyMap.GetKeyForId(x, UmbracoObjectTypes.DocumentType).Result), ContentCacheDataSerializerEntityType.Document);
|
||||
scope.Complete();
|
||||
|
||||
foreach (ContentCacheNode content in contentByContentTypeKey)
|
||||
{
|
||||
@@ -268,6 +276,6 @@ internal sealed class DocumentCacheService : IDocumentCacheService
|
||||
}
|
||||
}
|
||||
|
||||
scope.Complete();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -75,13 +75,16 @@ internal class MediaCacheService : IMediaCacheService
|
||||
return null;
|
||||
}
|
||||
|
||||
using ICoreScope scope = _scopeProvider.CreateCoreScope();
|
||||
|
||||
ContentCacheNode? contentCacheNode = await _hybridCache.GetOrCreateAsync(
|
||||
$"{key}", // Unique key to the cache entry
|
||||
async cancel => await _databaseCacheRepository.GetMediaSourceAsync(idAttempt.Result));
|
||||
async cancel =>
|
||||
{
|
||||
using ICoreScope scope = _scopeProvider.CreateCoreScope();
|
||||
ContentCacheNode? mediaCacheNode = await _databaseCacheRepository.GetMediaSourceAsync(idAttempt.Result);
|
||||
scope.Complete();
|
||||
return mediaCacheNode;
|
||||
});
|
||||
|
||||
scope.Complete();
|
||||
return contentCacheNode is null ? null : _publishedContentFactory.ToIPublishedMedia(contentCacheNode).CreateModel(_publishedModelFactory);
|
||||
}
|
||||
|
||||
@@ -93,11 +96,16 @@ internal class MediaCacheService : IMediaCacheService
|
||||
return null;
|
||||
}
|
||||
|
||||
using ICoreScope scope = _scopeProvider.CreateCoreScope();
|
||||
ContentCacheNode? contentCacheNode = await _hybridCache.GetOrCreateAsync(
|
||||
$"{keyAttempt.Result}", // Unique key to the cache entry
|
||||
async cancel => await _databaseCacheRepository.GetMediaSourceAsync(id));
|
||||
scope.Complete();
|
||||
async cancel =>
|
||||
{
|
||||
using ICoreScope scope = _scopeProvider.CreateCoreScope();
|
||||
ContentCacheNode? mediaCacheNode = await _databaseCacheRepository.GetMediaSourceAsync(id);
|
||||
scope.Complete();
|
||||
return mediaCacheNode;
|
||||
});
|
||||
|
||||
return contentCacheNode is null ? null : _publishedContentFactory.ToIPublishedMedia(contentCacheNode).CreateModel(_publishedModelFactory);
|
||||
}
|
||||
|
||||
@@ -144,7 +152,6 @@ internal class MediaCacheService : IMediaCacheService
|
||||
|
||||
public async Task SeedAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
using ICoreScope scope = _scopeProvider.CreateCoreScope();
|
||||
|
||||
foreach (Guid key in SeedKeys)
|
||||
{
|
||||
@@ -157,7 +164,13 @@ internal class MediaCacheService : IMediaCacheService
|
||||
|
||||
ContentCacheNode? cachedValue = await _hybridCache.GetOrCreateAsync<ContentCacheNode?>(
|
||||
cacheKey,
|
||||
async cancel => await _databaseCacheRepository.GetMediaSourceAsync(key),
|
||||
async cancel =>
|
||||
{
|
||||
using ICoreScope scope = _scopeProvider.CreateCoreScope();
|
||||
ContentCacheNode? mediaCacheNode = await _databaseCacheRepository.GetMediaSourceAsync(key);
|
||||
scope.Complete();
|
||||
return mediaCacheNode;
|
||||
},
|
||||
GetSeedEntryOptions());
|
||||
|
||||
if (cachedValue is null)
|
||||
@@ -165,8 +178,6 @@ internal class MediaCacheService : IMediaCacheService
|
||||
await _hybridCache.RemoveAsync(cacheKey);
|
||||
}
|
||||
}
|
||||
|
||||
scope.Complete();
|
||||
}
|
||||
|
||||
public void Rebuild(IReadOnlyCollection<int> contentTypeIds)
|
||||
|
||||
@@ -194,8 +194,8 @@ public static partial class UmbracoBuilderExtensions
|
||||
builder.Services.AddSingleton(RecurringBackgroundJobHostedService.CreateHostedServiceFactory);
|
||||
builder.Services.AddHostedService<RecurringBackgroundJobHostedServiceRunner>();
|
||||
builder.Services.AddHostedService<QueuedHostedService>();
|
||||
builder.Services.AddHostedService<NavigationInitializationHostedService>();
|
||||
builder.Services.AddHostedService<PublishStatusInitializationHostedService>();
|
||||
builder.AddNotificationAsyncHandler<PostRuntimePremigrationsUpgradeNotification, NavigationInitializationNotificationHandler>();
|
||||
builder.AddNotificationAsyncHandler<PostRuntimePremigrationsUpgradeNotification, PublishStatusInitializationNotificationHandler>();
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
@@ -172,7 +172,7 @@ public abstract class UmbracoIntegrationTest : UmbracoIntegrationTestBase
|
||||
.AddCoreMappingProfiles();
|
||||
}
|
||||
|
||||
services.RemoveAll(x=>x.ImplementationType == typeof(DocumentUrlServiceInitializer));
|
||||
services.RemoveAll(x=>x.ImplementationType == typeof(DocumentUrlServiceInitializerNotificationHandler));
|
||||
services.AddSignalR();
|
||||
services.AddMvc();
|
||||
|
||||
|
||||
@@ -35,7 +35,7 @@ public abstract class UmbracoIntegrationTestWithContent : UmbracoIntegrationTest
|
||||
protected ContentType ContentType { get; private set; }
|
||||
|
||||
[SetUp]
|
||||
public void Setup() => CreateTestData();
|
||||
public virtual void Setup() => CreateTestData();
|
||||
|
||||
public virtual void CreateTestData()
|
||||
{
|
||||
|
||||
@@ -1,23 +1,13 @@
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using NUnit.Framework;
|
||||
using Umbraco.Cms.Core.Cache;
|
||||
using Umbraco.Cms.Core.Handlers;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
using Umbraco.Cms.Core.Models.ContentEditing;
|
||||
using Umbraco.Cms.Core.Notifications;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
using Umbraco.Cms.Core.Sync;
|
||||
using Umbraco.Cms.Infrastructure.DependencyInjection;
|
||||
using Umbraco.Cms.Infrastructure.Examine;
|
||||
using Umbraco.Cms.Infrastructure.Examine.DependencyInjection;
|
||||
using Umbraco.Cms.Infrastructure.HostedServices;
|
||||
using Umbraco.Cms.Infrastructure.Search;
|
||||
using Umbraco.Cms.Tests.Common.Attributes;
|
||||
using Umbraco.Cms.Tests.Common.Builders;
|
||||
using Umbraco.Cms.Tests.Common.Testing;
|
||||
using Umbraco.Cms.Tests.Integration.Testing;
|
||||
using Umbraco.Cms.Tests.Integration.Umbraco.Infrastructure.Scoping;
|
||||
using Umbraco.Cms.Tests.Integration.Umbraco.Infrastructure.Services;
|
||||
|
||||
namespace Umbraco.Cms.Tests.Integration.Umbraco.Core.Services;
|
||||
|
||||
@@ -33,12 +23,15 @@ public class DocumentUrlServiceTest : UmbracoIntegrationTestWithContent
|
||||
builder.Services.AddUnique<IServerMessenger, ScopedRepositoryTests.LocalServerMessenger>();
|
||||
builder.AddNotificationHandler<ContentTreeChangeNotification, ContentTreeChangeDistributedCacheNotificationHandler>();
|
||||
|
||||
builder.Services.AddHostedService<DocumentUrlServiceInitializer>();
|
||||
|
||||
builder.Services.AddNotificationAsyncHandler<UmbracoApplicationStartingNotification, DocumentUrlServiceInitializerNotificationHandler>();
|
||||
}
|
||||
|
||||
|
||||
|
||||
public override void Setup()
|
||||
{
|
||||
DocumentUrlService.InitAsync(false, CancellationToken.None).GetAwaiter().GetResult();
|
||||
base.Setup();
|
||||
}
|
||||
//
|
||||
// [Test]
|
||||
// [LongRunning]
|
||||
|
||||
@@ -29,8 +29,14 @@ public class DocumentUrlServiceTest_HideTopLevel_False : UmbracoIntegrationTestW
|
||||
builder.Services.AddUnique<IServerMessenger, ScopedRepositoryTests.LocalServerMessenger>();
|
||||
builder.AddNotificationHandler<ContentTreeChangeNotification, ContentTreeChangeDistributedCacheNotificationHandler>();
|
||||
|
||||
builder.Services.AddHostedService<DocumentUrlServiceInitializer>();
|
||||
}
|
||||
|
||||
public override void Setup()
|
||||
{
|
||||
DocumentUrlService.InitAsync(false, CancellationToken.None).GetAwaiter().GetResult();
|
||||
base.Setup();
|
||||
}
|
||||
|
||||
[Test]
|
||||
[TestCase("/textpage/", "en-US", true, ExpectedResult = TextpageKey)]
|
||||
[TestCase("/textpage/text-page-1", "en-US", true, ExpectedResult = SubPageKey)]
|
||||
|
||||
@@ -5,13 +5,11 @@ using Microsoft.Extensions.DependencyInjection;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using Umbraco.Cms.Core;
|
||||
using Umbraco.Cms.Core.Cache;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
using Umbraco.Cms.Core.Models.ContentEditing;
|
||||
using Umbraco.Cms.Core.Notifications;
|
||||
using Umbraco.Cms.Core.Security;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
using Umbraco.Cms.Core.Sync;
|
||||
using Umbraco.Cms.Infrastructure.Examine;
|
||||
using Umbraco.Cms.Infrastructure.Examine.DependencyInjection;
|
||||
using Umbraco.Cms.Infrastructure.HostedServices;
|
||||
@@ -19,8 +17,6 @@ using Umbraco.Cms.Infrastructure.Search;
|
||||
using Umbraco.Cms.Tests.Common.Builders;
|
||||
using Umbraco.Cms.Tests.Common.Builders.Extensions;
|
||||
using Umbraco.Cms.Tests.Common.Testing;
|
||||
using Umbraco.Cms.Tests.Integration.Umbraco.Infrastructure.Scoping;
|
||||
using Umbraco.Cms.Tests.Integration.Umbraco.Infrastructure.Services;
|
||||
using Umbraco.Cms.Web.Common.Security;
|
||||
|
||||
namespace Umbraco.Cms.Tests.Integration.Umbraco.Examine.Lucene.UmbracoExamine;
|
||||
@@ -37,7 +33,9 @@ public class BackOfficeExamineSearcherTests : ExamineBaseTest
|
||||
var httpContext = new DefaultHttpContext();
|
||||
httpContext.RequestServices = Services;
|
||||
Mock.Get(TestHelper.GetHttpContextAccessor()).Setup(x => x.HttpContext).Returns(httpContext);
|
||||
}
|
||||
|
||||
DocumentUrlService.InitAsync(false, CancellationToken.None).GetAwaiter().GetResult();
|
||||
}
|
||||
|
||||
[TearDown]
|
||||
public void TearDown()
|
||||
@@ -48,6 +46,7 @@ public class BackOfficeExamineSearcherTests : ExamineBaseTest
|
||||
Services.DisposeIfDisposable();
|
||||
}
|
||||
|
||||
private IDocumentUrlService DocumentUrlService => GetRequiredService<IDocumentUrlService>();
|
||||
private IBackOfficeExamineSearcher BackOfficeExamineSearcher => GetRequiredService<IBackOfficeExamineSearcher>();
|
||||
|
||||
private IContentTypeService ContentTypeService => GetRequiredService<IContentTypeService>();
|
||||
|
||||
@@ -44,9 +44,8 @@ public abstract class ExamineBaseTest : UmbracoIntegrationTest
|
||||
builder
|
||||
.AddNotificationHandler<ContentTreeChangeNotification,
|
||||
ContentTreeChangeDistributedCacheNotificationHandler>();
|
||||
builder.Services.AddHostedService<DocumentUrlServiceInitializer>();
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Used to create and manage a testable index
|
||||
/// </summary>
|
||||
|
||||
@@ -37,6 +37,8 @@ public class ExamineExternalIndexTests : ExamineBaseTest
|
||||
var httpContext = new DefaultHttpContext();
|
||||
httpContext.RequestServices = Services;
|
||||
Mock.Get(TestHelper.GetHttpContextAccessor()).Setup(x => x.HttpContext).Returns(httpContext);
|
||||
|
||||
DocumentUrlService.InitAsync(false, CancellationToken.None).GetAwaiter().GetResult();
|
||||
}
|
||||
|
||||
[TearDown]
|
||||
@@ -52,6 +54,7 @@ public class ExamineExternalIndexTests : ExamineBaseTest
|
||||
private IExamineExternalIndexSearcherTest ExamineExternalIndexSearcher =>
|
||||
GetRequiredService<IExamineExternalIndexSearcherTest>();
|
||||
|
||||
private IDocumentUrlService DocumentUrlService => GetRequiredService<IDocumentUrlService>();
|
||||
private IContentTypeService ContentTypeService => GetRequiredService<IContentTypeService>();
|
||||
|
||||
private ContentService ContentService => (ContentService)GetRequiredService<IContentService>();
|
||||
|
||||
@@ -3,6 +3,7 @@ using Examine;
|
||||
using Lucene.Net.Util;
|
||||
using NUnit.Framework;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
using Umbraco.Cms.Infrastructure.Examine;
|
||||
using Umbraco.Cms.Tests.Common.Attributes;
|
||||
using Umbraco.Cms.Tests.Common.Builders;
|
||||
@@ -18,6 +19,15 @@ namespace Umbraco.Cms.Tests.Integration.Umbraco.Examine.Lucene.UmbracoExamine;
|
||||
[UmbracoTest(Database = UmbracoTestOptions.Database.NewSchemaPerFixture)]
|
||||
public class IndexTest : ExamineBaseTest
|
||||
{
|
||||
private IDocumentUrlService DocumentUrlService => GetRequiredService<IDocumentUrlService>();
|
||||
|
||||
[SetUp]
|
||||
public void Setup()
|
||||
{
|
||||
DocumentUrlService.InitAsync(false, CancellationToken.None).GetAwaiter().GetResult();
|
||||
}
|
||||
|
||||
|
||||
[Test]
|
||||
[LongRunning]
|
||||
public void GivenValidationParentNode_WhenContentIndexedUnderDifferentParent_DocumentIsNotIndexed()
|
||||
|
||||
@@ -18,6 +18,15 @@ namespace Umbraco.Cms.Tests.Integration.Umbraco.Examine.Lucene.UmbracoExamine;
|
||||
[UmbracoTest(Database = UmbracoTestOptions.Database.NewSchemaPerTest, Logger = UmbracoTestOptions.Logger.Console)]
|
||||
public class SearchTests : ExamineBaseTest
|
||||
{
|
||||
private IDocumentUrlService DocumentUrlService => GetRequiredService<IDocumentUrlService>();
|
||||
|
||||
[SetUp]
|
||||
public void Setup()
|
||||
{
|
||||
DocumentUrlService.InitAsync(false, CancellationToken.None).GetAwaiter().GetResult();
|
||||
}
|
||||
|
||||
|
||||
[Test]
|
||||
[LongRunning]
|
||||
public void Test_Sort_Order_Sorting()
|
||||
|
||||
@@ -52,14 +52,14 @@ public class MigrationTests
|
||||
|
||||
public ISqlContext SqlContext { get; set; }
|
||||
|
||||
#if DEBUG_SCOPES
|
||||
public ScopeInfo GetScopeInfo(IScope scope)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
public IEnumerable<ScopeInfo> ScopeInfos => throw new NotImplementedException();
|
||||
#endif
|
||||
public IScope AmbientScope { get; }
|
||||
|
||||
#if DEBUG_SCOPES
|
||||
public IEnumerable<ScopeInfo> ScopeInfos => throw new NotImplementedException();
|
||||
|
||||
public ScopeInfo GetScopeInfo(IScope scope) => throw new NotImplementedException();
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
private class TestPlan : MigrationPlan
|
||||
|
||||
Reference in New Issue
Block a user