Updates ManyObjectsResolverBase to properly check for resolution freezing and fixes a few issues found regarding
inheritance in complex situations like the new LazyManyObjectsResolverBase. Creates the new LazyManyObjectsResolverBase to lazily resolve types in order to create them. This is for work item : #U4-1522 Adds unit test to support.
This commit is contained in:
148
src/Umbraco.Core/ObjectResolution/LazyManyObjectsResolverbase.cs
Normal file
148
src/Umbraco.Core/ObjectResolution/LazyManyObjectsResolverbase.cs
Normal file
@@ -0,0 +1,148 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Web;
|
||||
|
||||
namespace Umbraco.Core.ObjectResolution
|
||||
{
|
||||
internal abstract class LazyManyObjectsResolverBase<TResolver, TResolved> : ManyObjectsResolverBase<TResolver, TResolved>
|
||||
where TResolved : class
|
||||
where TResolver : class
|
||||
{
|
||||
#region Constructors
|
||||
|
||||
protected LazyManyObjectsResolverBase(ObjectLifetimeScope scope = ObjectLifetimeScope.Application)
|
||||
: base(scope)
|
||||
{
|
||||
}
|
||||
|
||||
protected LazyManyObjectsResolverBase(HttpContextBase httpContext)
|
||||
: base(httpContext)
|
||||
{
|
||||
}
|
||||
|
||||
protected LazyManyObjectsResolverBase(IEnumerable<Lazy<Type>> value, ObjectLifetimeScope scope = ObjectLifetimeScope.Application)
|
||||
: this(scope)
|
||||
{
|
||||
AddTypes(value);
|
||||
}
|
||||
|
||||
protected LazyManyObjectsResolverBase(HttpContextBase httpContext, IEnumerable<Lazy<Type>> value)
|
||||
: this(httpContext)
|
||||
{
|
||||
|
||||
}
|
||||
#endregion
|
||||
|
||||
private readonly List<Lazy<Type>> _lazyTypes = new List<Lazy<Type>>();
|
||||
private bool _hasResolvedTypes = false;
|
||||
|
||||
/// <summary>
|
||||
/// Used for unit tests
|
||||
/// </summary>
|
||||
internal bool HasResolvedTypes
|
||||
{
|
||||
get { return _hasResolvedTypes; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Once this is called this will resolve all types registered in the lazy list
|
||||
/// </summary>
|
||||
protected override IEnumerable<Type> InstanceTypes
|
||||
{
|
||||
get
|
||||
{
|
||||
var list = _lazyTypes.Select(x => x.Value).ToArray();
|
||||
|
||||
//we need to validate each resolved type now since we could not do it before when inserting the lazy delegates
|
||||
if (!_hasResolvedTypes)
|
||||
{
|
||||
var uniqueList = new List<Type>();
|
||||
foreach (var l in list)
|
||||
{
|
||||
EnsureCorrectType(l);
|
||||
if (uniqueList.Contains(l))
|
||||
{
|
||||
throw new InvalidOperationException("The Type " + l + " already exists in the collection");
|
||||
}
|
||||
uniqueList.Add(l);
|
||||
}
|
||||
_hasResolvedTypes = true;
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
}
|
||||
|
||||
protected void AddTypes(IEnumerable<Lazy<Type>> types)
|
||||
{
|
||||
EnsureAddSupport();
|
||||
|
||||
EnsureResolutionNotFrozen();
|
||||
|
||||
using (GetWriteLock())
|
||||
{
|
||||
foreach (var t in types)
|
||||
{
|
||||
_lazyTypes.Add(t);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a lazy type to the list
|
||||
/// </summary>
|
||||
/// <param name="value"></param>
|
||||
public void AddType(Lazy<Type> value)
|
||||
{
|
||||
EnsureAddSupport();
|
||||
|
||||
EnsureResolutionNotFrozen();
|
||||
|
||||
using (GetWriteLock())
|
||||
{
|
||||
_lazyTypes.Add(value);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts the static type added to a lazy type and adds it to the internal list
|
||||
/// </summary>
|
||||
/// <param name="value"></param>
|
||||
public override void AddType(Type value)
|
||||
{
|
||||
AddType(new Lazy<Type>(() => value));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clears all lazy types
|
||||
/// </summary>
|
||||
public override void Clear()
|
||||
{
|
||||
EnsureClearSupport();
|
||||
|
||||
EnsureResolutionNotFrozen();
|
||||
|
||||
using (GetWriteLock())
|
||||
{
|
||||
_lazyTypes.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Does not support removal
|
||||
/// </summary>
|
||||
protected override bool SupportsRemove
|
||||
{
|
||||
get { return false; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Does not support insert
|
||||
/// </summary>
|
||||
protected override bool SupportsInsert
|
||||
{
|
||||
get { return false; }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Web;
|
||||
using System.Web.UI;
|
||||
using Umbraco.Core.Macros;
|
||||
@@ -56,7 +57,7 @@ namespace Umbraco.Core.ObjectResolution
|
||||
/// </remarks>
|
||||
internal List<Type> MacroControlTypes
|
||||
{
|
||||
get { return InstanceTypes; }
|
||||
get { return InstanceTypes.ToList(); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -6,14 +6,14 @@ using System.Web;
|
||||
|
||||
namespace Umbraco.Core.ObjectResolution
|
||||
{
|
||||
|
||||
internal abstract class ManyObjectsResolverBase<TResolver, TResolved> : ResolverBase<TResolver>
|
||||
where TResolved : class
|
||||
where TResolver : class
|
||||
{
|
||||
private List<TResolved> _applicationInstances = null;
|
||||
private readonly ReaderWriterLockSlim _lock = new ReaderWriterLockSlim();
|
||||
|
||||
private readonly List<Type> _instanceTypes = new List<Type>();
|
||||
|
||||
#region Constructors
|
||||
|
||||
|
||||
@@ -34,7 +34,7 @@ namespace Umbraco.Core.ObjectResolution
|
||||
}
|
||||
|
||||
LifetimeScope = scope;
|
||||
InstanceTypes = new List<Type>();
|
||||
_instanceTypes = new List<Type>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -48,7 +48,7 @@ namespace Umbraco.Core.ObjectResolution
|
||||
if (httpContext == null) throw new ArgumentNullException("httpContext");
|
||||
LifetimeScope = ObjectLifetimeScope.HttpRequest;
|
||||
CurrentHttpContext = httpContext;
|
||||
InstanceTypes = new List<Type>();
|
||||
_instanceTypes = new List<Type>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -58,8 +58,8 @@ namespace Umbraco.Core.ObjectResolution
|
||||
/// <param name="scope">If set to true will resolve singleton objects which will be created once for the lifetime of the application</param>
|
||||
protected ManyObjectsResolverBase(IEnumerable<Type> value, ObjectLifetimeScope scope = ObjectLifetimeScope.Application)
|
||||
: this(scope)
|
||||
{
|
||||
InstanceTypes = new List<Type>(value);
|
||||
{
|
||||
_instanceTypes = new List<Type>(value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -71,7 +71,7 @@ namespace Umbraco.Core.ObjectResolution
|
||||
protected ManyObjectsResolverBase(HttpContextBase httpContext, IEnumerable<Type> value)
|
||||
: this(httpContext)
|
||||
{
|
||||
InstanceTypes = new List<Type>(value);
|
||||
_instanceTypes = new List<Type>(value);
|
||||
}
|
||||
#endregion
|
||||
|
||||
@@ -83,7 +83,10 @@ namespace Umbraco.Core.ObjectResolution
|
||||
/// <summary>
|
||||
/// Returns the list of Types registered that instances will be created from
|
||||
/// </summary>
|
||||
protected List<Type> InstanceTypes { get; private set; }
|
||||
protected virtual IEnumerable<Type> InstanceTypes
|
||||
{
|
||||
get { return _instanceTypes; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the Current HttpContextBase used to construct this object if one exists.
|
||||
@@ -97,11 +100,7 @@ namespace Umbraco.Core.ObjectResolution
|
||||
protected ObjectLifetimeScope LifetimeScope { get; private set; }
|
||||
|
||||
private int _defaultPluginWeight = 10;
|
||||
private bool _supportsAdd = true;
|
||||
private bool _supportsInsert = true;
|
||||
private bool _supportsClear = true;
|
||||
private bool _supportsRemove = true;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Used in conjunction with GetSortedValues and WeightedPluginAttribute, if any of the objects
|
||||
/// being resolved do not contain the WeightedPluginAttribute then this will be the default weight applied
|
||||
@@ -189,15 +188,16 @@ namespace Umbraco.Core.ObjectResolution
|
||||
/// Removes a type.
|
||||
/// </summary>
|
||||
/// <param name="value">The type to remove.</param>
|
||||
public void RemoveType(Type value)
|
||||
public virtual void RemoveType(Type value)
|
||||
{
|
||||
if (!SupportsRemove)
|
||||
throw new InvalidOperationException("This resolver does not support Removing types");
|
||||
EnsureRemoveSupport();
|
||||
|
||||
using (new WriteLock(_lock))
|
||||
EnsureResolutionNotFrozen();
|
||||
|
||||
using (GetWriteLock())
|
||||
{
|
||||
EnsureCorrectType(value);
|
||||
InstanceTypes.Remove(value);
|
||||
_instanceTypes.Remove(value);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -215,8 +215,12 @@ namespace Umbraco.Core.ObjectResolution
|
||||
/// </summary>
|
||||
/// <param name="types"></param>
|
||||
protected void AddTypes(IEnumerable<Type> types)
|
||||
{
|
||||
using (var l = new WriteLock(_lock))
|
||||
{
|
||||
EnsureAddSupport();
|
||||
|
||||
EnsureResolutionNotFrozen();
|
||||
|
||||
using (GetWriteLock())
|
||||
{
|
||||
foreach(var t in types)
|
||||
{
|
||||
@@ -225,7 +229,7 @@ namespace Umbraco.Core.ObjectResolution
|
||||
{
|
||||
throw new InvalidOperationException("The Type " + t + " already exists in the collection");
|
||||
};
|
||||
InstanceTypes.Add(t);
|
||||
_instanceTypes.Add(t);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -234,19 +238,20 @@ namespace Umbraco.Core.ObjectResolution
|
||||
/// Adds a Type to the end of the list.
|
||||
/// </summary>
|
||||
/// <param name="value">The object to be added.</param>
|
||||
public void AddType(Type value)
|
||||
public virtual void AddType(Type value)
|
||||
{
|
||||
if (!SupportsAdd)
|
||||
throw new InvalidOperationException("This resolver does not support Adding new types");
|
||||
EnsureAddSupport();
|
||||
|
||||
using (var l = new WriteLock(_lock))
|
||||
EnsureResolutionNotFrozen();
|
||||
|
||||
using (GetWriteLock())
|
||||
{
|
||||
EnsureCorrectType(value);
|
||||
if (InstanceTypes.Contains(value))
|
||||
{
|
||||
throw new InvalidOperationException("The Type " + value + " already exists in the collection");
|
||||
};
|
||||
InstanceTypes.Add(value);
|
||||
_instanceTypes.Add(value);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -262,14 +267,15 @@ namespace Umbraco.Core.ObjectResolution
|
||||
/// <summary>
|
||||
/// Clears the list.
|
||||
/// </summary>
|
||||
public void Clear()
|
||||
public virtual void Clear()
|
||||
{
|
||||
if (!SupportsClear)
|
||||
throw new InvalidOperationException("This resolver does not support Clearing types");
|
||||
EnsureClearSupport();
|
||||
|
||||
using (new WriteLock(_lock))
|
||||
EnsureResolutionNotFrozen();
|
||||
|
||||
using (GetWriteLock())
|
||||
{
|
||||
InstanceTypes.Clear();
|
||||
_instanceTypes.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -278,12 +284,13 @@ namespace Umbraco.Core.ObjectResolution
|
||||
/// </summary>
|
||||
/// <param name="index">The zero-based index at which the object should be inserted.</param>
|
||||
/// <param name="value">The object to insert.</param>
|
||||
public void InsertType(int index, Type value)
|
||||
public virtual void InsertType(int index, Type value)
|
||||
{
|
||||
if (!SupportsInsert)
|
||||
throw new InvalidOperationException("This resolver does not support Inserting new types");
|
||||
EnsureInsertSupport();
|
||||
|
||||
using (var l = new UpgradeableReadLock(_lock))
|
||||
EnsureResolutionNotFrozen();
|
||||
|
||||
using (var l = GetWriteLock())
|
||||
{
|
||||
EnsureCorrectType(value);
|
||||
if (InstanceTypes.Contains(value))
|
||||
@@ -291,8 +298,7 @@ namespace Umbraco.Core.ObjectResolution
|
||||
throw new InvalidOperationException("The Type " + value + " already exists in the collection");
|
||||
};
|
||||
|
||||
l.UpgradeToWriteLock();
|
||||
InstanceTypes.Insert(index, value);
|
||||
_instanceTypes.Insert(index, value);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -306,7 +312,65 @@ namespace Umbraco.Core.ObjectResolution
|
||||
InsertType(index, typeof (T));
|
||||
}
|
||||
|
||||
private void EnsureCorrectType(Type t)
|
||||
/// <summary>
|
||||
/// Returns a WriteLock to use when modifying collections
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
protected WriteLock GetWriteLock()
|
||||
{
|
||||
return new WriteLock(_lock);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Throws an exception if resolution is frozen
|
||||
/// </summary>
|
||||
protected void EnsureResolutionNotFrozen()
|
||||
{
|
||||
if (Resolution.IsFrozen)
|
||||
throw new InvalidOperationException("The type list cannot be modified after resolution has been frozen");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Throws an exception if this does not support Remove
|
||||
/// </summary>
|
||||
protected void EnsureRemoveSupport()
|
||||
{
|
||||
if (!SupportsRemove)
|
||||
throw new InvalidOperationException("This resolver does not support Removing types");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Throws an exception if this does not support Clear
|
||||
/// </summary>
|
||||
protected void EnsureClearSupport()
|
||||
{
|
||||
if (!SupportsClear)
|
||||
throw new InvalidOperationException("This resolver does not support Clearing types");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Throws an exception if this does not support Add
|
||||
/// </summary>
|
||||
protected void EnsureAddSupport()
|
||||
{
|
||||
if (!SupportsAdd)
|
||||
throw new InvalidOperationException("This resolver does not support Adding new types");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Throws an exception if this does not support insert
|
||||
/// </summary>
|
||||
protected void EnsureInsertSupport()
|
||||
{
|
||||
if (!SupportsInsert)
|
||||
throw new InvalidOperationException("This resolver does not support Inserting new types");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Throws an exception if the type is not of the TResolved type
|
||||
/// </summary>
|
||||
/// <param name="t"></param>
|
||||
protected void EnsureCorrectType(Type t)
|
||||
{
|
||||
if (!TypeHelper.IsTypeAssignableFrom<TResolved>(t))
|
||||
throw new InvalidOperationException("The resolver " + this.GetType() + " can only accept types of " + typeof(TResolved) + ". The Type passed in to this method is " + t);
|
||||
@@ -314,22 +378,22 @@ namespace Umbraco.Core.ObjectResolution
|
||||
|
||||
protected virtual bool SupportsAdd
|
||||
{
|
||||
get { return _supportsAdd; }
|
||||
get { return true; }
|
||||
}
|
||||
|
||||
protected virtual bool SupportsInsert
|
||||
{
|
||||
get { return _supportsInsert; }
|
||||
get { return true; }
|
||||
}
|
||||
|
||||
protected virtual bool SupportsClear
|
||||
{
|
||||
get { return _supportsClear; }
|
||||
get { return true; }
|
||||
}
|
||||
|
||||
protected virtual bool SupportsRemove
|
||||
{
|
||||
get { return _supportsRemove; }
|
||||
get { return true; }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -70,6 +70,7 @@
|
||||
<Compile Include="HashCodeCombiner.cs" />
|
||||
<Compile Include="IO\FileSystemWrapper.cs" />
|
||||
<Compile Include="Media\IImageUrlProvider.cs" />
|
||||
<Compile Include="ObjectResolution\LazyManyObjectsResolverbase.cs" />
|
||||
<Compile Include="PublishedContentExtensions.cs" />
|
||||
<Compile Include="Dictionary\ICultureDictionary.cs" />
|
||||
<Compile Include="Dynamics\ClassFactory.cs" />
|
||||
|
||||
84
src/Umbraco.Tests/Resolvers/LazyManyObjectResolverTests.cs
Normal file
84
src/Umbraco.Tests/Resolvers/LazyManyObjectResolverTests.cs
Normal file
@@ -0,0 +1,84 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NUnit.Framework;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.ObjectResolution;
|
||||
|
||||
namespace Umbraco.Tests.Resolvers
|
||||
{
|
||||
[TestFixture]
|
||||
public class LazyManyObjectResolverTests
|
||||
{
|
||||
|
||||
[SetUp]
|
||||
public void Initialize()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
[TearDown]
|
||||
public void TearDown()
|
||||
{
|
||||
Resolution.IsFrozen = false;
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Ensure_Lazy_Type_Resolution()
|
||||
{
|
||||
var resolver = new LazyResolver(new[] {new Lazy<Type>(() => typeof (TransientObject3))});
|
||||
resolver.AddType<TransientObject1>();
|
||||
resolver.AddType(new Lazy<Type>(() => typeof(TransientObject2)));
|
||||
|
||||
Resolution.Freeze();
|
||||
|
||||
Assert.IsFalse(resolver.HasResolvedTypes);
|
||||
|
||||
var instances1 = resolver.Objects;
|
||||
|
||||
Assert.IsTrue(resolver.HasResolvedTypes);
|
||||
|
||||
Assert.AreEqual(3, instances1.Count());
|
||||
Assert.IsTrue(instances1.Select(x => x.GetType()).ContainsAll(new []{typeof(TransientObject1), typeof(TransientObject2), typeof(TransientObject3)}));
|
||||
}
|
||||
|
||||
#region Test classes
|
||||
|
||||
private interface ITestInterface
|
||||
{
|
||||
}
|
||||
|
||||
private class TransientObject1 : ITestInterface
|
||||
{
|
||||
}
|
||||
|
||||
private class TransientObject2 : ITestInterface
|
||||
{
|
||||
}
|
||||
|
||||
private class TransientObject3 : ITestInterface
|
||||
{
|
||||
}
|
||||
|
||||
private sealed class LazyResolver : LazyManyObjectsResolverBase<LazyResolver, ITestInterface>
|
||||
{
|
||||
public LazyResolver()
|
||||
: base(ObjectLifetimeScope.Transient)
|
||||
{
|
||||
}
|
||||
|
||||
public LazyResolver(IEnumerable<Lazy<Type>> values)
|
||||
:base (values, ObjectLifetimeScope.Transient)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public IEnumerable<ITestInterface> Objects
|
||||
{
|
||||
get { return Values; }
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -73,7 +73,7 @@ namespace Umbraco.Tests.Resolvers
|
||||
Assert.IsFalse(object.ReferenceEquals(instances1.Single(), instances3.Single()));
|
||||
}
|
||||
|
||||
#region
|
||||
#region Test classes
|
||||
|
||||
private interface ITestInterface
|
||||
{
|
||||
|
||||
@@ -86,6 +86,7 @@
|
||||
<Compile Include="ObjectExtensionsTests.cs" />
|
||||
<Compile Include="ContentStores\PublishContentStoreTests.cs" />
|
||||
<Compile Include="DataTypeFactoryTests.cs" />
|
||||
<Compile Include="Resolvers\LazyManyObjectResolverTests.cs" />
|
||||
<Compile Include="Routing\LookupByNiceUrlWithDomainsTests.cs" />
|
||||
<Compile Include="Routing\NiceUrlsProviderWithDomainsTests.cs" />
|
||||
<Compile Include="Routing\uQueryGetNodeIdByUrlTests.cs" />
|
||||
|
||||
Reference in New Issue
Block a user