2017-07-20 11:21:28 +02:00
|
|
|
|
using System;
|
2017-05-12 14:49:44 +02:00
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
|
using System.Linq;
|
|
|
|
|
|
using System.Xml;
|
|
|
|
|
|
using BenchmarkDotNet.Attributes;
|
2017-10-17 17:43:15 +02:00
|
|
|
|
using Moq;
|
2017-05-12 14:49:44 +02:00
|
|
|
|
using Umbraco.Core;
|
2018-02-08 16:08:52 +01:00
|
|
|
|
using Umbraco.Core.Logging;
|
|
|
|
|
|
using Umbraco.Core.Models;
|
2017-05-12 14:49:44 +02:00
|
|
|
|
using Umbraco.Core.Models.PublishedContent;
|
2017-10-17 17:43:15 +02:00
|
|
|
|
using Umbraco.Core.PropertyEditors;
|
2018-02-08 16:08:52 +01:00
|
|
|
|
using Umbraco.Core.Services;
|
2018-03-27 17:59:53 +02:00
|
|
|
|
using Umbraco.Tests.Benchmarks.Config;
|
2017-05-12 14:49:44 +02:00
|
|
|
|
using Umbraco.Web.PublishedCache.XmlPublishedCache;
|
|
|
|
|
|
|
|
|
|
|
|
namespace Umbraco.Tests.Benchmarks
|
|
|
|
|
|
{
|
2018-03-27 16:42:52 +02:00
|
|
|
|
[QuickRunWithMemoryDiagnoserConfig]
|
2017-05-12 14:49:44 +02:00
|
|
|
|
public class XmlPublishedContentInitBenchmarks
|
|
|
|
|
|
{
|
|
|
|
|
|
public XmlPublishedContentInitBenchmarks()
|
|
|
|
|
|
{
|
|
|
|
|
|
_xml10 = Build(10);
|
|
|
|
|
|
_xml100 = Build(100);
|
|
|
|
|
|
_xml1000 = Build(1000);
|
|
|
|
|
|
_xml10000 = Build(10000);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private readonly string[] _intAttributes = { "id", "parentID", "nodeType", "level", "writerID", "creatorID", "template", "sortOrder", "isDoc", "isDraft" };
|
|
|
|
|
|
private readonly string[] _strAttributes = { "nodeName", "urlName", "writerName", "creatorName", "path" };
|
|
|
|
|
|
private readonly string[] _dateAttributes = { "createDate", "updateDate" };
|
|
|
|
|
|
private readonly string[] _guidAttributes = { "key", "version" };
|
|
|
|
|
|
|
|
|
|
|
|
private XmlDocument Build(int children)
|
|
|
|
|
|
{
|
|
|
|
|
|
var xml = new XmlDocument();
|
|
|
|
|
|
var root = Build(xml, "Home", 10);
|
|
|
|
|
|
for (int i = 0; i < children; i++)
|
|
|
|
|
|
{
|
|
|
|
|
|
var child = Build(xml, "child" + i, 10);
|
|
|
|
|
|
root.AppendChild(child);
|
|
|
|
|
|
}
|
|
|
|
|
|
xml.AppendChild(root);
|
|
|
|
|
|
return xml;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private XmlElement Build(XmlDocument xml, string name, int propertyCount)
|
|
|
|
|
|
{
|
|
|
|
|
|
var random = new Random();
|
|
|
|
|
|
var content = xml.CreateElement(name);
|
|
|
|
|
|
foreach (var p in _intAttributes)
|
|
|
|
|
|
{
|
|
|
|
|
|
var a = xml.CreateAttribute(p);
|
|
|
|
|
|
a.Value = random.Next(1, 9).ToInvariantString();
|
|
|
|
|
|
content.Attributes.Append(a);
|
|
|
|
|
|
}
|
|
|
|
|
|
foreach (var p in _strAttributes)
|
|
|
|
|
|
{
|
|
|
|
|
|
var a = xml.CreateAttribute(p);
|
|
|
|
|
|
a.Value = Guid.NewGuid().ToString();
|
|
|
|
|
|
content.Attributes.Append(a);
|
|
|
|
|
|
}
|
|
|
|
|
|
foreach (var p in _guidAttributes)
|
|
|
|
|
|
{
|
|
|
|
|
|
var a = xml.CreateAttribute(p);
|
|
|
|
|
|
a.Value = Guid.NewGuid().ToString();
|
|
|
|
|
|
content.Attributes.Append(a);
|
|
|
|
|
|
}
|
|
|
|
|
|
foreach (var p in _dateAttributes)
|
|
|
|
|
|
{
|
|
|
|
|
|
var a = xml.CreateAttribute(p);
|
|
|
|
|
|
a.Value = DateTime.Now.ToString("o");
|
|
|
|
|
|
content.Attributes.Append(a);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < propertyCount; i++)
|
|
|
|
|
|
{
|
|
|
|
|
|
var prop = xml.CreateElement("prop" + i);
|
|
|
|
|
|
var cdata = xml.CreateCDataSection(string.Join("", Enumerable.Range(0, 10).Select(x => Guid.NewGuid().ToString())));
|
|
|
|
|
|
prop.AppendChild(cdata);
|
|
|
|
|
|
content.AppendChild(prop);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return content;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private readonly XmlDocument _xml10;
|
|
|
|
|
|
private readonly XmlDocument _xml100;
|
|
|
|
|
|
private readonly XmlDocument _xml1000;
|
|
|
|
|
|
private readonly XmlDocument _xml10000;
|
|
|
|
|
|
|
|
|
|
|
|
//out props
|
|
|
|
|
|
int id, nodeType, level, writerId, creatorId, template, sortOrder;
|
|
|
|
|
|
Guid key, version;
|
|
|
|
|
|
string name, urlName, writerName, creatorName, docTypeAlias, path;
|
|
|
|
|
|
bool isDraft;
|
|
|
|
|
|
DateTime createDate, updateDate;
|
|
|
|
|
|
PublishedContentType publishedContentType;
|
|
|
|
|
|
Dictionary<string, IPublishedProperty> properties;
|
|
|
|
|
|
|
|
|
|
|
|
[Benchmark(Baseline = true, OperationsPerInvoke = 10)]
|
|
|
|
|
|
public void Original_10_Children()
|
|
|
|
|
|
{
|
|
|
|
|
|
OriginalInitializeNode(_xml10.DocumentElement, false, false,
|
|
|
|
|
|
out id, out key, out template, out sortOrder, out name, out writerName, out urlName,
|
|
|
|
|
|
out creatorName, out creatorId, out writerId, out docTypeAlias, out nodeType, out path,
|
|
|
|
|
|
out version, out createDate, out updateDate, out level, out isDraft, out publishedContentType,
|
|
|
|
|
|
out properties);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
[Benchmark(OperationsPerInvoke = 10)]
|
|
|
|
|
|
public void Original_100_Children()
|
|
|
|
|
|
{
|
|
|
|
|
|
OriginalInitializeNode(_xml100.DocumentElement, false, false,
|
|
|
|
|
|
out id, out key, out template, out sortOrder, out name, out writerName, out urlName,
|
|
|
|
|
|
out creatorName, out creatorId, out writerId, out docTypeAlias, out nodeType, out path,
|
|
|
|
|
|
out version, out createDate, out updateDate, out level, out isDraft, out publishedContentType,
|
|
|
|
|
|
out properties);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
[Benchmark(OperationsPerInvoke = 10)]
|
|
|
|
|
|
public void Original_1000_Children()
|
|
|
|
|
|
{
|
|
|
|
|
|
OriginalInitializeNode(_xml1000.DocumentElement, false, false,
|
|
|
|
|
|
out id, out key, out template, out sortOrder, out name, out writerName, out urlName,
|
|
|
|
|
|
out creatorName, out creatorId, out writerId, out docTypeAlias, out nodeType, out path,
|
|
|
|
|
|
out version, out createDate, out updateDate, out level, out isDraft, out publishedContentType,
|
|
|
|
|
|
out properties);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
[Benchmark(OperationsPerInvoke = 10)]
|
|
|
|
|
|
public void Original_10000_Children()
|
|
|
|
|
|
{
|
|
|
|
|
|
OriginalInitializeNode(_xml10000.DocumentElement, false, false,
|
|
|
|
|
|
out id, out key, out template, out sortOrder, out name, out writerName, out urlName,
|
|
|
|
|
|
out creatorName, out creatorId, out writerId, out docTypeAlias, out nodeType, out path,
|
|
|
|
|
|
out version, out createDate, out updateDate, out level, out isDraft, out publishedContentType,
|
|
|
|
|
|
out properties);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
[Benchmark(OperationsPerInvoke = 10)]
|
|
|
|
|
|
public void Enhanced_10_Children()
|
|
|
|
|
|
{
|
2017-07-21 17:19:00 +02:00
|
|
|
|
XmlPublishedContent.InitializeNode(null, _xml10.DocumentElement, false,
|
2017-05-12 14:49:44 +02:00
|
|
|
|
out id, out key, out template, out sortOrder, out name, out writerName, out urlName,
|
|
|
|
|
|
out creatorName, out creatorId, out writerId, out docTypeAlias, out nodeType, out path,
|
2017-12-01 19:29:54 +01:00
|
|
|
|
out createDate, out updateDate, out level, out isDraft, out publishedContentType,
|
2017-05-12 14:49:44 +02:00
|
|
|
|
out properties, GetPublishedContentType);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
[Benchmark(OperationsPerInvoke = 10)]
|
|
|
|
|
|
public void Enhanced_100_Children()
|
|
|
|
|
|
{
|
2017-07-21 17:19:00 +02:00
|
|
|
|
XmlPublishedContent.InitializeNode(null, _xml100.DocumentElement, false,
|
2017-05-12 14:49:44 +02:00
|
|
|
|
out id, out key, out template, out sortOrder, out name, out writerName, out urlName,
|
|
|
|
|
|
out creatorName, out creatorId, out writerId, out docTypeAlias, out nodeType, out path,
|
2017-12-01 19:29:54 +01:00
|
|
|
|
out createDate, out updateDate, out level, out isDraft, out publishedContentType,
|
2017-05-12 14:49:44 +02:00
|
|
|
|
out properties, GetPublishedContentType);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
[Benchmark(OperationsPerInvoke = 10)]
|
|
|
|
|
|
public void Enhanced_1000_Children()
|
|
|
|
|
|
{
|
2017-07-21 17:19:00 +02:00
|
|
|
|
XmlPublishedContent.InitializeNode(null, _xml1000.DocumentElement, false,
|
2017-05-12 14:49:44 +02:00
|
|
|
|
out id, out key, out template, out sortOrder, out name, out writerName, out urlName,
|
|
|
|
|
|
out creatorName, out creatorId, out writerId, out docTypeAlias, out nodeType, out path,
|
2017-12-01 19:29:54 +01:00
|
|
|
|
out createDate, out updateDate, out level, out isDraft, out publishedContentType,
|
2017-05-12 14:49:44 +02:00
|
|
|
|
out properties, GetPublishedContentType);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
[Benchmark(OperationsPerInvoke = 10)]
|
|
|
|
|
|
public void Enhanced_10000_Children()
|
|
|
|
|
|
{
|
2017-07-21 17:19:00 +02:00
|
|
|
|
XmlPublishedContent.InitializeNode(null, _xml10000.DocumentElement, false,
|
2017-05-12 14:49:44 +02:00
|
|
|
|
out id, out key, out template, out sortOrder, out name, out writerName, out urlName,
|
|
|
|
|
|
out creatorName, out creatorId, out writerId, out docTypeAlias, out nodeType, out path,
|
2017-12-01 19:29:54 +01:00
|
|
|
|
out createDate, out updateDate, out level, out isDraft, out publishedContentType,
|
2017-05-12 14:49:44 +02:00
|
|
|
|
out properties, GetPublishedContentType);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
internal static void OriginalInitializeNode(XmlNode xmlNode, bool legacy, bool isPreviewing,
|
|
|
|
|
|
out int id, out Guid key, out int template, out int sortOrder, out string name, out string writerName, out string urlName,
|
|
|
|
|
|
out string creatorName, out int creatorId, out int writerId, out string docTypeAlias, out int docTypeId, out string path,
|
|
|
|
|
|
out Guid version, out DateTime createDate, out DateTime updateDate, out int level, out bool isDraft,
|
|
|
|
|
|
out PublishedContentType contentType, out Dictionary<string, IPublishedProperty> properties)
|
|
|
|
|
|
{
|
|
|
|
|
|
//initialize the out params with defaults:
|
|
|
|
|
|
writerName = null;
|
|
|
|
|
|
docTypeAlias = null;
|
|
|
|
|
|
id = template = sortOrder = template = creatorId = writerId = docTypeId = level = default(int);
|
|
|
|
|
|
key = version = default(Guid);
|
|
|
|
|
|
name = writerName = urlName = creatorName = docTypeAlias = path = null;
|
|
|
|
|
|
createDate = updateDate = default(DateTime);
|
|
|
|
|
|
isDraft = false;
|
|
|
|
|
|
contentType = null;
|
|
|
|
|
|
properties = null;
|
|
|
|
|
|
|
|
|
|
|
|
//return if this is null
|
|
|
|
|
|
if (xmlNode == null)
|
|
|
|
|
|
{
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (xmlNode.Attributes != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
id = int.Parse(xmlNode.Attributes.GetNamedItem("id").Value);
|
|
|
|
|
|
if (xmlNode.Attributes.GetNamedItem("key") != null) // because, migration
|
|
|
|
|
|
key = Guid.Parse(xmlNode.Attributes.GetNamedItem("key").Value);
|
|
|
|
|
|
if (xmlNode.Attributes.GetNamedItem("template") != null)
|
|
|
|
|
|
template = int.Parse(xmlNode.Attributes.GetNamedItem("template").Value);
|
|
|
|
|
|
if (xmlNode.Attributes.GetNamedItem("sortOrder") != null)
|
|
|
|
|
|
sortOrder = int.Parse(xmlNode.Attributes.GetNamedItem("sortOrder").Value);
|
|
|
|
|
|
if (xmlNode.Attributes.GetNamedItem("nodeName") != null)
|
|
|
|
|
|
name = xmlNode.Attributes.GetNamedItem("nodeName").Value;
|
|
|
|
|
|
if (xmlNode.Attributes.GetNamedItem("writerName") != null)
|
|
|
|
|
|
writerName = xmlNode.Attributes.GetNamedItem("writerName").Value;
|
|
|
|
|
|
if (xmlNode.Attributes.GetNamedItem("urlName") != null)
|
|
|
|
|
|
urlName = xmlNode.Attributes.GetNamedItem("urlName").Value;
|
|
|
|
|
|
// Creatorname is new in 2.1, so published xml might not have it!
|
|
|
|
|
|
try
|
|
|
|
|
|
{
|
|
|
|
|
|
creatorName = xmlNode.Attributes.GetNamedItem("creatorName").Value;
|
|
|
|
|
|
}
|
|
|
|
|
|
catch
|
|
|
|
|
|
{
|
|
|
|
|
|
creatorName = writerName;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//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)
|
|
|
|
|
|
writerId = int.Parse(xmlNode.Attributes.GetNamedItem("writerID").Value);
|
|
|
|
|
|
|
|
|
|
|
|
if (legacy)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (xmlNode.Attributes.GetNamedItem("nodeTypeAlias") != null)
|
|
|
|
|
|
docTypeAlias = xmlNode.Attributes.GetNamedItem("nodeTypeAlias").Value;
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
docTypeAlias = xmlNode.Name;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (xmlNode.Attributes.GetNamedItem("nodeType") != null)
|
|
|
|
|
|
docTypeId = int.Parse(xmlNode.Attributes.GetNamedItem("nodeType").Value);
|
|
|
|
|
|
if (xmlNode.Attributes.GetNamedItem("path") != null)
|
|
|
|
|
|
path = xmlNode.Attributes.GetNamedItem("path").Value;
|
|
|
|
|
|
if (xmlNode.Attributes.GetNamedItem("version") != null)
|
|
|
|
|
|
version = new Guid(xmlNode.Attributes.GetNamedItem("version").Value);
|
|
|
|
|
|
if (xmlNode.Attributes.GetNamedItem("createDate") != null)
|
|
|
|
|
|
createDate = DateTime.Parse(xmlNode.Attributes.GetNamedItem("createDate").Value);
|
|
|
|
|
|
if (xmlNode.Attributes.GetNamedItem("updateDate") != null)
|
|
|
|
|
|
updateDate = DateTime.Parse(xmlNode.Attributes.GetNamedItem("updateDate").Value);
|
|
|
|
|
|
if (xmlNode.Attributes.GetNamedItem("level") != null)
|
|
|
|
|
|
level = int.Parse(xmlNode.Attributes.GetNamedItem("level").Value);
|
|
|
|
|
|
|
|
|
|
|
|
isDraft = (xmlNode.Attributes.GetNamedItem("isDraft") != null);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// load data
|
|
|
|
|
|
|
|
|
|
|
|
var dataXPath = legacy ? "data" : "* [not(@isDoc)]";
|
|
|
|
|
|
var nodes = xmlNode.SelectNodes(dataXPath);
|
|
|
|
|
|
|
|
|
|
|
|
contentType = GetPublishedContentType(PublishedItemType.Content, docTypeAlias);
|
2017-07-20 11:21:28 +02:00
|
|
|
|
|
2017-05-12 14:49:44 +02:00
|
|
|
|
var propertyNodes = new Dictionary<string, XmlNode>();
|
|
|
|
|
|
if (nodes != null)
|
|
|
|
|
|
foreach (XmlNode n in nodes)
|
|
|
|
|
|
{
|
|
|
|
|
|
var attrs = n.Attributes;
|
|
|
|
|
|
if (attrs == null) continue;
|
|
|
|
|
|
var alias = legacy
|
|
|
|
|
|
? attrs.GetNamedItem("alias").Value
|
|
|
|
|
|
: n.Name;
|
|
|
|
|
|
propertyNodes[alias.ToLowerInvariant()] = n;
|
|
|
|
|
|
}
|
|
|
|
|
|
properties = contentType.PropertyTypes.Select(p =>
|
|
|
|
|
|
{
|
|
|
|
|
|
XmlNode n;
|
2018-01-10 12:48:51 +01:00
|
|
|
|
return propertyNodes.TryGetValue(p.Alias.ToLowerInvariant(), out n)
|
2017-07-21 17:19:00 +02:00
|
|
|
|
? new XmlPublishedProperty(p, null, isPreviewing, n)
|
|
|
|
|
|
: new XmlPublishedProperty(p, null, isPreviewing);
|
2017-05-12 14:49:44 +02:00
|
|
|
|
}).Cast<IPublishedProperty>().ToDictionary(
|
2018-01-10 12:48:51 +01:00
|
|
|
|
x => x.Alias,
|
2017-05-12 14:49:44 +02:00
|
|
|
|
x => x,
|
|
|
|
|
|
StringComparer.OrdinalIgnoreCase);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private static PublishedContentType GetPublishedContentType(PublishedItemType type, string alias)
|
|
|
|
|
|
{
|
2018-02-08 16:08:52 +01:00
|
|
|
|
var dataType = new DataType(new VoidEditor(Mock.Of<ILogger>())) { Id = 1 };
|
|
|
|
|
|
|
|
|
|
|
|
var dataTypeService = Mock.Of<IDataTypeService>();
|
|
|
|
|
|
Mock.Get(dataTypeService)
|
|
|
|
|
|
.Setup(x => x.GetDataType(It.IsAny<int>()))
|
|
|
|
|
|
.Returns<int>(id => id == 1 ? dataType : null);
|
|
|
|
|
|
Mock.Get(dataTypeService)
|
|
|
|
|
|
.Setup(x => x.GetAll())
|
|
|
|
|
|
.Returns(new[] { dataType });
|
|
|
|
|
|
|
|
|
|
|
|
var factory = new PublishedContentTypeFactory(Mock.Of<IPublishedModelFactory>(), new PropertyValueConverterCollection(Array.Empty<IPropertyValueConverter>()), dataTypeService);
|
2017-10-17 17:43:15 +02:00
|
|
|
|
return factory.CreateContentType(0, alias, new string[] {},
|
2018-02-08 16:08:52 +01:00
|
|
|
|
new List<PublishedPropertyType>(Enumerable.Range(0, 10).Select(x => factory.CreatePropertyType("prop" + x, 1))));
|
2017-05-12 14:49:44 +02:00
|
|
|
|
}
|
|
|
|
|
|
}
|
2017-07-20 11:21:28 +02:00
|
|
|
|
}
|