U4-8447 - prepare for NuCache (work-in-progress)

This commit is contained in:
Stephan
2016-05-26 17:12:04 +02:00
parent 8682940efb
commit 06574b8b40
197 changed files with 9380 additions and 9956 deletions

View File

@@ -7,31 +7,48 @@ namespace Umbraco.Core.Models
internal static class ContentTypeExtensions
{
/// <summary>
/// Get all descendant content types
/// Gets all descendant content types of a specified content type.
/// </summary>
/// <param name="contentType"></param>
/// <param name="contentTypeService"></param>
/// <returns></returns>
public static IEnumerable<TItem> Descendants<TItem>(this TItem contentType, IContentTypeServiceBase<TItem> contentTypeService)
/// <param name="contentType">The content type.</param>
/// <param name="contentTypeService">The content type service.</param>
/// <returns>The descendant content types.</returns>
/// <remarks>Descendants corresponds to the parent-child relationship, and has
/// nothing to do with compositions, though a child should always be composed
/// of its parent.</remarks>
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;
{
return contentTypeService.GetDescendants(contentType.Id, false);
}
/// <summary>
/// Get all descendant and self content types
/// Gets all descendant and self content types of a specified content type.
/// </summary>
/// <param name="contentType"></param>
/// <param name="contentTypeService"></param>
/// <returns></returns>
public static IEnumerable<TItem> DescendantsAndSelf<TItem>(this TItem contentType, IContentTypeServiceBase<TItem> contentTypeService)
/// <param name="contentType">The content type.</param>
/// <param name="contentTypeService">The content type service.</param>
/// <returns>The descendant and self content types.</returns>
/// <remarks>Descendants corresponds to the parent-child relationship, and has
/// nothing to do with compositions, though a child should always be composed
/// of its parent.</remarks>
public static IEnumerable<TItem> DescendantsAndSelf<TItem>(this TItem contentType, IContentTypeServiceBase<TItem> contentTypeService)
where TItem : IContentTypeComposition
{
var descendantsAndSelf = new[] { contentType }.Concat(contentType.Descendants<TItem>(contentTypeService));
return descendantsAndSelf;
return contentTypeService.GetDescendants(contentType.Id, true);
}
/// <summary>
/// Gets all content types directly or indirectly composed of a specified content type.
/// </summary>
/// <param name="contentType">The content type.</param>
/// <param name="contentTypeService">The content type service.</param>
/// <returns>The content types directly or indirectly composed of the content type.</returns>
/// <remarks>This corresponds to the composition relationship and has nothing to do
/// with the parent-child relationship, though a child should always be composed of
/// its parent.</remarks>
public static IEnumerable<TItem> ComposedOf<TItem>(this TItem contentType, IContentTypeServiceBase<TItem> contentTypeService)
where TItem : IContentTypeComposition
{
return contentTypeService.GetComposedOf(contentType.Id);
}
}
}

View File

@@ -1,7 +1,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Umbraco.Core.Cache;
namespace Umbraco.Core.Models.PublishedContent
{
@@ -21,23 +20,51 @@ namespace Umbraco.Core.Models.PublishedContent
// internal so it can be used by PublishedNoCache which does _not_ want to cache anything and so will never
// use the static cache getter PublishedContentType.GetPublishedContentType(alias) below - anything else
// should use it.
internal PublishedContentType(IContentTypeComposition contentType)
internal PublishedContentType(IContentType contentType)
: this(PublishedItemType.Content, contentType)
{ }
internal PublishedContentType(IMediaType mediaType)
: this(PublishedItemType.Media, mediaType)
{ }
internal PublishedContentType(IMemberType memberType)
: this(PublishedItemType.Member, memberType)
{ }
internal PublishedContentType(PublishedItemType itemType, IContentTypeComposition contentType)
{
Id = contentType.Id;
Alias = contentType.Alias;
ItemType = itemType;
_compositionAliases = new HashSet<string>(contentType.CompositionAliases(), StringComparer.InvariantCultureIgnoreCase);
_propertyTypes = contentType.CompositionPropertyTypes
.Select(x => new PublishedPropertyType(this, x))
.ToArray();
var propertyTypes = contentType.CompositionPropertyTypes
.Select(x => new PublishedPropertyType(this, x));
if (itemType == PublishedItemType.Member)
propertyTypes = WithMemberProperties(propertyTypes, this);
_propertyTypes = propertyTypes.ToArray();
InitializeIndexes();
}
// internal so it can be used for unit tests
internal PublishedContentType(int id, string alias, IEnumerable<PublishedPropertyType> propertyTypes)
: this(id, alias, PublishedItemType.Content, Enumerable.Empty<string>(), propertyTypes)
{ }
// internal so it can be used for unit tests
internal PublishedContentType(int id, string alias, IEnumerable<string> compositionAliases, IEnumerable<PublishedPropertyType> propertyTypes)
: this(id, alias, PublishedItemType.Content, compositionAliases, propertyTypes)
{ }
// internal so it can be used for unit tests
internal PublishedContentType(int id, string alias, PublishedItemType itemType, IEnumerable<string> compositionAliases, IEnumerable<PublishedPropertyType> propertyTypes)
{
Id = id;
Alias = alias;
ItemType = itemType;
_compositionAliases = new HashSet<string>(compositionAliases, StringComparer.InvariantCultureIgnoreCase);
if (itemType == PublishedItemType.Member)
propertyTypes = WithMemberProperties(propertyTypes);
_propertyTypes = propertyTypes.ToArray();
foreach (var propertyType in _propertyTypes)
propertyType.ContentType = this;
@@ -59,20 +86,62 @@ namespace Umbraco.Core.Models.PublishedContent
}
}
// NOTE: code below defines and add custom, built-in, Umbraco properties for members
// unless they are already user-defined in the content type, then they are skipped
// fixme should have constants for these
private const int TextboxDataTypeDefinitionId = -88;
//private const int BooleanDataTypeDefinitionId = -49;
//private const int DatetimeDataTypeDefinitionId = -36;
static readonly Dictionary<string, Tuple<int, string>> BuiltinProperties = new Dictionary<string, Tuple<int, string>>
{
// fixme is this ok?
{ "Email", Tuple.Create(TextboxDataTypeDefinitionId, Constants.PropertyEditors.TextboxAlias) },
{ "Username", Tuple.Create(TextboxDataTypeDefinitionId, Constants.PropertyEditors.TextboxAlias) },
//{ "PasswordQuestion", Tuple.Create(TextboxDataTypeDefinitionId, Constants.PropertyEditors.TextboxAlias) },
//{ "Comments", Tuple.Create(TextboxDataTypeDefinitionId, Constants.PropertyEditors.TextboxAlias) },
//{ "IsApproved", Tuple.Create(BooleanDataTypeDefinitionId, Constants.PropertyEditors.BooleanEditorAlias) },
//{ "IsLockedOut", Tuple.Create(BooleanDataTypeDefinitionId, Constants.PropertyEditors.BooleanEditorAlias) },
//{ "LastLockoutDate", Tuple.Create(DatetimeDataTypeDefinitionId, Constants.PropertyEditors.DatetimeEditorAlias) },
//{ "CreateDate", Tuple.Create(DatetimeDataTypeDefinitionId, Constants.PropertyEditors.DatetimeEditorAlias) },
//{ "LastLoginDate", Tuple.Create(DatetimeDataTypeDefinitionId, Constants.PropertyEditors.DatetimeEditorAlias) },
//{ "LastPasswordChangeDate", Tuple.Create(DatetimeDataTypeDefinitionId, Constants.PropertyEditors.DatetimeEditorAlias) },
};
private static IEnumerable<PublishedPropertyType> WithMemberProperties(IEnumerable<PublishedPropertyType> propertyTypes,
PublishedContentType contentType = null)
{
var aliases = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
foreach (var propertyType in propertyTypes)
{
aliases.Add(propertyType.PropertyTypeAlias);
yield return propertyType;
}
foreach (var kvp in BuiltinProperties.Where(kvp => aliases.Contains(kvp.Key) == false))
{
var propertyType = new PublishedPropertyType(kvp.Key, kvp.Value.Item1, kvp.Value.Item2, true);
if (contentType != null) propertyType.ContentType = contentType;
yield return propertyType;
}
}
#region Content type
public int Id { get; private set; }
public string Alias { get; private set; }
public HashSet<string> CompositionAliases { get { return _compositionAliases; } }
public PublishedItemType ItemType { get; private set; }
public HashSet<string> CompositionAliases => _compositionAliases;
#endregion
#region Properties
public IEnumerable<PublishedPropertyType> PropertyTypes
{
get { return _propertyTypes; }
}
public IEnumerable<PublishedPropertyType> PropertyTypes => _propertyTypes;
// alias is case-insensitive
// this is the ONLY place where we compare ALIASES!
@@ -98,108 +167,5 @@ namespace Umbraco.Core.Models.PublishedContent
}
#endregion
#region Cache
// these methods are called by ContentTypeCacheRefresher and DataTypeCacheRefresher
internal static void ClearAll()
{
Logging.LogHelper.Debug<PublishedContentType>("Clear all.");
// ok and faster to do it by types, assuming noone else caches PublishedContentType instances
//ApplicationContext.Current.ApplicationCache.ClearStaticCacheByKeySearch("PublishedContentType_");
ApplicationContext.Current.ApplicationCache.StaticCache.ClearCacheObjectTypes<PublishedContentType>();
}
internal static void ClearContentType(int id)
{
Logging.LogHelper.Debug<PublishedContentType>("Clear content type w/id {0}.", () => id);
// we don't support "get all" at the moment - so, cheating
var all = ApplicationContext.Current.ApplicationCache.StaticCache.GetCacheItemsByKeySearch<PublishedContentType>("PublishedContentType_").ToArray();
// the one we want to clear
var clr = all.FirstOrDefault(x => x.Id == id);
if (clr == null) return;
// those that have that one in their composition aliases
// note: CompositionAliases contains all recursive aliases
var oth = all.Where(x => x.CompositionAliases.InvariantContains(clr.Alias)).Select(x => x.Id);
// merge ids
var ids = oth.Concat(new[] { clr.Id }).ToArray();
// clear them all at once
// we don't support "clear many at once" at the moment - so, cheating
ApplicationContext.Current.ApplicationCache.StaticCache.ClearCacheObjectTypes<PublishedContentType>(
(key, value) => ids.Contains(value.Id));
}
internal static void ClearDataType(int id)
{
Logging.LogHelper.Debug<PublishedContentType>("Clear data type w/id {0}.", () => id);
// there is no recursion to handle here because a PublishedContentType contains *all* its
// properties ie both its own properties and those that were inherited (it's based upon an
// IContentTypeComposition) and so every PublishedContentType having a property based upon
// the cleared data type, be it local or inherited, will be cleared.
ApplicationContext.Current.ApplicationCache.StaticCache.ClearCacheObjectTypes<PublishedContentType>(
(key, value) => value.PropertyTypes.Any(x => x.DataTypeId == id));
}
public static PublishedContentType Get(PublishedItemType itemType, string alias)
{
var key = string.Format("PublishedContentType_{0}_{1}",
itemType.ToString().ToLowerInvariant(), alias.ToLowerInvariant());
var type = ApplicationContext.Current.ApplicationCache.StaticCache.GetCacheItem<PublishedContentType>(key,
() => CreatePublishedContentType(itemType, alias));
return type;
}
private static PublishedContentType CreatePublishedContentType(PublishedItemType itemType, string alias)
{
if (GetPublishedContentTypeCallback != null)
return GetPublishedContentTypeCallback(alias);
IContentTypeComposition contentType;
switch (itemType)
{
case PublishedItemType.Content:
contentType = ApplicationContext.Current.Services.ContentTypeService.Get(alias);
break;
case PublishedItemType.Media:
contentType = ApplicationContext.Current.Services.MediaTypeService.Get(alias);
break;
case PublishedItemType.Member:
contentType = ApplicationContext.Current.Services.MemberTypeService.Get(alias);
break;
default:
throw new ArgumentOutOfRangeException("itemType");
}
if (contentType == null)
throw new Exception(string.Format("ContentTypeService failed to find a {0} type with alias \"{1}\".",
itemType.ToString().ToLower(), alias));
return new PublishedContentType(contentType);
}
// for unit tests - changing the callback must reset the cache obviously
private static Func<string, PublishedContentType> _getPublishedContentTypeCallBack;
internal static Func<string, PublishedContentType> GetPublishedContentTypeCallback
{
get { return _getPublishedContentTypeCallBack; }
set
{
// see note above
//ClearAll();
ApplicationContext.Current.ApplicationCache.StaticCache.ClearCacheByKeySearch("PublishedContentType_");
_getPublishedContentTypeCallBack = value;
}
}
#endregion
}
}

View File

@@ -1,5 +1,4 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Xml.Linq;
@@ -107,14 +106,15 @@ namespace Umbraco.Core.Models.PublishedContent
/// <param name="propertyTypeAlias">The property type alias.</param>
/// <param name="dataTypeDefinitionId">The datatype definition identifier.</param>
/// <param name="propertyEditorAlias">The property editor alias.</param>
/// <param name="umbraco">A value indicating whether the property is an Umbraco-defined property.</param>
/// <remarks>
/// <para>The new published property type does not belong to a published content type.</para>
/// <para>The values of <paramref name="dataTypeDefinitionId"/> and <paramref name="propertyEditorAlias"/> are
/// assumed to be valid and consistent.</para>
/// </remarks>
internal PublishedPropertyType(string propertyTypeAlias, int dataTypeDefinitionId, string propertyEditorAlias)
internal PublishedPropertyType(string propertyTypeAlias, int dataTypeDefinitionId, string propertyEditorAlias, bool umbraco = false)
{
// ContentType
// ContentType
// - in unit tests, to be set by PublishedContentType when creating it
// - in detached types, remains null
@@ -122,6 +122,7 @@ namespace Umbraco.Core.Models.PublishedContent
DataTypeId = dataTypeDefinitionId;
PropertyEditorAlias = propertyEditorAlias;
IsUmbraco = umbraco;
InitializeConverters();
}
@@ -139,17 +140,22 @@ namespace Umbraco.Core.Models.PublishedContent
/// <summary>
/// Gets or sets the alias uniquely identifying the property type.
/// </summary>
public string PropertyTypeAlias { get; private set; }
public string PropertyTypeAlias { get; }
/// <summary>
/// Gets or sets the identifier uniquely identifying the data type supporting the property type.
/// </summary>
public int DataTypeId { get; private set; }
public int DataTypeId { get; }
/// <summary>
/// Gets or sets the alias uniquely identifying the property editor for the property type.
/// </summary>
public string PropertyEditorAlias { get; private set; }
public string PropertyEditorAlias { get; }
/// <summary>
/// Gets or sets a value indicating whether the property is an Umbraco-defined property.
/// </summary>
internal bool IsUmbraco { get; private set; }
#endregion
@@ -168,11 +174,11 @@ namespace Umbraco.Core.Models.PublishedContent
//TODO: Look at optimizing this method, it gets run for every property type for the document being rendered at startup,
// every precious second counts!
var converters = PropertyValueConvertersResolver.Current.Converters.ToArray();
var converters = PropertyValueConvertersResolver.Current.Converters.ToArray();
var defaultConvertersWithAttributes = PropertyValueConvertersResolver.Current.DefaultConverters;
_converter = null;
//get all converters for this property type
var foundConverters = converters.Where(x => x.IsConverter(this)).ToArray();
if (foundConverters.Length == 1)
@@ -199,7 +205,7 @@ namespace Umbraco.Core.Models.PublishedContent
ContentType.Alias, PropertyTypeAlias,
nonDefault[1].GetType().FullName, nonDefault[0].GetType().FullName));
}
else
else
{
//we need to remove any converters that have been shadowed by another converter
var foundDefaultConvertersWithAttributes = defaultConvertersWithAttributes.Where(x => foundConverters.Contains(x.Item1));
@@ -210,7 +216,7 @@ namespace Umbraco.Core.Models.PublishedContent
if (nonShadowedDefaultConverters.Length == 1)
{
//assign to the single default converter
_converter = nonShadowedDefaultConverters[0];
_converter = nonShadowedDefaultConverters[0];
}
else if (nonShadowedDefaultConverters.Length > 1)
{
@@ -223,7 +229,7 @@ namespace Umbraco.Core.Models.PublishedContent
nonShadowedDefaultConverters[1].GetType().FullName, nonShadowedDefaultConverters[0].GetType().FullName));
}
}
}
var converterMeta = _converter as IPropertyValueConverterMeta;
@@ -268,9 +274,9 @@ namespace Umbraco.Core.Models.PublishedContent
var attr = converter.GetType().GetCustomAttributes<PropertyValueCacheAttribute>(false)
.FirstOrDefault(x => x.Value == value || x.Value == PropertyCacheValue.All);
return attr == null ? PropertyCacheLevel.Request : attr.Level;
return attr?.Level ?? PropertyCacheLevel.Request;
}
// converts the raw value into the source value
// uses converters, else falls back to dark (& performance-wise expensive) magic
// source: the property raw value
@@ -278,13 +284,13 @@ namespace Umbraco.Core.Models.PublishedContent
public object ConvertDataToSource(object source, bool preview)
{
// use the converter else use dark (& performance-wise expensive) magic
return _converter != null
? _converter.ConvertDataToSource(this, source, preview)
return _converter != null
? _converter.ConvertDataToSource(this, source, preview)
: ConvertUsingDarkMagic(source);
}
// gets the source cache level
public PropertyCacheLevel SourceCacheLevel { get { return _sourceCacheLevel; } }
public PropertyCacheLevel SourceCacheLevel => _sourceCacheLevel;
// converts the source value into the clr value
// uses converters, else returns the source value
@@ -295,12 +301,12 @@ namespace Umbraco.Core.Models.PublishedContent
// use the converter if any
// else just return the source value
return _converter != null
? _converter.ConvertSourceToObject(this, source, preview)
? _converter.ConvertSourceToObject(this, source, preview)
: source;
}
// gets the value cache level
public PropertyCacheLevel ObjectCacheLevel { get { return _objectCacheLevel; } }
public PropertyCacheLevel ObjectCacheLevel => _objectCacheLevel;
// converts the source value into the xpath value
// uses the converter else returns the source value as a string
@@ -322,7 +328,7 @@ namespace Umbraco.Core.Models.PublishedContent
}
// gets the xpath cache level
public PropertyCacheLevel XPathCacheLevel { get { return _xpathCacheLevel; } }
public PropertyCacheLevel XPathCacheLevel => _xpathCacheLevel;
internal static object ConvertUsingDarkMagic(object source)
{
@@ -356,23 +362,17 @@ namespace Umbraco.Core.Models.PublishedContent
}
// gets the property CLR type
public Type ClrType { get { return _clrType; } }
public Type ClrType => _clrType;
#endregion
#region Detached
private PropertyCacheLevel _sourceCacheLevelReduced = 0;
private PropertyCacheLevel _objectCacheLevelReduced = 0;
private PropertyCacheLevel _xpathCacheLevelReduced = 0;
internal bool IsDetachedOrNested
{
// enough to test source
get { return _sourceCacheLevelReduced != 0; }
}
internal bool IsDetachedOrNested => _sourceCacheLevelReduced != 0;
/// <summary>
/// Creates a detached clone of this published property type.
@@ -389,13 +389,13 @@ namespace Umbraco.Core.Models.PublishedContent
throw new Exception("PublishedPropertyType is already detached/nested.");
var detached = new PublishedPropertyType(this);
detached._sourceCacheLevel
= detached._objectCacheLevel
= detached._xpathCacheLevel
detached._sourceCacheLevel
= detached._objectCacheLevel
= detached._xpathCacheLevel
= PropertyCacheLevel.Content;
// set to none to a) indicate it's detached / nested and b) make sure any nested
// types switch all their cache to .Content
detached._sourceCacheLevelReduced
detached._sourceCacheLevelReduced
= detached._objectCacheLevelReduced
= detached._xpathCacheLevelReduced
= PropertyCacheLevel.None;