Refactor PublishedContentTypes

This commit is contained in:
Stephan
2017-10-17 17:43:15 +02:00
parent cfe5350269
commit 14eb4fe6e0
41 changed files with 840 additions and 427 deletions

View File

@@ -18,6 +18,7 @@ using Umbraco.Core.Persistence;
using Umbraco.Core.Persistence.DatabaseModelDefinitions;
using Umbraco.Core.Persistence.Repositories;
using Umbraco.Core.Persistence.UnitOfWork;
using Umbraco.Core.PropertyEditors;
using Umbraco.Core.Scoping;
using Umbraco.Core.Services;
using Umbraco.Core.Services.Changes;
@@ -33,6 +34,7 @@ namespace Umbraco.Web.PublishedCache.NuCache
class FacadeService : FacadeServiceBase
{
private readonly ServiceContext _serviceContext;
private readonly IPublishedContentTypeFactory _publishedContentTypeFactory;
private readonly IScopeProvider _scopeProvider;
private readonly IScopeUnitOfWorkProvider _uowProvider;
private readonly Database _dataSource;
@@ -73,13 +75,16 @@ namespace Umbraco.Web.PublishedCache.NuCache
//private static int _singletonCheck;
public FacadeService(Options options, MainDom mainDom, IRuntimeState runtime, ServiceContext serviceContext, IScopeUnitOfWorkProvider uowProvider, IFacadeAccessor facadeAccessor, ILogger logger, IScopeProvider scopeProvider)
public FacadeService(Options options, MainDom mainDom, IRuntimeState runtime,
ServiceContext serviceContext, IPublishedContentTypeFactory publishedContentTypeFactory,
IScopeUnitOfWorkProvider uowProvider, IFacadeAccessor facadeAccessor, ILogger logger, IScopeProvider scopeProvider)
: base(facadeAccessor)
{
//if (Interlocked.Increment(ref _singletonCheck) > 1)
// throw new Exception("Singleton must be instancianted only once!");
_serviceContext = serviceContext;
_publishedContentTypeFactory = publishedContentTypeFactory;
_uowProvider = uowProvider;
_dataSource = new Database();
_logger = logger;
@@ -270,7 +275,7 @@ namespace Umbraco.Web.PublishedCache.NuCache
// content (and types) are read-locked
var contentTypes = _serviceContext.ContentTypeService.GetAll()
.Select(x => new PublishedContentType(PublishedItemType.Content, x));
.Select(x => _publishedContentTypeFactory.CreateContentType(PublishedItemType.Content, x));
_contentStore.UpdateContentTypes(null, contentTypes, null);
_localContentDb?.Clear();
@@ -286,7 +291,7 @@ namespace Umbraco.Web.PublishedCache.NuCache
private void LoadContentFromLocalDbLocked(IScopeUnitOfWork uow)
{
var contentTypes = _serviceContext.ContentTypeService.GetAll()
.Select(x => new PublishedContentType(PublishedItemType.Content, x));
.Select(x => _publishedContentTypeFactory.CreateContentType(PublishedItemType.Content, x));
_contentStore.UpdateContentTypes(null, contentTypes, null);
_logger.Debug<FacadeService>("Loading content from local db...");
@@ -338,7 +343,7 @@ namespace Umbraco.Web.PublishedCache.NuCache
// locks & notes: see content
var mediaTypes = _serviceContext.MediaTypeService.GetAll()
.Select(x => new PublishedContentType(PublishedItemType.Media, x));
.Select(x => _publishedContentTypeFactory.CreateContentType(PublishedItemType.Media, x));
_mediaStore.UpdateContentTypes(null, mediaTypes, null);
_localMediaDb?.Clear();
@@ -354,7 +359,7 @@ namespace Umbraco.Web.PublishedCache.NuCache
private void LoadMediaFromLocalDbLocked(IScopeUnitOfWork uow)
{
var mediaTypes = _serviceContext.MediaTypeService.GetAll()
.Select(x => new PublishedContentType(PublishedItemType.Media, x));
.Select(x => _publishedContentTypeFactory.CreateContentType(PublishedItemType.Media, x));
_mediaStore.UpdateContentTypes(null, mediaTypes, null);
_logger.Debug<FacadeService>("Loading media from local db...");
@@ -815,8 +820,12 @@ namespace Umbraco.Web.PublishedCache.NuCache
#region Content Types
private IEnumerable<PublishedContentType> CreateContentTypes(PublishedItemType itemType, params int[] ids)
private IEnumerable<PublishedContentType> CreateContentTypes(PublishedItemType itemType, int[] ids)
{
// XxxTypeService.GetAll(empty) returns everything!
if (ids.Length == 0)
return Enumerable.Empty<PublishedContentType>();
IEnumerable<IContentTypeComposition> contentTypes;
switch (itemType)
{
@@ -835,7 +844,7 @@ namespace Umbraco.Web.PublishedCache.NuCache
// some may be missing - not checking here
return contentTypes.Select(x => new PublishedContentType(itemType, x));
return contentTypes.Select(x => _publishedContentTypeFactory.CreateContentType(itemType, x));
}
private PublishedContentType CreateContentType(PublishedItemType itemType, int id)
@@ -856,7 +865,7 @@ namespace Umbraco.Web.PublishedCache.NuCache
throw new ArgumentOutOfRangeException(nameof(itemType));
}
return contentType == null ? null : new PublishedContentType(itemType, contentType);
return contentType == null ? null : _publishedContentTypeFactory.CreateContentType(itemType, contentType);
}
private void RefreshContentTypesLocked(IEnumerable<int> removedIds, IEnumerable<int> refreshedIds, IEnumerable<int> otherIds, IEnumerable<int> newIds)
@@ -981,7 +990,7 @@ namespace Umbraco.Web.PublishedCache.NuCache
var facadeCache = new StaticCacheProvider();
var memberTypeCache = new PublishedContentTypeCache(null, null, _serviceContext.MemberTypeService, _logger);
var memberTypeCache = new PublishedContentTypeCache(null, null, _serviceContext.MemberTypeService, _publishedContentTypeFactory, _logger);
var domainCache = new DomainCache(domainSnap);

View File

@@ -5,6 +5,7 @@ using Umbraco.Core.Persistence.UnitOfWork;
using Umbraco.Core.Scoping;
using Umbraco.Core.Services;
using LightInject;
using Umbraco.Core.Models.PublishedContent;
namespace Umbraco.Web.PublishedCache.NuCache
{
@@ -20,6 +21,7 @@ namespace Umbraco.Web.PublishedCache.NuCache
factory.GetInstance<MainDom>(),
factory.GetInstance<IRuntimeState>(),
factory.GetInstance<ServiceContext>(),
factory.GetInstance<IPublishedContentTypeFactory>(),
factory.GetInstance<IScopeUnitOfWorkProvider>(),
factory.GetInstance<IFacadeAccessor>(),
factory.GetInstance<ILogger>(),

View File

@@ -2,7 +2,6 @@
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using Umbraco.Core;
using Umbraco.Core.Logging;
using Umbraco.Core.Models;
using Umbraco.Core.Models.PublishedContent;
@@ -10,8 +9,10 @@ using Umbraco.Core.Services;
namespace Umbraco.Web.PublishedCache
{
// caches content, media and member types
/// <summary>
/// Represents a content type cache.
/// </summary>
/// <remarks>This cache is not snapshotted, so it refreshes any time things change.</remarks>
public class PublishedContentTypeCache
{
private readonly Dictionary<string, PublishedContentType> _typesByAlias = new Dictionary<string, PublishedContentType>();
@@ -19,51 +20,87 @@ namespace Umbraco.Web.PublishedCache
private readonly IContentTypeService _contentTypeService;
private readonly IMediaTypeService _mediaTypeService;
private readonly IMemberTypeService _memberTypeService;
public readonly ILogger _logger;
private readonly IPublishedContentTypeFactory _publishedContentTypeFactory;
private readonly ILogger _logger;
private readonly ReaderWriterLockSlim _lock = new ReaderWriterLockSlim();
internal PublishedContentTypeCache(IContentTypeService contentTypeService, IMediaTypeService mediaTypeService, IMemberTypeService memberTypeService, ILogger logger)
// default ctor
internal PublishedContentTypeCache(IContentTypeService contentTypeService, IMediaTypeService mediaTypeService, IMemberTypeService memberTypeService, IPublishedContentTypeFactory publishedContentTypeFactory, ILogger logger)
{
_contentTypeService = contentTypeService;
_mediaTypeService = mediaTypeService;
_memberTypeService = memberTypeService;
_logger = logger;
_publishedContentTypeFactory = publishedContentTypeFactory;
}
// for unit tests ONLY
internal PublishedContentTypeCache(ILogger logger)
internal PublishedContentTypeCache(ILogger logger, IPublishedContentTypeFactory publishedContentTypeFactory)
{
_logger = logger;
_publishedContentTypeFactory = publishedContentTypeFactory;
}
/// <summary>
/// Clears all cached content types.
/// </summary>
public void ClearAll()
{
_logger.Debug<PublishedContentTypeCache>("Clear all.");
using (new WriteLock(_lock))
try
{
_lock.EnterWriteLock();
_typesByAlias.Clear();
_typesById.Clear();
}
finally
{
if (_lock.IsWriteLockHeld)
_lock.ExitWriteLock();
}
}
/// <summary>
/// Clears a cached content type.
/// </summary>
/// <param name="id">An identifier.</param>
public void ClearContentType(int id)
{
_logger.Debug<PublishedContentTypeCache>("Clear content type w/id {0}.", () => id);
using (var l = new UpgradeableReadLock(_lock))
try
{
PublishedContentType type;
if (_typesById.TryGetValue(id, out type) == false)
_lock.EnterUpgradeableReadLock();
if (_typesById.TryGetValue(id, out var type) == false)
return;
l.UpgradeToWriteLock();
try
{
_lock.EnterWriteLock();
_typesByAlias.Remove(GetAliasKey(type));
_typesById.Remove(id);
_typesByAlias.Remove(GetAliasKey(type));
_typesById.Remove(id);
}
finally
{
if (_lock.IsWriteLockHeld)
_lock.ExitWriteLock();
}
}
finally
{
if (_lock.IsUpgradeableReadLockHeld)
_lock.ExitUpgradeableReadLock();
}
}
/// <summary>
/// Clears all cached content types referencing a data type.
/// </summary>
/// <param name="id">A data type identifier.</param>
public void ClearDataType(int id)
{
_logger.Debug<PublishedContentTypeCache>("Clear data type w/id {0}.", () => id);
@@ -73,8 +110,10 @@ namespace Umbraco.Web.PublishedCache
// IContentTypeComposition) and so every PublishedContentType having a property based upon
// the cleared data type, be it local or inherited, will be cleared.
using (new WriteLock(_lock))
try
{
_lock.EnterWriteLock();
var toRemove = _typesById.Values.Where(x => x.PropertyTypes.Any(xx => xx.DataTypeId == id)).ToArray();
foreach (var type in toRemove)
{
@@ -82,32 +121,84 @@ namespace Umbraco.Web.PublishedCache
_typesById.Remove(type.Id);
}
}
}
public PublishedContentType Get(PublishedItemType itemType, string alias)
{
var aliasKey = GetAliasKey(itemType, alias);
using (var l = new UpgradeableReadLock(_lock))
finally
{
PublishedContentType type;
if (_typesByAlias.TryGetValue(aliasKey, out type))
return type;
type = CreatePublishedContentType(itemType, alias);
l.UpgradeToWriteLock();
return _typesByAlias[aliasKey] = _typesById[type.Id] = type;
if (_lock.IsWriteLockHeld)
_lock.ExitWriteLock();
}
}
/// <summary>
/// Gets a published content type.
/// </summary>
/// <param name="itemType">An item type.</param>
/// <param name="alias">An alias.</param>
/// <returns>The published content type corresponding to the item type and alias.</returns>
public PublishedContentType Get(PublishedItemType itemType, string alias)
{
var aliasKey = GetAliasKey(itemType, alias);
try
{
_lock.EnterUpgradeableReadLock();
if (_typesByAlias.TryGetValue(aliasKey, out var type))
return type;
type = CreatePublishedContentType(itemType, alias);
try
{
_lock.EnterWriteLock();
return _typesByAlias[aliasKey] = _typesById[type.Id] = type;
}
finally
{
if (_lock.IsWriteLockHeld)
_lock.ExitWriteLock();
}
}
finally
{
if (_lock.IsUpgradeableReadLockHeld)
_lock.ExitUpgradeableReadLock();
}
}
/// <summary>
/// Gets a published content type.
/// </summary>
/// <param name="itemType">An item type.</param>
/// <param name="id">An identifier.</param>
/// <returns>The published content type corresponding to the item type and identifier.</returns>
public PublishedContentType Get(PublishedItemType itemType, int id)
{
using (var l = new UpgradeableReadLock(_lock))
try
{
PublishedContentType type;
if (_typesById.TryGetValue(id, out type))
_lock.EnterUpgradeableReadLock();
if (_typesById.TryGetValue(id, out var type))
return type;
type = CreatePublishedContentType(itemType, id);
l.UpgradeToWriteLock();
return _typesByAlias[GetAliasKey(type)] = _typesById[type.Id] = type;
try
{
_lock.EnterWriteLock();
return _typesByAlias[GetAliasKey(type)] = _typesById[type.Id] = type;
}
finally
{
if (_lock.IsWriteLockHeld)
_lock.ExitWriteLock();
}
}
finally
{
if (_lock.IsUpgradeableReadLockHeld)
_lock.ExitUpgradeableReadLock();
}
}
@@ -135,7 +226,7 @@ namespace Umbraco.Web.PublishedCache
if (contentType == null)
throw new Exception($"ContentTypeService failed to find a {itemType.ToString().ToLower()} type with alias \"{alias}\".");
return new PublishedContentType(itemType, contentType);
return _publishedContentTypeFactory.CreateContentType(itemType, contentType);
}
private PublishedContentType CreatePublishedContentType(PublishedItemType itemType, int id)
@@ -162,22 +253,29 @@ namespace Umbraco.Web.PublishedCache
if (contentType == null)
throw new Exception($"ContentTypeService failed to find a {itemType.ToString().ToLower()} type with id {id}.");
return new PublishedContentType(itemType, contentType);
return _publishedContentTypeFactory.CreateContentType(itemType, contentType);
}
// for unit tests - changing the callback must reset the cache obviously
private Func<string, PublishedContentType> _getPublishedContentTypeByAlias;
internal Func<string, PublishedContentType> GetPublishedContentTypeByAlias
{
get { return _getPublishedContentTypeByAlias; }
get => _getPublishedContentTypeByAlias;
set
{
using (new WriteLock(_lock))
try
{
_lock.EnterWriteLock();
_typesByAlias.Clear();
_typesById.Clear();
_getPublishedContentTypeByAlias = value;
}
finally
{
if (_lock.IsWriteLockHeld)
_lock.ExitWriteLock();
}
}
}
@@ -185,15 +283,22 @@ namespace Umbraco.Web.PublishedCache
private Func<int, PublishedContentType> _getPublishedContentTypeById;
internal Func<int, PublishedContentType> GetPublishedContentTypeById
{
get { return _getPublishedContentTypeById; }
get => _getPublishedContentTypeById;
set
{
using (new WriteLock(_lock))
try
{
_lock.EnterWriteLock();
_typesByAlias.Clear();
_typesById.Clear();
_getPublishedContentTypeById = value;
}
finally
{
if (_lock.IsWriteLockHeld)
_lock.ExitWriteLock();
}
}
}
@@ -201,13 +306,20 @@ namespace Umbraco.Web.PublishedCache
{
string k;
if (itemType == PublishedItemType.Content)
k = "c";
else if (itemType == PublishedItemType.Media)
k = "m";
else if (itemType == PublishedItemType.Member)
k = "m";
else throw new ArgumentOutOfRangeException(nameof(itemType));
switch (itemType)
{
case PublishedItemType.Content:
k = "c";
break;
case PublishedItemType.Media:
k = "m";
break;
case PublishedItemType.Member:
k = "m";
break;
default:
throw new ArgumentOutOfRangeException(nameof(itemType));
}
return k + ":" + alias;
}

View File

@@ -1,5 +1,4 @@
using System;
using System.Collections.Generic;
using System.Collections.Generic;
using System.Linq;
using Umbraco.Core;
using Umbraco.Core.Cache;
@@ -9,7 +8,6 @@ using Umbraco.Core.Models;
using Umbraco.Core.Models.Membership;
using Umbraco.Core.Models.PublishedContent;
using Umbraco.Core.Persistence.UnitOfWork;
using Umbraco.Core.PropertyEditors;
using Umbraco.Core.Scoping;
using Umbraco.Core.Services;
using Umbraco.Core.Strings;
@@ -35,6 +33,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache
// used in WebBootManager + tests
public FacadeService(ServiceContext serviceContext,
IPublishedContentTypeFactory publishedContentTypeFactory,
IScopeProvider scopeProvider,
IScopeUnitOfWorkProvider uowProvider,
ICacheProvider requestCache,
@@ -43,11 +42,12 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache
ILogger logger,
MainDom mainDom,
bool testing = false, bool enableRepositoryEvents = true)
: this(serviceContext, scopeProvider, uowProvider, requestCache, segmentProviders, facadeAccessor, logger, null, mainDom, testing, enableRepositoryEvents)
: this(serviceContext, publishedContentTypeFactory, scopeProvider, uowProvider, requestCache, segmentProviders, facadeAccessor, logger, null, mainDom, testing, enableRepositoryEvents)
{ }
// used in some tests
internal FacadeService(ServiceContext serviceContext,
IPublishedContentTypeFactory publishedContentTypeFactory,
IScopeProvider scopeProvider,
IScopeUnitOfWorkProvider uowProvider,
ICacheProvider requestCache,
@@ -56,10 +56,11 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache
PublishedContentTypeCache contentTypeCache,
MainDom mainDom,
bool testing, bool enableRepositoryEvents)
: this(serviceContext, scopeProvider, uowProvider, requestCache, Enumerable.Empty<IUrlSegmentProvider>(), facadeAccessor, logger, contentTypeCache, mainDom, testing, enableRepositoryEvents)
: this(serviceContext, publishedContentTypeFactory, scopeProvider, uowProvider, requestCache, Enumerable.Empty<IUrlSegmentProvider>(), facadeAccessor, logger, contentTypeCache, mainDom, testing, enableRepositoryEvents)
{ }
private FacadeService(ServiceContext serviceContext,
IPublishedContentTypeFactory publishedContentTypeFactory,
IScopeProvider scopeProvider,
IScopeUnitOfWorkProvider uowProvider,
ICacheProvider requestCache,
@@ -73,7 +74,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache
{
_routesCache = new RoutesCache();
_contentTypeCache = contentTypeCache
?? new PublishedContentTypeCache(serviceContext.ContentTypeService, serviceContext.MediaTypeService, serviceContext.MemberTypeService, logger);
?? new PublishedContentTypeCache(serviceContext.ContentTypeService, serviceContext.MediaTypeService, serviceContext.MemberTypeService, publishedContentTypeFactory, logger);
_xmlStore = new XmlStore(serviceContext, scopeProvider, uowProvider, _routesCache, _contentTypeCache, segmentProviders, facadeAccessor, mainDom, testing, enableRepositoryEvents);

View File

@@ -8,6 +8,7 @@ using Umbraco.Core.Logging;
using Umbraco.Core.Scoping;
using Umbraco.Web.HealthCheck.Checks.DataIntegrity;
using LightInject;
using Umbraco.Core.Models.PublishedContent;
namespace Umbraco.Web.PublishedCache.XmlPublishedCache
{
@@ -21,6 +22,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache
// register the XML facade service
composition.SetFacadeService(factory => new FacadeService(
factory.GetInstance<ServiceContext>(),
factory.GetInstance<IPublishedContentTypeFactory>(),
factory.GetInstance<IScopeProvider>(),
factory.GetInstance<IScopeUnitOfWorkProvider>(),
factory.GetInstance<CacheHelper>().RequestCache,