removes LegacyTransientObjectsResolver (no longer needed), makes CacheRefreshersResolver into a container resolver so we can ctor inject into them (these should NOT be transient instances), Fixes more extension methods that rely on ApplicationContext.Current which exposes a few issues like DescendantsAndSelf since that was only getting them for content types!

This commit is contained in:
Shannon
2016-05-18 18:12:41 +02:00
parent 885595b1e6
commit f51a98435f
10 changed files with 114 additions and 160 deletions

View File

@@ -1,5 +1,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using LightInject;
using Umbraco.Core.Logging;
using Umbraco.Core.ObjectResolution;
using Umbraco.Core.Cache;
@@ -11,36 +13,34 @@ namespace Umbraco.Core
/// <summary>
/// A resolver to return all ICacheRefresher objects
/// </summary>
internal sealed class CacheRefreshersResolver : LegacyTransientObjectsResolver<CacheRefreshersResolver, ICacheRefresher>
internal sealed class CacheRefreshersResolver : ContainerLazyManyObjectsResolver<CacheRefreshersResolver, ICacheRefresher>
{
/// <summary>
/// Constructor
/// </summary>
/// <param name="serviceProvider"></param>
/// <param name="serviceContainer"></param>
/// <param name="logger"></param>
/// <param name="refreshers"></param>
internal CacheRefreshersResolver(IServiceProvider serviceProvider, ILogger logger, Func<IEnumerable<Type>> refreshers)
: base(serviceProvider, logger, refreshers)
internal CacheRefreshersResolver(IServiceContainer serviceContainer, ILogger logger, Func<IEnumerable<Type>> refreshers)
: base(serviceContainer, logger, refreshers)
{
}
/// <summary>
/// Gets the <see cref="ICacheRefresher"/> implementations.
/// </summary>
public IEnumerable<ICacheRefresher> CacheRefreshers
{
get
{
EnsureIsInitialized();
return Values;
}
}
public IEnumerable<ICacheRefresher> CacheRefreshers => Values;
protected override Guid GetUniqueIdentifier(ICacheRefresher obj)
{
return obj.UniqueIdentifier;
}
/// <summary>
/// Returns an instance for the type identified by its unique type identifier.
/// </summary>
/// <param name="id">The type identifier.</param>
/// <returns>The value of the type uniquely identified by <paramref name="id"/>.</returns>
public ICacheRefresher GetById(Guid id)
{
return Values.FirstOrDefault(x => x.UniqueIdentifier == id);
}
}
}

View File

@@ -437,7 +437,7 @@ namespace Umbraco.Core
// new RepositoryFactory(ApplicationCache));
CacheRefreshersResolver.Current = new CacheRefreshersResolver(
ServiceProvider, ProfilingLogger.Logger,
Container, ProfilingLogger.Logger,
() => PluginManager.ResolveCacheRefreshers());
PackageActionsResolver.Current = new PackageActionsResolver(

View File

@@ -10,10 +10,11 @@ namespace Umbraco.Core.Models
/// Get all descendant content types
/// </summary>
/// <param name="contentType"></param>
/// <param name="contentTypeService"></param>
/// <returns></returns>
public static IEnumerable<IContentTypeBase> Descendants(this IContentTypeBase contentType)
{
var contentTypeService = ApplicationContext.Current.Services.ContentTypeService;
public static IEnumerable<TItem> Descendants<TItem>(this TItem contentType, IContentTypeServiceBase<TItem> contentTypeService)
where TItem : IContentTypeComposition
{
var descendants = contentTypeService.GetChildren(contentType.Id)
.SelectRecursive(type => contentTypeService.GetChildren(type.Id));
return descendants;
@@ -23,22 +24,14 @@ namespace Umbraco.Core.Models
/// Get all descendant and self content types
/// </summary>
/// <param name="contentType"></param>
/// <param name="contentTypeService"></param>
/// <returns></returns>
public static IEnumerable<IContentTypeBase> DescendantsAndSelf(this IContentTypeBase contentType)
public static IEnumerable<TItem> DescendantsAndSelf<TItem>(this TItem contentType, IContentTypeServiceBase<TItem> contentTypeService)
where TItem : IContentTypeComposition
{
var descendantsAndSelf = new[] { contentType }.Concat(contentType.Descendants());
var descendantsAndSelf = new[] { contentType }.Concat(contentType.Descendants<TItem>(contentTypeService));
return descendantsAndSelf;
}
///// <summary>
///// Returns the descendant content type Ids for the given content type
///// </summary>
///// <param name="contentType"></param>
///// <returns></returns>
//public static IEnumerable<int> DescendantIds(this IContentTypeBase contentType)
//{
// return ((ContentTypeService) ApplicationContext.Current.Services.ContentTypeService)
// .GetDescendantContentTypeIds(contentType.Id);
//}
}
}

View File

@@ -1,91 +0,0 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Threading;
using Umbraco.Core.Logging;
namespace Umbraco.Core.ObjectResolution
{
/// <summary>
/// The base class for old legacy factories such as the DataTypeFactory or CacheResolverFactory.
/// </summary>
/// <typeparam name="TResolver">The type of the concrete resolver class.</typeparam>
/// <typeparam name="TResolved">The type of the resolved objects.</typeparam>
/// <remarks>
/// This class contains basic functionality to mimic the functionality in these old factories since they all return
/// transient objects (though this should be changed) and the method GetById needs to lookup a type to an ID and since
/// these old classes don't contain metadata, the objects need to be instantiated first to get their metadata, we then store this
/// for use in the GetById method.
/// </remarks>
internal abstract class LegacyTransientObjectsResolver<TResolver, TResolved> : LazyManyObjectsResolverBase<TResolver, TResolved>
where TResolved : class
where TResolver : ResolverBase
{
private readonly ReaderWriterLockSlim _lock = new ReaderWriterLockSlim();
private ConcurrentDictionary<Guid, Type> _id2Type;
#region Constructors
/// <summary>
/// Initializes a new instance of the <see cref="LegacyTransientObjectsResolver{TResolver, TResolved}"/> class with an initial list of object types.
/// </summary>
/// <param name="serviceProvider"></param>
/// <param name="logger"></param>
/// <param name="value">A function returning the list of object types.</param>
/// <remarks>
/// We are creating Transient instances (new instances each time) because this is how the legacy code worked and
/// I don't want to muck anything up by changing them to application based instances.
/// TODO: However, it would make much more sense to do this and would speed up the application plus this would make the GetById method much easier.
/// </remarks>
protected LegacyTransientObjectsResolver(IServiceProvider serviceProvider, ILogger logger, Func<IEnumerable<Type>> value)
: base(serviceProvider, logger, value, ObjectLifetimeScope.Transient) // new objects every time
{ }
#endregion
/// <summary>
/// Returns the unique identifier of the type of a specified object.
/// </summary>
/// <param name="value">The object.</param>
/// <returns>The unique identifier of the type of <paramref name="value"/>.</returns>
protected abstract Guid GetUniqueIdentifier(TResolved value);
/// <summary>
/// Returns a new instance for the type identified by its unique type identifier.
/// </summary>
/// <param name="id">The type identifier.</param>
/// <returns>The value of the type uniquely identified by <paramref name="id"/>.</returns>
public TResolved GetById(Guid id)
{
EnsureIsInitialized();
return _id2Type.ContainsKey(id) == false
? null
: ServiceProvider.GetService(_id2Type[id]) as TResolved;
}
/// <summary>
/// Populates the identifiers-to-types dictionnary.
/// </summary>
/// <remarks>
/// <para>This allow us to instantiate a type by ID since these legacy types doesn't contain any metadata.</para>
/// <para>We instanciate all types once to get their unique identifier, then build the dictionary so that
/// when GetById is called, we can instanciate a single object.</para>
/// </remarks>
protected void EnsureIsInitialized()
{
using (var l = new UpgradeableReadLock(_lock))
{
if (_id2Type == null)
{
l.UpgradeToWriteLock();
_id2Type = new ConcurrentDictionary<Guid, Type>();
foreach (var value in Values)
{
_id2Type.TryAdd(GetUniqueIdentifier(value), value.GetType());
}
}
}
}
}
}

View File

@@ -62,9 +62,35 @@ namespace Umbraco.Core.Services
}
else
{
//if a property was deleted or alias changed, then update all content of the current content type
// and all of it's desscendant doc types.
toUpdate.AddRange(contentType.DescendantsAndSelf());
//TODO: This is pretty nasty, fix this
var contentTypeService = this as IContentTypeService;
if (contentTypeService != null)
{
//if a property was deleted or alias changed, then update all content of the current content type
// and all of it's desscendant doc types.
toUpdate.AddRange(((IContentType) contentType).DescendantsAndSelf(contentTypeService));
}
else
{
var mediaTypeService = this as IMediaTypeService;
if (mediaTypeService != null)
{
//if a property was deleted or alias changed, then update all content of the current content type
// and all of it's desscendant doc types.
toUpdate.AddRange(((IMediaType) contentType).DescendantsAndSelf(mediaTypeService));
}
else
{
var memberTypeService = this as IMemberTypeService;
if (memberTypeService != null)
{
//if a property was deleted or alias changed, then update all content of the current content type
// and all of it's desscendant doc types.
toUpdate.AddRange(((IMemberType)contentType).DescendantsAndSelf(memberTypeService));
}
}
}
}
}
}
@@ -550,7 +576,7 @@ namespace Umbraco.Core.Services
uow.WriteLock(WriteLockIds);
// all descendants are going to be deleted
var descendantsAndSelf = item.DescendantsAndSelf()
var descendantsAndSelf = item.DescendantsAndSelf(this)
.ToArray();
// delete content
@@ -587,7 +613,7 @@ namespace Umbraco.Core.Services
uow.WriteLock(WriteLockIds);
// all descendants are going to be deleted
var allDescendantsAndSelf = itemsA.SelectMany(xx => xx.DescendantsAndSelf())
var allDescendantsAndSelf = itemsA.SelectMany(xx => xx.DescendantsAndSelf(this))
.Distinct()
.ToArray();

View File

@@ -1191,7 +1191,6 @@
<Compile Include="LambdaExpressionCacheKey.cs" />
<Compile Include="_Legacy\PackageActions\IPackageAction.cs" />
<Compile Include="_Legacy\PackageActions\PackageActionsResolver.cs" />
<Compile Include="ObjectResolution\LegacyTransientObjectsResolver.cs" />
<Compile Include="Logging\AsynchronousRollingFileAppender.cs" />
<Compile Include="Logging\LoggingTaskExtension.cs" />
<Compile Include="Logging\LogHelper.cs" />

View File

@@ -29,7 +29,7 @@ namespace Umbraco.Tests.Cache.DistributedCache
ServerMessengerResolver.Current = new ServerMessengerResolver(
container, factory => new TestServerMessenger());
CacheRefreshersResolver.Current = new CacheRefreshersResolver(
new ActivatorServiceProvider(), Mock.Of<ILogger>(), () => new[] { typeof(TestCacheRefresher) });
container, Mock.Of<ILogger>(), () => new[] { typeof(TestCacheRefresher) });
Resolution.Freeze();
}

View File

@@ -123,7 +123,7 @@ namespace Umbraco.Tests.Services
var master = hierarchy.First();
//Act
var descendants = master.Descendants();
var descendants = master.Descendants(ServiceContext.ContentTypeService);
//Assert
Assert.AreEqual(10, descendants.Count());
@@ -139,7 +139,7 @@ namespace Umbraco.Tests.Services
var master = hierarchy.First();
//Act
var descendants = master.DescendantsAndSelf();
var descendants = master.DescendantsAndSelf(ServiceContext.ContentTypeService);
//Assert
Assert.AreEqual(11, descendants.Count());

View File

@@ -1,16 +1,11 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web.Script.Serialization;
using Umbraco.Core;
using Umbraco.Core.Cache;
using Umbraco.Core.Models;
using Umbraco.Core.Models.EntityBase;
using Umbraco.Core.Models.PublishedContent;
using Umbraco.Core.Models.Rdbms;
using Umbraco.Web.PublishedCache;
using Umbraco.Web.PublishedCache.XmlPublishedCache;
namespace Umbraco.Web.Cache
@@ -24,6 +19,12 @@ namespace Umbraco.Web.Cache
/// </remarks>
public sealed class ContentTypeCacheRefresher : JsonCacheRefresherBase<ContentTypeCacheRefresher>
{
private readonly ApplicationContext _applicationContext;
public ContentTypeCacheRefresher(ApplicationContext applicationContext)
{
_applicationContext = applicationContext;
}
#region Static helpers
@@ -45,7 +46,7 @@ namespace Umbraco.Web.Cache
/// <param name="contentType"></param>
/// <param name="isDeleted">if the item was deleted</param>
/// <returns></returns>
private static JsonPayload FromContentType(IContentTypeBase contentType, bool isDeleted = false)
private JsonPayload FromContentType(IContentTypeBase contentType, bool isDeleted = false)
{
var payload = new JsonPayload
{
@@ -57,10 +58,30 @@ namespace Umbraco.Web.Cache
? typeof(IContentType).Name
: (contentType is IMediaType)
? typeof(IMediaType).Name
: typeof(IMemberType).Name,
DescendantPayloads = contentType.Descendants().Select(x => FromContentType(x)).ToArray(),
: typeof(IMemberType).Name,
WasDeleted = isDeleted
};
if (contentType is IContentType)
{
payload.DescendantPayloads = ((IContentType)contentType)
.Descendants(_applicationContext.Services.ContentTypeService)
.Select(x => FromContentType(x)).ToArray();
}
else if (contentType is IMediaType)
{
payload.DescendantPayloads = ((IMediaType)contentType)
.Descendants(_applicationContext.Services.MediaTypeService)
.Select(x => FromContentType(x)).ToArray();
}
else if (contentType is IMemberType)
{
payload.DescendantPayloads = ((IMemberType)contentType)
.Descendants(_applicationContext.Services.MemberTypeService)
.Select(x => FromContentType(x)).ToArray();
}
//here we need to check if the alias of the content type changed or if one of the properties was removed.
var dirty = contentType as IRememberBeingDirty;
if (dirty != null)
@@ -78,7 +99,7 @@ namespace Umbraco.Web.Cache
/// <param name="isDeleted">specify false if this is an update, otherwise true if it is a deletion</param>
/// <param name="contentTypes"></param>
/// <returns></returns>
internal static string SerializeToJsonPayload(bool isDeleted, params IContentTypeBase[] contentTypes)
internal string SerializeToJsonPayload(bool isDeleted, params IContentTypeBase[] contentTypes)
{
var serializer = new JavaScriptSerializer();
var items = contentTypes.Select(x => FromContentType(x, isDeleted)).ToArray();
@@ -134,11 +155,11 @@ namespace Umbraco.Web.Cache
ClearAllIsolatedCacheByEntityType<IMemberType>();
//all property type cache
ApplicationContext.Current.ApplicationCache.RuntimeCache.ClearCacheByKeySearch(CacheKeys.PropertyTypeCacheKey);
_applicationContext.ApplicationCache.RuntimeCache.ClearCacheByKeySearch(CacheKeys.PropertyTypeCacheKey);
//all content type property cache
ApplicationContext.Current.ApplicationCache.RuntimeCache.ClearCacheByKeySearch(CacheKeys.ContentTypePropertiesCacheKey);
_applicationContext.ApplicationCache.RuntimeCache.ClearCacheByKeySearch(CacheKeys.ContentTypePropertiesCacheKey);
//all content type cache
ApplicationContext.Current.ApplicationCache.RuntimeCache.ClearCacheByKeySearch(CacheKeys.ContentTypeCacheKey);
_applicationContext.ApplicationCache.RuntimeCache.ClearCacheByKeySearch(CacheKeys.ContentTypeCacheKey);
//clear static object cache
global::umbraco.cms.businesslogic.ContentType.RemoveAllDataTypeCache();
@@ -187,8 +208,8 @@ namespace Umbraco.Web.Cache
{
var needsContentRefresh = false;
ApplicationContext.Current.ApplicationCache.RuntimeCache.ClearCacheByKeySearch(CacheKeys.IdToKeyCacheKey);
ApplicationContext.Current.ApplicationCache.RuntimeCache.ClearCacheByKeySearch(CacheKeys.KeyToIdCacheKey);
_applicationContext.ApplicationCache.RuntimeCache.ClearCacheByKeySearch(CacheKeys.IdToKeyCacheKey);
_applicationContext.ApplicationCache.RuntimeCache.ClearCacheByKeySearch(CacheKeys.KeyToIdCacheKey);
payloads.ForEach(payload =>
{
@@ -265,18 +286,18 @@ namespace Umbraco.Web.Cache
/// <returns>
/// Return true if the alias of the content type changed
/// </returns>
private static void ClearContentTypeCache(JsonPayload payload)
private void ClearContentTypeCache(JsonPayload payload)
{
//clears the cache for each property type associated with the content type
foreach (var pid in payload.PropertyTypeIds)
{
ApplicationContext.Current.ApplicationCache.RuntimeCache.ClearCacheItem(CacheKeys.PropertyTypeCacheKey + pid);
_applicationContext.ApplicationCache.RuntimeCache.ClearCacheItem(CacheKeys.PropertyTypeCacheKey + pid);
}
//clears the cache associated with the Content type itself
ApplicationContext.Current.ApplicationCache.RuntimeCache.ClearCacheItem(string.Format("{0}{1}", CacheKeys.ContentTypeCacheKey, payload.Id));
_applicationContext.ApplicationCache.RuntimeCache.ClearCacheItem(string.Format("{0}{1}", CacheKeys.ContentTypeCacheKey, payload.Id));
//clears the cache associated with the content type properties collection
ApplicationContext.Current.ApplicationCache.RuntimeCache.ClearCacheItem(CacheKeys.ContentTypePropertiesCacheKey + payload.Id);
_applicationContext.ApplicationCache.RuntimeCache.ClearCacheItem(CacheKeys.ContentTypePropertiesCacheKey + payload.Id);
//clears the dictionary object cache of the legacy ContentType
global::umbraco.cms.businesslogic.ContentType.RemoveFromDataTypeCache(payload.Alias);
@@ -300,7 +321,7 @@ namespace Umbraco.Web.Cache
ClearContentTypeCache(
ids.Select(
x =>
ApplicationContext.Current.Services.ContentTypeService.Get(x) as IContentTypeBase)
_applicationContext.Services.ContentTypeService.Get(x) as IContentTypeBase)
.WhereNotNull()
.Select(x => FromContentType(x, isDeleted))
.ToArray());

View File

@@ -304,13 +304,15 @@ namespace Umbraco.Web.Cache
public static void RefreshContentTypeCache(this DistributedCache dc, IContentType contentType)
{
if (contentType == null) return;
dc.RefreshByJson(DistributedCache.ContentTypeCacheRefresherGuid, ContentTypeCacheRefresher.SerializeToJsonPayload(false, contentType));
var contentTypeCacheRefresher = (ContentTypeCacheRefresher)CacheRefreshersResolver.Current.GetById(DistributedCache.ContentTypeCacheRefresherGuid);
dc.RefreshByJson(DistributedCache.ContentTypeCacheRefresherGuid, contentTypeCacheRefresher.SerializeToJsonPayload(false, contentType));
}
public static void RemoveContentTypeCache(this DistributedCache dc, IContentType contentType)
{
if (contentType == null) return;
dc.RefreshByJson(DistributedCache.ContentTypeCacheRefresherGuid, ContentTypeCacheRefresher.SerializeToJsonPayload(true, contentType));
var contentTypeCacheRefresher = (ContentTypeCacheRefresher)CacheRefreshersResolver.Current.GetById(DistributedCache.ContentTypeCacheRefresherGuid);
dc.RefreshByJson(DistributedCache.ContentTypeCacheRefresherGuid, contentTypeCacheRefresher.SerializeToJsonPayload(true, contentType));
}
#endregion
@@ -320,13 +322,15 @@ namespace Umbraco.Web.Cache
public static void RefreshMediaTypeCache(this DistributedCache dc, IMediaType mediaType)
{
if (mediaType == null) return;
dc.RefreshByJson(DistributedCache.ContentTypeCacheRefresherGuid, ContentTypeCacheRefresher.SerializeToJsonPayload(false, mediaType));
var contentTypeCacheRefresher = (ContentTypeCacheRefresher)CacheRefreshersResolver.Current.GetById(DistributedCache.ContentTypeCacheRefresherGuid);
dc.RefreshByJson(DistributedCache.ContentTypeCacheRefresherGuid, contentTypeCacheRefresher.SerializeToJsonPayload(false, mediaType));
}
public static void RemoveMediaTypeCache(this DistributedCache dc, IMediaType mediaType)
{
if (mediaType == null) return;
dc.RefreshByJson(DistributedCache.ContentTypeCacheRefresherGuid, ContentTypeCacheRefresher.SerializeToJsonPayload(true, mediaType));
var contentTypeCacheRefresher = (ContentTypeCacheRefresher)CacheRefreshersResolver.Current.GetById(DistributedCache.ContentTypeCacheRefresherGuid);
dc.RefreshByJson(DistributedCache.ContentTypeCacheRefresherGuid, contentTypeCacheRefresher.SerializeToJsonPayload(true, mediaType));
}
#endregion
@@ -336,13 +340,15 @@ namespace Umbraco.Web.Cache
public static void RefreshMemberTypeCache(this DistributedCache dc, IMemberType memberType)
{
if (memberType == null) return;
dc.RefreshByJson(DistributedCache.ContentTypeCacheRefresherGuid, ContentTypeCacheRefresher.SerializeToJsonPayload(false, memberType));
var contentTypeCacheRefresher = (ContentTypeCacheRefresher)CacheRefreshersResolver.Current.GetById(DistributedCache.ContentTypeCacheRefresherGuid);
dc.RefreshByJson(DistributedCache.ContentTypeCacheRefresherGuid, contentTypeCacheRefresher.SerializeToJsonPayload(false, memberType));
}
public static void RemoveMemberTypeCache(this DistributedCache dc, IMemberType memberType)
{
if (memberType == null) return;
dc.RefreshByJson(DistributedCache.ContentTypeCacheRefresherGuid, ContentTypeCacheRefresher.SerializeToJsonPayload(true, memberType));
var contentTypeCacheRefresher = (ContentTypeCacheRefresher)CacheRefreshersResolver.Current.GetById(DistributedCache.ContentTypeCacheRefresherGuid);
dc.RefreshByJson(DistributedCache.ContentTypeCacheRefresherGuid, contentTypeCacheRefresher.SerializeToJsonPayload(true, memberType));
}
#endregion