Files
Umbraco-CMS/src/Umbraco.Web.Common/ModelsBuilder/InMemoryAuto/InMemoryModelFactory.cs

863 lines
34 KiB
C#
Raw Normal View History

2019-06-24 11:58:36 +02:00
using System.Collections;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.Loader;
2019-06-24 11:58:36 +02:00
using System.Text;
using System.Text.RegularExpressions;
using Microsoft.AspNetCore.Mvc.Razor;
using Microsoft.Extensions.Logging;
2021-01-13 15:18:59 +11:00
using Microsoft.Extensions.Options;
using Umbraco.Cms.Core;
using Umbraco.Cms.Core.Configuration;
using Umbraco.Cms.Core.Configuration.Models;
using Umbraco.Cms.Core.Hosting;
using Umbraco.Cms.Core.Logging;
using Umbraco.Cms.Core.Models.PublishedContent;
Merge remote-tracking branch 'origin/netcore/dev' into netcore/feature/AB10314-mb-embedded-dependencies # Conflicts: # src/Umbraco.Infrastructure/ModelsBuilder/ApiVersion.cs # src/Umbraco.Infrastructure/ModelsBuilder/Building/Builder.cs # src/Umbraco.Infrastructure/ModelsBuilder/Building/ModelsGenerator.cs # src/Umbraco.Infrastructure/ModelsBuilder/Building/PropertyModel.cs # src/Umbraco.Infrastructure/ModelsBuilder/Building/TextBuilder.cs # src/Umbraco.Infrastructure/ModelsBuilder/Building/TextHeaderWriter.cs # src/Umbraco.Infrastructure/ModelsBuilder/Building/TypeModel.cs # src/Umbraco.Infrastructure/ModelsBuilder/Building/TypeModelHasher.cs # src/Umbraco.Infrastructure/ModelsBuilder/LiveModelsProvider.cs # src/Umbraco.Infrastructure/ModelsBuilder/ModelsBuilderAssemblyAttribute.cs # src/Umbraco.Infrastructure/ModelsBuilder/ModelsBuilderDashboard.cs # src/Umbraco.Infrastructure/ModelsBuilder/ModelsGenerationError.cs # src/Umbraco.Infrastructure/ModelsBuilder/OutOfDateModelsStatus.cs # src/Umbraco.Infrastructure/ModelsBuilder/PublishedElementExtensions.cs # src/Umbraco.Infrastructure/ModelsBuilder/PublishedModelUtility.cs # src/Umbraco.Infrastructure/ModelsBuilder/RoslynCompiler.cs # src/Umbraco.Infrastructure/ModelsBuilder/TypeExtensions.cs # src/Umbraco.Infrastructure/ModelsBuilder/UmbracoServices.cs # src/Umbraco.ModelsBuilder.Embedded/ImplementPropertyTypeAttribute.cs # src/Umbraco.ModelsBuilder.Embedded/Umbraco.ModelsBuilder.Embedded.csproj # src/Umbraco.Tests.UnitTests/Umbraco.ModelsBuilder.Embedded/BuilderTests.cs # src/Umbraco.Tests.UnitTests/Umbraco.ModelsBuilder.Embedded/UmbracoApplicationTests.cs # src/Umbraco.Web.BackOffice/ModelsBuilder/ContentTypeModelValidator.cs # src/Umbraco.Web.BackOffice/ModelsBuilder/ContentTypeModelValidatorBase.cs # src/Umbraco.Web.BackOffice/ModelsBuilder/DashboardReport.cs # src/Umbraco.Web.BackOffice/ModelsBuilder/DisableModelsBuilderNotificationHandler.cs # src/Umbraco.Web.BackOffice/ModelsBuilder/MediaTypeModelValidator.cs # src/Umbraco.Web.BackOffice/ModelsBuilder/MemberTypeModelValidator.cs # src/Umbraco.Web.BackOffice/ModelsBuilder/ModelsBuilderDashboardController.cs # src/Umbraco.Web.Common/ModelsBuilder/DependencyInjection/UmbracoBuilderExtensions.cs # src/Umbraco.Web.Common/ModelsBuilder/ModelsBuilderNotificationHandler.cs # src/Umbraco.Web.Common/ModelsBuilder/PureLiveModelFactory.cs # src/Umbraco.Web.Common/ModelsBuilder/RefreshingRazorViewEngine.cs # src/Umbraco.Web.Common/ModelsBuilder/UmbracoAssemblyLoadContext.cs # src/Umbraco.Web.UI.NetCore/Startup.cs # src/Umbraco.Web.Website/DependencyInjection/UmbracoBuilderExtensions.cs
2021-02-22 09:00:33 +01:00
using Umbraco.Cms.Infrastructure.ModelsBuilder;
using Umbraco.Cms.Infrastructure.ModelsBuilder.Building;
using Umbraco.Extensions;
2019-06-24 11:58:36 +02:00
using File = System.IO.File;
namespace Umbraco.Cms.Web.Common.ModelsBuilder.InMemoryAuto
2019-06-24 11:58:36 +02:00
{
Merge remote-tracking branch 'origin/v8/8.16' into v9/feature/merge_v8_11082021 # Conflicts: # .github/CONTRIBUTING.md # build/NuSpecs/UmbracoCms.Core.nuspec # build/NuSpecs/UmbracoCms.Web.nuspec # build/NuSpecs/UmbracoCms.nuspec # src/SolutionInfo.cs # src/Umbraco.Core/Cache/AppCaches.cs # src/Umbraco.Core/Cache/AppPolicedCacheDictionary.cs # src/Umbraco.Core/Cache/DeepCloneAppCache.cs # src/Umbraco.Core/Cache/WebCachingAppCache.cs # src/Umbraco.Core/CompositionExtensions.cs # src/Umbraco.Core/Models/Identity/BackOfficeIdentityUser.cs # src/Umbraco.Core/Models/PropertyGroupCollection.cs # src/Umbraco.Core/Models/PropertyTypeCollection.cs # src/Umbraco.Core/Persistence/Repositories/Implement/ExternalLoginRepository.cs # src/Umbraco.Core/ReadLock.cs # src/Umbraco.Core/Routing/SiteDomainMapper.cs # src/Umbraco.Core/UpgradeableReadLock.cs # src/Umbraco.Core/WriteLock.cs # src/Umbraco.Examine/ExamineExtensions.cs # src/Umbraco.Infrastructure/Examine/UmbracoFieldDefinitionCollection.cs # src/Umbraco.Infrastructure/Persistence/Dtos/ContentTypeDto.cs # src/Umbraco.Infrastructure/Persistence/Dtos/DictionaryDto.cs # src/Umbraco.Infrastructure/Persistence/Repositories/Implement/MemberGroupRepository.cs # src/Umbraco.Infrastructure/Persistence/Repositories/Implement/TemplateRepository.cs # src/Umbraco.Infrastructure/Persistence/Repositories/Implement/UserRepository.cs # src/Umbraco.Infrastructure/Services/IdKeyMap.cs # src/Umbraco.Infrastructure/Services/Implement/ContentService.cs # src/Umbraco.ModelsBuilder.Embedded/PureLiveModelFactory.cs # src/Umbraco.Tests/App.config # src/Umbraco.Web.BackOffice/Controllers/EntityController.cs # src/Umbraco.Web.UI.Client/package.json # src/Umbraco.Web.UI.NetCore/umbraco/config/lang/da.xml # src/Umbraco.Web.UI.NetCore/umbraco/config/lang/en.xml # src/Umbraco.Web.UI.NetCore/umbraco/config/lang/en_us.xml # src/Umbraco.Web.UI/Umbraco.Web.UI.csproj # src/Umbraco.Web.UI/Umbraco/config/lang/cy.xml # src/Umbraco.Web.UI/web.Template.config # src/Umbraco.Web/CacheHelperExtensions.cs # src/Umbraco.Web/Editors/RelationTypeController.cs # src/Umbraco.Web/Logging/WebProfilerProvider.cs # src/Umbraco.Web/Models/Mapping/MemberMapDefinition.cs # src/Umbraco.Web/PublishedCache/NuCache/MemberCache.cs # src/Umbraco.Web/Routing/ContentFinderByConfigured404.cs # src/Umbraco.Web/Routing/NotFoundHandlerHelper.cs # src/Umbraco.Web/Security/BackOfficeUserManager.cs # src/Umbraco.Web/Umbraco.Web.csproj
2021-08-11 19:11:35 +02:00
internal class InMemoryModelFactory : IAutoPublishedModelFactory, IRegisteredObject, IDisposable
2019-06-24 11:58:36 +02:00
{
private static readonly Regex s_usingRegex = new Regex("^using(.*);", RegexOptions.Compiled | RegexOptions.Multiline);
private static readonly Regex s_aattrRegex = new Regex("^\\[assembly:(.*)\\]", RegexOptions.Compiled | RegexOptions.Multiline);
private static readonly Regex s_assemblyVersionRegex = new Regex("AssemblyVersion\\(\"[0-9]+.[0-9]+.[0-9]+.[0-9]+\"\\)", RegexOptions.Compiled);
private static readonly string[] s_ourFiles = { "models.hash", "models.generated.cs", "all.generated.cs", "all.dll.path", "models.err", "Compiled" };
2019-06-24 11:58:36 +02:00
private readonly ReaderWriterLockSlim _locker = new ReaderWriterLockSlim();
2020-09-15 08:45:40 +02:00
private readonly IProfilingLogger _profilingLogger;
private readonly ILogger<InMemoryModelFactory> _logger;
2022-03-29 13:44:21 +02:00
private readonly FileSystemWatcher? _watcher;
private readonly Lazy<UmbracoServices> _umbracoServices; // TODO: this is because of circular refs :(
2020-09-02 14:44:01 +02:00
private readonly IHostingEnvironment _hostingEnvironment;
private readonly IApplicationShutdownRegistry _hostingLifetime;
private readonly ModelsGenerationError _errors;
2020-09-02 14:44:01 +02:00
private readonly IPublishedValueFallback _publishedValueFallback;
private readonly InMemoryAssemblyLoadContextManager _loadContextManager;
private readonly RuntimeCompilationCacheBuster _runtimeCompilationCacheBuster;
2022-03-29 13:44:21 +02:00
private readonly Lazy<string> _pureLiveDirectory = null!;
private readonly int _debugLevel;
private Infos _infos = new Infos { ModelInfos = null, ModelTypeMap = new Dictionary<string, Type>() };
private volatile bool _hasModels; // volatile 'cos reading outside lock
private bool _pendingRebuild;
private int _ver;
private int? _skipver;
private RoslynCompiler? _roslynCompiler;
private ModelsBuilderSettings _config;
Merge remote-tracking branch 'origin/v8/8.16' into v9/feature/merge_v8_11082021 # Conflicts: # .github/CONTRIBUTING.md # build/NuSpecs/UmbracoCms.Core.nuspec # build/NuSpecs/UmbracoCms.Web.nuspec # build/NuSpecs/UmbracoCms.nuspec # src/SolutionInfo.cs # src/Umbraco.Core/Cache/AppCaches.cs # src/Umbraco.Core/Cache/AppPolicedCacheDictionary.cs # src/Umbraco.Core/Cache/DeepCloneAppCache.cs # src/Umbraco.Core/Cache/WebCachingAppCache.cs # src/Umbraco.Core/CompositionExtensions.cs # src/Umbraco.Core/Models/Identity/BackOfficeIdentityUser.cs # src/Umbraco.Core/Models/PropertyGroupCollection.cs # src/Umbraco.Core/Models/PropertyTypeCollection.cs # src/Umbraco.Core/Persistence/Repositories/Implement/ExternalLoginRepository.cs # src/Umbraco.Core/ReadLock.cs # src/Umbraco.Core/Routing/SiteDomainMapper.cs # src/Umbraco.Core/UpgradeableReadLock.cs # src/Umbraco.Core/WriteLock.cs # src/Umbraco.Examine/ExamineExtensions.cs # src/Umbraco.Infrastructure/Examine/UmbracoFieldDefinitionCollection.cs # src/Umbraco.Infrastructure/Persistence/Dtos/ContentTypeDto.cs # src/Umbraco.Infrastructure/Persistence/Dtos/DictionaryDto.cs # src/Umbraco.Infrastructure/Persistence/Repositories/Implement/MemberGroupRepository.cs # src/Umbraco.Infrastructure/Persistence/Repositories/Implement/TemplateRepository.cs # src/Umbraco.Infrastructure/Persistence/Repositories/Implement/UserRepository.cs # src/Umbraco.Infrastructure/Services/IdKeyMap.cs # src/Umbraco.Infrastructure/Services/Implement/ContentService.cs # src/Umbraco.ModelsBuilder.Embedded/PureLiveModelFactory.cs # src/Umbraco.Tests/App.config # src/Umbraco.Web.BackOffice/Controllers/EntityController.cs # src/Umbraco.Web.UI.Client/package.json # src/Umbraco.Web.UI.NetCore/umbraco/config/lang/da.xml # src/Umbraco.Web.UI.NetCore/umbraco/config/lang/en.xml # src/Umbraco.Web.UI.NetCore/umbraco/config/lang/en_us.xml # src/Umbraco.Web.UI/Umbraco.Web.UI.csproj # src/Umbraco.Web.UI/Umbraco/config/lang/cy.xml # src/Umbraco.Web.UI/web.Template.config # src/Umbraco.Web/CacheHelperExtensions.cs # src/Umbraco.Web/Editors/RelationTypeController.cs # src/Umbraco.Web/Logging/WebProfilerProvider.cs # src/Umbraco.Web/Models/Mapping/MemberMapDefinition.cs # src/Umbraco.Web/PublishedCache/NuCache/MemberCache.cs # src/Umbraco.Web/Routing/ContentFinderByConfigured404.cs # src/Umbraco.Web/Routing/NotFoundHandlerHelper.cs # src/Umbraco.Web/Security/BackOfficeUserManager.cs # src/Umbraco.Web/Umbraco.Web.csproj
2021-08-11 19:11:35 +02:00
private bool _disposedValue;
public InMemoryModelFactory(
Lazy<UmbracoServices> umbracoServices,
2020-09-15 08:45:40 +02:00
IProfilingLogger profilingLogger,
ILogger<InMemoryModelFactory> logger,
IOptionsMonitor<ModelsBuilderSettings> config,
IHostingEnvironment hostingEnvironment,
IApplicationShutdownRegistry hostingLifetime,
IPublishedValueFallback publishedValueFallback,
InMemoryAssemblyLoadContextManager loadContextManager,
RuntimeCompilationCacheBuster runtimeCompilationCacheBuster)
2019-06-24 11:58:36 +02:00
{
_umbracoServices = umbracoServices;
2020-09-15 08:45:40 +02:00
_profilingLogger = profilingLogger;
2019-06-24 11:58:36 +02:00
_logger = logger;
_config = config.CurrentValue;
2020-09-02 14:44:01 +02:00
_hostingEnvironment = hostingEnvironment;
_hostingLifetime = hostingLifetime;
2020-09-02 14:44:01 +02:00
_publishedValueFallback = publishedValueFallback;
_loadContextManager = loadContextManager;
_runtimeCompilationCacheBuster = runtimeCompilationCacheBuster;
2020-09-02 14:44:01 +02:00
_errors = new ModelsGenerationError(config, _hostingEnvironment);
2019-06-24 11:58:36 +02:00
_ver = 1; // zero is for when we had no version
_skipver = -1; // nothing to skip
2021-01-13 15:18:59 +11:00
if (!hostingEnvironment.IsHosted)
{
return;
}
2019-06-24 11:58:36 +02:00
config.OnChange(x => _config = x);
_pureLiveDirectory = new Lazy<string>(PureLiveDirectoryAbsolute);
if (!Directory.Exists(_pureLiveDirectory.Value))
2021-01-13 15:18:59 +11:00
{
Directory.CreateDirectory(_pureLiveDirectory.Value);
2021-01-13 15:18:59 +11:00
}
2019-06-24 11:58:36 +02:00
// BEWARE! if the watcher is not properly released then for some reason the
// BuildManager will start confusing types - using a 'registered object' here
// though we should probably plug into Umbraco's MainDom - which is internal
_hostingLifetime.RegisterObject(this);
_watcher = new FileSystemWatcher(_pureLiveDirectory.Value);
2019-06-24 11:58:36 +02:00
_watcher.Changed += WatcherOnChanged;
_watcher.EnableRaisingEvents = true;
// get it here, this need to be fast
_debugLevel = _config.DebugLevel;
}
/// <summary>
/// Gets the currently loaded Live models assembly
/// </summary>
/// <remarks>
/// Can be null
/// </remarks>
2022-03-29 13:44:21 +02:00
public Assembly? CurrentModelsAssembly { get; private set; }
2019-06-24 11:58:36 +02:00
/// <inheritdoc />
public object SyncRoot { get; } = new object();
private UmbracoServices UmbracoServices => _umbracoServices.Value;
/// <summary>
/// Gets the RoslynCompiler
/// </summary>
2021-01-13 15:18:59 +11:00
private RoslynCompiler RoslynCompiler
{
get
{
if (_roslynCompiler != null)
{
return _roslynCompiler;
}
_roslynCompiler = new RoslynCompiler();
2021-01-13 15:18:59 +11:00
return _roslynCompiler;
}
}
/// <inheritdoc />
public bool Enabled => _config.ModelsMode == ModelsMode.InMemoryAuto;
2021-01-13 15:18:59 +11:00
2019-06-24 11:58:36 +02:00
public IPublishedElement CreateModel(IPublishedElement element)
{
// get models, rebuilding them if needed
Dictionary<string, ModelInfo>? infos = EnsureModels().ModelInfos;
2019-06-24 11:58:36 +02:00
if (infos == null)
2021-01-13 15:18:59 +11:00
{
2019-06-24 11:58:36 +02:00
return element;
2021-01-13 15:18:59 +11:00
}
2019-06-24 11:58:36 +02:00
// be case-insensitive
var contentTypeAlias = element.ContentType.Alias;
// lookup model constructor (else null)
infos.TryGetValue(contentTypeAlias, out var info);
2019-06-24 11:58:36 +02:00
// create model
2022-03-29 13:44:21 +02:00
return info is null || info.Ctor is null ? element : info.Ctor(element, _publishedValueFallback);
2019-06-24 11:58:36 +02:00
}
/// <inheritdoc />
public Type GetModelType(string? alias)
{
Infos infos = EnsureModels();
// fail fast
if (alias is null ||
infos.ModelInfos is null ||
!infos.ModelInfos.TryGetValue(alias, out ModelInfo? modelInfo) ||
modelInfo.ModelType is null)
{
return typeof(IPublishedElement);
}
return modelInfo.ModelType;
}
2019-06-24 11:58:36 +02:00
// this runs only once the factory is ready
// NOT when building models
public Type MapModelType(Type type)
{
2021-01-13 15:18:59 +11:00
Infos infos = EnsureModels();
2019-06-24 11:58:36 +02:00
return ModelType.Map(type, infos.ModelTypeMap);
}
// this runs only once the factory is ready
// NOT when building models
2022-03-29 13:44:21 +02:00
public IList CreateModelList(string? alias)
2019-06-24 11:58:36 +02:00
{
2021-01-13 15:18:59 +11:00
Infos infos = EnsureModels();
2019-06-24 11:58:36 +02:00
// fail fast
if (alias is null || infos.ModelInfos is null || !infos.ModelInfos.TryGetValue(alias, out ModelInfo? modelInfo))
2021-01-13 15:18:59 +11:00
{
2019-06-24 11:58:36 +02:00
return new List<IPublishedElement>();
2021-01-13 15:18:59 +11:00
}
2019-06-24 11:58:36 +02:00
2022-03-29 13:44:21 +02:00
Func<IList>? ctor = modelInfo.ListCtor;
2021-01-13 15:18:59 +11:00
if (ctor != null)
{
return ctor();
}
2019-06-24 11:58:36 +02:00
2022-03-29 13:44:21 +02:00
if (modelInfo.ModelType is null)
{
return new List<IPublishedElement>();
}
2021-01-13 15:18:59 +11:00
Type listType = typeof(List<>).MakeGenericType(modelInfo.ModelType);
2019-06-24 11:58:36 +02:00
ctor = modelInfo.ListCtor = ReflectionUtilities.EmitConstructor<Func<IList>>(declaring: listType);
2022-03-29 13:44:21 +02:00
return ctor is null ? new List<IPublishedElement>() : ctor();
2019-06-24 11:58:36 +02:00
}
/// <inheritdoc />
public void Reset()
{
if (Enabled)
2021-01-13 15:18:59 +11:00
{
ResetModels();
2021-01-13 15:18:59 +11:00
}
}
2019-06-24 11:58:36 +02:00
// tells the factory that it should build a new generation of models
private void ResetModels()
{
if (_logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug))
{
_logger.LogDebug("Resetting models.");
}
2019-06-24 11:58:36 +02:00
try
{
_locker.EnterWriteLock();
_hasModels = false;
_pendingRebuild = true;
if (!Directory.Exists(_pureLiveDirectory.Value))
2021-01-13 15:18:59 +11:00
{
Directory.CreateDirectory(_pureLiveDirectory.Value);
2021-01-13 15:18:59 +11:00
}
2019-06-24 11:58:36 +02:00
// clear stuff
var modelsHashFile = Path.Combine(_pureLiveDirectory.Value, "models.hash");
var dllPathFile = Path.Combine(_pureLiveDirectory.Value, "all.dll.path");
2019-06-24 11:58:36 +02:00
2021-01-13 15:18:59 +11:00
if (File.Exists(dllPathFile))
{
File.Delete(dllPathFile);
}
if (File.Exists(modelsHashFile))
{
File.Delete(modelsHashFile);
}
2019-06-24 11:58:36 +02:00
}
finally
{
if (_locker.IsWriteLockHeld)
2021-01-13 15:18:59 +11:00
{
2019-06-24 11:58:36 +02:00
_locker.ExitWriteLock();
2021-01-13 15:18:59 +11:00
}
2019-06-24 11:58:36 +02:00
}
}
// ensure that the factory is running with the lastest generation of models
internal Infos EnsureModels()
{
if (_debugLevel > 0)
2021-01-13 15:18:59 +11:00
{
if (_logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug))
{
_logger.LogDebug("Ensuring models.");
}
2021-01-13 15:18:59 +11:00
}
2019-06-24 11:58:36 +02:00
// don't use an upgradeable lock here because only 1 thread at a time could enter it
try
{
_locker.EnterReadLock();
if (_hasModels)
2021-01-13 15:18:59 +11:00
{
2019-06-24 11:58:36 +02:00
return _infos;
2021-01-13 15:18:59 +11:00
}
2019-06-24 11:58:36 +02:00
}
finally
{
if (_locker.IsReadLockHeld)
2021-01-13 15:18:59 +11:00
{
2019-06-24 11:58:36 +02:00
_locker.ExitReadLock();
2021-01-13 15:18:59 +11:00
}
2019-06-24 11:58:36 +02:00
}
try
{
_locker.EnterUpgradeableReadLock();
2021-01-13 15:18:59 +11:00
if (_hasModels)
{
return _infos;
}
2019-06-24 11:58:36 +02:00
_locker.EnterWriteLock();
// we don't have models,
// either they haven't been loaded from the cache yet
// or they have been reseted and are pending a rebuild
using (!_profilingLogger.IsEnabled(Core.Logging.LogLevel.Debug) ? null : _profilingLogger.DebugDuration<InMemoryModelFactory>("Get models.", "Got models."))
2019-06-24 11:58:36 +02:00
{
try
{
Assembly assembly = GetModelsAssembly(_pendingRebuild);
CurrentModelsAssembly = assembly;
2019-06-24 11:58:36 +02:00
/*
* We used to use an event here, and a RefreshingRazorViewEngine to bust the caches,
* this worked by essentially completely recreating the entire ViewEngine/ViewCompiler every time we generate models.
* There was this note about first load:
* NOTE: That on first load, if there is content, this will execute before the razor view engine
* has loaded which means it hasn't yet bound to this event so there's no need to worry about if
* it will be eagerly re-generated unnecessarily on first render. BUT we should be aware that if we
* change this to use the event aggregator that will no longer be the case.
*
* Now we have our own ViewCompiler, and clear the caches more directly, however what the comment mentioned
* is not really a big problem since this will execute before the razor view engine has loaded,
* which means the cache will be empty already.
*/
_runtimeCompilationCacheBuster.BustCache();
2021-01-13 15:18:59 +11:00
IEnumerable<Type> types = assembly.ExportedTypes.Where(x => x.Inherits<PublishedContentModel>() || x.Inherits<PublishedElementModel>());
2019-06-24 11:58:36 +02:00
_infos = RegisterModels(types);
_errors.Clear();
2019-06-24 11:58:36 +02:00
}
catch (Exception e)
{
try
{
_logger.LogError(e, "Failed to build models.");
2020-09-15 08:45:40 +02:00
_logger.LogWarning("Running without models."); // be explicit
_errors.Report("Failed to build InMemory models.", e);
2019-06-24 11:58:36 +02:00
}
finally
{
CurrentModelsAssembly = null;
2019-06-24 11:58:36 +02:00
_infos = new Infos { ModelInfos = null, ModelTypeMap = new Dictionary<string, Type>() };
}
}
// don't even try again
_hasModels = true;
}
return _infos;
}
finally
{
if (_locker.IsWriteLockHeld)
2021-01-13 15:18:59 +11:00
{
2019-06-24 11:58:36 +02:00
_locker.ExitWriteLock();
2021-01-13 15:18:59 +11:00
}
2019-06-24 11:58:36 +02:00
if (_locker.IsUpgradeableReadLockHeld)
2021-01-13 15:18:59 +11:00
{
2019-06-24 11:58:36 +02:00
_locker.ExitUpgradeableReadLock();
2021-01-13 15:18:59 +11:00
}
2019-06-24 11:58:36 +02:00
}
}
public string PureLiveDirectoryAbsolute() => _hostingEnvironment.MapPathContentRoot(Core.Constants.SystemDirectories.TempData + "/InMemoryAuto");
// This is NOT thread safe but it is only called from within a lock
private Assembly ReloadAssembly(string pathToAssembly)
{
_loadContextManager.RenewAssemblyLoadContext();
// NOTE: We cannot use in-memory assemblies due to the way the razor engine works which must use
// application parts in order to add references to it's own CSharpCompiler.
// These parts must have real paths since that is how the references are loaded. In that
// case we'll need to work on temp files so that the assembly isn't locked.
// Get a temp file path
// NOTE: We cannot use Path.GetTempFileName(), see this issue:
// https://github.com/dotnet/AspNetCore.Docs/issues/3589 which can cause issues, this is recommended instead
var tempFile = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName());
File.Copy(pathToAssembly, tempFile, true);
// Load it in
Assembly assembly = _loadContextManager.LoadModelsAssembly(tempFile);
return assembly;
}
// This is NOT thread safe but it is only called from within a lock
private Assembly GetModelsAssembly(bool forceRebuild)
2019-06-24 11:58:36 +02:00
{
if (!Directory.Exists(_pureLiveDirectory.Value))
2021-01-13 15:18:59 +11:00
{
Directory.CreateDirectory(_pureLiveDirectory.Value);
2021-01-13 15:18:59 +11:00
}
2019-06-24 11:58:36 +02:00
2021-01-13 15:18:59 +11:00
IList<TypeModel> typeModels = UmbracoServices.GetAllTypes();
var currentHash = TypeModelHasher.Hash(typeModels);
var modelsHashFile = Path.Combine(_pureLiveDirectory.Value, "models.hash");
var modelsSrcFile = Path.Combine(_pureLiveDirectory.Value, "models.generated.cs");
var projFile = Path.Combine(_pureLiveDirectory.Value, "all.generated.cs");
var dllPathFile = Path.Combine(_pureLiveDirectory.Value, "all.dll.path");
2019-06-24 11:58:36 +02:00
// caching the generated models speeds up booting
// currentHash hashes both the types & the user's partials
if (!forceRebuild)
{
if (_logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug))
{
_logger.LogDebug("Looking for cached models.");
}
2019-06-24 11:58:36 +02:00
if (File.Exists(modelsHashFile) && File.Exists(projFile))
{
var cachedHash = File.ReadAllText(modelsHashFile);
if (currentHash != cachedHash)
{
if (_logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug))
{
_logger.LogDebug("Found obsolete cached models.");
}
2019-06-24 11:58:36 +02:00
forceRebuild = true;
}
// else cachedHash matches currentHash, we can try to load an existing dll
}
else
{
2020-09-15 08:45:40 +02:00
_logger.LogDebug("Could not find cached models.");
2019-06-24 11:58:36 +02:00
forceRebuild = true;
}
}
Assembly assembly;
if (!forceRebuild)
{
// try to load the dll directly (avoid rebuilding)
//
// ensure that the .dll file does not have a corresponding .dll.delete file
// as that would mean the the .dll file is going to be deleted and should not
// be re-used - that should not happen in theory, but better be safe
if (File.Exists(dllPathFile))
{
var dllPath = File.ReadAllText(dllPathFile);
if (_logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug))
{
_logger.LogDebug("Cached models dll at {dllPath}.",dllPath);
}
2019-06-24 11:58:36 +02:00
2020-09-03 14:18:09 +02:00
if (File.Exists(dllPath) && !File.Exists(dllPath + ".delete"))
2019-06-24 11:58:36 +02:00
{
assembly = ReloadAssembly(dllPath);
2022-03-29 13:44:21 +02:00
ModelsBuilderAssemblyAttribute? attr = assembly.GetCustomAttribute<ModelsBuilderAssemblyAttribute>();
if (attr != null && attr.IsInMemory && attr.SourceHash == currentHash)
2019-06-24 11:58:36 +02:00
{
// if we were to resume at that revision, then _ver would keep increasing
// and that is probably a bad idea - so, we'll always rebuild starting at
// ver 1, but we remember we want to skip that one - so we never end up
// with the "same but different" version of the assembly in memory
2022-03-29 13:44:21 +02:00
_skipver = assembly.GetName().Version?.Revision;
if (_logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug))
{
_logger.LogDebug("Loading cached models (dll).");
}
2019-06-24 11:58:36 +02:00
return assembly;
}
if (_logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug))
{
_logger.LogDebug("Cached models dll cannot be loaded (invalid assembly).");
}
2019-06-24 11:58:36 +02:00
}
else if (!File.Exists(dllPath))
2021-01-13 15:18:59 +11:00
{
if (_logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug))
{
_logger.LogDebug("Cached models dll does not exist.");
}
2021-01-13 15:18:59 +11:00
}
2019-06-24 11:58:36 +02:00
else if (File.Exists(dllPath + ".delete"))
2021-01-13 15:18:59 +11:00
{
if (_logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug))
{
_logger.LogDebug("Cached models dll is marked for deletion.");
}
2021-01-13 15:18:59 +11:00
}
2019-06-24 11:58:36 +02:00
else
2021-01-13 15:18:59 +11:00
{
if (_logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug))
{
_logger.LogDebug("Cached models dll cannot be loaded (why?).");
}
2021-01-13 15:18:59 +11:00
}
2019-06-24 11:58:36 +02:00
}
// must reset the version in the file else it would keep growing
// loading cached modules only happens when the app restarts
var text = File.ReadAllText(projFile);
2021-01-13 15:18:59 +11:00
Match match = s_assemblyVersionRegex.Match(text);
2019-06-24 11:58:36 +02:00
if (match.Success)
{
text = text.Replace(match.Value, "AssemblyVersion(\"0.0.0." + _ver + "\")");
File.WriteAllText(projFile, text);
}
_ver++;
try
{
var assemblyPath = GetOutputAssemblyPath(currentHash);
RoslynCompiler.CompileToFile(projFile, assemblyPath);
assembly = ReloadAssembly(assemblyPath);
2019-06-24 11:58:36 +02:00
File.WriteAllText(dllPathFile, assembly.Location);
2020-09-08 11:23:27 +02:00
File.WriteAllText(modelsHashFile, currentHash);
TryDeleteUnusedAssemblies(dllPathFile);
2019-06-24 11:58:36 +02:00
}
catch
{
ClearOnFailingToCompile(dllPathFile, modelsHashFile, projFile);
throw;
}
if (_logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug))
{
_logger.LogDebug("Loading cached models (source).");
}
2019-06-24 11:58:36 +02:00
return assembly;
}
// need to rebuild
if (_logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug))
{
_logger.LogDebug("Rebuilding models.");
}
2019-06-24 11:58:36 +02:00
// generate code, save
var code = GenerateModelsCode(typeModels);
2019-06-24 11:58:36 +02:00
// add extra attributes,
// IsLive=true helps identifying Assemblies that contain live models
2019-06-24 11:58:36 +02:00
// AssemblyVersion is so that we have a different version for each rebuild
var ver = _ver == _skipver ? ++_ver : _ver;
_ver++;
string mbAssemblyDirective = $@"[assembly:ModelsBuilderAssembly(IsInMemory = true, SourceHash = ""{currentHash}"")]
2021-01-13 15:18:59 +11:00
[assembly:System.Reflection.AssemblyVersion(""0.0.0.{ver}"")]";
code = code.Replace("//ASSATTR", mbAssemblyDirective);
2019-06-24 11:58:36 +02:00
File.WriteAllText(modelsSrcFile, code);
// generate proj, save
var projFiles = new Dictionary<string, string>
{
{ "models.generated.cs", code },
2019-06-24 11:58:36 +02:00
};
var proj = GenerateModelsProj(projFiles);
File.WriteAllText(projFile, proj);
// compile and register
try
{
var assemblyPath = GetOutputAssemblyPath(currentHash);
RoslynCompiler.CompileToFile(projFile, assemblyPath);
assembly = ReloadAssembly(assemblyPath);
File.WriteAllText(dllPathFile, assemblyPath);
2019-06-24 11:58:36 +02:00
File.WriteAllText(modelsHashFile, currentHash);
TryDeleteUnusedAssemblies(dllPathFile);
2019-06-24 11:58:36 +02:00
}
catch
{
ClearOnFailingToCompile(dllPathFile, modelsHashFile, projFile);
throw;
}
if (_logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug))
{
_logger.LogDebug("Done rebuilding.");
}
2019-06-24 11:58:36 +02:00
return assembly;
}
private void TryDeleteUnusedAssemblies(string dllPathFile)
2020-09-03 14:18:09 +02:00
{
if (File.Exists(dllPathFile))
{
var dllPath = File.ReadAllText(dllPathFile);
2022-03-29 13:44:21 +02:00
DirectoryInfo? dirInfo = new DirectoryInfo(dllPath).Parent;
IEnumerable<FileInfo>? files = dirInfo?.GetFiles().Where(f => f.FullName != dllPath);
if (files is null)
{
return;
}
2021-01-13 15:18:59 +11:00
foreach (FileInfo file in files)
{
try
{
File.Delete(file.FullName);
}
2021-01-13 15:18:59 +11:00
catch (UnauthorizedAccessException)
{
// The file is in use, we'll try again next time...
// This shouldn't happen anymore.
}
}
2020-09-03 14:18:09 +02:00
}
}
2020-09-02 15:35:05 +02:00
private string GetOutputAssemblyPath(string currentHash)
{
var dirInfo = new DirectoryInfo(Path.Combine(_pureLiveDirectory.Value, "Compiled"));
2020-09-02 15:35:05 +02:00
if (!dirInfo.Exists)
2021-01-13 15:18:59 +11:00
{
2020-09-03 15:03:02 +02:00
Directory.CreateDirectory(dirInfo.FullName);
2021-01-13 15:18:59 +11:00
}
2020-09-02 15:46:48 +02:00
return Path.Combine(dirInfo.FullName, $"generated.cs{currentHash}.dll");
2020-09-02 15:35:05 +02:00
}
2019-06-24 11:58:36 +02:00
private void ClearOnFailingToCompile(string dllPathFile, string modelsHashFile, string projFile)
{
if (_logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug))
{
_logger.LogDebug("Failed to compile.");
}
2019-06-24 11:58:36 +02:00
// the dll file reference still points to the previous dll, which is obsolete
// now and will be deleted by ASP.NET eventually, so better clear that reference.
// also touch the proj file to force views to recompile - don't delete as it's
// useful to have the source around for debugging.
try
{
2021-01-13 15:18:59 +11:00
if (File.Exists(dllPathFile))
{
File.Delete(dllPathFile);
}
if (File.Exists(modelsHashFile))
{
File.Delete(modelsHashFile);
}
if (File.Exists(projFile))
{
File.SetLastWriteTime(projFile, DateTime.Now);
}
2019-06-24 11:58:36 +02:00
}
catch
{ /* enough */
}
2019-06-24 11:58:36 +02:00
}
private static Infos RegisterModels(IEnumerable<Type> types)
{
2021-01-13 15:18:59 +11:00
Type[] ctorArgTypes = new[] { typeof(IPublishedElement), typeof(IPublishedValueFallback) };
2019-06-24 11:58:36 +02:00
var modelInfos = new Dictionary<string, ModelInfo>(StringComparer.InvariantCultureIgnoreCase);
var map = new Dictionary<string, Type>();
2021-01-13 15:18:59 +11:00
foreach (Type type in types)
2019-06-24 11:58:36 +02:00
{
2022-03-29 13:44:21 +02:00
ConstructorInfo? constructor = null;
Type? parameterType = null;
2019-06-24 11:58:36 +02:00
2021-01-13 15:18:59 +11:00
foreach (ConstructorInfo ctor in type.GetConstructors())
2019-06-24 11:58:36 +02:00
{
2021-01-13 15:18:59 +11:00
ParameterInfo[] parms = ctor.GetParameters();
2020-09-02 14:44:01 +02:00
if (parms.Length == 2 && typeof(IPublishedElement).IsAssignableFrom(parms[0].ParameterType) && typeof(IPublishedValueFallback).IsAssignableFrom(parms[1].ParameterType))
2019-06-24 11:58:36 +02:00
{
if (constructor != null)
2021-01-13 15:18:59 +11:00
{
2019-06-24 11:58:36 +02:00
throw new InvalidOperationException($"Type {type.FullName} has more than one public constructor with one argument of type, or implementing, IPropertySet.");
2021-01-13 15:18:59 +11:00
}
2019-06-24 11:58:36 +02:00
constructor = ctor;
parameterType = parms[0].ParameterType;
}
}
if (constructor == null)
2021-01-13 15:18:59 +11:00
{
2019-06-24 11:58:36 +02:00
throw new InvalidOperationException($"Type {type.FullName} is missing a public constructor with one argument of type, or implementing, IPropertySet.");
2021-01-13 15:18:59 +11:00
}
2019-06-24 11:58:36 +02:00
2022-03-29 13:44:21 +02:00
PublishedModelAttribute? attribute = type.GetCustomAttribute<PublishedModelAttribute>(false);
2019-06-24 11:58:36 +02:00
var typeName = attribute == null ? type.Name : attribute.ContentTypeAlias;
if (modelInfos.TryGetValue(typeName, out var modelInfo))
2021-01-13 15:18:59 +11:00
{
2022-03-29 13:44:21 +02:00
throw new InvalidOperationException($"Both types {type.FullName} and {modelInfo.ModelType?.FullName} want to be a model type for content type with alias \"{typeName}\".");
2021-01-13 15:18:59 +11:00
}
2019-06-24 11:58:36 +02:00
2020-07-14 10:08:59 +10:00
// TODO: use Core's ReflectionUtilities.EmitCtor !!
// Yes .. DynamicMethod is uber slow
2020-07-14 10:08:59 +10:00
// TODO: But perhaps https://docs.microsoft.com/en-us/dotnet/api/system.reflection.emit.constructorbuilder?view=netcore-3.1 is better still?
// See CtorInvokeBenchmarks
var meth = new DynamicMethod(string.Empty, typeof(IPublishedElement), ctorArgTypes, type.Module, true);
2021-01-13 15:18:59 +11:00
ILGenerator gen = meth.GetILGenerator();
2019-06-24 11:58:36 +02:00
gen.Emit(OpCodes.Ldarg_0);
2020-09-02 15:18:22 +02:00
gen.Emit(OpCodes.Ldarg_1);
2019-06-24 11:58:36 +02:00
gen.Emit(OpCodes.Newobj, constructor);
gen.Emit(OpCodes.Ret);
2020-09-02 14:44:01 +02:00
var func = (Func<IPublishedElement, IPublishedValueFallback, IPublishedElement>)meth.CreateDelegate(typeof(Func<IPublishedElement, IPublishedValueFallback, IPublishedElement>));
2019-06-24 11:58:36 +02:00
modelInfos[typeName] = new ModelInfo { ParameterType = parameterType, Ctor = func, ModelType = type };
map[typeName] = type;
}
return new Infos { ModelInfos = modelInfos.Count > 0 ? modelInfos : null, ModelTypeMap = map };
}
private string GenerateModelsCode(IList<TypeModel> typeModels)
{
if (!Directory.Exists(_pureLiveDirectory.Value))
2021-01-13 15:18:59 +11:00
{
Directory.CreateDirectory(_pureLiveDirectory.Value);
2021-01-13 15:18:59 +11:00
}
2019-06-24 11:58:36 +02:00
foreach (var file in Directory.GetFiles(_pureLiveDirectory.Value, "*.generated.cs"))
2021-01-13 15:18:59 +11:00
{
2019-06-24 11:58:36 +02:00
File.Delete(file);
2021-01-13 15:18:59 +11:00
}
2019-06-24 11:58:36 +02:00
var builder = new TextBuilder(_config, typeModels);
2019-06-24 11:58:36 +02:00
var codeBuilder = new StringBuilder();
builder.Generate(codeBuilder, builder.GetModelsToGenerate());
var code = codeBuilder.ToString();
return code;
}
private static string GenerateModelsProj(IDictionary<string, string> files)
{
// ideally we would generate a CSPROJ file but then we'd need a BuildProvider for csproj
// trying to keep things simple for the time being, just write everything to one big file
// group all 'using' at the top of the file (else fails)
var usings = new List<string>();
2021-01-13 15:18:59 +11:00
foreach (string k in files.Keys.ToList())
{
files[k] = s_usingRegex.Replace(files[k], m =>
2019-06-24 11:58:36 +02:00
{
usings.Add(m.Groups[1].Value);
return string.Empty;
});
2021-01-13 15:18:59 +11:00
}
2019-06-24 11:58:36 +02:00
// group all '[assembly:...]' at the top of the file (else fails)
var aattrs = new List<string>();
2021-01-13 15:18:59 +11:00
foreach (string k in files.Keys.ToList())
{
files[k] = s_aattrRegex.Replace(files[k], m =>
2019-06-24 11:58:36 +02:00
{
aattrs.Add(m.Groups[1].Value);
return string.Empty;
});
2021-01-13 15:18:59 +11:00
}
2019-06-24 11:58:36 +02:00
var text = new StringBuilder();
foreach (var u in usings.Distinct())
{
text.Append("using ");
text.Append(u);
text.Append(";\r\n");
}
2021-01-13 15:18:59 +11:00
2019-06-24 11:58:36 +02:00
foreach (var a in aattrs)
{
text.Append("[assembly:");
text.Append(a);
text.Append("]\r\n");
}
2021-01-13 15:18:59 +11:00
2019-06-24 11:58:36 +02:00
text.Append("\r\n\r\n");
2021-01-13 15:18:59 +11:00
foreach (KeyValuePair<string, string> f in files)
2019-06-24 11:58:36 +02:00
{
text.Append("// FILE: ");
text.Append(f.Key);
text.Append("\r\n\r\n");
text.Append(f.Value);
text.Append("\r\n\r\n\r\n");
}
2021-01-13 15:18:59 +11:00
2019-06-24 11:58:36 +02:00
text.Append("// EOF\r\n");
return text.ToString();
}
private void WatcherOnChanged(object sender, FileSystemEventArgs args)
{
var changed = args.Name;
// don't reset when our files change because we are building!
//
// comment it out, and always ignore our files, because it seems that some
// race conditions can occur on slow Cloud filesystems and then we keep
// rebuilding
// if (_building && OurFiles.Contains(changed))
// {
// //_logger.LogInformation<InMemoryModelFactory>("Ignoring files self-changes.");
2019-06-24 11:58:36 +02:00
// return;
// }
2019-06-24 11:58:36 +02:00
// always ignore our own file changes
2021-01-13 15:18:59 +11:00
if (s_ourFiles.Contains(changed))
{
2019-06-24 11:58:36 +02:00
return;
}
2019-06-24 11:58:36 +02:00
2020-09-15 08:45:40 +02:00
_logger.LogInformation("Detected files changes.");
2019-06-24 11:58:36 +02:00
// don't reset while being locked
lock (SyncRoot)
{
2019-06-24 11:58:36 +02:00
ResetModels();
}
2019-06-24 11:58:36 +02:00
}
public void Stop(bool immediate)
{
Merge remote-tracking branch 'origin/v8/8.16' into v9/feature/merge_v8_11082021 # Conflicts: # .github/CONTRIBUTING.md # build/NuSpecs/UmbracoCms.Core.nuspec # build/NuSpecs/UmbracoCms.Web.nuspec # build/NuSpecs/UmbracoCms.nuspec # src/SolutionInfo.cs # src/Umbraco.Core/Cache/AppCaches.cs # src/Umbraco.Core/Cache/AppPolicedCacheDictionary.cs # src/Umbraco.Core/Cache/DeepCloneAppCache.cs # src/Umbraco.Core/Cache/WebCachingAppCache.cs # src/Umbraco.Core/CompositionExtensions.cs # src/Umbraco.Core/Models/Identity/BackOfficeIdentityUser.cs # src/Umbraco.Core/Models/PropertyGroupCollection.cs # src/Umbraco.Core/Models/PropertyTypeCollection.cs # src/Umbraco.Core/Persistence/Repositories/Implement/ExternalLoginRepository.cs # src/Umbraco.Core/ReadLock.cs # src/Umbraco.Core/Routing/SiteDomainMapper.cs # src/Umbraco.Core/UpgradeableReadLock.cs # src/Umbraco.Core/WriteLock.cs # src/Umbraco.Examine/ExamineExtensions.cs # src/Umbraco.Infrastructure/Examine/UmbracoFieldDefinitionCollection.cs # src/Umbraco.Infrastructure/Persistence/Dtos/ContentTypeDto.cs # src/Umbraco.Infrastructure/Persistence/Dtos/DictionaryDto.cs # src/Umbraco.Infrastructure/Persistence/Repositories/Implement/MemberGroupRepository.cs # src/Umbraco.Infrastructure/Persistence/Repositories/Implement/TemplateRepository.cs # src/Umbraco.Infrastructure/Persistence/Repositories/Implement/UserRepository.cs # src/Umbraco.Infrastructure/Services/IdKeyMap.cs # src/Umbraco.Infrastructure/Services/Implement/ContentService.cs # src/Umbraco.ModelsBuilder.Embedded/PureLiveModelFactory.cs # src/Umbraco.Tests/App.config # src/Umbraco.Web.BackOffice/Controllers/EntityController.cs # src/Umbraco.Web.UI.Client/package.json # src/Umbraco.Web.UI.NetCore/umbraco/config/lang/da.xml # src/Umbraco.Web.UI.NetCore/umbraco/config/lang/en.xml # src/Umbraco.Web.UI.NetCore/umbraco/config/lang/en_us.xml # src/Umbraco.Web.UI/Umbraco.Web.UI.csproj # src/Umbraco.Web.UI/Umbraco/config/lang/cy.xml # src/Umbraco.Web.UI/web.Template.config # src/Umbraco.Web/CacheHelperExtensions.cs # src/Umbraco.Web/Editors/RelationTypeController.cs # src/Umbraco.Web/Logging/WebProfilerProvider.cs # src/Umbraco.Web/Models/Mapping/MemberMapDefinition.cs # src/Umbraco.Web/PublishedCache/NuCache/MemberCache.cs # src/Umbraco.Web/Routing/ContentFinderByConfigured404.cs # src/Umbraco.Web/Routing/NotFoundHandlerHelper.cs # src/Umbraco.Web/Security/BackOfficeUserManager.cs # src/Umbraco.Web/Umbraco.Web.csproj
2021-08-11 19:11:35 +02:00
Dispose();
_hostingLifetime.UnregisterObject(this);
2019-06-24 11:58:36 +02:00
}
Merge remote-tracking branch 'origin/v8/8.16' into v9/feature/merge_v8_11082021 # Conflicts: # .github/CONTRIBUTING.md # build/NuSpecs/UmbracoCms.Core.nuspec # build/NuSpecs/UmbracoCms.Web.nuspec # build/NuSpecs/UmbracoCms.nuspec # src/SolutionInfo.cs # src/Umbraco.Core/Cache/AppCaches.cs # src/Umbraco.Core/Cache/AppPolicedCacheDictionary.cs # src/Umbraco.Core/Cache/DeepCloneAppCache.cs # src/Umbraco.Core/Cache/WebCachingAppCache.cs # src/Umbraco.Core/CompositionExtensions.cs # src/Umbraco.Core/Models/Identity/BackOfficeIdentityUser.cs # src/Umbraco.Core/Models/PropertyGroupCollection.cs # src/Umbraco.Core/Models/PropertyTypeCollection.cs # src/Umbraco.Core/Persistence/Repositories/Implement/ExternalLoginRepository.cs # src/Umbraco.Core/ReadLock.cs # src/Umbraco.Core/Routing/SiteDomainMapper.cs # src/Umbraco.Core/UpgradeableReadLock.cs # src/Umbraco.Core/WriteLock.cs # src/Umbraco.Examine/ExamineExtensions.cs # src/Umbraco.Infrastructure/Examine/UmbracoFieldDefinitionCollection.cs # src/Umbraco.Infrastructure/Persistence/Dtos/ContentTypeDto.cs # src/Umbraco.Infrastructure/Persistence/Dtos/DictionaryDto.cs # src/Umbraco.Infrastructure/Persistence/Repositories/Implement/MemberGroupRepository.cs # src/Umbraco.Infrastructure/Persistence/Repositories/Implement/TemplateRepository.cs # src/Umbraco.Infrastructure/Persistence/Repositories/Implement/UserRepository.cs # src/Umbraco.Infrastructure/Services/IdKeyMap.cs # src/Umbraco.Infrastructure/Services/Implement/ContentService.cs # src/Umbraco.ModelsBuilder.Embedded/PureLiveModelFactory.cs # src/Umbraco.Tests/App.config # src/Umbraco.Web.BackOffice/Controllers/EntityController.cs # src/Umbraco.Web.UI.Client/package.json # src/Umbraco.Web.UI.NetCore/umbraco/config/lang/da.xml # src/Umbraco.Web.UI.NetCore/umbraco/config/lang/en.xml # src/Umbraco.Web.UI.NetCore/umbraco/config/lang/en_us.xml # src/Umbraco.Web.UI/Umbraco.Web.UI.csproj # src/Umbraco.Web.UI/Umbraco/config/lang/cy.xml # src/Umbraco.Web.UI/web.Template.config # src/Umbraco.Web/CacheHelperExtensions.cs # src/Umbraco.Web/Editors/RelationTypeController.cs # src/Umbraco.Web/Logging/WebProfilerProvider.cs # src/Umbraco.Web/Models/Mapping/MemberMapDefinition.cs # src/Umbraco.Web/PublishedCache/NuCache/MemberCache.cs # src/Umbraco.Web/Routing/ContentFinderByConfigured404.cs # src/Umbraco.Web/Routing/NotFoundHandlerHelper.cs # src/Umbraco.Web/Security/BackOfficeUserManager.cs # src/Umbraco.Web/Umbraco.Web.csproj
2021-08-11 19:11:35 +02:00
protected virtual void Dispose(bool disposing)
{
if (!_disposedValue)
{
if (disposing)
{
2022-03-29 13:44:21 +02:00
if (_watcher is not null)
{
_watcher.EnableRaisingEvents = false;
_watcher.Dispose();
}
Merge remote-tracking branch 'origin/v8/8.16' into v9/feature/merge_v8_11082021 # Conflicts: # .github/CONTRIBUTING.md # build/NuSpecs/UmbracoCms.Core.nuspec # build/NuSpecs/UmbracoCms.Web.nuspec # build/NuSpecs/UmbracoCms.nuspec # src/SolutionInfo.cs # src/Umbraco.Core/Cache/AppCaches.cs # src/Umbraco.Core/Cache/AppPolicedCacheDictionary.cs # src/Umbraco.Core/Cache/DeepCloneAppCache.cs # src/Umbraco.Core/Cache/WebCachingAppCache.cs # src/Umbraco.Core/CompositionExtensions.cs # src/Umbraco.Core/Models/Identity/BackOfficeIdentityUser.cs # src/Umbraco.Core/Models/PropertyGroupCollection.cs # src/Umbraco.Core/Models/PropertyTypeCollection.cs # src/Umbraco.Core/Persistence/Repositories/Implement/ExternalLoginRepository.cs # src/Umbraco.Core/ReadLock.cs # src/Umbraco.Core/Routing/SiteDomainMapper.cs # src/Umbraco.Core/UpgradeableReadLock.cs # src/Umbraco.Core/WriteLock.cs # src/Umbraco.Examine/ExamineExtensions.cs # src/Umbraco.Infrastructure/Examine/UmbracoFieldDefinitionCollection.cs # src/Umbraco.Infrastructure/Persistence/Dtos/ContentTypeDto.cs # src/Umbraco.Infrastructure/Persistence/Dtos/DictionaryDto.cs # src/Umbraco.Infrastructure/Persistence/Repositories/Implement/MemberGroupRepository.cs # src/Umbraco.Infrastructure/Persistence/Repositories/Implement/TemplateRepository.cs # src/Umbraco.Infrastructure/Persistence/Repositories/Implement/UserRepository.cs # src/Umbraco.Infrastructure/Services/IdKeyMap.cs # src/Umbraco.Infrastructure/Services/Implement/ContentService.cs # src/Umbraco.ModelsBuilder.Embedded/PureLiveModelFactory.cs # src/Umbraco.Tests/App.config # src/Umbraco.Web.BackOffice/Controllers/EntityController.cs # src/Umbraco.Web.UI.Client/package.json # src/Umbraco.Web.UI.NetCore/umbraco/config/lang/da.xml # src/Umbraco.Web.UI.NetCore/umbraco/config/lang/en.xml # src/Umbraco.Web.UI.NetCore/umbraco/config/lang/en_us.xml # src/Umbraco.Web.UI/Umbraco.Web.UI.csproj # src/Umbraco.Web.UI/Umbraco/config/lang/cy.xml # src/Umbraco.Web.UI/web.Template.config # src/Umbraco.Web/CacheHelperExtensions.cs # src/Umbraco.Web/Editors/RelationTypeController.cs # src/Umbraco.Web/Logging/WebProfilerProvider.cs # src/Umbraco.Web/Models/Mapping/MemberMapDefinition.cs # src/Umbraco.Web/PublishedCache/NuCache/MemberCache.cs # src/Umbraco.Web/Routing/ContentFinderByConfigured404.cs # src/Umbraco.Web/Routing/NotFoundHandlerHelper.cs # src/Umbraco.Web/Security/BackOfficeUserManager.cs # src/Umbraco.Web/Umbraco.Web.csproj
2021-08-11 19:11:35 +02:00
_locker.Dispose();
}
_disposedValue = true;
}
}
public void Dispose()
{
// Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
Dispose(disposing: true);
}
2021-01-13 15:18:59 +11:00
internal class Infos
{
2022-03-29 13:44:21 +02:00
public Dictionary<string, Type>? ModelTypeMap { get; set; }
2021-01-13 15:18:59 +11:00
2022-03-29 13:44:21 +02:00
public Dictionary<string, ModelInfo>? ModelInfos { get; set; }
2021-01-13 15:18:59 +11:00
}
internal class ModelInfo
{
2022-03-29 13:44:21 +02:00
public Type? ParameterType { get; set; }
2021-01-13 15:18:59 +11:00
2022-03-29 13:44:21 +02:00
public Func<IPublishedElement, IPublishedValueFallback, IPublishedElement>? Ctor { get; set; }
2021-01-13 15:18:59 +11:00
2022-03-29 13:44:21 +02:00
public Type? ModelType { get; set; }
2021-01-13 15:18:59 +11:00
2022-03-29 13:44:21 +02:00
public Func<IList>? ListCtor { get; set; }
2021-01-13 15:18:59 +11:00
}
2019-06-24 11:58:36 +02:00
}
}