2016-05-27 14:26:28 +02:00
|
|
|
|
using System;
|
|
|
|
|
|
using System.Xml.Serialization;
|
|
|
|
|
|
using Umbraco.Core.Cache;
|
|
|
|
|
|
using Umbraco.Core.Models.PublishedContent;
|
|
|
|
|
|
using Umbraco.Core.PropertyEditors;
|
|
|
|
|
|
|
|
|
|
|
|
namespace Umbraco.Web.PublishedCache.NuCache
|
|
|
|
|
|
{
|
|
|
|
|
|
[Serializable]
|
|
|
|
|
|
[XmlType(Namespace = "http://umbraco.org/webservices/")]
|
2017-07-12 14:09:31 +02:00
|
|
|
|
internal class Property : PublishedPropertyBase
|
2016-05-27 14:26:28 +02:00
|
|
|
|
{
|
2016-05-30 19:54:36 +02:00
|
|
|
|
private readonly IFacadeAccessor _facadeAccessor;
|
2016-06-10 16:37:28 +02:00
|
|
|
|
private readonly object _sourceValue;
|
2016-05-27 14:26:28 +02:00
|
|
|
|
private readonly Guid _contentUid;
|
|
|
|
|
|
private readonly bool _isPreviewing;
|
|
|
|
|
|
private readonly bool _isMember;
|
2017-07-21 17:19:00 +02:00
|
|
|
|
private readonly IPublishedContent _content;
|
2016-05-27 14:26:28 +02:00
|
|
|
|
|
2016-06-10 16:37:28 +02:00
|
|
|
|
private readonly object _locko = new object();
|
2016-05-27 14:26:28 +02:00
|
|
|
|
|
2016-06-10 16:37:28 +02:00
|
|
|
|
private bool _interInitialized;
|
|
|
|
|
|
private object _interValue;
|
|
|
|
|
|
private CacheValues _cacheValues;
|
|
|
|
|
|
private string _valuesCacheKey;
|
2016-05-27 14:26:28 +02:00
|
|
|
|
private string _recurseCacheKey;
|
|
|
|
|
|
|
|
|
|
|
|
// initializes a published content property with no value
|
2017-07-21 17:19:00 +02:00
|
|
|
|
public Property(PublishedPropertyType propertyType, PublishedContent content, IFacadeAccessor facadeAccessor, PropertyCacheLevel referenceCacheLevel = PropertyCacheLevel.Content)
|
2016-06-10 16:37:28 +02:00
|
|
|
|
: this(propertyType, content, null, facadeAccessor, referenceCacheLevel)
|
2016-05-27 14:26:28 +02:00
|
|
|
|
{ }
|
|
|
|
|
|
|
|
|
|
|
|
// initializes a published content property with a value
|
2017-07-21 17:19:00 +02:00
|
|
|
|
public Property(PublishedPropertyType propertyType, PublishedContent content, object sourceValue, IFacadeAccessor facadeAccessor, PropertyCacheLevel referenceCacheLevel = PropertyCacheLevel.Content)
|
2016-06-10 16:37:28 +02:00
|
|
|
|
: base(propertyType, referenceCacheLevel)
|
2016-05-27 14:26:28 +02:00
|
|
|
|
{
|
2016-06-10 16:37:28 +02:00
|
|
|
|
_sourceValue = sourceValue;
|
2016-05-27 14:26:28 +02:00
|
|
|
|
_contentUid = content.Key;
|
2017-07-21 17:19:00 +02:00
|
|
|
|
_content = content;
|
|
|
|
|
|
_isPreviewing = content.IsPreviewing;
|
2016-05-27 14:26:28 +02:00
|
|
|
|
_isMember = content.ContentType.ItemType == PublishedItemType.Member;
|
2016-05-30 19:54:36 +02:00
|
|
|
|
_facadeAccessor = facadeAccessor;
|
2016-05-27 14:26:28 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// clone for previewing as draft a published content that is published and has no draft
|
2017-07-21 17:19:00 +02:00
|
|
|
|
public Property(Property origin, IPublishedContent content)
|
2016-06-10 16:37:28 +02:00
|
|
|
|
: base(origin.PropertyType, origin.ReferenceCacheLevel)
|
2016-05-27 14:26:28 +02:00
|
|
|
|
{
|
2016-06-10 16:37:28 +02:00
|
|
|
|
_sourceValue = origin._sourceValue;
|
2016-05-27 14:26:28 +02:00
|
|
|
|
_contentUid = origin._contentUid;
|
2017-07-21 17:19:00 +02:00
|
|
|
|
_content = content;
|
2016-05-27 14:26:28 +02:00
|
|
|
|
_isPreviewing = true;
|
|
|
|
|
|
_isMember = origin._isMember;
|
2016-05-30 19:54:36 +02:00
|
|
|
|
_facadeAccessor = origin._facadeAccessor;
|
2016-05-27 14:26:28 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2016-06-10 16:37:28 +02:00
|
|
|
|
public override bool HasValue => _sourceValue != null
|
|
|
|
|
|
&& ((_sourceValue is string) == false || string.IsNullOrWhiteSpace((string)_sourceValue) == false);
|
2016-05-27 14:26:28 +02:00
|
|
|
|
|
2016-06-10 16:37:28 +02:00
|
|
|
|
private class CacheValues
|
2016-05-27 14:26:28 +02:00
|
|
|
|
{
|
2016-06-10 16:37:28 +02:00
|
|
|
|
public bool ObjectInitialized;
|
|
|
|
|
|
public object ObjectValue;
|
2016-05-27 14:26:28 +02:00
|
|
|
|
public bool XPathInitialized;
|
2016-06-10 16:37:28 +02:00
|
|
|
|
public object XPathValue;
|
2016-05-27 14:26:28 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2016-06-10 16:37:28 +02:00
|
|
|
|
// used to cache the recursive *property* for this property
|
|
|
|
|
|
internal string RecurseCacheKey => _recurseCacheKey
|
2016-05-30 19:54:36 +02:00
|
|
|
|
?? (_recurseCacheKey = CacheKeys.PropertyRecurse(_contentUid, PropertyTypeAlias, _isPreviewing));
|
2016-05-27 14:26:28 +02:00
|
|
|
|
|
2016-06-10 16:37:28 +02:00
|
|
|
|
// used to cache the CacheValues of this property
|
|
|
|
|
|
internal string ValuesCacheKey => _valuesCacheKey
|
|
|
|
|
|
?? (_valuesCacheKey = CacheKeys.PropertyCacheValues(_contentUid, PropertyTypeAlias, _isPreviewing));
|
2016-05-27 14:26:28 +02:00
|
|
|
|
|
2016-06-10 16:37:28 +02:00
|
|
|
|
private CacheValues GetCacheValues(PropertyCacheLevel cacheLevel)
|
2016-05-27 14:26:28 +02:00
|
|
|
|
{
|
2016-06-10 16:37:28 +02:00
|
|
|
|
CacheValues cacheValues;
|
2016-05-27 14:26:28 +02:00
|
|
|
|
Facade facade;
|
|
|
|
|
|
ICacheProvider cache;
|
|
|
|
|
|
switch (cacheLevel)
|
|
|
|
|
|
{
|
|
|
|
|
|
case PropertyCacheLevel.None:
|
|
|
|
|
|
// never cache anything
|
2016-06-10 16:37:28 +02:00
|
|
|
|
cacheValues = new CacheValues();
|
2016-05-27 14:26:28 +02:00
|
|
|
|
break;
|
|
|
|
|
|
case PropertyCacheLevel.Content:
|
|
|
|
|
|
// cache within the property object itself, ie within the content object
|
2016-06-10 16:37:28 +02:00
|
|
|
|
cacheValues = _cacheValues ?? (_cacheValues = new CacheValues());
|
2016-05-27 14:26:28 +02:00
|
|
|
|
break;
|
2016-06-10 16:37:28 +02:00
|
|
|
|
case PropertyCacheLevel.Snapshot:
|
2016-05-27 14:26:28 +02:00
|
|
|
|
// cache within the snapshot cache, unless previewing, then use the facade or
|
|
|
|
|
|
// snapshot cache (if we don't want to pollute the snapshot cache with short-lived
|
|
|
|
|
|
// data) depending on settings
|
|
|
|
|
|
// for members, always cache in the facade cache - never pollute snapshot cache
|
2016-05-30 19:54:36 +02:00
|
|
|
|
facade = (Facade) _facadeAccessor.Facade;
|
2016-05-27 14:26:28 +02:00
|
|
|
|
cache = facade == null
|
2016-06-10 16:37:28 +02:00
|
|
|
|
? null
|
2016-05-27 14:26:28 +02:00
|
|
|
|
: ((_isPreviewing == false || FacadeService.FullCacheWhenPreviewing) && (_isMember == false)
|
2016-06-10 16:37:28 +02:00
|
|
|
|
? facade.SnapshotCache
|
2016-05-27 14:26:28 +02:00
|
|
|
|
: facade.FacadeCache);
|
2016-06-10 16:37:28 +02:00
|
|
|
|
cacheValues = GetCacheValues(cache);
|
2016-05-27 14:26:28 +02:00
|
|
|
|
break;
|
2016-06-10 16:37:28 +02:00
|
|
|
|
case PropertyCacheLevel.Facade:
|
2016-05-27 14:26:28 +02:00
|
|
|
|
// cache within the facade cache
|
2016-06-10 16:37:28 +02:00
|
|
|
|
facade = (Facade) _facadeAccessor.Facade;
|
2016-05-30 19:54:36 +02:00
|
|
|
|
cache = facade?.FacadeCache;
|
2016-06-10 16:37:28 +02:00
|
|
|
|
cacheValues = GetCacheValues(cache);
|
2016-05-27 14:26:28 +02:00
|
|
|
|
break;
|
|
|
|
|
|
default:
|
|
|
|
|
|
throw new InvalidOperationException("Invalid cache level.");
|
|
|
|
|
|
}
|
2016-06-10 16:37:28 +02:00
|
|
|
|
return cacheValues;
|
2016-05-27 14:26:28 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2016-06-10 16:37:28 +02:00
|
|
|
|
private CacheValues GetCacheValues(ICacheProvider cache)
|
2016-05-27 14:26:28 +02:00
|
|
|
|
{
|
|
|
|
|
|
if (cache == null) // no cache, don't cache
|
2016-06-10 16:37:28 +02:00
|
|
|
|
return new CacheValues();
|
|
|
|
|
|
return (CacheValues) cache.GetCacheItem(ValuesCacheKey, () => new CacheValues());
|
2016-05-27 14:26:28 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2016-06-10 16:37:28 +02:00
|
|
|
|
private object GetInterValue()
|
2016-05-27 14:26:28 +02:00
|
|
|
|
{
|
2016-06-10 16:37:28 +02:00
|
|
|
|
if (_interInitialized) return _interValue;
|
|
|
|
|
|
|
2017-07-21 17:19:00 +02:00
|
|
|
|
_interValue = PropertyType.ConvertSourceToInter(_content, _sourceValue, _isPreviewing);
|
2016-06-10 16:37:28 +02:00
|
|
|
|
_interInitialized = true;
|
|
|
|
|
|
return _interValue;
|
2016-05-27 14:26:28 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2016-06-10 16:37:28 +02:00
|
|
|
|
public override object SourceValue => _sourceValue;
|
2016-05-27 14:26:28 +02:00
|
|
|
|
|
|
|
|
|
|
public override object Value
|
|
|
|
|
|
{
|
|
|
|
|
|
get
|
|
|
|
|
|
{
|
|
|
|
|
|
lock (_locko)
|
|
|
|
|
|
{
|
2016-06-10 16:37:28 +02:00
|
|
|
|
var cacheValues = GetCacheValues(PropertyType.CacheLevel);
|
|
|
|
|
|
if (cacheValues.ObjectInitialized) return cacheValues.ObjectValue;
|
|
|
|
|
|
|
|
|
|
|
|
// initial reference cache level always is .Content
|
2017-07-21 17:19:00 +02:00
|
|
|
|
cacheValues.ObjectValue = PropertyType.ConvertInterToObject(_content, PropertyCacheLevel.Content, GetInterValue(), _isPreviewing);
|
2016-06-10 16:37:28 +02:00
|
|
|
|
cacheValues.ObjectInitialized = true;
|
|
|
|
|
|
return cacheValues.ObjectValue;
|
2016-05-27 14:26:28 +02:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public override object XPathValue
|
|
|
|
|
|
{
|
|
|
|
|
|
get
|
|
|
|
|
|
{
|
|
|
|
|
|
lock (_locko)
|
|
|
|
|
|
{
|
2016-06-10 16:37:28 +02:00
|
|
|
|
var cacheValues = GetCacheValues(PropertyType.CacheLevel);
|
|
|
|
|
|
if (cacheValues.XPathInitialized) return cacheValues.XPathValue;
|
|
|
|
|
|
|
|
|
|
|
|
// initial reference cache level always is .Content
|
2017-07-21 17:19:00 +02:00
|
|
|
|
cacheValues.XPathValue = PropertyType.ConvertInterToXPath(_content, PropertyCacheLevel.Content, GetInterValue(), _isPreviewing);
|
2016-06-10 16:37:28 +02:00
|
|
|
|
cacheValues.XPathInitialized = true;
|
|
|
|
|
|
return cacheValues.XPathValue;
|
2016-05-27 14:26:28 +02:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|