/// <summary>
        /// Loads the Object identified by the given Node as an object of the given type based on information from the Configuration Graph
        /// </summary>
        /// <param name="g">Configuration Graph</param>
        /// <param name="objNode">Object Node</param>
        /// <param name="targetType">Target Type</param>
        /// <returns></returns>
        /// <remarks>
        /// <para>
        /// Callers of this method should be careful to check that the Object returned is of a usable type to them.  The Target Type parameter does not guarantee that the return value is of that type it is only used to determine which registered instances of <see cref="IObjectLoader">IObjectLoader</see> are potentially capable of creating the desired Object
        /// </para>
        /// <para>
        /// Callers should also take care that any Objects returned from this method are disposed of when the caller no longer has a use for them as otherwise the reference kept in the cache here will cause the Object to remain in-memory consuming resources
        /// </para>
        /// </remarks>
        public static Object LoadObject(IGraph g, INode objNode, Type targetType)
        {
            if (targetType == null) throw new DotNetRdfConfigurationException("Unable to load the Object identified by the Node '" + objNode.ToString() + "' as a null target type was provided - this may be due to a failure to specify a fully qualified type name with the dnr:type property for this object");
            if (objNode == null) throw new DotNetRdfConfigurationException("Unable to load an Object as a null Object Node was provided");

            if (objNode.NodeType == NodeType.GraphLiteral || objNode.NodeType == NodeType.Literal)
            {
                throw new DotNetRdfConfigurationException("Unable to load an Object as the Object Node was not a URI/Blank Node as required");
            }

            //Use an Object caching mechanism to avoid instantiating the same thing multiple times since this could be VERY costly
            CachedObjectKey key = new CachedObjectKey(objNode, g);
            if (_cache.ContainsKey(key))
            {
                if (_cache[key] == null)
                {
                    //This means we've begun trying to cache the Object but haven't loaded it yet
                    //i.e. we've encountered an indirect circular reference or the caller failed to check
                    //for direct circular references with the CheckCircularReference() method
                    throw new DotNetRdfConfigurationException("Unable to load the Object identified by the Node '" + objNode.ToString() + "' as we have already started trying to load this Object which indicates that your Configuration Graph contains a circular reference");
                }
                else if (_cache[key] is UnloadableObject)
                {
                    //We don't retry loading if we fail
                    throw new DotNetRdfConfigurationException("Unable to load the Object identified by the Node '" + objNode.ToString() + "' as previous attempt(s) to load the Object failed.  Call ClearCache() before attempting loading if you wish to retry loading");
                }
                else
                {
                    //Return from Cache
                    return _cache[key];
                }
            }
            else
            {
                _cache.Add(key, null);
            }

            Object temp = null;

            //Try and find an Object Loader that can load this object
            try
            {
                foreach (IObjectFactory loader in _factories)
                {
                    if (loader.CanLoadObject(targetType))
                    {
                        if (loader.TryLoadObject(g, objNode, targetType, out temp)) break;
                    }
                }
            }
            catch (DotNetRdfConfigurationException)
            {
                _cache[key] = new UnloadableObject();
                throw;
            }
            catch (Exception ex)
            {
                _cache[key] = new UnloadableObject();
                throw new DotNetRdfConfigurationException("Unable to load the Object identified by the Node '" + objNode.ToString() + "' as an error occurred in the Object Loader which attempted to load it", ex);
            }

            //Error or return
            if (temp == null) throw new DotNetRdfConfigurationException("Unable to load the Object identified by the Node '" + objNode.ToString() + "' as an instance of type '" + targetType.ToString() + "' since no Object Loaders are able to load this type");
            _cache[key] = temp;
            return temp;
        }
 /// <summary>
 /// Gets whether the given Object has already been loaded and cached
 /// </summary>
 /// <param name="g">Configuration Graph</param>
 /// <param name="objNode">Object Node</param>
 /// <returns></returns>
 /// <remarks>
 /// If this returns true then loading that object again should be essentially instantaneous as it will come from the cache
 /// </remarks>
 public static bool IsCached(IGraph g, INode objNode)
 {
     CachedObjectKey key = new CachedObjectKey(objNode, g);
     return _cache.ContainsKey(key);
 }