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:
Shannon Deminick
2013-01-23 07:45:00 +03:00
parent 1568e5f19c
commit aea1a03453
7 changed files with 343 additions and 44 deletions

View 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; }
}
}
}

View File

@@ -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>

View File

@@ -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; }
}
}
}

View File

@@ -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" />

View 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
}
}

View File

@@ -73,7 +73,7 @@ namespace Umbraco.Tests.Resolvers
Assert.IsFalse(object.ReferenceEquals(instances1.Single(), instances3.Single()));
}
#region
#region Test classes
private interface ITestInterface
{

View File

@@ -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" />