diff --git a/src/Umbraco.Core/IDisposeOnRequestEnd.cs b/src/Umbraco.Core/IDisposeOnRequestEnd.cs
new file mode 100644
index 0000000000..cf1ec3a177
--- /dev/null
+++ b/src/Umbraco.Core/IDisposeOnRequestEnd.cs
@@ -0,0 +1,14 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace Umbraco.Core
+{
+ ///
+ /// Any class implementing this interface that is added to the httpcontext.items keys or values will be disposed of at the end of the request.
+ ///
+ public interface IDisposeOnRequestEnd : IDisposable
+ {
+ }
+}
diff --git a/src/Umbraco.Core/Models/UmbracoObjectTypesExtensions.cs b/src/Umbraco.Core/Models/UmbracoObjectTypesExtensions.cs
index a97f1fe976..7cc130600b 100644
--- a/src/Umbraco.Core/Models/UmbracoObjectTypesExtensions.cs
+++ b/src/Umbraco.Core/Models/UmbracoObjectTypesExtensions.cs
@@ -1,4 +1,5 @@
using System;
+using System.Collections.Concurrent;
using System.Collections.Generic;
using Umbraco.Core.CodeAnnotations;
@@ -9,7 +10,8 @@ namespace Umbraco.Core.Models
///
public static class UmbracoObjectTypesExtensions
{
- private static readonly Dictionary UmbracoObjectTypeCache = new Dictionary();
+ //MUST be concurrent to avoid thread collisions!
+ private static readonly ConcurrentDictionary UmbracoObjectTypeCache = new ConcurrentDictionary();
///
/// Get an UmbracoObjectTypes value from it's name
@@ -48,24 +50,22 @@ namespace Umbraco.Core.Models
/// a GUID value of the UmbracoObjectTypes
public static Guid GetGuid(this UmbracoObjectTypes umbracoObjectType)
{
- if (UmbracoObjectTypeCache.ContainsKey(umbracoObjectType))
- return UmbracoObjectTypeCache[umbracoObjectType];
+ return UmbracoObjectTypeCache.GetOrAdd(umbracoObjectType, types =>
+ {
+ var type = typeof(UmbracoObjectTypes);
+ var memInfo = type.GetMember(umbracoObjectType.ToString());
+ var attributes = memInfo[0].GetCustomAttributes(typeof(UmbracoObjectTypeAttribute),
+ false);
- var type = typeof(UmbracoObjectTypes);
- var memInfo = type.GetMember(umbracoObjectType.ToString());
- var attributes = memInfo[0].GetCustomAttributes(typeof(UmbracoObjectTypeAttribute),
- false);
+ if (attributes.Length == 0)
+ return Guid.Empty;
- if (attributes.Length == 0)
- return Guid.Empty;
+ var attribute = ((UmbracoObjectTypeAttribute)attributes[0]);
+ if (attribute == null)
+ return Guid.Empty;
- var attribute = ((UmbracoObjectTypeAttribute)attributes[0]);
- if (attribute == null)
- return Guid.Empty;
-
- UmbracoObjectTypeCache.Add(umbracoObjectType, attribute.ObjectId);
-
- return attribute.ObjectId;
+ return attribute.ObjectId;
+ });
}
///
diff --git a/src/Umbraco.Core/Persistence/UmbracoDatabase.cs b/src/Umbraco.Core/Persistence/UmbracoDatabase.cs
index 522e0b4173..98d5d9cebc 100644
--- a/src/Umbraco.Core/Persistence/UmbracoDatabase.cs
+++ b/src/Umbraco.Core/Persistence/UmbracoDatabase.cs
@@ -16,7 +16,7 @@ namespace Umbraco.Core.Persistence
/// can then override any additional execution (such as additional loggging, functionality, etc...) that we need to without breaking compatibility since we'll always be exposing
/// this object instead of the base PetaPoco database object.
///
- public class UmbracoDatabase : Database
+ public class UmbracoDatabase : Database, IDisposeOnRequestEnd
{
private readonly Guid _instanceId = Guid.NewGuid();
///
diff --git a/src/Umbraco.Core/TypeHelper.cs b/src/Umbraco.Core/TypeHelper.cs
index 9e19b46c31..7d0810cf13 100644
--- a/src/Umbraco.Core/TypeHelper.cs
+++ b/src/Umbraco.Core/TypeHelper.cs
@@ -16,6 +16,16 @@ namespace Umbraco.Core
private static readonly ConcurrentDictionary GetFieldsCache = new ConcurrentDictionary();
private static readonly ConcurrentDictionary, PropertyInfo[]> GetPropertiesCache = new ConcurrentDictionary, PropertyInfo[]>();
+ ///
+ /// Checks if the method is actually overriding a base method
+ ///
+ ///
+ ///
+ public static bool IsOverride(MethodInfo m)
+ {
+ return m.GetBaseDefinition().DeclaringType != m.DeclaringType;
+ }
+
///
/// Find all assembly references that are referencing the assignTypeFrom Type's assembly found in the assemblyList
///
diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj
index 1aae6f7b51..f07ffcf758 100644
--- a/src/Umbraco.Core/Umbraco.Core.csproj
+++ b/src/Umbraco.Core/Umbraco.Core.csproj
@@ -274,6 +274,7 @@
+
diff --git a/src/Umbraco.Tests/Trees/BaseContentTreeTests.cs b/src/Umbraco.Tests/Trees/BaseContentTreeTests.cs
new file mode 100644
index 0000000000..4a2c856a15
--- /dev/null
+++ b/src/Umbraco.Tests/Trees/BaseContentTreeTests.cs
@@ -0,0 +1,127 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using NUnit.Framework;
+using umbraco.cms.presentation.Trees;
+
+namespace Umbraco.Tests.Trees
+{
+ [TestFixture]
+ public class BaseContentTreeTests
+ {
+
+ [TearDown]
+ public void TestTearDown()
+ {
+ BaseTree.AfterTreeRender -= EventHandler;
+ BaseTree.BeforeTreeRender -= EventHandler;
+ }
+
+ [Test]
+ public void Run_Optimized()
+ {
+ var tree1 = new MyOptimizedContentTree1("content");
+ var tree2 = new MyOptimizedContentTree2("content");
+
+ Assert.IsTrue(tree1.UseOptimizedRendering);
+ Assert.IsTrue(tree2.UseOptimizedRendering);
+ }
+
+ [Test]
+ public void Not_Optimized_Events_AfterRender()
+ {
+ var tree = new MyOptimizedContentTree1("content");
+
+ BaseTree.AfterTreeRender += EventHandler;
+
+ Assert.IsFalse(tree.UseOptimizedRendering);
+ }
+
+ [Test]
+ public void Not_Optimized_Events_BeforeRender()
+ {
+ var tree = new MyOptimizedContentTree1("content");
+
+ BaseTree.BeforeTreeRender += EventHandler;
+
+ Assert.IsFalse(tree.UseOptimizedRendering);
+ }
+
+ [Test]
+ public void Not_Optimized_Overriden_Method()
+ {
+ var tree = new MyNotOptimizedContentTree("content");
+
+ Assert.IsFalse(tree.UseOptimizedRendering);
+ }
+
+ private void EventHandler(object sender, TreeEventArgs treeEventArgs)
+ {
+
+ }
+
+ //optimized because we are not overriding OnRenderNode
+ public class MyOptimizedContentTree1 : BaseContentTree
+ {
+ public MyOptimizedContentTree1(string application)
+ : base(application)
+ {
+ }
+
+ protected override void CreateRootNode(ref XmlTreeNode rootNode)
+ {
+
+ }
+ }
+
+ public class MyOptimizedContentTree2 : BaseContentTree
+ {
+ public MyOptimizedContentTree2(string application)
+ : base(application)
+ {
+ }
+
+ protected override bool LoadMinimalDocument
+ {
+ get { return true; }
+ }
+
+ protected override void CreateRootNode(ref XmlTreeNode rootNode)
+ {
+
+ }
+
+ //even if we override it will still be optimized because of the LoadMinimalDocument flag
+ protected override void OnRenderNode(ref XmlTreeNode xNode, umbraco.cms.businesslogic.web.Document doc)
+ {
+ base.OnRenderNode(ref xNode, doc);
+ }
+ }
+
+ public class MyNotOptimizedContentTree : BaseContentTree
+ {
+ public MyNotOptimizedContentTree(string application)
+ : base(application)
+ {
+ }
+
+ protected override void CreateRootNode(ref XmlTreeNode rootNode)
+ {
+
+ }
+
+ protected override bool LoadMinimalDocument
+ {
+ get { return false; }
+ }
+
+ protected override void OnRenderNode(ref XmlTreeNode xNode, umbraco.cms.businesslogic.web.Document doc)
+ {
+ base.OnRenderNode(ref xNode, doc);
+ }
+ }
+
+
+ }
+}
diff --git a/src/Umbraco.Tests/Trees/BaseMediaTreeTests.cs b/src/Umbraco.Tests/Trees/BaseMediaTreeTests.cs
new file mode 100644
index 0000000000..0e93b3db78
--- /dev/null
+++ b/src/Umbraco.Tests/Trees/BaseMediaTreeTests.cs
@@ -0,0 +1,65 @@
+using NUnit.Framework;
+using umbraco.cms.presentation.Trees;
+
+namespace Umbraco.Tests.Trees
+{
+ [TestFixture]
+ public class BaseMediaTreeTests
+ {
+
+ [TearDown]
+ public void TestTearDown()
+ {
+ BaseTree.AfterTreeRender -= EventHandler;
+ BaseTree.BeforeTreeRender -= EventHandler;
+ }
+
+ [Test]
+ public void Run_Optimized()
+ {
+ var tree = new MyOptimizedMediaTree("media");
+
+ Assert.IsTrue(tree.UseOptimizedRendering);
+ }
+
+ [Test]
+ public void Not_Optimized_Events_AfterRender()
+ {
+ var tree = new MyOptimizedMediaTree("media");
+
+ BaseTree.AfterTreeRender += EventHandler;
+
+ Assert.IsFalse(tree.UseOptimizedRendering);
+ }
+
+ [Test]
+ public void Not_Optimized_Events_BeforeRender()
+ {
+ var tree = new MyOptimizedMediaTree("media");
+
+ BaseTree.BeforeTreeRender += EventHandler;
+
+ Assert.IsFalse(tree.UseOptimizedRendering);
+ }
+
+ private void EventHandler(object sender, TreeEventArgs treeEventArgs)
+ {
+
+ }
+
+ public class MyOptimizedMediaTree : BaseMediaTree
+ {
+ public MyOptimizedMediaTree(string application)
+ : base(application)
+ {
+ }
+
+ protected override void CreateRootNode(ref XmlTreeNode rootNode)
+ {
+
+ }
+ }
+
+
+ }
+}
\ No newline at end of file
diff --git a/src/Umbraco.Tests/Umbraco.Tests.csproj b/src/Umbraco.Tests/Umbraco.Tests.csproj
index 49f8d6e6b3..dd84e8706c 100644
--- a/src/Umbraco.Tests/Umbraco.Tests.csproj
+++ b/src/Umbraco.Tests/Umbraco.Tests.csproj
@@ -427,6 +427,8 @@
+
+
diff --git a/src/Umbraco.Web/Routing/PublishedContentRequestEngine.cs b/src/Umbraco.Web/Routing/PublishedContentRequestEngine.cs
index fb6aca88b3..27ab92eadd 100644
--- a/src/Umbraco.Web/Routing/PublishedContentRequestEngine.cs
+++ b/src/Umbraco.Web/Routing/PublishedContentRequestEngine.cs
@@ -28,11 +28,13 @@ namespace Umbraco.Web.Routing
/// The content request.
public PublishedContentRequestEngine(PublishedContentRequest pcr)
{
+ if (pcr == null) throw new ArgumentException("pcr is null.");
_pcr = pcr;
+
_routingContext = pcr.RoutingContext;
-
- var umbracoContext = _routingContext.UmbracoContext;
if (_routingContext == null) throw new ArgumentException("pcr.RoutingContext is null.");
+
+ var umbracoContext = _routingContext.UmbracoContext;
if (umbracoContext == null) throw new ArgumentException("pcr.RoutingContext.UmbracoContext is null.");
if (umbracoContext.RoutingContext != _routingContext) throw new ArgumentException("RoutingContext confusion.");
// no! not set yet.
diff --git a/src/Umbraco.Web/UmbracoContext.cs b/src/Umbraco.Web/UmbracoContext.cs
index 35896c9358..756b1abfcd 100644
--- a/src/Umbraco.Web/UmbracoContext.cs
+++ b/src/Umbraco.Web/UmbracoContext.cs
@@ -16,7 +16,7 @@ namespace Umbraco.Web
///
/// Class that encapsulates Umbraco information of a specific HTTP request
///
- public class UmbracoContext : DisposableObject
+ public class UmbracoContext : DisposableObject, IDisposeOnRequestEnd
{
private const string HttpContextItemName = "Umbraco.Web.UmbracoContext";
private static readonly object Locker = new object();
diff --git a/src/Umbraco.Web/UmbracoModule.cs b/src/Umbraco.Web/UmbracoModule.cs
index ea0f861921..8a43d34dc3 100644
--- a/src/Umbraco.Web/UmbracoModule.cs
+++ b/src/Umbraco.Web/UmbracoModule.cs
@@ -527,11 +527,15 @@ namespace Umbraco.Web
///
private static void DisposeHttpContextItems(HttpContext http)
{
+ // do not process if client-side request
+ if (http.Request.Url.IsClientSideRequest())
+ return;
+
//get a list of keys to dispose
var keys = new HashSet