diff --git a/src/Umbraco.Core/ApplicationContext.cs b/src/Umbraco.Core/ApplicationContext.cs
index 776714b839..db58fb7858 100644
--- a/src/Umbraco.Core/ApplicationContext.cs
+++ b/src/Umbraco.Core/ApplicationContext.cs
@@ -1,9 +1,11 @@
using System;
using System.Configuration;
+using System.Threading;
using System.Web;
using System.Web.Caching;
using Umbraco.Core.Configuration;
using Umbraco.Core.Logging;
+using Umbraco.Core.ObjectResolution;
using Umbraco.Core.Services;
@@ -15,7 +17,7 @@ namespace Umbraco.Core
///
/// one per AppDomain, represents the global Umbraco application
///
- public class ApplicationContext
+ public class ApplicationContext : IDisposable
{
///
/// Constructor
@@ -186,5 +188,38 @@ namespace Umbraco.Core
}
internal set { _services = value; }
}
+
+
+ private volatile bool _disposed;
+ private readonly ReaderWriterLockSlim _disposalLocker = new ReaderWriterLockSlim();
+
+ ///
+ /// This will dispose and reset all resources used to run the application
+ ///
+ ///
+ /// IMPORTANT: Never dispose this object if you require the Umbraco application to run, disposing this object
+ /// is generally used for unit testing and when your application is shutting down after you have booted Umbraco.
+ ///
+ void IDisposable.Dispose()
+ {
+ // Only operate if we haven't already disposed
+ if (_disposed) return;
+
+ using (new WriteLock(_disposalLocker))
+ {
+ // Check again now we're inside the lock
+ if (_disposed) return;
+
+ //First we'll reset all resolvers
+ ResolverCollection.ResetAll();
+ //Next resolution itself (though this should be taken care of by resetting any of the resolvers above)
+ Resolution.Reset();
+ //Next, lets reset the plugin manager
+ PluginManager.Current = null;
+
+ // Indicate that the instance has been disposed.
+ _disposed = true;
+ }
+ }
}
}
diff --git a/src/Umbraco.Core/ObjectResolution/LazyManyObjectsResolverbase.cs b/src/Umbraco.Core/ObjectResolution/LazyManyObjectsResolverbase.cs
index f8476c790c..6aaf59106c 100644
--- a/src/Umbraco.Core/ObjectResolution/LazyManyObjectsResolverbase.cs
+++ b/src/Umbraco.Core/ObjectResolution/LazyManyObjectsResolverbase.cs
@@ -20,7 +20,7 @@ namespace Umbraco.Core.ObjectResolution
///
internal abstract class LazyManyObjectsResolverBase : ManyObjectsResolverBase
where TResolved : class
- where TResolver : class
+ where TResolver : ResolverBase
{
#region Constructors
diff --git a/src/Umbraco.Core/ObjectResolution/LegacyTransientObjectsResolver.cs b/src/Umbraco.Core/ObjectResolution/LegacyTransientObjectsResolver.cs
index eb871d4b73..9362ed54fe 100644
--- a/src/Umbraco.Core/ObjectResolution/LegacyTransientObjectsResolver.cs
+++ b/src/Umbraco.Core/ObjectResolution/LegacyTransientObjectsResolver.cs
@@ -19,7 +19,7 @@ namespace Umbraco.Core.ObjectResolution
///
internal abstract class LegacyTransientObjectsResolver : LazyManyObjectsResolverBase
where TResolved : class
- where TResolver : class
+ where TResolver : ResolverBase
{
private readonly ReaderWriterLockSlim _lock = new ReaderWriterLockSlim();
private ConcurrentDictionary _id2type;
diff --git a/src/Umbraco.Core/ObjectResolution/ManyObjectsResolverBase.cs b/src/Umbraco.Core/ObjectResolution/ManyObjectsResolverBase.cs
index 1a13f866d0..4a5f7cbd99 100644
--- a/src/Umbraco.Core/ObjectResolution/ManyObjectsResolverBase.cs
+++ b/src/Umbraco.Core/ObjectResolution/ManyObjectsResolverBase.cs
@@ -6,14 +6,14 @@ using System.Web;
namespace Umbraco.Core.ObjectResolution
{
- ///
+ ///
/// The base class for all many-objects resolvers.
///
/// The type of the concrete resolver class.
/// The type of the resolved objects.
public abstract class ManyObjectsResolverBase : ResolverBase
- where TResolved : class
- where TResolver : class
+ where TResolved : class
+ where TResolver : ResolverBase
{
private IEnumerable _applicationInstances = null;
private readonly ReaderWriterLockSlim _lock = new ReaderWriterLockSlim();
diff --git a/src/Umbraco.Core/ObjectResolution/ResolverBase.cs b/src/Umbraco.Core/ObjectResolution/ResolverBase.cs
index 1e542eae59..fd22a76610 100644
--- a/src/Umbraco.Core/ObjectResolution/ResolverBase.cs
+++ b/src/Umbraco.Core/ObjectResolution/ResolverBase.cs
@@ -3,26 +3,52 @@ using System.Threading;
namespace Umbraco.Core.ObjectResolution
{
+ ///
+ /// Base non-generic class for resolvers
+ ///
+ public abstract class ResolverBase
+ {
+ protected ResolverBase(Action resetAction)
+ {
+ //add itself to the internal collection
+ ResolverCollection.Add(this, resetAction);
+ }
+
+ }
+
///
/// The base class for all resolvers.
///
/// The type of the concrete resolver class.
/// Provides singleton management to all resolvers.
- public abstract class ResolverBase
- where TResolver : class
+ public abstract class ResolverBase : ResolverBase
+ where TResolver : ResolverBase
{
- static TResolver _resolver;
-
- ///
- /// The lock for the singleton.
- ///
- ///
- /// Though resharper says this is in error, it is actually correct. We want a different lock object for each generic type.
- /// See this for details: http://confluence.jetbrains.net/display/ReSharper/Static+field+in+generic+type
- ///
- static readonly ReaderWriterLockSlim ResolversLock = new ReaderWriterLockSlim();
- ///
+ ///
+ /// The underlying singleton object instance
+ ///
+ static TResolver _resolver;
+
+ ///
+ /// The lock for the singleton.
+ ///
+ ///
+ /// Though resharper says this is in error, it is actually correct. We want a different lock object for each generic type.
+ /// See this for details: http://confluence.jetbrains.net/display/ReSharper/Static+field+in+generic+type
+ ///
+ static readonly ReaderWriterLockSlim ResolversLock = new ReaderWriterLockSlim();
+
+ ///
+ /// Constructor set the reset action for the underlying object
+ ///
+ protected ResolverBase()
+ : base(() => Reset())
+ {
+
+ }
+
+ ///
/// Gets or sets the resolver singleton instance.
///
/// The value can be set only once, and cannot be read before it has been set.
@@ -84,15 +110,18 @@ namespace Umbraco.Core.ObjectResolution
//In order to reset a resolver, we always must reset the resolution
if (resetResolution)
{
- Resolution.Reset();
+ Resolution.Reset();
}
-
+
+ //ensure its removed from the collection
+ ResolverCollection.Remove(_resolver);
using (Resolution.Configuration)
using (new WriteLock(ResolversLock))
{
_resolver = null;
}
+
}
}
}
diff --git a/src/Umbraco.Core/ObjectResolution/ResolverCollection.cs b/src/Umbraco.Core/ObjectResolution/ResolverCollection.cs
new file mode 100644
index 0000000000..34a7ff68bc
--- /dev/null
+++ b/src/Umbraco.Core/ObjectResolution/ResolverCollection.cs
@@ -0,0 +1,69 @@
+using System;
+using System.Collections.Concurrent;
+using System.Linq;
+
+namespace Umbraco.Core.ObjectResolution
+{
+ ///
+ /// Simply used to track all ManyObjectsResolverBase instances so that we can
+ /// reset them all at once really easily.
+ ///
+ ///
+ /// Normally we'd use TypeFinding for this but because many of the resolvers are internal this won't work.
+ /// We'd rather not keep a static list of them so we'll dynamically add to this list based on the base
+ /// class of the ManyObjectsResolverBase.
+ ///
+ internal static class ResolverCollection
+ {
+ private static readonly ConcurrentDictionary Resolvers = new ConcurrentDictionary();
+
+ ///
+ /// Returns the number of resolvers created
+ ///
+ internal static int Count
+ {
+ get { return Resolvers.Count; }
+ }
+
+ ///
+ /// Resets all resolvers
+ ///
+ internal static void ResetAll()
+ {
+ //take out each item from the bag and reset it
+ var keys = Resolvers.Keys.ToArray();
+ foreach (var k in keys)
+ {
+ Action resetAction;
+ while (Resolvers.TryRemove(k, out resetAction))
+ {
+ //call the reset action for the resolver
+ resetAction();
+ }
+ }
+ }
+
+ ///
+ /// This is called when the static Reset method or a ResolverBase{T} is called.
+ ///
+ internal static void Remove(ResolverBase resolver)
+ {
+ if (resolver == null) return;
+ Action action;
+ Resolvers.TryRemove(resolver, out action);
+ }
+
+ ///
+ /// Adds a resolver to the collection
+ ///
+ ///
+ ///
+ ///
+ /// This is called when the creation of a ResolverBase occurs
+ ///
+ internal static void Add(ResolverBase resolver, Action resetAction)
+ {
+ Resolvers.TryAdd(resolver, resetAction);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Umbraco.Core/ObjectResolution/SingleObjectResolverBase.cs b/src/Umbraco.Core/ObjectResolution/SingleObjectResolverBase.cs
index 224467db6f..5d0e57c8f9 100644
--- a/src/Umbraco.Core/ObjectResolution/SingleObjectResolverBase.cs
+++ b/src/Umbraco.Core/ObjectResolution/SingleObjectResolverBase.cs
@@ -13,7 +13,7 @@ namespace Umbraco.Core.ObjectResolution
///
public abstract class SingleObjectResolverBase : ResolverBase
where TResolved : class
- where TResolver : class
+ where TResolver : ResolverBase
{
private readonly ReaderWriterLockSlim _lock = new ReaderWriterLockSlim();
private readonly bool _canBeNull;
diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj
index 7c1302648f..69ad1f60e6 100644
--- a/src/Umbraco.Core/Umbraco.Core.csproj
+++ b/src/Umbraco.Core/Umbraco.Core.csproj
@@ -206,6 +206,7 @@
+
diff --git a/src/Umbraco.Tests/Resolvers/ResolutionTests.cs b/src/Umbraco.Tests/Resolvers/ResolutionTests.cs
index 2debaa1bd4..8e9da977a2 100644
--- a/src/Umbraco.Tests/Resolvers/ResolutionTests.cs
+++ b/src/Umbraco.Tests/Resolvers/ResolutionTests.cs
@@ -18,14 +18,14 @@ namespace Umbraco.Tests.Resolvers
public void Setup()
{
TestHelper.SetupLog4NetForTests();
-
- BaseResolver.Reset();
}
[TearDown]
public void TearDown()
{
BaseResolver.Reset();
+ BaseResolver2.Reset();
+ BaseResolver3.Reset();
}
#region Resolvers and Resolved
@@ -33,6 +33,12 @@ namespace Umbraco.Tests.Resolvers
class BaseResolver : ResolverBase
{ }
+ class BaseResolver2 : ResolverBase
+ { }
+
+ class BaseResolver3 : ResolverBase
+ { }
+
#endregion
#region Test Resolution
@@ -215,5 +221,45 @@ namespace Umbraco.Tests.Resolvers
}
#endregion
+
+ [Test]
+ public void Resolver_Collection_Is_Updated()
+ {
+ BaseResolver.Current = new BaseResolver();
+ BaseResolver2.Current = new BaseResolver2();
+ BaseResolver3.Current = new BaseResolver3();
+ Assert.AreEqual(3, ResolverCollection.Count);
+ }
+
+ [Test]
+ public void Resolver_Collection_Is_Reset()
+ {
+ BaseResolver.Current = new BaseResolver();
+ BaseResolver2.Current = new BaseResolver2();
+ BaseResolver3.Current = new BaseResolver3();
+
+ ResolverCollection.ResetAll();
+
+ Assert.AreEqual(0, ResolverCollection.Count);
+ Assert.Throws(() =>
+ {
+ var c = BaseResolver.Current;
+ });
+ Assert.Throws(() =>
+ {
+ var c = BaseResolver2.Current;
+ });
+ Assert.Throws(() =>
+ {
+ var c = BaseResolver3.Current;
+ });
+
+ //this should not error!
+ BaseResolver.Current = new BaseResolver();
+ BaseResolver2.Current = new BaseResolver2();
+ BaseResolver3.Current = new BaseResolver3();
+
+ Assert.Pass();
+ }
}
}