/// <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(); } }
/// <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; }
/// <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); } }
/// <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); } }
/// <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; } } } }
/// <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)); }
/// <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; } } } }
/// <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); } }
/// <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; } } } }
/// <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; } } }
/// <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)); }
/// <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); }
/// <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; } }
/// <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) { }
/// <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; } }
/// <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); } }
/// <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); } }
/// <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; } }
/// <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); }
/// <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); } }
/// <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); }
/// <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; } }