Merge pull request #1351 from umbraco/temp-u4-8649

U4-8469 - xml cache TLC
This commit is contained in:
Shannon Deminick
2016-06-27 17:16:06 +02:00
committed by GitHub
4 changed files with 175 additions and 132 deletions

View File

@@ -277,15 +277,13 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache
private static IPublishedContent ConvertToDocument(XmlNode xmlNode, bool isPreviewing)
{
return xmlNode == null
? null
: (new XmlPublishedContent(xmlNode, isPreviewing)).CreateModel();
return xmlNode == null ? null : XmlPublishedContent.Get(xmlNode, isPreviewing);
}
private static IEnumerable<IPublishedContent> ConvertToDocuments(XmlNodeList xmlNodes, bool isPreviewing)
{
return xmlNodes.Cast<XmlNode>()
.Select(xmlNode => (new XmlPublishedContent(xmlNode, isPreviewing)).CreateModel());
.Select(xmlNode => XmlPublishedContent.Get(xmlNode, isPreviewing));
}
#endregion

View File

@@ -224,10 +224,12 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache
"Could not retrieve media {0} from Examine index, reverting to looking up media via legacy library.GetMedia method",
() => id);
var media = global::umbraco.library.GetMedia(id, false);
//var media = global::umbraco.library.GetMedia(id, false);
//return ConvertFromXPathNodeIterator(media, id);
return ConvertFromXPathNodeIterator(media, id);
}
var media = ApplicationContext.Current.Services.MediaService.GetById(id);
return media == null ? null : ConvertFromIMedia(media);
}
internal CacheValues ConvertFromXPathNodeIterator(XPathNodeIterator media, int id)
{
@@ -364,14 +366,49 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache
//return content.CreateModel();
}
/// <summary>
/// We will need to first check if the document was loaded by Examine, if so we'll need to check if this property exists
/// in the results, if it does not, then we'll have to revert to looking up in the db.
/// </summary>
/// <param name="dd"> </param>
/// <param name="alias"></param>
/// <returns></returns>
private IPublishedProperty GetProperty(DictionaryPublishedContent dd, string alias)
internal CacheValues ConvertFromIMedia(IMedia media)
{
var values = new Dictionary<string, string>();
var creator = _applicationContext.Services.UserService.GetProfileById(media.CreatorId);
var creatorName = creator == null ? "" : creator.Name;
values["id"] = media.Id.ToString();
values["key"] = media.Key.ToString();
values["parentID"] = media.ParentId.ToString();
values["level"] = media.Level.ToString();
values["creatorID"] = media.CreatorId.ToString();
values["creatorName"] = creatorName;
values["writerID"] = media.CreatorId.ToString();
values["writerName"] = creatorName;
values["template"] = "0";
values["urlName"] = "";
values["sortOrder"] = media.SortOrder.ToString();
values["createDate"] = media.CreateDate.ToString("yyyy-MM-dd HH:mm:ss");
values["updateDate"] = media.UpdateDate.ToString("yyyy-MM-dd HH:mm:ss");
values["nodeName"] = media.Name;
values["path"] = media.Path;
values["nodeType"] = media.ContentType.Id.ToString();
values["nodeTypeAlias"] = media.ContentType.Alias;
// add the user props
foreach (var prop in media.Properties)
values[prop.Alias] = prop.Value == null ? null : prop.Value.ToString();
return new CacheValues
{
Values = values
};
}
/// <summary>
/// We will need to first check if the document was loaded by Examine, if so we'll need to check if this property exists
/// in the results, if it does not, then we'll have to revert to looking up in the db.
/// </summary>
/// <param name="dd"> </param>
/// <param name="alias"></param>
/// <returns></returns>
private IPublishedProperty GetProperty(DictionaryPublishedContent dd, string alias)
{
//lets check if the alias does not exist on the document.
//NOTE: Examine will not index empty values and we do not output empty XML Elements to the cache - either of these situations
@@ -601,7 +638,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache
_keysAdded.Add(alias);
string value;
const bool isPreviewing = false; // false :: never preview a media
var property = valueDictionary.TryGetValue(alias, out value) == false
var property = valueDictionary.TryGetValue(alias, out value) == false || value == null
? new XmlPublishedProperty(propertyType, isPreviewing)
: new XmlPublishedProperty(propertyType, isPreviewing, value);
_properties.Add(property);

View File

@@ -1,6 +1,5 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Xml;
using System.Xml.Serialization;
@@ -26,44 +25,26 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache
/// </summary>
/// <param name="xmlNode">The Xml node.</param>
/// <param name="isPreviewing">A value indicating whether the published content is being previewed.</param>
public XmlPublishedContent(XmlNode xmlNode, bool isPreviewing)
private XmlPublishedContent(XmlNode xmlNode, bool isPreviewing)
{
_xmlNode = xmlNode;
_isPreviewing = isPreviewing;
InitializeStructure();
Initialize();
InitializeChildren();
}
/// <summary>
/// Initializes a new instance of the <c>XmlPublishedContent</c> class with an Xml node,
/// and a value indicating whether to lazy-initialize the instance.
/// </summary>
/// <param name="xmlNode">The Xml node.</param>
/// <param name="isPreviewing">A value indicating whether the published content is being previewed.</param>
/// <param name="lazyInitialize">A value indicating whether to lazy-initialize the instance.</param>
/// <remarks>Lazy-initializationg is NOT thread-safe.</remarks>
internal XmlPublishedContent(XmlNode xmlNode, bool isPreviewing, bool lazyInitialize)
{
_xmlNode = xmlNode;
_isPreviewing = isPreviewing;
InitializeStructure();
if (lazyInitialize == false)
{
Initialize();
InitializeChildren();
}
}
private readonly XmlNode _xmlNode;
private bool _initialized;
private readonly bool _isPreviewing;
private bool _nodeInitialized;
private bool _parentInitialized;
private bool _childrenInitialized;
private readonly ICollection<IPublishedContent> _children = new Collection<IPublishedContent>();
private IEnumerable<IPublishedContent> _children = Enumerable.Empty<IPublishedContent>();
private IPublishedContent _parent;
private int _id;
private PublishedContentType _contentType;
private Dictionary<string, IPublishedProperty> _properties;
private int _id;
private Guid _key;
private int _template;
private string _name;
@@ -78,28 +59,25 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache
private DateTime _createDate;
private DateTime _updateDate;
private Guid _version;
private IPublishedProperty[] _properties;
private int _sortOrder;
private int _level;
private bool _isDraft;
private readonly bool _isPreviewing;
private PublishedContentType _contentType;
public override IEnumerable<IPublishedContent> Children
{
get
{
if (_initialized == false)
Initialize();
if (_childrenInitialized == false)
InitializeChildren();
return _children.OrderBy(x => x.SortOrder);
if (_nodeInitialized == false) InitializeNode();
if (_childrenInitialized == false) InitializeChildren();
return _children;
}
}
public override IPublishedProperty GetProperty(string alias)
{
return Properties.FirstOrDefault(x => x.PropertyTypeAlias.InvariantEquals(alias));
if (_nodeInitialized == false) InitializeNode();
IPublishedProperty property;
return _properties.TryGetValue(alias, out property) ? property : null;
}
// override to implement cache
@@ -116,7 +94,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache
var key = string.Format("RECURSIVE_PROPERTY::{0}::{1}", Id, alias.ToLowerInvariant());
var value = cache.GetOrAdd(key, k => base.GetProperty(alias, true));
if (value == null)
if (value == null)
return null;
var property = value as IPublishedProperty;
@@ -135,8 +113,8 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache
{
get
{
if (_initialized == false)
Initialize();
if (_nodeInitialized == false) InitializeNode();
if (_parentInitialized == false) InitializeParent();
return _parent;
}
}
@@ -145,8 +123,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache
{
get
{
if (_initialized == false)
Initialize();
if (_nodeInitialized == false) InitializeNode();
return _id;
}
}
@@ -155,8 +132,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache
{
get
{
if (_initialized == false)
Initialize();
if (_nodeInitialized == false) InitializeNode();
return _key;
}
}
@@ -165,8 +141,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache
{
get
{
if (_initialized == false)
Initialize();
if (_nodeInitialized == false) InitializeNode();
return _template;
}
}
@@ -175,8 +150,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache
{
get
{
if (_initialized == false)
Initialize();
if (_nodeInitialized == false) InitializeNode();
return _sortOrder;
}
}
@@ -185,8 +159,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache
{
get
{
if (_initialized == false)
Initialize();
if (_nodeInitialized == false) InitializeNode();
return _name;
}
}
@@ -195,8 +168,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache
{
get
{
if (_initialized == false)
Initialize();
if (_nodeInitialized == false) InitializeNode();
return _docTypeAlias;
}
}
@@ -205,8 +177,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache
{
get
{
if (_initialized == false)
Initialize();
if (_nodeInitialized == false) InitializeNode();
return _docTypeId;
}
}
@@ -215,8 +186,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache
{
get
{
if (_initialized == false)
Initialize();
if (_nodeInitialized == false) InitializeNode();
return _writerName;
}
}
@@ -225,8 +195,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache
{
get
{
if (_initialized == false)
Initialize();
if (_nodeInitialized == false) InitializeNode();
return _creatorName;
}
}
@@ -235,8 +204,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache
{
get
{
if (_initialized == false)
Initialize();
if (_nodeInitialized == false) InitializeNode();
return _writerId;
}
}
@@ -245,8 +213,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache
{
get
{
if (_initialized == false)
Initialize();
if (_nodeInitialized == false) InitializeNode();
return _creatorId;
}
}
@@ -255,8 +222,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache
{
get
{
if (_initialized == false)
Initialize();
if (_nodeInitialized == false) InitializeNode();
return _path;
}
}
@@ -265,8 +231,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache
{
get
{
if (_initialized == false)
Initialize();
if (_nodeInitialized == false) InitializeNode();
return _createDate;
}
}
@@ -275,8 +240,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache
{
get
{
if (_initialized == false)
Initialize();
if (_nodeInitialized == false) InitializeNode();
return _updateDate;
}
}
@@ -285,8 +249,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache
{
get
{
if (_initialized == false)
Initialize();
if (_nodeInitialized == false) InitializeNode();
return _version;
}
}
@@ -295,8 +258,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache
{
get
{
if (_initialized == false)
Initialize();
if (_nodeInitialized == false) InitializeNode();
return _urlName;
}
}
@@ -305,8 +267,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache
{
get
{
if (_initialized == false)
Initialize();
if (_nodeInitialized == false) InitializeNode();
return _level;
}
}
@@ -315,8 +276,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache
{
get
{
if (_initialized == false)
Initialize();
if (_nodeInitialized == false) InitializeNode();
return _isDraft;
}
}
@@ -325,9 +285,8 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache
{
get
{
if (_initialized == false)
Initialize();
return _properties;
if (_nodeInitialized == false) InitializeNode();
return _properties.Values;
}
}
@@ -335,24 +294,26 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache
{
get
{
if (_initialized == false)
Initialize();
if (_nodeInitialized == false) InitializeNode();
return _contentType;
}
}
private void InitializeStructure()
{
// load parent if it exists and is a node
var parent = _xmlNode == null ? null : _xmlNode.ParentNode;
private void InitializeParent()
{
if (_xmlNode == null) return;
var parent = _xmlNode.ParentNode;
if (parent == null) return;
if (parent.Name == "node" || (parent.Attributes != null && parent.Attributes.GetNamedItem("isDoc") != null))
_parent = (new XmlPublishedContent(parent, _isPreviewing, true)).CreateModel();
_parent = Get(parent, _isPreviewing);
// warn: this is not thread-safe...
_parentInitialized = true;
}
private void Initialize()
private void InitializeNode()
{
if (_xmlNode == null) return;
@@ -381,7 +342,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache
_creatorName = _writerName;
}
//Added the actual userID, as a user cannot be looked up via full name only...
//Added the actual userID, as a user cannot be looked up via full name only...
if (_xmlNode.Attributes.GetNamedItem("creatorID") != null)
_creatorId = int.Parse(_xmlNode.Attributes.GetNamedItem("creatorID").Value);
if (_xmlNode.Attributes.GetNamedItem("writerID") != null)
@@ -423,22 +384,27 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache
if (nodes != null)
foreach (XmlNode n in nodes)
{
var attrs = n.Attributes;
if (attrs == null) continue;
var alias = UmbracoConfig.For.UmbracoSettings().Content.UseLegacyXmlSchema
? n.Attributes.GetNamedItem("alias").Value
? attrs.GetNamedItem("alias").Value
: n.Name;
propertyNodes[alias.ToLowerInvariant()] = n;
}
_properties = _contentType.PropertyTypes.Select(p =>
{
XmlNode n;
return propertyNodes.TryGetValue(p.PropertyTypeAlias.ToLowerInvariant(), out n)
? new XmlPublishedProperty(p, _isPreviewing, n)
: new XmlPublishedProperty(p, _isPreviewing);
}).Cast<IPublishedProperty>().ToArray();
_properties = _contentType.PropertyTypes.Select(p =>
{
XmlNode n;
return propertyNodes.TryGetValue(p.PropertyTypeAlias.ToLowerInvariant(), out n)
? new XmlPublishedProperty(p, _isPreviewing, n)
: new XmlPublishedProperty(p, _isPreviewing);
}).Cast<IPublishedProperty>().ToDictionary(
x => x.PropertyTypeAlias,
x => x,
StringComparer.OrdinalIgnoreCase);
// warn: this is not thread-safe...
_initialized = true;
_nodeInitialized = true;
}
private void InitializeChildren()
@@ -449,14 +415,37 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache
var childXPath = UmbracoConfig.For.UmbracoSettings().Content.UseLegacyXmlSchema ? "node" : "* [@isDoc]";
var nav = _xmlNode.CreateNavigator();
var expr = nav.Compile(childXPath);
expr.AddSort("@sortOrder", XmlSortOrder.Ascending, XmlCaseOrder.None, "", XmlDataType.Number);
//expr.AddSort("@sortOrder", XmlSortOrder.Ascending, XmlCaseOrder.None, "", XmlDataType.Number);
var iterator = nav.Select(expr);
while (iterator.MoveNext())
_children.Add(
(new XmlPublishedContent(((IHasXmlNode)iterator.Current).GetNode(), _isPreviewing, true)).CreateModel());
_children = iterator.Cast<XPathNavigator>()
.Select(n => Get(((IHasXmlNode) n).GetNode(), _isPreviewing))
.OrderBy(x => x.SortOrder)
.ToList();
// warn: this is not thread-safe
_childrenInitialized = true;
}
/// <summary>
/// Gets an IPublishedContent corresponding to an Xml cache node.
/// </summary>
/// <param name="node">The Xml node.</param>
/// <param name="isPreviewing">A value indicating whether we are previewing or not.</param>
/// <returns>The IPublishedContent corresponding to the Xml cache node.</returns>
/// <remarks>Maintains a per-request cache of IPublishedContent items in order to make
/// sure that we create only one instance of each for the duration of a request. The
/// returned IPublishedContent is a model, if models are enabled.</remarks>
public static IPublishedContent Get(XmlNode node, bool isPreviewing)
{
// only 1 per request
var attrs = node.Attributes;
var id = attrs == null ? null : attrs.GetNamedItem("id").Value;
if (id.IsNullOrWhiteSpace()) throw new InvalidOperationException("Node has no ID attribute.");
var cache = ApplicationContext.Current.ApplicationCache.RequestCache;
var key = "XMLPUBLISHEDCONTENT_" + id; // dont bother with preview, wont change during request in v7
return (IPublishedContent) cache.GetCacheItem(key, () => (new XmlPublishedContent(node, isPreviewing)).CreateModel());
}
}
}

View File

@@ -2,9 +2,7 @@ using System;
using System.Xml;
using System.Xml.Serialization;
using Umbraco.Core;
using Umbraco.Core.Configuration;
using Umbraco.Core.Models.PublishedContent;
using Umbraco.Web.Models;
namespace Umbraco.Web.PublishedCache.XmlPublishedCache
{
@@ -17,10 +15,15 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache
internal class XmlPublishedProperty : PublishedPropertyBase
{
private readonly string _xmlValue; // the raw, xml node value
private readonly Lazy<object> _sourceValue;
private readonly Lazy<object> _objectValue;
private readonly Lazy<object> _xpathValue;
private readonly bool _isPreviewing;
// in v7 we're not using XPath value so don't allocate that Lazy.
// as for the rest... we're single threaded here, keep it simple
//private readonly Lazy<object> _sourceValue;
//private readonly Lazy<object> _objectValue;
//private readonly Lazy<object> _xpathValue;
private object _objectValue;
private bool _objectValueComputed;
private readonly bool _isPreviewing;
/// <summary>
/// Gets the raw value of the property.
@@ -34,10 +37,26 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache
get { return _xmlValue.Trim().Length > 0; }
}
public override object Value { get { return _objectValue.Value; } }
public override object XPathValue { get { return _xpathValue.Value; } }
public override object Value
{
get
{
// NOT caching the source (intermediate) value since we'll never need it
// everything in Xml cache in v7 is per-request anyways
// also, properties should not be shared between requests and therefore
// are single threaded, so the following code should be safe & fast
public XmlPublishedProperty(PublishedPropertyType propertyType, bool isPreviewing, XmlNode propertyXmlData)
if (_objectValueComputed) return _objectValue;
var sourceValue = PropertyType.ConvertDataToSource(_xmlValue, _isPreviewing);
_objectValue = PropertyType.ConvertSourceToObject(sourceValue, _isPreviewing);
_objectValueComputed = true;
return _objectValue;
}
}
public override object XPathValue { get { throw new NotImplementedException(); } }
public XmlPublishedProperty(PublishedPropertyType propertyType, bool isPreviewing, XmlNode propertyXmlData)
: this(propertyType, isPreviewing)
{
if (propertyXmlData == null)
@@ -59,9 +78,9 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache
_xmlValue = string.Empty;
_isPreviewing = isPreviewing;
_sourceValue = new Lazy<object>(() => PropertyType.ConvertDataToSource(_xmlValue, _isPreviewing));
_objectValue = new Lazy<object>(() => PropertyType.ConvertSourceToObject(_sourceValue.Value, _isPreviewing));
_xpathValue = new Lazy<object>(() => PropertyType.ConvertSourceToXPath(_sourceValue.Value, _isPreviewing));
//_sourceValue = new Lazy<object>(() => PropertyType.ConvertDataToSource(_xmlValue, _isPreviewing));
//_objectValue = new Lazy<object>(() => PropertyType.ConvertSourceToObject(_sourceValue.Value, _isPreviewing));
//_xpathValue = new Lazy<object>(() => PropertyType.ConvertSourceToXPath(_sourceValue.Value, _isPreviewing));
}
}
}