Example #1
0
        /// <summary>
        /// Adds the type to the type path.
        /// </summary>
        /// <param name="typeRequestInfo">The type request info.</param>
        /// <param name="throwExceptionForDuplicateTypes">If set to <c>true</c>, this method will throw a <see cref="CircularDependencyException" /> for duplicate types.</param>
        /// <param name="ignoreValueTypes">If set to <c>true</c>, value types will be ignored.</param>
        /// <exception cref="ArgumentNullException">The <paramref name="typeRequestInfo" /> is <c>null</c>.</exception>
        /// <exception cref="CircularDependencyException">The type is already in the type path and <paramref name="throwExceptionForDuplicateTypes" /> is <c>true</c>.</exception>
        private void PushType(TypeRequestInfo typeRequestInfo, bool throwExceptionForDuplicateTypes, bool ignoreValueTypes)
        {
            Argument.IsNotNull("typeRequestInfo", typeRequestInfo);

            if (ignoreValueTypes)
            {
                if (typeRequestInfo.Type.IsValueTypeEx())
                {
                    return;
                }
            }

            var lastTypeRequest = _typePath.LastOrDefault();

            if ((lastTypeRequest == typeRequestInfo) && IgnoreDuplicateRequestsDirectlyAfterEachother)
            {
                Log.Debug("Requesting type {0} twice after eachother, ignoring second request", typeRequestInfo);
                return;
            }

            bool alreadyContainsType = _typePath.Contains(typeRequestInfo);

            _typePath.Add(typeRequestInfo);

            if (throwExceptionForDuplicateTypes)
            {
                ThrowsExceptionIfInvalid();
            }
        }
Example #2
0
        /// <summary>
        /// Adds the type to the type path.
        /// </summary>
        /// <param name="typeRequestInfo">The type request info.</param>
        /// <param name="throwExceptionForDuplicateTypes">If set to <c>true</c>, this method will throw a <see cref="CircularDependencyException" /> for duplicate types.</param>
        /// <param name="ignoreValueTypes">If set to <c>true</c>, value types will be ignored.</param>
        /// <exception cref="ArgumentNullException">The <paramref name="typeRequestInfo" /> is <c>null</c>.</exception>
        /// <exception cref="CircularDependencyException">The type is already in the type path and <paramref name="throwExceptionForDuplicateTypes" /> is <c>true</c>.</exception>
        private void PushType(TypeRequestInfo typeRequestInfo, bool throwExceptionForDuplicateTypes, bool ignoreValueTypes)
        {
            Argument.IsNotNull("typeRequestInfo", typeRequestInfo);

            if (ignoreValueTypes)
            {
                if (typeRequestInfo.Type.IsValueTypeEx())
                {
                    return;
                }
            }

            var lastTypeRequest = _typePath.LastOrDefault();

            if (lastTypeRequest == typeRequestInfo)
            {
                Log.Debug("Requesting type {0} twice after eachother, ignoring second request", typeRequestInfo);
                return;
            }

            bool alreadyContainsType = _typePath.Contains(typeRequestInfo);

            _typePath.Add(typeRequestInfo);

            if (throwExceptionForDuplicateTypes)
            {
                if (alreadyContainsType)
                {
                    string error = string.Format("Found a circular dependency while resolving '{0}', it is used by '{1}'", FirstType, _typePath[_typePath.Count - 2]);
                    Log.Error(error);
                    throw new CircularDependencyException(this, string.Format("{0}. For more information, view the enclosed TypeRequestPath", error));
                }
            }
        }
            public void SetsValuesCorrectly()
            {
                var typeRequestInfo = new TypeRequestInfo(typeof(int), "mytag");

                Assert.AreEqual(typeof(int), typeRequestInfo.Type);
                Assert.AreEqual("mytag", typeRequestInfo.Tag);
            }
        /// <summary>
        /// Initializes a new instance of the <see cref="CircularDependencyException" /> class.
        /// </summary>
        /// <param name="duplicateRequestInfo">Type request that occurred second time.</param>
        /// <param name="typePath">The type path.</param>
        /// <param name="message">The message.</param>
        /// <exception cref="ArgumentNullException">The <paramref name="typePath"/> is <c>null</c>.</exception>
        internal CircularDependencyException(TypeRequestInfo duplicateRequestInfo, ITypeRequestPath typePath, string message)
            : base(message)
        {
            Argument.IsNotNull("duplicateRequestInfo", duplicateRequestInfo);
            Argument.IsNotNull("typePath", typePath);

            DuplicateRequestInfo = duplicateRequestInfo;
            TypePath             = typePath;
        }
Example #5
0
        /// <summary>
        /// Initializes a new instance of the <see cref="TypeRequestPath" /> class.
        /// </summary>
        /// <param name="typeRequestInfos">The type requests already in the path.</param>
        /// <param name="ignoreValueTypes">If set to <c>true</c>, this type path will ignore value types.</param>
        /// <exception cref="ArgumentException">The <paramref name="typeRequestInfos" /> is <c>null</c> or an empty array.</exception>
        public TypeRequestPath(TypeRequestInfo[] typeRequestInfos, bool ignoreValueTypes = true)
        {
            Argument.IsNotNullOrEmptyArray("typeRequestInfos", typeRequestInfos);

            IgnoreValueTypes = ignoreValueTypes;

            foreach (var typeRequestInfo in typeRequestInfos)
            {
                PushType(typeRequestInfo, false, false);
            }
        }
Example #6
0
        /// <summary>
        /// Initializes a new instance of the <see cref="TypeRequestPath" /> class.
        /// </summary>
        /// <param name="typeRequestInfos">The type requests already in the path.</param>
        /// <param name="ignoreValueTypes">If set to <c>true</c>, this type path will ignore value types.</param>
        /// <param name="name">The name, can be <c>null</c>.</param>
        /// <exception cref="ArgumentException">The <paramref name="typeRequestInfos" /> is <c>null</c> or an empty array.</exception>
        public TypeRequestPath(TypeRequestInfo[] typeRequestInfos, bool ignoreValueTypes = true, string name = null)
        {
            Argument.IsNotNullOrEmptyArray("typeRequestInfos", typeRequestInfos);

            IgnoreValueTypes = ignoreValueTypes;
            IgnoreDuplicateRequestsDirectlyAfterEachother = true;
            Name = name;

            foreach (var typeRequestInfo in typeRequestInfos)
            {
                PushType(typeRequestInfo, false, false);
            }
        }
Example #7
0
 /// <summary>
 /// Completes the type request path by checking if the currently created type is the same as the first
 /// type meaning that the type is successfully created and the current type request path can be set to <c>null</c>.
 /// </summary>
 /// <param name="typeRequestInfoForTypeJustConstructed">The type request info.</param>
 private void CompleteTypeRequestPathIfRequired(TypeRequestInfo typeRequestInfoForTypeJustConstructed)
 {
     lock (_lockObject)
     {
         if (_currentTypeRequestPath != null)
         {
             if (_currentTypeRequestPath.FirstType == typeRequestInfoForTypeJustConstructed)
             {
                 _currentTypeRequestPath = null;
             }
         }
     }
 }
Example #8
0
        /// <summary>
        /// Marks the type as created and removes all the history until this type from the request path.
        /// </summary>
        /// <param name="typeRequestInfo">The type request info.</param>
        /// <exception cref="ArgumentNullException">The <paramref name="typeRequestInfo"/> is <c>null</c>.</exception>
        public void MarkTypeAsCreated(TypeRequestInfo typeRequestInfo)
        {
            Argument.IsNotNull("typeRequestInfo", typeRequestInfo);

            for (int i = _typePath.Count - 1; i >= 0; i--)
            {
                if (_typePath[i] == typeRequestInfo)
                {
                    _typePath.RemoveRange(i, _typePath.Count - i);
                    return;
                }
            }
        }
            public void FunctionsCorrectlyForDifferentTags()
            {
                var obj1 = new TypeRequestInfo(typeof(int), "mytag1");
                var obj2 = new TypeRequestInfo(typeof(int), "mytag2");

                Assert.IsFalse(obj1 == obj2);
                Assert.IsFalse(obj2 == obj1);

                Assert.IsTrue(obj1 != obj2);
                Assert.IsTrue(obj2 != obj1);

                Assert.IsFalse(obj1.Equals(obj2));
                Assert.IsFalse(obj2.Equals(obj1));
            }
Example #10
0
        /// <summary>
        /// Marks the specified type as not being created. If this was the only type being constructed, the type request
        /// path will be closed.
        /// </summary>
        /// <param name="typeRequestInfoForTypeJustConstructed">The type request info for type just constructed.</param>
        private void CloseCurrentTypeIfRequired(TypeRequestInfo typeRequestInfoForTypeJustConstructed)
        {
            lock (_serviceLocator)
            {
                if (_currentTypeRequestPath != null)
                {
                    _currentTypeRequestPath.MarkTypeAsNotCreated(typeRequestInfoForTypeJustConstructed);

                    if (_currentTypeRequestPath.TypeCount == 1)
                    {
                        // We failed to create the only type in the request path, exit
                        _currentTypeRequestPath = null;
                    }
                }
            }
        }
Example #11
0
        /// <summary>
        /// Resolves the type from a known container.
        /// </summary>
        /// <param name="serviceType">Type of the service.</param>
        /// <param name="tag">The tag to register the service with. The default value is <c>null</c>.</param>
        /// <returns>An instance of the type registered on the service.</returns>
        /// <exception cref="ArgumentNullException">The <paramref name="serviceType"/> is <c>null</c>.</exception>
        /// <exception cref="ArgumentOutOfRangeException">The type is not found in any container.</exception>
        private object ResolveTypeFromKnownContainer(Type serviceType, object tag)
        {
            Argument.IsNotNull("serviceType", serviceType);

            lock (this)
            {
                var typeRequestInfo = new TypeRequestInfo(serviceType);
                if (_currentTypeRequestPath == null)
                {
                    _currentTypeRequestPath = new TypeRequestPath(typeRequestInfo, name: "ServiceLocator");
                    _currentTypeRequestPath.IgnoreDuplicateRequestsDirectlyAfterEachother = false;
                }
                else
                {
                    _currentTypeRequestPath.PushType(typeRequestInfo, false);

                    if (!_currentTypeRequestPath.IsValid)
                    {
                        // Reset path for next types that are being resolved
                        var typeRequestPath = _currentTypeRequestPath;
                        _currentTypeRequestPath = null;

                        typeRequestPath.ThrowsExceptionIfInvalid();
                    }
                }

                var serviceInfo        = new ServiceInfo(serviceType, tag);
                var registeredTypeInfo = _registeredTypes[serviceInfo];

                object instance = registeredTypeInfo.CreateServiceFunc(registeredTypeInfo);
                if (instance == null)
                {
                    ThrowTypeNotRegisteredException(serviceType);
                }

                if (IsTypeRegisteredAsSingleton(serviceType, tag))
                {
                    RegisterInstance(serviceType, instance, tag, this);
                }

                CompleteTypeRequestPathIfRequired(typeRequestInfo);

                return(instance);
            }
        }
Example #12
0
        /// <summary>
        /// Completes the type request path by checking if the currently created type is the same as the first
        /// type meaning that the type is successfully created and the current type request path can be set to <c>null</c>.
        /// </summary>
        /// <param name="typeRequestInfoForTypeJustConstructed">The type request info.</param>
        private void CompleteTypeRequestPathIfRequired(TypeRequestInfo typeRequestInfoForTypeJustConstructed)
        {
            lock (this)
            {
                if (_currentTypeRequestPath != null)
                {
                    if (_currentTypeRequestPath.LastType == typeRequestInfoForTypeJustConstructed)
                    {
                        _currentTypeRequestPath.MarkTypeAsCreated(typeRequestInfoForTypeJustConstructed);
                    }

                    if (_currentTypeRequestPath.TypeCount == 0)
                    {
                        _currentTypeRequestPath = null;
                    }
                }
            }
        }
Example #13
0
        /// <summary>
        /// Resolves the type from a known container.
        /// </summary>
        /// <param name="serviceInfo">The service information.</param>
        /// <returns>
        /// An instance of the type registered on the service.
        /// </returns>
        /// <exception cref="ArgumentNullException">The <paramref name="serviceInfo" /> is <c>null</c>.</exception>
        /// <exception cref="ArgumentOutOfRangeException">The type is not found in any container.</exception>
        private object ResolveTypeFromKnownContainer(ServiceInfo serviceInfo)
        {
            Argument.IsNotNull("serviceInfo", serviceInfo);

            lock (_lockObject)
            {
                var previousTypeRequestPath = _currentTypeRequestPath.Value;
                try
                {
                    var typeRequestInfo = new TypeRequestInfo(serviceInfo.Type, serviceInfo.Tag);
                    _currentTypeRequestPath.Value = TypeRequestPath.Branch(previousTypeRequestPath, typeRequestInfo);

                    var registeredTypeInfo = _registeredTypes[serviceInfo];

                    var serviceType = serviceInfo.Type;
                    var tag         = serviceInfo.Tag;

                    var instance = registeredTypeInfo.CreateServiceFunc(registeredTypeInfo);

                    if (instance != null && instance is Type)
                    {
                        instance = _typeFactory.CreateInstance((Type)instance);
                    }

                    if (instance == null)
                    {
                        ThrowTypeNotRegisteredException(serviceType);
                    }

                    if (IsTypeRegisteredAsSingleton(serviceType, tag))
                    {
                        RegisterInstance(serviceType, instance, tag, this);
                    }

                    return(instance);
                }
                finally
                {
                    _currentTypeRequestPath.Value = previousTypeRequestPath;
                }
            }
        }
Example #14
0
        /// <summary>
        /// Creates branch of type request path.
        /// </summary>
        /// <param name="parent">Parent path</param>
        /// <param name="typeRequestInfo">Appended path item</param>
        /// <returns></returns>
        public static TypeRequestPath Branch(TypeRequestPath parent, TypeRequestInfo typeRequestInfo)
        {
            Argument.IsNotNull("parent", parent);
            Argument.IsNotNull("typeRequestInfo", typeRequestInfo);
            //if (parent._typePath.Inde(o => o.Equals(typeRequestInfo)))
            int previousIndex = Array.IndexOf(parent._typePath, typeRequestInfo);

            if (previousIndex >= 0)
            {
                string circlePath = FormatPath(parent._typePath, previousIndex);
                circlePath += " => " + typeRequestInfo.Type.Name;

                throw Log.ErrorAndCreateException(msg => new CircularDependencyException(typeRequestInfo, parent, msg),
                                                  $"Found a circular dependency '{circlePath}' while resolving '{parent.FirstType}. For more information, view the enclosed TypeRequestPath'");
            }

            var parentTypePath = parent._typePath;
            var typePath       = new TypeRequestInfo[parent.TypeCount + 1];

            Array.Copy(parentTypePath, typePath, parentTypePath.Length);
            typePath[typePath.Length - 1] = typeRequestInfo;
            return(new TypeRequestPath(typePath, parent.Name));
        }
Example #15
0
        /// <summary>
        /// Creates an instance of the specified type using the specified parameters as injection values.
        /// </summary>
        /// <param name="typeToConstruct">The type to construct.</param>
        /// <param name="tag">The preferred tag when resolving dependencies.</param>
        /// <param name="parameters">The parameters to inject.</param>
        /// <param name="autoCompleteDependencies">if set to <c>true</c>, the additional dependencies will be auto completed.</param>
        /// <returns>The instantiated type using dependency injection.</returns>
        /// <exception cref="ArgumentNullException">The <paramref name="typeToConstruct" /> is <c>null</c>.</exception>
        private object CreateInstanceWithSpecifiedParameters(Type typeToConstruct, object tag, object[] parameters, bool autoCompleteDependencies)
        {
            Argument.IsNotNull("typeToConstruct", typeToConstruct);

            if (parameters == null)
            {
                parameters = ArrayShim.Empty <object>();
            }

            var previousRequestPath = _currentTypeRequestPath.Value;

            try
            {
                var typeRequestInfo = new TypeRequestInfo(typeToConstruct);
                _currentTypeRequestPath.Value = TypeRequestPath.Branch(previousRequestPath, typeRequestInfo);

                var constructorCacheKey = new ConstructorCacheKey(typeToConstruct, autoCompleteDependencies, parameters);

                var typeConstructorsMetadata = GetTypeMetaData(typeToConstruct);

                var constructorCacheValue = GetConstructor(constructorCacheKey);

                if (constructorCacheValue.ConstructorInfo != null)
                {
                    var cachedConstructor            = constructorCacheValue.ConstructorInfo;
                    var instanceCreatedWithInjection = TryCreateToConstruct(typeToConstruct, cachedConstructor, tag, parameters, false, false, typeConstructorsMetadata);
                    if (instanceCreatedWithInjection != null)
                    {
                        return(instanceCreatedWithInjection);
                    }

                    Log.Warning("Found constructor for type '{0}' in constructor, but it failed to create an instance. Removing the constructor from the cache", typeToConstruct.FullName);

                    SetConstructor(constructorCacheKey, constructorCacheValue, null);
                }

                Log.Debug("Creating instance of type '{0}' using specific parameters. No constructor found in the cache, so searching for the right one", typeToConstruct.FullName);

                var constructors = typeConstructorsMetadata.GetConstructors(parameters.Length, !autoCompleteDependencies).SortByParametersMatchDistance(parameters).ToList();

                for (int i = 0; i < constructors.Count; i++)
                {
                    var constructor = constructors[i];

                    var instanceCreatedWithInjection = TryCreateToConstruct(typeToConstruct, constructor, tag, parameters, true, i < constructors.Count - 1, typeConstructorsMetadata);
                    if (instanceCreatedWithInjection != null)
                    {
                        // We found a constructor that works, cache it
                        SetConstructor(constructorCacheKey, constructorCacheValue, constructor);

                        // Only update the rule when using a constructor for the first time, not when using it from the cache
                        ApiCop.UpdateRule <TooManyDependenciesApiCopRule>("TypeFactory.LimitDependencyInjection", x => x.SetNumberOfDependenciesInjected(typeToConstruct, constructor.GetParameters().Count()));

                        return(instanceCreatedWithInjection);
                    }
                }

                Log.Debug("No constructor could be used, cannot construct type '{0}' with the specified parameters", typeToConstruct.FullName);
            }
            catch (CircularDependencyException)
            {
                throw;
            }
            catch (Exception ex)
            {
                Log.Warning(ex, "Failed to construct type '{0}'", typeToConstruct.FullName);

                throw;
            }
            finally
            {
                _currentTypeRequestPath.Value = previousRequestPath;
            }

            return(null);
        }
Example #16
0
        /// <summary>
        /// Completes the type request path by checking if the currently created type is the same as the first
        /// type meaning that the type is successfully created and the current type request path can be set to <c>null</c>.
        /// </summary>
        /// <param name="typeRequestInfoForTypeJustConstructed">The type request info.</param>
        private void CompleteTypeRequestPathIfRequired(TypeRequestInfo typeRequestInfoForTypeJustConstructed)
        {
            lock (this)
            {
                if (_currentTypeRequestPath != null)
                {
                    if (_currentTypeRequestPath.LastType == typeRequestInfoForTypeJustConstructed)
                    {
                        _currentTypeRequestPath.MarkTypeAsCreated(typeRequestInfoForTypeJustConstructed);
                    }

                    if (_currentTypeRequestPath.TypeCount == 0)
                    {
                        _currentTypeRequestPath = null;
                    }
                }
            }
        }
Example #17
0
        /// <summary>
        /// Resolves the type from a known container.
        /// </summary>
        /// <param name="serviceType">Type of the service.</param>
        /// <param name="tag">The tag to register the service with. The default value is <c>null</c>.</param>
        /// <returns>An instance of the type registered on the service.</returns>
        /// <exception cref="ArgumentNullException">The <paramref name="serviceType"/> is <c>null</c>.</exception>
        /// <exception cref="ArgumentOutOfRangeException">The type is not found in any container.</exception>
        private object ResolveTypeFromKnownContainer(Type serviceType, object tag)
        {
            Argument.IsNotNull("serviceType", serviceType);

            lock (this)
            {
                var typeRequestInfo = new TypeRequestInfo(serviceType, tag);
                if (_currentTypeRequestPath == null)
                {
                    _currentTypeRequestPath = new TypeRequestPath(typeRequestInfo, name: "ServiceLocator");
                    _currentTypeRequestPath.IgnoreDuplicateRequestsDirectlyAfterEachother = false;
                }
                else
                {
                    _currentTypeRequestPath.PushType(typeRequestInfo, false);

                    if (!_currentTypeRequestPath.IsValid)
                    {
                        // Reset path for next types that are being resolved
                        var typeRequestPath = _currentTypeRequestPath;
                        _currentTypeRequestPath = null;

                        typeRequestPath.ThrowsExceptionIfInvalid();
                    }
                }

                var serviceInfo = new ServiceInfo(serviceType, tag);
                var registeredTypeInfo = _registeredTypes[serviceInfo];

                object instance = registeredTypeInfo.CreateServiceFunc(registeredTypeInfo);
                if (instance == null)
                {
                    ThrowTypeNotRegisteredException(serviceType);
                }

                if (IsTypeRegisteredAsSingleton(serviceType, tag))
                {
                    RegisterInstance(serviceType, instance, tag, this);
                }

                CompleteTypeRequestPathIfRequired(typeRequestInfo);

                return instance;
            }
        }
Example #18
0
 /// <summary>
 /// Initializes a new instance of the <see cref="TypeRequestPath"/> class.
 /// </summary>
 /// <param name="typeRequestInfo">The type request info.</param>
 /// <param name="ignoreValueTypes">If set to <c>true</c>, this type path will ignore value types.</param>
 /// <exception cref="ArgumentNullException">The <paramref name="typeRequestInfo" /> is <c>null</c>.</exception>
 public TypeRequestPath(TypeRequestInfo typeRequestInfo, bool ignoreValueTypes = true)
     : this(new [] { typeRequestInfo }, ignoreValueTypes)
 {
 }
Example #19
0
        /// <summary>
        /// Creates an instance of the specified type using the specified parameters as injection values.
        /// </summary>
        /// <param name="typeToConstruct">The type to construct.</param>
        /// <param name="parameters">The parameters to inject.</param>
        /// <param name="autoCompleteDependencies">if set to <c>true</c>, the additional dependencies will be auto completed.</param>
        /// <param name="preventCircularDependencies">if set to <c>true</c>, prevent circular dependencies using the <see cref="TypeRequestPath"/>.</param>
        /// <returns>The instantiated type using dependency injection.</returns>
        /// <exception cref="ArgumentNullException">The <paramref name="typeToConstruct" /> is <c>null</c>.</exception>
        private object CreateInstanceWithSpecifiedParameters(Type typeToConstruct, object[] parameters,
            bool autoCompleteDependencies, bool preventCircularDependencies = true)
        {
            Argument.IsNotNull("typeToConstruct", typeToConstruct);

            if (parameters == null)
            {
                parameters = new object[] { };
            }

            lock (_serviceLocator)
            {
                TypeRequestInfo typeRequestInfo = null;

                try
                {
                    if (preventCircularDependencies)
                    {
                        typeRequestInfo = new TypeRequestInfo(typeToConstruct);
                        if (_currentTypeRequestPath == null)
                        {
                            _currentTypeRequestPath = new TypeRequestPath(typeRequestInfo, name: TypeRequestPathName);
                        }
                        else
                        {
                            _currentTypeRequestPath.PushType(typeRequestInfo, true);
                        }
                    }

                    var constructorCache = GetConstructorCache(autoCompleteDependencies);
                    var constructorCacheKey = new ConstructorCacheKey(typeToConstruct, parameters);

                    if (constructorCache.ContainsKey(constructorCacheKey))
                    {
                        var cachedConstructor = constructorCache[constructorCacheKey];
                        var instanceCreatedWithInjection = TryCreateToConstruct(typeToConstruct, cachedConstructor,
                            parameters, false, false);
                        if (instanceCreatedWithInjection != null)
                        {
                            if (preventCircularDependencies)
                            {
                                CompleteTypeRequestPathIfRequired(typeRequestInfo);
                            }

                            return instanceCreatedWithInjection;
                        }

                        Log.Warning("Found constructor for type '{0}' in constructor, but it failed to create an instance. Removing the constructor from the cache", typeToConstruct.FullName);

                        constructorCache.Remove(constructorCacheKey);
                    }

                    Log.Debug("Creating instance of type '{0}' using specific parameters. No constructor found in the cache, so searching for the right one", typeToConstruct.FullName);

                    var typeConstructorsMetadata = GetTypeMetaData(typeToConstruct);
                    var constructors = typeConstructorsMetadata.GetConstructors(parameters.Count(), !autoCompleteDependencies);

                    for (int i = 0; i < constructors.Count; i++)
                    {
                        var constructor = constructors[i];

                        var instanceCreatedWithInjection = TryCreateToConstruct(typeToConstruct, constructor,
                            parameters, true, i < constructors.Count - 1);
                        if (instanceCreatedWithInjection != null)
                        {
                            if (preventCircularDependencies)
                            {
                                CompleteTypeRequestPathIfRequired(typeRequestInfo);
                            }

                            // We found a constructor that works, cache it
                            constructorCache[constructorCacheKey] = constructor;

                            // Only update the rule when using a constructor for the first time, not when using it from the cache
                            ApiCop.UpdateRule<TooManyDependenciesApiCopRule>("TypeFactory.LimitDependencyInjection",
                                x => x.SetNumberOfDependenciesInjected(typeToConstruct, constructor.GetParameters().Count()));

                            return instanceCreatedWithInjection;
                        }
                    }

                    Log.Debug("No constructor could be used, cannot construct type '{0}' with the specified parameters", typeToConstruct.FullName);
                }
                catch (CircularDependencyException)
                {
                    throw;
                }
                catch (Exception ex)
                {
                    if (preventCircularDependencies)
                    {
                        CloseCurrentTypeIfRequired(typeRequestInfo);
                    }

                    Log.Warning(ex, "Failed to construct type '{0}'", typeToConstruct.FullName);

                    throw;
                }

                if (preventCircularDependencies)
                {
                    CloseCurrentTypeIfRequired(typeRequestInfo);
                }

                return null;
            }
        }
Example #20
0
        /// <summary>
        /// Creates an instance of the specified type using the specified parameters as injection values.
        /// </summary>
        /// <param name="typeToConstruct">The type to construct.</param>
        /// <param name="parameters">The parameters to inject.</param>
        /// <param name="autoCompleteDependencies">if set to <c>true</c>, the additional dependencies will be auto completed.</param>
        /// <param name="preventCircularDependencies">if set to <c>true</c>, prevent circular dependencies using the <see cref="TypeRequestPath"/>.</param>
        /// <returns>The instantiated type using dependency injection.</returns>
        /// <exception cref="ArgumentNullException">The <paramref name="typeToConstruct" /> is <c>null</c>.</exception>
        private object CreateInstanceWithSpecifiedParameters(Type typeToConstruct, object[] parameters,
                                                             bool autoCompleteDependencies, bool preventCircularDependencies = true)
        {
            Argument.IsNotNull("typeToConstruct", typeToConstruct);

            if (parameters == null)
            {
                parameters = new object[] { };
            }

            lock (_serviceLocator)
            {
                TypeRequestInfo typeRequestInfo = null;

                try
                {
                    if (preventCircularDependencies)
                    {
                        typeRequestInfo = new TypeRequestInfo(typeToConstruct);
                        if (_currentTypeRequestPath == null)
                        {
                            _currentTypeRequestPath = new TypeRequestPath(typeRequestInfo, name: TypeRequestPathName);
                        }
                        else
                        {
                            _currentTypeRequestPath.PushType(typeRequestInfo, true);
                        }
                    }

                    var constructorCache    = GetConstructorCache(autoCompleteDependencies);
                    var constructorCacheKey = new ConstructorCacheKey(typeToConstruct, parameters);

                    if (constructorCache.ContainsKey(constructorCacheKey))
                    {
                        var cachedConstructor            = constructorCache[constructorCacheKey];
                        var instanceCreatedWithInjection = TryCreateToConstruct(typeToConstruct, cachedConstructor,
                                                                                parameters, false, false);
                        if (instanceCreatedWithInjection != null)
                        {
                            if (preventCircularDependencies)
                            {
                                CompleteTypeRequestPathIfRequired(typeRequestInfo);
                            }

                            return(instanceCreatedWithInjection);
                        }

                        Log.Warning("Found constructor for type '{0}' in constructor, but it failed to create an instance. Removing the constructor from the cache", typeToConstruct.FullName);

                        constructorCache.Remove(constructorCacheKey);
                    }

                    Log.Debug("Creating instance of type '{0}' using specific parameters. No constructor found in the cache, so searching for the right one", typeToConstruct.FullName);

                    var typeConstructorsMetadata = GetTypeMetaData(typeToConstruct);
                    var constructors             = typeConstructorsMetadata.GetConstructors(parameters.Count(), !autoCompleteDependencies);

                    for (int i = 0; i < constructors.Count; i++)
                    {
                        var constructor = constructors[i];

                        var instanceCreatedWithInjection = TryCreateToConstruct(typeToConstruct, constructor,
                                                                                parameters, true, i < constructors.Count - 1);
                        if (instanceCreatedWithInjection != null)
                        {
                            if (preventCircularDependencies)
                            {
                                CompleteTypeRequestPathIfRequired(typeRequestInfo);
                            }

                            // We found a constructor that works, cache it
                            constructorCache[constructorCacheKey] = constructor;

                            // Only update the rule when using a constructor for the first time, not when using it from the cache
                            ApiCop.UpdateRule <TooManyDependenciesApiCopRule>("TypeFactory.LimitDependencyInjection",
                                                                              x => x.SetNumberOfDependenciesInjected(typeToConstruct, constructor.GetParameters().Count()));

                            return(instanceCreatedWithInjection);
                        }
                    }

                    Log.Debug("No constructor could be used, cannot construct type '{0}' with the specified parameters", typeToConstruct.FullName);
                }
                catch (CircularDependencyException)
                {
                    throw;
                }
                catch (Exception ex)
                {
                    if (preventCircularDependencies)
                    {
                        CloseCurrentTypeIfRequired(typeRequestInfo);
                    }

                    Log.Warning(ex, "Failed to construct type '{0}'", typeToConstruct.FullName);

                    throw;
                }

                if (preventCircularDependencies)
                {
                    CloseCurrentTypeIfRequired(typeRequestInfo);
                }

                return(null);
            }
        }
Example #21
0
        /// <summary>
        /// Creates an instance of the specified type using dependency injection.
        /// </summary>
        /// <param name="typeToConstruct">The type to construct.</param>
        /// <returns>The instantiated type using dependency injection.</returns>
        /// <exception cref="ArgumentNullException">The <paramref name="typeToConstruct"/> is <c>null</c>.</exception>
        public object CreateInstance(Type typeToConstruct)
        {
            Argument.IsNotNull("typeToConstruct", typeToConstruct);

            lock (_lockObject)
            {
                TypeRequestInfo typeRequestInfo = null;

                try
                {
                    typeRequestInfo = new TypeRequestInfo(typeToConstruct);
                    if (_currentTypeRequestPath == null)
                    {
                        _currentTypeRequestPath = new TypeRequestPath(typeRequestInfo, name: TypeRequestPathName);
                    }
                    else
                    {
                        _currentTypeRequestPath.PushType(typeRequestInfo, true);
                    }

                    var constructorCacheKey = new ConstructorCacheKey(typeToConstruct, new object[] { });
                    if (_constructorCache.ContainsKey(constructorCacheKey))
                    {
                        var cachedConstructor            = _constructorCache[constructorCacheKey];
                        var instanceCreatedWithInjection = TryCreateWithConstructorInjection(typeToConstruct, cachedConstructor, false);
                        if (instanceCreatedWithInjection != null)
                        {
                            CompleteTypeRequestPathIfRequired(typeRequestInfo);

                            return(instanceCreatedWithInjection);
                        }

                        Log.Warning("Found constructor for type '{0}' in constructor, but it failed to create an instance. Removing the constructor from the cache", typeToConstruct.FullName);

                        _constructorCache.Remove(constructorCacheKey);
                    }

                    Log.Debug("Creating instance of type '{0}'. No constructor found in the cache, so searching for the right one.", typeToConstruct.FullName);

                    var typeConstructorsMetadata = GetTypeMetaData(typeToConstruct);
                    var constructors             = typeConstructorsMetadata.GetConstructors();
                    for (int i = 0; i < constructors.Count; i++)
                    {
                        var constructor = constructors[i];

                        var instanceCreatedWithInjection = TryCreateWithConstructorInjection(typeToConstruct, constructor, i < constructors.Count - 1);
                        if (instanceCreatedWithInjection != null)
                        {
                            CompleteTypeRequestPathIfRequired(typeRequestInfo);

                            // We found a constructor that works, cache it
                            _constructorCache[constructorCacheKey] = constructor;

                            return(instanceCreatedWithInjection);
                        }
                    }

                    var createdInstanceUsingActivator = CreateInstanceUsingActivator(typeToConstruct);
                    if (createdInstanceUsingActivator != null)
                    {
                        CompleteTypeRequestPathIfRequired(typeRequestInfo);

                        return(createdInstanceUsingActivator);
                    }
                }
                catch (CircularDependencyException)
                {
                    throw;
                }
                catch (Exception ex)
                {
                    CloseCurrentTypeIfRequired(typeRequestInfo);

                    Log.Warning(ex, "Failed to construct type '{0}'", typeToConstruct.FullName);

                    throw;
                }

                CloseCurrentTypeIfRequired(typeRequestInfo);

                return(null);
            }
        }
Example #22
0
        /// <summary>
        /// Marks the type as created and removes all the history until this type from the request path.
        /// </summary>
        /// <param name="typeRequestInfo">The type request info.</param>
        /// <exception cref="ArgumentNullException">The <paramref name="typeRequestInfo"/> is <c>null</c>.</exception>
        public void MarkTypeAsCreated(TypeRequestInfo typeRequestInfo)
        {
            Argument.IsNotNull("typeRequestInfo", typeRequestInfo);

            for (int i = _typePath.Count - 1; i >= 0; i--)
            {
                if (_typePath[i] == typeRequestInfo)
                {
                    _typePath.RemoveRange(i, _typePath.Count - i);
                    return;
                }
            }
        }
Example #23
0
        /// <summary>
        /// Adds the type to the type path.
        /// </summary>
        /// <param name="typeRequestInfo">The type request info.</param>
        /// <param name="throwExceptionForDuplicateTypes">If set to <c>true</c>, this method will throw a <see cref="CircularDependencyException" /> for duplicate types.</param>
        /// <param name="ignoreValueTypes">If set to <c>true</c>, value types will be ignored.</param>
        /// <exception cref="ArgumentNullException">The <paramref name="typeRequestInfo" /> is <c>null</c>.</exception>
        /// <exception cref="CircularDependencyException">The type is already in the type path and <paramref name="throwExceptionForDuplicateTypes" /> is <c>true</c>.</exception>
        private void PushType(TypeRequestInfo typeRequestInfo, bool throwExceptionForDuplicateTypes, bool ignoreValueTypes)
        {
            Argument.IsNotNull("typeRequestInfo", typeRequestInfo);

            if (ignoreValueTypes)
            {
                if (typeRequestInfo.Type.IsValueTypeEx())
                {
                    return;
                }
            }

            var lastTypeRequest = _typePath.LastOrDefault();
            if (lastTypeRequest == typeRequestInfo)
            {
                Log.Debug("Requesting type {0} twice after eachother, ignoring second request", typeRequestInfo);
                return;
            }

            bool alreadyContainsType = _typePath.Contains(typeRequestInfo);

            _typePath.Add(typeRequestInfo);

            if (throwExceptionForDuplicateTypes)
            {
                if (alreadyContainsType)
                {
                    string error = string.Format("Found a circular dependency while resolving '{0}', it is used by '{1}'", FirstType, _typePath[_typePath.Count - 2]);
                    Log.Error(error);
                    throw new CircularDependencyException(this, string.Format("{0}. For more information, view the enclosed TypeRequestPath", error));
                }
            }
        }
Example #24
0
        /// <summary>
        /// Creates an instance of the specified type using dependency injection.
        /// </summary>
        /// <param name="typeToConstruct">The type to construct.</param>
        /// <returns>The instantiated type using dependency injection.</returns>
        /// <exception cref="ArgumentNullException">The <paramref name="typeToConstruct"/> is <c>null</c>.</exception>
        public object CreateInstance(Type typeToConstruct)
        {
            Argument.IsNotNull("typeToConstruct", typeToConstruct);

            lock (_lockObject)
            {
                TypeRequestInfo typeRequestInfo = null;

                try
                {
#if !DISABLE_TYPEPATH
                    typeRequestInfo = new TypeRequestInfo(typeToConstruct);
                    if (_currentTypeRequestPath == null)
                    {
                        _currentTypeRequestPath = new TypeRequestPath(typeRequestInfo);
                    }
                    else
                    {
                        _currentTypeRequestPath.PushType(typeRequestInfo, true);
                    }
#endif

                    if (_constructorCache.ContainsKey(typeToConstruct))
                    {
                        var instanceCreatedWithInjection = TryCreateWithConstructorInjection(typeToConstruct, _constructorCache[typeToConstruct]);
                        if (instanceCreatedWithInjection != null)
                        {
#if !DISABLE_TYPEPATH
                            CompleteTypeRequestPathIfRequired(typeRequestInfo);
#endif

                            return instanceCreatedWithInjection;
                        }

                        Log.Warning("Found constructor for type '{0}' in constructor, but it failed to create an instance. Removing the constructor from the cache", typeToConstruct.FullName);

                        _constructorCache.Remove(typeToConstruct);
                    }

                    Log.Debug("Creating instance of type '{0}'. No constructor found in the cache, so searching for the right one.", typeToConstruct.FullName);

                    var constructors = (from constructor in typeToConstruct.GetConstructorsEx()
                                        orderby constructor.GetParameters().Count() descending
                                        select constructor).ToList();

                    foreach (var constructor in constructors)
                    {
                        var instanceCreatedWithInjection = TryCreateWithConstructorInjection(typeToConstruct, constructor);
                        if (instanceCreatedWithInjection != null)
                        {
#if !DISABLE_TYPEPATH
                            CompleteTypeRequestPathIfRequired(typeRequestInfo);
#endif

                            // We found a constructor that works, cache it
                            _constructorCache[typeToConstruct] = constructor;

                            return instanceCreatedWithInjection;
                        }
                    }

                    var createdInstanceUsingActivator = CreateInstanceUsingActivator(typeToConstruct);
                    if (createdInstanceUsingActivator != null)
                    {
#if !DISABLE_TYPEPATH
                        CompleteTypeRequestPathIfRequired(typeRequestInfo);
#endif

                        return createdInstanceUsingActivator;
                    }
                }
                catch (CircularDependencyException)
                {
                    throw;
                }
                catch (Exception ex)
                {
                    CloseCurrentTypeTypeIfRequired(typeRequestInfo);

                    Log.Warning(ex, "Failed to construct type '{0}'", typeToConstruct.FullName);

                    throw;
                }

                CloseCurrentTypeTypeIfRequired(typeRequestInfo);

                return null;
            }
        }
Example #25
0
 /// <summary>
 /// Pushes the type to the type path.
 /// </summary>
 /// <param name="typeRequestInfo">The type request info.</param>
 /// <param name="throwExceptionForDuplicateTypes">If set to <c>true</c>, this method will throw a <see cref="CircularDependencyException"/> for duplicate types.</param>
 /// <exception cref="ArgumentNullException">The <paramref name="typeRequestInfo"/> is <c>null</c>.</exception>
 /// <exception cref="CircularDependencyException">The type is already in the type path and <paramref name="throwExceptionForDuplicateTypes"/> is <c>true</c>.</exception>
 public void PushType(TypeRequestInfo typeRequestInfo, bool throwExceptionForDuplicateTypes)
 {
     PushType(typeRequestInfo, throwExceptionForDuplicateTypes, IgnoreValueTypes);
 }
Example #26
0
 /// <summary>
 /// Completes the type request path by checking if the currently created type is the same as the first
 /// type meaning that the type is successfully created and the current type request path can be set to <c>null</c>.
 /// </summary>
 /// <param name="typeRequestInfoForTypeJustConstructed">The type request info.</param>
 private void CompleteTypeRequestPathIfRequired(TypeRequestInfo typeRequestInfoForTypeJustConstructed)
 {
     lock (_lockObject)
     {
         if (_currentTypeRequestPath != null)
         {
             if (_currentTypeRequestPath.FirstType == typeRequestInfoForTypeJustConstructed)
             {
                 _currentTypeRequestPath = null;
             }
         }
     }
 }
Example #27
0
 /// <summary>
 /// Pushes the type to the type path.
 /// </summary>
 /// <param name="typeRequestInfo">The type request info.</param>
 /// <param name="throwExceptionForDuplicateTypes">If set to <c>true</c>, this method will throw a <see cref="CircularDependencyException"/> for duplicate types.</param>
 /// <exception cref="ArgumentNullException">The <paramref name="typeRequestInfo"/> is <c>null</c>.</exception>
 /// <exception cref="CircularDependencyException">The type is already in the type path and <paramref name="throwExceptionForDuplicateTypes"/> is <c>true</c>.</exception>
 public void PushType(TypeRequestInfo typeRequestInfo, bool throwExceptionForDuplicateTypes)
 {
     PushType(typeRequestInfo, throwExceptionForDuplicateTypes, IgnoreValueTypes);
 }
Example #28
0
        /// <summary>
        /// Creates an instance of the specified type using dependency injection.
        /// </summary>
        /// <param name="typeToConstruct">The type to construct.</param>
        /// <returns>The instantiated type using dependency injection.</returns>
        /// <exception cref="ArgumentNullException">The <paramref name="typeToConstruct"/> is <c>null</c>.</exception>
        public object CreateInstance(Type typeToConstruct)
        {
            Argument.IsNotNull("typeToConstruct", typeToConstruct);

            lock (_lockObject)
            {
                TypeRequestInfo typeRequestInfo = null;

                try
                {
#if !DISABLE_TYPEPATH
                    typeRequestInfo = new TypeRequestInfo(typeToConstruct);
                    if (_currentTypeRequestPath == null)
                    {
                        _currentTypeRequestPath = new TypeRequestPath(typeRequestInfo);
                    }
                    else
                    {
                        _currentTypeRequestPath.PushType(typeRequestInfo, true);
                    }
#endif

                    if (_constructorCache.ContainsKey(typeToConstruct))
                    {
                        var instanceCreatedWithInjection = TryCreateWithConstructorInjection(typeToConstruct, _constructorCache[typeToConstruct]);
                        if (instanceCreatedWithInjection != null)
                        {
#if !DISABLE_TYPEPATH
                            CompleteTypeRequestPathIfRequired(typeRequestInfo);
#endif

                            return(instanceCreatedWithInjection);
                        }

                        Log.Warning("Found constructor for type '{0}' in constructor, but it failed to create an instance. Removing the constructor from the cache", typeToConstruct.FullName);

                        _constructorCache.Remove(typeToConstruct);
                    }

                    Log.Debug("Creating instance of type '{0}'. No constructor found in the cache, so searching for the right one.", typeToConstruct.FullName);

                    var constructors = (from constructor in typeToConstruct.GetConstructorsEx()
                                        orderby constructor.GetParameters().Count() descending
                                        select constructor).ToList();

                    foreach (var constructor in constructors)
                    {
                        var instanceCreatedWithInjection = TryCreateWithConstructorInjection(typeToConstruct, constructor);
                        if (instanceCreatedWithInjection != null)
                        {
#if !DISABLE_TYPEPATH
                            CompleteTypeRequestPathIfRequired(typeRequestInfo);
#endif

                            // We found a constructor that works, cache it
                            _constructorCache[typeToConstruct] = constructor;

                            return(instanceCreatedWithInjection);
                        }
                    }

                    var createdInstanceUsingActivator = CreateInstanceUsingActivator(typeToConstruct);
                    if (createdInstanceUsingActivator != null)
                    {
#if !DISABLE_TYPEPATH
                        CompleteTypeRequestPathIfRequired(typeRequestInfo);
#endif

                        return(createdInstanceUsingActivator);
                    }
                }
                catch (CircularDependencyException)
                {
                    throw;
                }
                catch (Exception ex)
                {
                    CloseCurrentTypeTypeIfRequired(typeRequestInfo);

                    Log.Warning(ex, "Failed to construct type '{0}'", typeToConstruct.FullName);

                    throw;
                }

                CloseCurrentTypeTypeIfRequired(typeRequestInfo);

                return(null);
            }
        }
Example #29
0
        /// <summary>
        /// Adds the type to the type path.
        /// </summary>
        /// <param name="typeRequestInfo">The type request info.</param>
        /// <param name="throwExceptionForDuplicateTypes">If set to <c>true</c>, this method will throw a <see cref="CircularDependencyException" /> for duplicate types.</param>
        /// <param name="ignoreValueTypes">If set to <c>true</c>, value types will be ignored.</param>
        /// <exception cref="ArgumentNullException">The <paramref name="typeRequestInfo" /> is <c>null</c>.</exception>
        /// <exception cref="CircularDependencyException">The type is already in the type path and <paramref name="throwExceptionForDuplicateTypes" /> is <c>true</c>.</exception>
        private void PushType(TypeRequestInfo typeRequestInfo, bool throwExceptionForDuplicateTypes, bool ignoreValueTypes)
        {
            Argument.IsNotNull("typeRequestInfo", typeRequestInfo);

            if (ignoreValueTypes)
            {
                if (typeRequestInfo.Type.IsValueTypeEx())
                {
                    return;
                }
            }

            var lastTypeRequest = _typePath.LastOrDefault();
            if ((lastTypeRequest == typeRequestInfo) && IgnoreDuplicateRequestsDirectlyAfterEachother)
            {
                Log.Debug("Requesting type {0} twice after eachother, ignoring second request", typeRequestInfo);
                return;
            }

            bool alreadyContainsType = _typePath.Contains(typeRequestInfo);

            _typePath.Add(typeRequestInfo);

            if (throwExceptionForDuplicateTypes)
            {
                ThrowsExceptionIfInvalid();
            }
        }
Example #30
0
 /// <summary>
 /// Initializes a new instance of the <see cref="TypeRequestPath"/> class.
 /// </summary>
 /// <param name="typeRequestInfo">The type request info.</param>
 /// <param name="ignoreValueTypes">If set to <c>true</c>, this type path will ignore value types.</param>
 /// <exception cref="ArgumentNullException">The <paramref name="typeRequestInfo" /> is <c>null</c>.</exception>
 public TypeRequestPath(TypeRequestInfo typeRequestInfo, bool ignoreValueTypes = true)
     : this(new [] { typeRequestInfo }, ignoreValueTypes) { }
Example #31
0
 /// <summary>
 /// Initializes a new instance of the <see cref="TypeRequestPath" /> class.
 /// </summary>
 /// <param name="typeRequestInfo">The type request info.</param>
 /// <param name="ignoreValueTypes">If set to <c>true</c>, this type path will ignore value types.</param>
 /// <param name="name">The name, can be <c>null</c>.</param>
 /// <exception cref="ArgumentNullException">The <paramref name="typeRequestInfo" /> is <c>null</c>.</exception>
 public TypeRequestPath(TypeRequestInfo typeRequestInfo, bool ignoreValueTypes = true, string name = null)
     : this(new [] { typeRequestInfo }, ignoreValueTypes, name) { }
            public void DoesNotIgnoreValueTypesIfIgnoreValueTypesIsFalse()
            {
                var firstType = new TypeRequestInfo(typeof(X));
                var path = new TypeRequestPath(firstType, false);

                path.PushType(new TypeRequestInfo(typeof(DateTime)), false);
                Assert.AreEqual(typeof(DateTime), path.LastType.Type);

                path.PushType(new TypeRequestInfo(typeof(double)), false);
                Assert.AreEqual(typeof(double), path.LastType.Type);

                path.PushType(new TypeRequestInfo(typeof(int)), false);
                Assert.AreEqual(typeof(int), path.LastType.Type);
            }
Example #33
0
        /// <summary>
        /// Marks the specified type as not being created. If this was the only type being constructed, the type request
        /// path will be closed.
        /// </summary>
        /// <param name="typeRequestInfoForTypeJustConstructed">The type request info for type just constructed.</param>
        private void CloseCurrentTypeIfRequired(TypeRequestInfo typeRequestInfoForTypeJustConstructed)
        {
            lock (_serviceLocator)
            {
                if (_currentTypeRequestPath != null)
                {
                    _currentTypeRequestPath.MarkTypeAsNotCreated(typeRequestInfoForTypeJustConstructed);

                    if (_currentTypeRequestPath.TypeCount == 1)
                    {
                        // We failed to create the only type in the request path, exit
                        _currentTypeRequestPath = null;
                    }
                }
            }
        }
Example #34
0
 /// <summary>
 /// Initializes a new instance of the <see cref="TypeRequestPath" /> class.
 /// </summary>
 /// <param name="typeRequestInfo">The type request info.</param>
 /// <param name="ignoreValueTypes">If set to <c>true</c>, this type path will ignore value types.</param>
 /// <param name="name">The name, can be <c>null</c>.</param>
 /// <exception cref="ArgumentNullException">The <paramref name="typeRequestInfo" /> is <c>null</c>.</exception>
 public TypeRequestPath(TypeRequestInfo typeRequestInfo, bool ignoreValueTypes = true, string name = null)
     : this(new [] { typeRequestInfo }, ignoreValueTypes, name)
 {
 }
Example #35
0
        /// <summary>
        /// Creates an instance of the specified type using dependency injection.
        /// </summary>
        /// <param name="typeToConstruct">The type to construct.</param>
        /// <returns>The instantiated type using dependency injection.</returns>
        /// <exception cref="ArgumentNullException">The <paramref name="typeToConstruct"/> is <c>null</c>.</exception>
        public object CreateInstance(Type typeToConstruct)
        {
            Argument.IsNotNull("typeToConstruct", typeToConstruct);

            lock (_lockObject)
            {
                TypeRequestInfo typeRequestInfo = null;

                try
                {
                    typeRequestInfo = new TypeRequestInfo(typeToConstruct);
                    if (_currentTypeRequestPath == null)
                    {
                        _currentTypeRequestPath = new TypeRequestPath(typeRequestInfo, name: TypeRequestPathName);
                    }
                    else
                    {
                        _currentTypeRequestPath.PushType(typeRequestInfo, true);
                    }

                    var constructorCacheKey = new ConstructorCacheKey(typeToConstruct, new object[] { });
                    if (_constructorCache.ContainsKey(constructorCacheKey))
                    {
                        var cachedConstructor = _constructorCache[constructorCacheKey];
                        var instanceCreatedWithInjection = TryCreateWithConstructorInjection(typeToConstruct, cachedConstructor, false);
                        if (instanceCreatedWithInjection != null)
                        {
                            CompleteTypeRequestPathIfRequired(typeRequestInfo);

                            return instanceCreatedWithInjection;
                        }

                        Log.Warning("Found constructor for type '{0}' in constructor, but it failed to create an instance. Removing the constructor from the cache", typeToConstruct.FullName);

                        _constructorCache.Remove(constructorCacheKey);
                    }

                    Log.Debug("Creating instance of type '{0}'. No constructor found in the cache, so searching for the right one.", typeToConstruct.FullName);

                    var typeConstructorsMetadata = GetTypeMetaData(typeToConstruct);
                    var constructors = typeConstructorsMetadata.GetConstructors();
                    for (int i = 0; i < constructors.Count; i++)
                    {
                        var constructor = constructors[i];

                        var instanceCreatedWithInjection = TryCreateWithConstructorInjection(typeToConstruct, constructor, i < constructors.Count - 1);
                        if (instanceCreatedWithInjection != null)
                        {
                            CompleteTypeRequestPathIfRequired(typeRequestInfo);

                            // We found a constructor that works, cache it
                            _constructorCache[constructorCacheKey] = constructor;

                            return instanceCreatedWithInjection;
                        }
                    }

                    var createdInstanceUsingActivator = CreateInstanceUsingActivator(typeToConstruct);
                    if (createdInstanceUsingActivator != null)
                    {
                        CompleteTypeRequestPathIfRequired(typeRequestInfo);

                        return createdInstanceUsingActivator;
                    }
                }
                catch (CircularDependencyException)
                {
                    throw;
                }
                catch (Exception ex)
                {
                    CloseCurrentTypeIfRequired(typeRequestInfo);

                    Log.Warning(ex, "Failed to construct type '{0}'", typeToConstruct.FullName);

                    throw;
                }

                CloseCurrentTypeIfRequired(typeRequestInfo);

                return null;
            }
        }