From bfd8e96f710074c235a087f13f49562403e08579 Mon Sep 17 00:00:00 2001 From: Stephan Date: Wed, 16 Jan 2013 13:10:34 -0100 Subject: [PATCH] Core.ObjectsResolution - cleanup + start making things public --- .../MacroFieldEditorsResolver.cs | 2 + .../ManyObjectsResolverBase.cs | 332 ++++++++++++------ .../ObjectResolution/ObjectLifetimeScope.cs | 16 +- .../ObjectResolution/Resolution.cs | 32 +- .../ObjectResolution/ResolverBase.cs | 26 +- .../SingleObjectResolverBase.cs | 65 +++- .../WeightedPluginAttribute.cs | 10 +- 7 files changed, 352 insertions(+), 131 deletions(-) diff --git a/src/Umbraco.Core/ObjectResolution/MacroFieldEditorsResolver.cs b/src/Umbraco.Core/ObjectResolution/MacroFieldEditorsResolver.cs index f0b6867a38..6015c14d94 100644 --- a/src/Umbraco.Core/ObjectResolution/MacroFieldEditorsResolver.cs +++ b/src/Umbraco.Core/ObjectResolution/MacroFieldEditorsResolver.cs @@ -8,6 +8,8 @@ using umbraco.interfaces; namespace Umbraco.Core.ObjectResolution { + // FIXME - specific resolvers should not be in Umbraco.Core.ObjectResolution ?? + /// /// A resolver to return all IMacroGuiRendering objects /// diff --git a/src/Umbraco.Core/ObjectResolution/ManyObjectsResolverBase.cs b/src/Umbraco.Core/ObjectResolution/ManyObjectsResolverBase.cs index 9343b1a58b..64d850c66e 100644 --- a/src/Umbraco.Core/ObjectResolution/ManyObjectsResolverBase.cs +++ b/src/Umbraco.Core/ObjectResolution/ManyObjectsResolverBase.cs @@ -6,30 +6,37 @@ using System.Web; namespace Umbraco.Core.ObjectResolution { - internal abstract class ManyObjectsResolverBase : ResolverBase + /// + /// 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 { - private List _applicationInstances = null; + private IEnumerable _applicationInstances = null; private readonly ReaderWriterLockSlim _lock = new ReaderWriterLockSlim(); private readonly List _instanceTypes = new List(); + private int _defaultPluginWeight = 10; + #region Constructors - - + /// /// Initializes a new instance of the class with an empty list of objects. /// - /// The lifetime scope of instantiated objects, default is per Application + /// The lifetime scope of instantiated objects, default is per Application. + /// If is per HttpRequest then there must be a current HttpContext. + /// is per HttpRequest but the current HttpContext is null. protected ManyObjectsResolverBase(ObjectLifetimeScope scope = ObjectLifetimeScope.Application) { CanResolveBeforeFrozen = false; 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); } @@ -38,50 +45,56 @@ namespace Umbraco.Core.ObjectResolution } /// - /// Initializes a new instance of the class with an empty list of objects. + /// Initializes a new instance of the class with an empty list of objects, /// with creation of objects based on an HttpRequest lifetime scope. /// - /// + /// The HttpContextBase corresponding to the HttpRequest. + /// is null. protected ManyObjectsResolverBase(HttpContextBase httpContext) { CanResolveBeforeFrozen = false; - if (httpContext == null) throw new ArgumentNullException("httpContext"); + if (httpContext == null) + throw new ArgumentNullException("httpContext"); LifetimeScope = ObjectLifetimeScope.HttpRequest; CurrentHttpContext = httpContext; _instanceTypes = new List(); } /// - /// Initializes a new instance of the class with an initial list of objects. + /// Initializes a new instance of the class with an initial list of object types. /// - /// The list of objects. - /// If set to true will resolve singleton objects which will be created once for the lifetime of the application + /// The list of object types. + /// The lifetime scope of instantiated objects, default is per Application. + /// If is per HttpRequest then there must be a current HttpContext. + /// is per HttpRequest but the current HttpContext is null. protected ManyObjectsResolverBase(IEnumerable value, ObjectLifetimeScope scope = ObjectLifetimeScope.Application) : this(scope) { - _instanceTypes = new List(value); + _instanceTypes = value.ToList(); } /// - /// Initializes a new instance of the class with an initial list of objects + /// Initializes a new instance of the class with an initial list of objects, /// with creation of objects based on an HttpRequest lifetime scope. /// - /// - /// + /// The HttpContextBase corresponding to the HttpRequest. + /// The list of object types. + /// is null. protected ManyObjectsResolverBase(HttpContextBase httpContext, IEnumerable value) : this(httpContext) { - _instanceTypes = new List(value); + _instanceTypes = value.ToList(); } #endregion /// - /// used internally for special resolvers to be able to resolve objects before resolution is frozen. + /// Gets or sets a value indicating whether the resolver can resolve objects before resolution is frozen. /// + /// This is false by default and is used for some special internal resolvers. internal bool CanResolveBeforeFrozen { get; set; } /// - /// Returns the list of Types registered that instances will be created from + /// Gets the list of types to create instances from. /// protected virtual IEnumerable InstanceTypes { @@ -89,114 +102,144 @@ namespace Umbraco.Core.ObjectResolution } /// - /// Returns the Current HttpContextBase used to construct this object if one exists. - /// If one exists then the LifetimeScope will be ObjectLifetimeScope.HttpRequest + /// Gets or sets the used to initialize this object, if any. /// + /// If not null, then LifetimeScope will be ObjectLifetimeScope.HttpRequest. protected HttpContextBase CurrentHttpContext { get; private set; } /// - /// Returns the ObjectLifetimeScope for created objects + /// Gets or sets the lifetime scope of resolved objects. /// protected ObjectLifetimeScope LifetimeScope { get; private set; } - private int _defaultPluginWeight = 10; - /// - /// 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 - /// to the object. + /// Gets the resolved object instances, sorted by weight. /// + /// The sorted resolved object instances. + /// + /// The order is based upon the WeightedPluginAttribute and DefaultPluginWeight. + /// Weights are sorted ascendingly (lowest weights come first). + /// + protected IEnumerable GetSortedValues() + { + var values = Values.ToList(); + + // FIXME - so we're re-sorting each time? + + values.Sort((f1, f2) => GetObjectWeight(f1).CompareTo(GetObjectWeight(f2))); + return values; + } + + /// + /// Gets or sets the default type weight. + /// + /// Determines the weight of types that do not have a WeightedPluginAttribute set on + /// them, when calling GetSortedValues. protected virtual int DefaultPluginWeight { get { return _defaultPluginWeight; } set { _defaultPluginWeight = value; } } - /// - /// If a resolver requries that objects are resolved with a specific order using the WeightedPluginAttribute - /// then this method should be used instead of the Values property. - /// - /// - protected IEnumerable GetSortedValues() + int GetObjectWeight(object o) { - var vals = Values.ToList(); - //ensure they are sorted - vals.Sort((f1, f2) => - { - Func getWeight = o => - { - var weightAttribute = f1.GetType().GetCustomAttribute(true); - return weightAttribute != null ? weightAttribute.Weight : DefaultPluginWeight; - }; - return getWeight(f1).CompareTo(getWeight(f2)); - }); - return vals; - } + var type = o.GetType(); + var attr = type.GetCustomAttribute(true); + return attr == null ? DefaultPluginWeight : attr.Weight; + } /// - /// Returns the list of new object instances. + /// Gets the resolved object instances. /// + /// CanResolveBeforeFrozen is false, and resolution is not frozen. protected IEnumerable Values { get { - //We cannot return values unless resolution is locked + // cannot return values unless resolution is frozen, or we can if (!CanResolveBeforeFrozen && !Resolution.IsFrozen) - throw new InvalidOperationException("Values cannot be returned until Resolution is frozen"); + throw new InvalidOperationException("Values cannot be returned until resolution is frozen"); + + // note: we apply .ToArray() to the output of CreateInstance() because that is an IEnumerable that + // comes from the PluginManager we want to be _sure_ that it's not a Linq of some sort, but the + // instances have actually been instanciated when we return. 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 + // create new instances per HttpContext + var key = this.GetType().FullName; // use full type name as key 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) + // create if not already there + if (CurrentHttpContext.Items[key] == null) { l.UpgradeToWriteLock(); - //add the items to the context items (based on full type name) - CurrentHttpContext.Items[this.GetType().FullName] = new List(CreateInstances()); + CurrentHttpContext.Items[key] = CreateInstances().ToArray(); } - return (List)CurrentHttpContext.Items[this.GetType().FullName]; + return (List)CurrentHttpContext.Items[key]; } + case ObjectLifetimeScope.Application: - //create new instances per application, this means we'll lazily create them and once created, cache them + // create new instances per application using(var l = new UpgradeableReadLock(_lock)) { + // create if not already there if (_applicationInstances == null) { l.UpgradeToWriteLock(); - _applicationInstances = new List(CreateInstances()); + _applicationInstances = CreateInstances().ToArray(); } return _applicationInstances; } + case ObjectLifetimeScope.Transient: default: - //create new instances each time - return CreateInstances(); + // create new instances each time + return CreateInstances().ToArray(); } } } + /// + /// Creates the object instances for the types contained in the types collection. + /// + /// A list of objects of type . protected virtual IEnumerable CreateInstances() { return PluginManager.Current.CreateInstances(InstanceTypes); - } + } + + /// + /// Ensures that a type is a valid type for the resolver. + /// + /// The type to test. + /// the type is not a valid type for the resolver. + protected void EnsureCorrectType(Type value) + { + if (!TypeHelper.IsTypeAssignableFrom(value)) + throw new InvalidOperationException(string.Format( + "Type {0} is not an acceptable type for resolver {1}.", value.FullName, this.GetType().FullName)); + } + + #region Types collection manipulation /// /// Removes a type. /// /// The type to remove. + /// the resolver does not support removing types, or + /// the type is not a valid type for the resolver. public virtual void RemoveType(Type value) { EnsureRemoveSupport(); - EnsureResolutionNotFrozen(); - using (GetWriteLock()) + using (var l = new UpgradeableReadLock(_lock)) { EnsureCorrectType(value); + + l.UpgradeToWriteLock(); _instanceTypes.Remove(value); } } @@ -204,114 +247,203 @@ namespace Umbraco.Core.ObjectResolution /// /// Removes a type. /// - /// + /// The type to remove. + /// the resolver does not support removing types, or + /// the type is not a valid type for the resolver. public void RemoveType() { - RemoveType(typeof (T)); + RemoveType(typeof(T)); } /// - /// protected method allow the inheritor to add many types at once + /// Adds types. /// - /// + /// The types to add. + /// The types are appended at the end of the list. + /// the resolver does not support adding types, or + /// a type is not a valid type for the resolver, or a type is already in the collection of types. protected void AddTypes(IEnumerable types) { EnsureAddSupport(); - EnsureResolutionNotFrozen(); - using (GetWriteLock()) + using (new WriteLock(_lock)) { foreach(var t in types) { EnsureCorrectType(t); if (InstanceTypes.Contains(t)) { - throw new InvalidOperationException("The Type " + t + " already exists in the collection"); - }; + throw new InvalidOperationException(string.Format( + "Type {0} is already in the collection of types.", t.FullName)); + } _instanceTypes.Add(t); } } } /// - /// Adds a Type to the end of the list. + /// Adds a type. /// - /// The object to be added. + /// The type to add. + /// The type is appended at the end of the list. + /// the resolver does not support adding types, or + /// the type is not a valid type for the resolver, or the type is already in the collection of types. public virtual void AddType(Type value) { EnsureAddSupport(); - EnsureResolutionNotFrozen(); - using (GetWriteLock()) + using (var l = new UpgradeableReadLock(_lock)) { EnsureCorrectType(value); if (InstanceTypes.Contains(value)) { - throw new InvalidOperationException("The Type " + value + " already exists in the collection"); - }; + throw new InvalidOperationException(string.Format( + "Type {0} is already in the collection of types.", value.FullName)); + } + + l.UpgradeToWriteLock(); _instanceTypes.Add(value); } } /// - /// Adds a Type to the end of the list. + /// Adds a type. /// - /// + /// The type to add. + /// The type is appended at the end of the list. + /// the resolver does not support adding types, or + /// the type is not a valid type for the resolver, or the type is already in the collection of types. public void AddType() { - AddType(typeof (T)); + AddType(typeof(T)); } /// - /// Clears the list. + /// Clears the list of types. /// + /// the resolver does not support clearing types. public virtual void Clear() { EnsureClearSupport(); - EnsureResolutionNotFrozen(); - using (GetWriteLock()) + using (new WriteLock(_lock)) { _instanceTypes.Clear(); } } /// - /// Inserts a Type at the specified index. + /// Inserts a type at the specified index. /// - /// The zero-based index at which the object should be inserted. - /// The object to insert. + /// The zero-based index at which the type should be inserted. + /// The type to insert. + /// the resolver does not support inserting types, or + /// the type is not a valid type for the resolver, or the type is already in the collection of types. + /// is out of range. public virtual void InsertType(int index, Type value) { EnsureInsertSupport(); - EnsureResolutionNotFrozen(); - using (var l = GetWriteLock()) + using (var l = new UpgradeableReadLock(_lock)) { EnsureCorrectType(value); if (InstanceTypes.Contains(value)) { - throw new InvalidOperationException("The Type " + value + " already exists in the collection"); - }; + throw new InvalidOperationException(string.Format( + "Type {0} is already in the collection of types.", value.FullName)); + } + l.UpgradeToWriteLock(); _instanceTypes.Insert(index, value); } } /// - /// Inserts a Type at the specified index. + /// Inserts a type at the specified index. /// - /// - /// + /// The type to insert. + /// The zero-based index at which the type should be inserted. + /// is out of range. public void InsertType(int index) { - InsertType(index, typeof (T)); + InsertType(index, typeof(T)); } + /// Inserts a type before a specified, already existing type. + /// + /// The existing type before which to insert. + /// The type to insert. + /// the resolver does not support inserting types, or + /// one of the types is not a valid type for the resolver, or the existing type is not in the collection, + /// or the new type is already in the collection of types. + public virtual void InsertTypeBefore(Type existingType, Type value) + { + EnsureInsertSupport(); + EnsureResolutionNotFrozen(); + + using (var l = new UpgradeableReadLock(_lock)) + { + EnsureCorrectType(existingType); + EnsureCorrectType(value); + if (!InstanceTypes.Contains(existingType)) + { + throw new InvalidOperationException(string.Format( + "Type {0} is not in the collection of types.", existingType.FullName)); + } + if (InstanceTypes.Contains(value)) + { + throw new InvalidOperationException(string.Format( + "Type {0} is already in the collection of types.", value.FullName)); + } + int index = InstanceTypes.IndexOf(existingType); + + l.UpgradeToWriteLock(); + _instanceTypes.Insert(index, value); + } + } + + /// + /// Inserts a type before a specified, already existing type. + /// + /// The existing type before which to insert. + /// The type to insert. + /// the resolver does not support inserting types, or + /// one of the types is not a valid type for the resolver, or the existing type is not in the collection, + /// or the new type is already in the collection of types. + public void InsertTypeBefore() + { + InsertTypeBefore(typeof(Texisting), typeof(T)); + } + + /// + /// Returns a value indicating whether the specified type is already in the collection of types. + /// + /// The type to look for. + /// A value indicating whether the type is already in the collection of types. + public virtual bool ContainsType(Type value) + { + using (new ReadLock(_lock)) + { + return _instanceTypes.Contains(value); + } + } + + /// + /// Returns a value indicating whether the specified type is already in the collection of types. + /// + /// The type to look for. + /// A value indicating whether the type is already in the collection of types. + public bool ContainsType() + { + return ContainsType(typeof(T)); + } + + #endregion + /// /// Returns a WriteLock to use when modifying collections /// @@ -366,16 +498,6 @@ namespace Umbraco.Core.ObjectResolution throw new InvalidOperationException("This resolver does not support Inserting new types"); } - /// - /// Throws an exception if the type is not of the TResolved type - /// - /// - protected void EnsureCorrectType(Type t) - { - if (!TypeHelper.IsTypeAssignableFrom(t)) - throw new InvalidOperationException("The resolver " + this.GetType() + " can only accept types of " + typeof(TResolved) + ". The Type passed in to this method is " + t); - } - protected virtual bool SupportsAdd { get { return true; } diff --git a/src/Umbraco.Core/ObjectResolution/ObjectLifetimeScope.cs b/src/Umbraco.Core/ObjectResolution/ObjectLifetimeScope.cs index ac0101d80f..e7330ae124 100644 --- a/src/Umbraco.Core/ObjectResolution/ObjectLifetimeScope.cs +++ b/src/Umbraco.Core/ObjectResolution/ObjectLifetimeScope.cs @@ -1,9 +1,23 @@ namespace Umbraco.Core.ObjectResolution { - internal enum ObjectLifetimeScope + /// + /// Specifies the lifetime scope of resolved objects. + /// + public enum ObjectLifetimeScope { + /// + /// A per-request object instance is created. + /// HttpRequest, + + /// + /// A single application-wide object instance is created. + /// Application, + + /// + /// A new object instance is created each time one is requested. + /// Transient } } \ No newline at end of file diff --git a/src/Umbraco.Core/ObjectResolution/Resolution.cs b/src/Umbraco.Core/ObjectResolution/Resolution.cs index 1016d5b838..8e0670d169 100644 --- a/src/Umbraco.Core/ObjectResolution/Resolution.cs +++ b/src/Umbraco.Core/ObjectResolution/Resolution.cs @@ -2,29 +2,43 @@ namespace Umbraco.Core.ObjectResolution { - - - - // notes: nothing in Resolving is thread-safe because everything should happen when the app is starting - + /// + /// Represents the status of objects resolution. + /// + /// + /// Objects resolution can be frozen ie nothing can change anymore. + /// Nothing in resolution is thread-safe, because everything should take place when the application is starting. + /// internal class Resolution { + // NOTE : must clarify freezing... SingleObjectResolverBase does not honor it... + + /// + /// Occurs when resolution is frozen. + /// + /// Occurs only once, since resolution can be frozen only once. public static event EventHandler Frozen; /// - /// Gets a value indicating that resolution is frozen + /// Gets or sets a value indicating whether resolution of objects is frozen. /// - /// - /// The internal setter is normally used for unit tests - /// + /// The internal setter is to be used in unit tests. public static bool IsFrozen { get; internal set; } + /// + /// Ensures that resolution is not frozen, else throws. + /// + /// resolution is frozen. public static void EnsureNotFrozen() { if (Resolution.IsFrozen) throw new InvalidOperationException("Resolution is frozen. It is not possible to modify resolvers once resolution is frozen."); } + /// + /// Freezes resolution. + /// + /// resolution is already frozen. public static void Freeze() { if (Resolution.IsFrozen) diff --git a/src/Umbraco.Core/ObjectResolution/ResolverBase.cs b/src/Umbraco.Core/ObjectResolution/ResolverBase.cs index a6578559c4..f212005d36 100644 --- a/src/Umbraco.Core/ObjectResolution/ResolverBase.cs +++ b/src/Umbraco.Core/ObjectResolution/ResolverBase.cs @@ -4,16 +4,17 @@ using System.Threading; namespace Umbraco.Core.ObjectResolution { /// - /// base class for resolvers which declare a singleton accessor + /// The base class for all resolvers. /// - /// - internal abstract class ResolverBase + /// The type of the concrete resolver class. + /// Provides singleton management to all resolvers. + public abstract class ResolverBase where TResolver : class { static TResolver _resolver; /// - /// The lock for the singleton + /// 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. @@ -21,6 +22,12 @@ namespace Umbraco.Core.ObjectResolution /// static readonly ReaderWriterLockSlim ResolversLock = new ReaderWriterLockSlim(); + /// + /// Gets or sets the resolver singleton instance. + /// + /// The value can be set only once, and cannot be read before it has been set. + /// value is read before it has been set, or value is set again once it has already been set. + /// value is null. public static TResolver Current { get @@ -28,7 +35,9 @@ namespace Umbraco.Core.ObjectResolution using (new ReadLock(ResolversLock)) { if (_resolver == null) - throw new InvalidOperationException("Current has not been initialized on " + typeof(TResolver) + ". You must initialize Current before trying to read it."); + throw new InvalidOperationException(string.Format( + "Current has not been initialized on {0}. You must initialize Current before trying to read it.", + typeof(TResolver).FullName)); return _resolver; } } @@ -40,15 +49,18 @@ namespace Umbraco.Core.ObjectResolution 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."); + throw new InvalidOperationException(string.Format( + "Current has already been initialized on {0}. It is not possible to re-initialize Current once it has been initialized.", + typeof(TResolver).FullName)); _resolver = value; } } } /// - /// used in unit tests to reset current to null + /// Resets the resolver singleton instance to null. /// + /// To be used in unit tests. internal static void Reset() { _resolver = null; diff --git a/src/Umbraco.Core/ObjectResolution/SingleObjectResolverBase.cs b/src/Umbraco.Core/ObjectResolution/SingleObjectResolverBase.cs index b74a486f3c..6940f908ee 100644 --- a/src/Umbraco.Core/ObjectResolution/SingleObjectResolverBase.cs +++ b/src/Umbraco.Core/ObjectResolution/SingleObjectResolverBase.cs @@ -3,49 +3,100 @@ namespace Umbraco.Core.ObjectResolution { /// - /// A Resolver to return and set a Single registered object. + /// The base class for all single-object resolvers. /// - /// - /// + /// The type of the concrete resolver class. + /// The type of the resolved object. /// - /// Used for 'singly' registered objects. An example is like the MVC Controller Factory, only one exists application wide and it can - /// be get/set. + /// Resolves "single" objects ie objects for which there is only one application-wide instance, such as the MVC Controller factory. /// - internal abstract class SingleObjectResolverBase : ResolverBase + public abstract class SingleObjectResolverBase : ResolverBase where TResolved : class where TResolver : class { TResolved _resolved; readonly bool _canBeNull; + // NOTE - we're not freezing resolution here so it is potentially possible to change the instance at any time? + + #region Constructors + + /// + /// Initialize a new instance of the class. + /// + /// By default CanBeNull is false, so Value has to be initialized before being read, + /// otherwise an exception will be thrown when reading it. protected SingleObjectResolverBase() : this(false) { } + /// + /// Initialize a new instance of the class with an instance of the resolved object. + /// + /// An instance of the resolved object. + /// By default CanBeNull is false, so value has to be non-null, or Value has to be + /// initialized before being accessed, otherwise an exception will be thrown when reading it. protected SingleObjectResolverBase(TResolved value) : this(false) { _resolved = value; } + /// + /// Initialize a new instance of the class with a value indicating whether the resolved object instance can be null. + /// + /// A value indicating whether the resolved object instance can be null. + /// If CanBeNull is false, Value has to be initialized before being read, + /// otherwise an exception will be thrown when reading it. protected SingleObjectResolverBase(bool canBeNull) { _canBeNull = canBeNull; } + /// + /// Initialize a new instance of the class with an instance of the resolved object, + /// and a value indicating whether that instance can be null. + /// + /// An instance of the resolved object. + /// A value indicating whether the resolved object instance can be null. + /// If CanBeNull is false, value has to be non-null, or Value has to be initialized before being read, + /// otherwise an exception will be thrown when reading it. protected SingleObjectResolverBase(TResolved value, bool canBeNull) { _resolved = value; _canBeNull = canBeNull; } + #endregion + /// - /// Gets/sets the value of the object + /// Gets a value indicating whether the resolved object instance can be null. /// + public bool CanBeNull + { + get { return _canBeNull; } + } + + /// + /// Gets a value indicating whether the resolved object instance is null. + /// + public bool HasValue + { + get { return _resolved != null; } + } + + /// + /// Gets or sets the resolved object instance. + /// + /// + /// value is set to null, but cannot be null (CanBeNull is false). + /// value is read and is null, but cannot be null (CanBeNull is false). protected TResolved Value { get { + if (!_canBeNull && _resolved == null) + throw new InvalidOperationException(""); return _resolved; } diff --git a/src/Umbraco.Core/ObjectResolution/WeightedPluginAttribute.cs b/src/Umbraco.Core/ObjectResolution/WeightedPluginAttribute.cs index 9ea4fec101..323142bd4c 100644 --- a/src/Umbraco.Core/ObjectResolution/WeightedPluginAttribute.cs +++ b/src/Umbraco.Core/ObjectResolution/WeightedPluginAttribute.cs @@ -3,17 +3,23 @@ using System; namespace Umbraco.Core.ObjectResolution { /// - /// Some many object resolvers require that the objects that they resolve have weights applied to them so that - /// the objects are returned in a sorted order, this attribute is used in these scenarios. + /// Indicates the relative weight of a resolved object type. /// [AttributeUsage(AttributeTargets.Class, AllowMultiple = false)] internal class WeightedPluginAttribute : Attribute { + /// + /// Initializes a new instance of the class with a weight. + /// + /// The object type weight. public WeightedPluginAttribute(int weight) { Weight = weight; } + /// + /// Gets or sets the weight of the object type. + /// public int Weight { get; private set; } } } \ No newline at end of file