Rename namespace from Umbraco.Core.Resolving to Umbraco.Core.ObjectResolution

This commit is contained in:
shannon@ShandemVaio
2012-08-10 13:18:13 +06:00
parent 3415554142
commit e131011667
38 changed files with 40 additions and 52 deletions

View File

@@ -0,0 +1,92 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Threading;
namespace Umbraco.Core.ObjectResolution
{
/// <summary>
/// A base resolver used for old legacy factories such as the DataTypeFactory or CacheResolverFactory.
/// </summary>
/// <typeparam name="TResolver"></typeparam>
/// <typeparam name="TResolved"> </typeparam>
/// <remarks>
/// This class contains basic functionality to mimic the functionality in these old factories since they all return
/// transient objects (though this should be changed) and the method GetById needs to lookup a type to an ID and since
/// these old classes don't contain metadata, the objects need to be instantiated first to get their metadata, we then store this
/// for use in the GetById method.
/// </remarks>
internal abstract class LegacyTransientObjectsResolver<TResolver, TResolved> : ManyObjectsResolverBase<TResolver, TResolved>
where TResolved : class
where TResolver : class
{
#region Constructors
/// <summary>
/// Constructor
/// </summary>
/// <param name="refreshers"></param>
/// <remarks>
/// We are creating Transient instances (new instances each time) because this is how the legacy code worked and
/// I don't want to muck anything up by changing them to application based instances.
/// TODO: However, it would make much more sense to do this and would speed up the application plus this would make the GetById method much easier.
/// </remarks>
protected LegacyTransientObjectsResolver(IEnumerable<Type> refreshers)
: base(ObjectLifetimeScope.Transient) // false = new objects every time
{
foreach (var l in refreshers)
{
this.AddType(l);
}
}
#endregion
/// <summary>
/// Maintains a list of Ids and their types when first call to CacheResolvers or GetById occurs, this is used
/// in order to return a single object by id without instantiating the entire type stack.
/// </summary>
private ConcurrentDictionary<Guid, Type> _trackIdToType;
private readonly ReaderWriterLockSlim _lock = new ReaderWriterLockSlim();
/// <summary>
/// method to return the unique id for type T
/// </summary>
/// <param name="obj"></param>
/// <returns></returns>
protected abstract Guid GetUniqueIdentifier(TResolved obj);
/// <summary>
/// Returns a new ICacheRefresher instance by id
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
public TResolved GetById(Guid id)
{
EnsureRefreshersList();
return !_trackIdToType.ContainsKey(id)
? null
: PluginManager.Current.CreateInstance<TResolved>(_trackIdToType[id]);
}
/// <summary>
/// Populates the refreshers dictionary to allow us to instantiate a type by Id since the ICacheRefresher type doesn't contain any metadata
/// </summary>
protected void EnsureRefreshersList()
{
using (var l = new UpgradeableReadLock(_lock))
{
if (_trackIdToType == null)
{
l.UpgradeToWriteLock();
_trackIdToType = new ConcurrentDictionary<Guid, Type>();
foreach (var v in Values)
{
_trackIdToType.TryAdd(GetUniqueIdentifier(v), v.GetType());
}
}
}
}
}
}

View File

@@ -0,0 +1,90 @@
using System;
using System.Collections.Generic;
using System.Web.UI;
using Umbraco.Core.Macros;
using umbraco.interfaces;
namespace Umbraco.Core.ObjectResolution
{
/// <summary>
/// A resolver to return all IMacroGuiRendering objects
/// </summary>
/// <remarks>
/// Much of this classes methods are based on legacy code from umbraco.editorControls.macrocontainer.MacroControlFactory
/// this code should probably be reviewed and cleaned up if necessary.
/// </remarks>
internal sealed class MacroFieldEditorsResolver : ManyObjectsResolverBase<MacroFieldEditorsResolver, IMacroGuiRendering>
{
/// <summary>
/// Constructor
/// </summary>
/// <param name="macroEditors"></param>
internal MacroFieldEditorsResolver(IEnumerable<Type> macroEditors)
: base(macroEditors, ObjectLifetimeScope.Transient)
{
}
/// <summary>
/// Gets the <see cref="IMacroGuiRendering"/> implementations.
/// </summary>
public IEnumerable<IMacroGuiRendering> MacroFieldEditors
{
get
{
return Values;
}
}
/// <summary>
/// Gets the value based on the type of control
/// </summary>
/// <param name="macroControl"></param>
/// <returns></returns>
/// <remarks>
/// This is legacy code migrated from umbraco.editorControls.macrocontainer.MacroControlFactory
/// </remarks>
internal string GetValueFromMacroControl(Control macroControl)
{
return ((IMacroGuiRendering)macroControl).Value;
}
/// <remarks>
/// This is legacy code migrated from umbraco.editorControls.macrocontainer.MacroControlFactory
/// </remarks>
internal List<Type> MacroControlTypes
{
get { return InstanceTypes; }
}
/// <summary>
/// Create an instance of a Macro control and return it.
/// Because the macro control uses inline client script whichs is not generated after postback
/// That's why we use the Page Picker instead of the content picker of the macro.
/// </summary>
/// <remarks>
/// This is legacy code migrated from umbraco.editorControls.macrocontainer.MacroControlFactory
/// </remarks>
internal Control GetMacroRenderControlByType(PersistableMacroProperty prop, string uniqueId)
{
var m = MacroControlTypes.FindLast(macroGuiCcontrol => macroGuiCcontrol.ToString() == string.Format("{0}.{1}", prop.AssemblyName, prop.TypeName));
var instance = PluginManager.Current.CreateInstance<IMacroGuiRendering>(m);
if (instance != null)
{
if (!string.IsNullOrEmpty(prop.Value))
{
instance.Value = prop.Value;
}
var macroControl = instance as Control;
if (macroControl != null)
{
macroControl.ID = uniqueId;
return macroControl;
}
}
return null;
}
}
}

View File

@@ -0,0 +1,236 @@
using System;
using System.Collections.Generic;
using System.Threading;
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();
#region Constructors
/// <summary>
/// Initializes a new instance of the <see cref="ManyObjectsResolverBase{TResolver, TResolved}"/> class with an empty list of objects.
/// </summary>
/// <param name="scope">The lifetime scope of instantiated objects, default is per Application</param>
protected ManyObjectsResolverBase(ObjectLifetimeScope scope = ObjectLifetimeScope.Application)
{
if (scope == ObjectLifetimeScope.HttpRequest)
{
if (HttpContext.Current == null)
{
throw new InvalidOperationException("Use alternative constructor accepting a HttpContextBase object in order to set the lifetime scope to HttpRequest when HttpContext.Current is null");
}
CurrentHttpContext = new HttpContextWrapper(HttpContext.Current);
}
LifetimeScope = scope;
InstanceTypes = new List<Type>();
}
/// <summary>
/// Initializes a new instance of the <see cref="ManyObjectsResolverBase{TResolver, TResolved}"/> class with an empty list of objects.
/// with creation of objects based on an HttpRequest lifetime scope.
/// </summary>
/// <param name="httpContext"></param>
protected ManyObjectsResolverBase(HttpContextBase httpContext)
{
LifetimeScope = ObjectLifetimeScope.HttpRequest;
CurrentHttpContext = httpContext;
InstanceTypes = new List<Type>();
}
/// <summary>
/// Initializes a new instance of the <see cref="ManyObjectsResolverBase{TResolver, TResolved}"/> class with an initial list of objects.
/// </summary>
/// <param name="value">The list of objects.</param>
/// <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);
}
/// <summary>
/// Initializes a new instance of the <see cref="ManyObjectsResolverBase{TResolver, TResolved}"/> class with an initial list of objects
/// with creation of objects based on an HttpRequest lifetime scope.
/// </summary>
/// <param name="httpContext"></param>
/// <param name="value"></param>
protected ManyObjectsResolverBase(HttpContextBase httpContext, IEnumerable<Type> value)
: this(httpContext)
{
InstanceTypes = new List<Type>(value);
}
#endregion
/// <summary>
/// Returns the list of Types registered that instances will be created from
/// </summary>
protected List<Type> InstanceTypes { get; private set; }
/// <summary>
/// Returns the Current HttpContextBase used to construct this object if one exists.
/// If one exists then the LifetimeScope will be ObjectLifetimeScope.HttpRequest
/// </summary>
protected HttpContextBase CurrentHttpContext { get; private set; }
/// <summary>
/// Returns the ObjectLifetimeScope for created objects
/// </summary>
protected ObjectLifetimeScope LifetimeScope { get; private set; }
/// <summary>
/// Returns the list of new object instances.
/// </summary>
protected IEnumerable<TResolved> Values
{
get
{
//We cannot return values unless resolution is locked
if (!Resolution.IsFrozen)
throw new InvalidOperationException("Values cannot be returned until Resolution is frozen");
switch (LifetimeScope)
{
case ObjectLifetimeScope.HttpRequest:
//create new instances per HttpContext, this means we'll lazily create them and once created, cache them in the HttpContext
//create new instances per application, this means we'll lazily create them and once created, cache them
using (var l = new UpgradeableReadLock(_lock))
{
//check if the items contain the key (based on the full type name)
if (CurrentHttpContext.Items[this.GetType().FullName] == null)
{
l.UpgradeToWriteLock();
//add the items to the context items (based on full type name)
CurrentHttpContext.Items[this.GetType().FullName] = new List<TResolved>(CreateInstances());
}
return (List<TResolved>)CurrentHttpContext.Items[this.GetType().FullName];
}
case ObjectLifetimeScope.Application:
//create new instances per application, this means we'll lazily create them and once created, cache them
using(var l = new UpgradeableReadLock(_lock))
{
if (_applicationInstances == null)
{
l.UpgradeToWriteLock();
_applicationInstances = new List<TResolved>(CreateInstances());
}
return _applicationInstances;
}
case ObjectLifetimeScope.Transient:
default:
//create new instances each time
return CreateInstances();
}
}
}
protected virtual IEnumerable<TResolved> CreateInstances()
{
return PluginManager.Current.CreateInstances<TResolved>(InstanceTypes);
}
/// <summary>
/// Removes a type.
/// </summary>
/// <param name="value">The type to remove.</param>
public void RemoveType(Type value)
{
EnsureCorrectType(value);
using (new WriteLock(_lock))
{
InstanceTypes.Remove(value);
}
}
/// <summary>
/// Removes a type.
/// </summary>
/// <typeparam name="T"></typeparam>
public void RemoveType<T>()
{
RemoveType(typeof (T));
}
/// <summary>
/// Adds a Type to the end of the list.
/// </summary>
/// <param name="value">The object to be added.</param>
public void AddType(Type value)
{
EnsureCorrectType(value);
using (var l = new UpgradeableReadLock(_lock))
{
if (InstanceTypes.Contains(value))
{
throw new InvalidOperationException("The Type " + value + " already exists in the collection");
};
l.UpgradeToWriteLock();
InstanceTypes.Add(value);
}
}
/// <summary>
/// Adds a Type to the end of the list.
/// </summary>
/// <typeparam name="T"></typeparam>
public void AddType<T>()
{
AddType(typeof (T));
}
/// <summary>
/// Clears the list.
/// </summary>
public void Clear()
{
using (new WriteLock(_lock))
{
InstanceTypes.Clear();
}
}
/// <summary>
/// Inserts a Type at the specified index.
/// </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)
{
EnsureCorrectType(value);
using (var l = new UpgradeableReadLock(_lock))
{
if (InstanceTypes.Contains(value))
{
throw new InvalidOperationException("The Type " + value + " already exists in the collection");
};
l.UpgradeToWriteLock();
InstanceTypes.Insert(index, value);
}
}
/// <summary>
/// Inserts a Type at the specified index.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="index"></param>
public void InsertType<T>(int index)
{
InsertType(index, typeof (T));
}
private 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);
}
}
}

View File

@@ -0,0 +1,9 @@
namespace Umbraco.Core.ObjectResolution
{
internal enum ObjectLifetimeScope
{
HttpRequest,
Application,
Transient
}
}

View File

@@ -0,0 +1,38 @@
using System;
namespace Umbraco.Core.ObjectResolution
{
// notes: nothing in Resolving is thread-safe because everything should happen when the app is starting
internal class Resolution
{
public static event EventHandler Frozen;
/// <summary>
/// Gets a value indicating that resolution is frozen
/// </summary>
/// <remarks>
/// The internal setter is normally used for unit tests
/// </remarks>
public static bool IsFrozen { get; internal set; }
public static void EnsureNotFrozen()
{
if (Resolution.IsFrozen)
throw new InvalidOperationException("Resolution is frozen. It is not possible to modify resolvers once resolution is frozen.");
}
public static void Freeze()
{
if (Resolution.IsFrozen)
throw new InvalidOperationException("Resolution is frozen. It is not possible to freeze it again.");
IsFrozen = true;
if (Frozen != null)
Frozen(null, null);
}
}
}

View File

@@ -0,0 +1,57 @@
using System;
using System.Threading;
namespace Umbraco.Core.ObjectResolution
{
/// <summary>
/// base class for resolvers which declare a singleton accessor
/// </summary>
/// <typeparam name="TResolver"></typeparam>
internal abstract class ResolverBase<TResolver>
where TResolver : class
{
static TResolver _resolver;
/// <summary>
/// The lock for the singleton
/// </summary>
/// <remarks>
/// 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
/// </remarks>
static readonly ReaderWriterLockSlim ResolversLock = new ReaderWriterLockSlim();
public static TResolver Current
{
get
{
using (new ReadLock(ResolversLock))
{
if (_resolver == null)
throw new InvalidOperationException("Current has not been initialized. You must initialize Current before trying to read it.");
return _resolver;
}
}
set
{
using (new WriteLock(ResolversLock))
{
if (value == null)
throw new ArgumentNullException("value");
if (_resolver != null)
throw new InvalidOperationException("Current has already been initialized. It is not possible to re-initialize Current once it has been initialized.");
_resolver = value;
}
}
}
/// <summary>
/// used in unit tests to reset current to null
/// </summary>
internal static void Reset()
{
_resolver = null;
}
}
}

View File

@@ -0,0 +1,60 @@
using System;
namespace Umbraco.Core.ObjectResolution
{
/// <summary>
/// A Resolver to return and set a Single registered object.
/// </summary>
/// <typeparam name="TResolved"></typeparam>
/// <typeparam name="TResolver"> </typeparam>
/// <remarks>
/// Used for 'singly' registered objects. An example is like the MVC Controller Factory, only one exists application wide and it can
/// be get/set.
/// </remarks>
internal abstract class SingleObjectResolverBase<TResolver, TResolved> : ResolverBase<TResolver>
where TResolved : class
where TResolver : class
{
TResolved _resolved;
readonly bool _canBeNull;
protected SingleObjectResolverBase()
: this(false)
{ }
protected SingleObjectResolverBase(TResolved value)
: this(false)
{
_resolved = value;
}
protected SingleObjectResolverBase(bool canBeNull)
{
_canBeNull = canBeNull;
}
protected SingleObjectResolverBase(TResolved value, bool canBeNull)
{
_resolved = value;
_canBeNull = canBeNull;
}
/// <summary>
/// Gets/sets the value of the object
/// </summary>
protected TResolved Value
{
get
{
return _resolved;
}
set
{
if (!_canBeNull && value == null)
throw new ArgumentNullException("value");
_resolved = value;
}
}
}
}