diff --git a/src/OrchardCore/OrchardCore.Infrastructure/Shell/ShellDescriptorManager.cs b/src/OrchardCore/OrchardCore.Infrastructure/Shell/ShellDescriptorManager.cs index a1f33d7ecbb..15c64682e42 100644 --- a/src/OrchardCore/OrchardCore.Infrastructure/Shell/ShellDescriptorManager.cs +++ b/src/OrchardCore/OrchardCore.Infrastructure/Shell/ShellDescriptorManager.cs @@ -2,16 +2,15 @@ using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; -using Dapper; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; +using OrchardCore.Data.Documents; using OrchardCore.Environment.Extensions; using OrchardCore.Environment.Shell.Configuration; using OrchardCore.Environment.Shell.Descriptor; using OrchardCore.Environment.Shell.Descriptor.Models; using OrchardCore.Modules; -using YesSql; namespace OrchardCore.Environment.Shell.Data.Descriptors { @@ -25,7 +24,7 @@ public class ShellDescriptorManager : IShellDescriptorManager private readonly IEnumerable _alwaysEnabledFeatures; private readonly IEnumerable _shellDescriptorManagerEventHandlers; private readonly IExtensionManager _extensionManager; - private readonly ISession _session; + private readonly IDocumentStore _documentStore; private readonly ILogger _logger; private ShellDescriptor _shellDescriptor; @@ -36,7 +35,7 @@ public ShellDescriptorManager( IEnumerable shellFeatures, IEnumerable shellDescriptorManagerEventHandlers, IExtensionManager extensionManager, - ISession session, + IDocumentStore documentStore, ILogger logger) { _shellSettings = shellSettings; @@ -44,51 +43,62 @@ public ShellDescriptorManager( _alwaysEnabledFeatures = shellFeatures.Where(f => f.AlwaysEnabled).ToArray(); _shellDescriptorManagerEventHandlers = shellDescriptorManagerEventHandlers; _extensionManager = extensionManager; - _session = session; + _documentStore = documentStore; _logger = logger; } public async Task GetShellDescriptorAsync() { - // Prevent multiple queries during the same request - if (_shellDescriptor == null) + if (_shellDescriptor != null) { - _shellDescriptor = await _session.Query().FirstOrDefaultAsync(); + return _shellDescriptor; + } + + (var cacheable, var shellDescriptor) = await _documentStore.GetOrCreateImmutableAsync(); + + if (shellDescriptor.SerialNumber == 0) + { + return null; + } - if (_shellDescriptor != null) + if (!cacheable) + { + // Clone ShellDescriptor + shellDescriptor = new ShellDescriptor { - var configuredFeatures = new ConfiguredFeatures(); - _shellConfiguration.Bind(configuredFeatures); + SerialNumber = shellDescriptor.SerialNumber, + Installed = new List(shellDescriptor.Installed), + }; + } - var features = _alwaysEnabledFeatures - .Concat(configuredFeatures.Features.Select(id => new ShellFeature(id) { AlwaysEnabled = true })) - .Concat(_shellDescriptor.Features) - .Distinct(); + // Init shell descriptor and load features + var configuredFeatures = new ConfiguredFeatures(); + _shellConfiguration.Bind(configuredFeatures); - var featureIds = features.Select(sf => sf.Id).ToArray(); + var features = _alwaysEnabledFeatures + .Concat(configuredFeatures.Features.Select(id => new ShellFeature(id) { AlwaysEnabled = true })) + .Concat(shellDescriptor.Features) + .Distinct(); - var missingDependencies = (await _extensionManager.LoadFeaturesAsync(featureIds)) - .Select(entry => entry.FeatureInfo.Id) - .Except(featureIds) - .Select(id => new ShellFeature(id)); + var featureIds = features.Select(sf => sf.Id).ToArray(); - _shellDescriptor.Features = features - .Concat(missingDependencies) - .ToList(); - } - } + var missingDependencies = (await _extensionManager.LoadFeaturesAsync(featureIds)) + .Select(entry => entry.FeatureInfo.Id) + .Except(featureIds) + .Select(id => new ShellFeature(id)); - return _shellDescriptor; + shellDescriptor.Features = features + .Concat(missingDependencies) + .ToList(); + + return _shellDescriptor = shellDescriptor; } public async Task UpdateShellDescriptorAsync(int priorSerialNumber, IEnumerable enabledFeatures) { - var shellDescriptorRecord = await GetShellDescriptorAsync(); - var serialNumber = shellDescriptorRecord == null - ? 0 - : shellDescriptorRecord.SerialNumber; + var shellDescriptor = await _documentStore.GetOrCreateMutableAsync(); - if (priorSerialNumber != serialNumber) + if (priorSerialNumber != shellDescriptor.SerialNumber) { throw new InvalidOperationException("Invalid serial number for shell descriptor"); } @@ -98,32 +108,22 @@ public async Task UpdateShellDescriptorAsync(int priorSerialNumber, IEnumerable< _logger.LogInformation("Updating shell descriptor for tenant '{TenantName}' ...", _shellSettings.Name); } - if (shellDescriptorRecord == null) - { - shellDescriptorRecord = new ShellDescriptor { SerialNumber = 1 }; - } - else - { - shellDescriptorRecord.SerialNumber++; - } - - shellDescriptorRecord.Features = _alwaysEnabledFeatures.Union(enabledFeatures).ToList(); - shellDescriptorRecord.Installed = shellDescriptorRecord.Installed.Union(shellDescriptorRecord.Features).ToList(); + shellDescriptor.SerialNumber++; + shellDescriptor.Features = _alwaysEnabledFeatures.Union(enabledFeatures).ToList(); + shellDescriptor.Installed = shellDescriptor.Installed.Union(shellDescriptor.Features).ToList(); if (_logger.IsEnabled(LogLevel.Information)) { _logger.LogInformation("Shell descriptor updated for tenant '{TenantName}'.", _shellSettings.Name); } - _session.Save(shellDescriptorRecord); + await _documentStore.UpdateAsync(shellDescriptor, _ => Task.CompletedTask); + await _documentStore.CommitAsync(); // In the 'ChangedAsync()' event the shell will be released so that, on request, a new one will be built. // So, we commit the session earlier to prevent a new shell from being built from an outdated descriptor. - - await _session.SaveChangesAsync(); - - await _shellDescriptorManagerEventHandlers.InvokeAsync((handler, shellDescriptorRecord, _shellSettings) => - handler.ChangedAsync(shellDescriptorRecord, _shellSettings), shellDescriptorRecord, _shellSettings, _logger); + await _shellDescriptorManagerEventHandlers.InvokeAsync((handler, shellDescriptor, _shellSettings) => + handler.ChangedAsync(shellDescriptor, _shellSettings), shellDescriptor, _shellSettings, _logger); } private class ConfiguredFeatures