private IPlan[] CreatePlans(Type requestedType, INode current, string bindingName, string planName, INode planRoot, IConstructorArgument[] arguments) { var resolvedMappings = ResolveTypes(requestedType, bindingName, current); var plans = (IPlan[])Activator.CreateInstance(typeof(IPlan <>).MakeGenericType(requestedType).MakeArrayType(), resolvedMappings.Length); for (var i = 0; i < resolvedMappings.Length; i++) { var resolvedMapping = resolvedMappings[i]; // If the resolved target is a generic type definition, we need to fill in the // generic type arguments from the request. var targetNonGeneric = resolvedMapping.Target; if (targetNonGeneric != null && targetNonGeneric.IsGenericTypeDefinition) { targetNonGeneric = targetNonGeneric.MakeGenericType(requestedType.GenericTypeArguments); } var scopeNode = current; if (resolvedMapping.LifetimeScope != null) { scopeNode = resolvedMapping.LifetimeScope.GetContainingNode(); } if (scopeNode != null && resolvedMapping.UniquePerScope) { var existing = scopeNode.Children.FirstOrDefault(x => x.Type != null && x.Type.IsAssignableFrom(targetNonGeneric)); if (existing != null) { if (existing.Planned && existing.PlanRoot != planRoot) { // Flag that the plan root is now dependant on the other // plan being resolved. planRoot?.DependentOnPlans.Add(existing.PlanRoot); } plans[i] = existing; continue; } } Type nodeToCreate; if (targetNonGeneric != null) { nodeToCreate = typeof(DefaultNode <>).MakeGenericType(targetNonGeneric); } else { nodeToCreate = typeof(DefaultNode <>).MakeGenericType(requestedType); } var createdNode = (DefaultNode)Activator.CreateInstance(nodeToCreate); createdNode.Name = string.Empty; createdNode.Parent = scopeNode; createdNode.Planned = true; createdNode.Type = targetNonGeneric ?? requestedType; createdNode.PlanName = planName; createdNode.PlanRoot = planRoot; if (createdNode.Type.ContainsGenericParameters) { throw new InvalidOperationException("The type still contained generic type parameters even after initial binding resolution."); } // If there is no plan root, then we are the plan root. if (planRoot == null) { planRoot = createdNode; } try { if (resolvedMapping.TargetMethod != null) { createdNode.PlannedMethod = resolvedMapping.TargetMethod; plans[i] = createdNode; continue; } if (resolvedMapping.TargetFactory) { var attribute = createdNode.Type.GetCustomAttribute <GeneratedFactoryAttribute>(); if (attribute == null) { // This node won't be valid because it's planned, has no value and // has no constructor. createdNode.InvalidHint = "The factory interface '" + createdNode.Type + "' doesn't have a generated factory for it. " + "Make sure the factory interface inherits from " + "IGenerateFactory so that the generator will " + "implement it for you."; plans[i] = createdNode; continue; } var resolvedFactoryClass = createdNode.Type.Assembly.GetTypes() .FirstOrDefault(x => x.FullName == attribute.FullTypeName); if (resolvedFactoryClass == null) { // This node won't be valid because it's planned, has no value and // has no constructor. createdNode.InvalidHint = "The generated factory class '" + attribute.FullTypeName + "' could not be found in the assembly."; plans[i] = createdNode; continue; } // If the factory class is generic, pass in type parameters as needed. if (resolvedFactoryClass != null && resolvedFactoryClass.IsGenericTypeDefinition) { resolvedFactoryClass = resolvedFactoryClass.MakeGenericType(requestedType.GenericTypeArguments); } createdNode.Type = resolvedFactoryClass; } if (createdNode.Type == null) { // This node won't be valid because it's planned, has no value and // has no constructor. createdNode.InvalidHint = "There was no valid target for the binding (is the 'To' method missing?)"; plans[i] = createdNode; continue; } if (createdNode.Type == requestedType && (requestedType.IsInterface || requestedType.IsAbstract)) { // This node won't be valid because it's planned, has no value and // has no constructor. createdNode.InvalidHint = "The target type '" + requestedType + "' isn't valid because it can't be constructed."; plans[i] = createdNode; continue; } createdNode.PlannedConstructor = createdNode.Type.GetConstructors(BindingFlags.Public | BindingFlags.Instance).FirstOrDefault(); if (createdNode.PlannedConstructor == null) { // This node won't be valid because it's planned, has no value and // has no constructor. createdNode.InvalidHint = "There was no valid public constructor for '" + createdNode.Type.FullName + "'"; plans[i] = createdNode; continue; } createdNode.PlannedConstructorArguments = new List <IUnresolvedArgument>(); var parameters = createdNode.PlannedConstructor.GetParameters(); var slots = new DefaultUnresolvedArgument[parameters.Length]; // First apply additional constructor arguments to the slots. if (arguments != null) { foreach (var additional in arguments) { for (var s = 0; s < slots.Length; s++) { if (additional.Satisifies(createdNode.PlannedConstructor, parameters[s])) { var plannedArgument = new DefaultUnresolvedArgument(); plannedArgument.ArgumentType = UnresolvedArgumentType.FactoryArgument; plannedArgument.FactoryArgumentValue = additional.GetValue(); slots[s] = plannedArgument; } } } } for (var ii = 0; ii < slots.Length; ii++) { if (slots[ii] != null) { // Already filled in. continue; } var parameter = parameters[ii]; var plannedArgument = new DefaultUnresolvedArgument(); plannedArgument.ParameterName = parameter.Name; if (parameter.ParameterType == typeof(ICurrentNode)) { plannedArgument.ArgumentType = UnresolvedArgumentType.CurrentNode; plannedArgument.CurrentNode = new DefaultCurrentNode(createdNode); } else if (parameter.ParameterType == typeof(IKernel)) { plannedArgument.ArgumentType = UnresolvedArgumentType.KnownValue; plannedArgument.KnownValue = this; } else { plannedArgument.ArgumentType = UnresolvedArgumentType.Type; plannedArgument.UnresolvedType = parameters[ii].ParameterType; plannedArgument.ParameterName = parameters[ii].GetCustomAttribute <NamedAttribute>()?.Name; plannedArgument.IsOptional = parameters[ii].GetCustomAttribute <OptionalAttribute>() != null; } slots[ii] = plannedArgument; } createdNode.PlannedConstructorArguments = new List <IUnresolvedArgument>(slots); foreach (var argument in createdNode.PlannedConstructorArguments) { switch (argument.ArgumentType) { case UnresolvedArgumentType.Type: if (argument.IsMultipleResult) { var children = CreatePlans( argument.MultipleResultElementType, createdNode, argument.ParameterName, planName, planRoot, null); foreach (var child in children) { if (child.ParentPlan == createdNode) { if (!createdNode.ChildrenInternal.Contains((INode)child)) { createdNode.ChildrenInternal.Add((INode)child); } } } ((DefaultUnresolvedArgument)argument).PlannedTargets = children; } else { var child = CreatePlan( argument.UnresolvedType, createdNode, argument.ParameterName, planName, planRoot, null); if (child.ParentPlan == createdNode) { if (!createdNode.ChildrenInternal.Contains((INode)child)) { createdNode.ChildrenInternal.Add((INode)child); } } ((DefaultUnresolvedArgument)argument).PlannedTarget = child; } break; } } if (createdNode.Parent == null) { _hierarchy.RootNodes.Add(createdNode); } else { var childrenInternal = ((DefaultNode)scopeNode).ChildrenInternal; if (!childrenInternal.Contains(createdNode)) { childrenInternal.Add(createdNode); } } plans[i] = createdNode; } finally { planRoot.PlannedCreatedNodes.Add(createdNode); } } return(plans); }
private List <IPlan> CreatePlans( #endif Type requestedType, INode current, string bindingName, string planName, INode planRoot, IInjectionAttribute[] injectionAttributes, IConstructorArgument[] arguments, Dictionary <Type, List <IMapping> > transientBindings) { injectionAttributes = injectionAttributes ?? new IInjectionAttribute[0]; arguments = arguments ?? new IConstructorArgument[0]; // If the plan requires an existing node, we must defer resolution until the plan // has been fully formed. For now, we create a node with Deferred set to // true, the requested type and desired scope node (if applicable). var requireExistingAttribute = injectionAttributes.OfType <RequireExistingAttribute>().FirstOrDefault(); if (requireExistingAttribute != null) { if (planRoot == null) { // We can't defer resolution on a plan root (it doesn't even make sense // because there won't ever be anything to satisfy the request). throw new InvalidOperationException( "A plan root had a RequireExisting injection attribute, which " + "can't ever be satisfied."); } var deferredSearchOptions = new List <KeyValuePair <Type, INode> >(); // Add deferred search option if the current plan has a scope injection attribute. var scopeAttribute = injectionAttributes.OfType <ScopeAttribute>().FirstOrDefault(); if (scopeAttribute != null) { // We don't have a resolved mapping here, so we pass in null as the second argument. var scopeNode = scopeAttribute.ScopeFromContext(current, null); deferredSearchOptions.Add(new KeyValuePair <Type, INode>( requestedType, scopeNode)); } // Add deferred search options based on explicit mappings in the kernel. #if !PLATFORM_UNITY var requireResolvedMappings = await ResolveTypes(requestedType, bindingName, current, transientBindings); var requirePlans = (IPlan[])Activator.CreateInstance(typeof(IPlan <>).MakeGenericType(requestedType).MakeArrayType(), 1); #else var requireResolvedMappings = ResolveTypes(requestedType, bindingName, current, transientBindings); var requirePlans = new List <IPlan>(1); requirePlans.Add(null); #endif foreach (var mapping in requireResolvedMappings) { // The mechanism of adding additional desired types based on the bindings // can only work for bindings that provide both a known target type, and // a lifetime scope. if (mapping.Target != null && mapping.LifetimeScope != null) { deferredSearchOptions.Add(new KeyValuePair <Type, INode>( mapping.Target, mapping.LifetimeScope.GetContainingNode())); } } // Create the deferred node. Type nodeToCreate = typeof(DefaultNode <>).MakeGenericType(requestedType); var createdNode = (DefaultNode)Activator.CreateInstance(nodeToCreate); createdNode.Name = string.Empty; createdNode.Parent = current; createdNode.Planned = true; createdNode.Deferred = true; #if PLATFORM_UNITY createdNode.DeferredSearchOptions = deferredSearchOptions.ToDictionary(k => k.Key, v => v.Value); #else createdNode.DeferredSearchOptions = deferredSearchOptions.AsReadOnly(); #endif createdNode.PlanName = planName; createdNode.PlanRoot = planRoot; createdNode.RequestedType = requestedType; // Add it to the list of deferred nodes on the plan root. planRoot.DeferredCreatedNodes.Add(createdNode); // Set the required plans and return it. requirePlans[0] = createdNode; return(requirePlans); } // Otherwise, construct plans based on the kernel configuration. #if !PLATFORM_UNITY var resolvedMappings = await ResolveTypes(requestedType, bindingName, current, transientBindings); var plans = (IPlan[])Activator.CreateInstance(typeof(IPlan <>).MakeGenericType(requestedType).MakeArrayType(), resolvedMappings.Length); #else var resolvedMappings = ResolveTypes(requestedType, bindingName, current, transientBindings); var plans = new List <IPlan>(resolvedMappings.Length); #endif for (var i = 0; i < resolvedMappings.Length; i++) { #if PLATFORM_UNITY plans.Add(null); #endif var resolvedMapping = resolvedMappings[i]; var localPlanRoot = planRoot; // If the resolved target is a generic type definition, we need to fill in the // generic type arguments from the request. var targetNonGeneric = resolvedMapping.Target; if (targetNonGeneric != null && targetNonGeneric.IsGenericTypeDefinition) { #if !PLATFORM_UNITY targetNonGeneric = targetNonGeneric.MakeGenericType(requestedType.GenericTypeArguments); #else targetNonGeneric = targetNonGeneric.MakeGenericType(requestedType.GetGenericArguments()); #endif } // Use the current node as the scope, unless the binding overrides the scope. var scopeNode = current; var uniquePerScope = resolvedMapping.UniquePerScope; if (resolvedMapping.LifetimeScope != null) { scopeNode = resolvedMapping.LifetimeScope.GetContainingNode(); } // If the parameter or injection location has a scope attribute, that overrides // the bindings default scope. var scopeAttribute = injectionAttributes.OfType <ScopeAttribute>().FirstOrDefault(); if (scopeAttribute != null) { scopeNode = scopeAttribute.ScopeFromContext(current, resolvedMapping); uniquePerScope = scopeAttribute.UniquePerScope; } // If the binding is set to be unique per scope, find an existing plan for // this binding in the current scope if one exists. if (scopeNode != null && uniquePerScope) { var existing = scopeNode.Children.FirstOrDefault(x => x.Type != null && x.Type.IsAssignableFrom(targetNonGeneric)); if (existing != null) { if (existing.Planned && existing.PlanRoot != localPlanRoot) { // Flag that the plan root is now dependent on the other // plan being resolved. if (localPlanRoot != null) { localPlanRoot.DependentOnPlans.Add(existing.PlanRoot); } } plans[i] = existing; continue; } } Type nodeToCreate; if (targetNonGeneric != null) { nodeToCreate = typeof(DefaultNode <>).MakeGenericType(targetNonGeneric); } else { nodeToCreate = typeof(DefaultNode <>).MakeGenericType(requestedType); } var createdNode = (DefaultNode)Activator.CreateInstance(nodeToCreate); createdNode.Name = string.Empty; createdNode.Parent = scopeNode; createdNode.Planned = true; createdNode.Type = targetNonGeneric ?? requestedType; createdNode.PlanName = planName; createdNode.PlanRoot = localPlanRoot; createdNode.RequestedType = requestedType; if (createdNode.Type.ContainsGenericParameters) { throw new InvalidOperationException("The type still contained generic type parameters even after initial binding resolution."); } // If there is no plan root, then we are the plan root. if (localPlanRoot == null) { localPlanRoot = createdNode; } try { if (resolvedMapping.DiscardNodeOnResolve) { // We discard this node from the hierarchy once the plan is resolved or discarded. localPlanRoot.DiscardOnResolve.Add(createdNode); } if (resolvedMapping.TargetMethod != null) { createdNode.PlannedMethod = resolvedMapping.TargetMethod; plans[i] = createdNode; continue; } if (resolvedMapping.TargetFactory) { var attribute = createdNode.Type.GetCustomAttribute <GeneratedFactoryAttribute>(); if (attribute == null) { // This node won't be valid because it's planned, has no value and // has no constructor. createdNode.InvalidHint = "The factory interface '" + createdNode.Type + "' doesn't have a generated factory for it. " + "Make sure the factory interface inherits from " + "IGenerateFactory so that the generator will " + "implement it for you."; plans[i] = createdNode; continue; } var targetName = resolvedMapping.TargetFactoryNotSupported ? attribute.NotSupportedFullTypeName : attribute.FullTypeName; #if !PLATFORM_UNITY var resolvedFactoryClass = (await GetTypeForAssembly(createdNode.Type.Assembly, targetName)); #else var resolvedFactoryClass = (GetTypeForAssembly(createdNode.Type.Assembly, targetName)); #endif if (resolvedFactoryClass == null) { // This node won't be valid because it's planned, has no value and // has no constructor. createdNode.InvalidHint = "The generated factory class '" + targetName + "' could not be found in the assembly."; plans[i] = createdNode; continue; } // If the factory class is generic, pass in type parameters as needed. if (resolvedFactoryClass != null && resolvedFactoryClass.IsGenericTypeDefinition) { #if !PLATFORM_UNITY resolvedFactoryClass = resolvedFactoryClass.MakeGenericType(requestedType.GenericTypeArguments); #else resolvedFactoryClass = resolvedFactoryClass.MakeGenericType(requestedType.GetGenericArguments()); #endif } createdNode.Type = resolvedFactoryClass; } if (createdNode.Type == null) { // This node won't be valid because it's planned, has no value and // has no constructor. createdNode.InvalidHint = "There was no valid target for the binding (is the 'To' method missing?)"; plans[i] = createdNode; continue; } if (createdNode.Type == requestedType && (requestedType.IsInterface || requestedType.IsAbstract)) { // This node won't be valid because it's planned, has no value and // has no constructor. createdNode.InvalidHint = "The target type '" + requestedType + "' isn't valid because it can't be constructed."; plans[i] = createdNode; continue; } createdNode.PlannedConstructor = createdNode.Type.GetConstructors(BindingFlags.Public | BindingFlags.Instance).FirstOrDefault(); if (createdNode.PlannedConstructor == null) { // This node won't be valid because it's planned, has no value and // has no constructor. createdNode.InvalidHint = "There was no valid public constructor for '" + createdNode.Type.FullName + "'"; plans[i] = createdNode; continue; } createdNode.PlannedConstructorArguments = new List <IUnresolvedArgument>(); var parameters = createdNode.PlannedConstructor.GetParameters(); var slots = new DefaultUnresolvedArgument[parameters.Length]; // First apply additional constructor arguments to the slots. if (arguments != null) { foreach (var additional in arguments) { for (var s = 0; s < slots.Length; s++) { if (additional.Satisifies(createdNode.PlannedConstructor, parameters[s])) { var plannedArgument = new DefaultUnresolvedArgument(); plannedArgument.ArgumentType = UnresolvedArgumentType.FactoryArgument; plannedArgument.FactoryArgumentValue = additional.GetValue(); slots[s] = plannedArgument; } } } } for (var ii = 0; ii < slots.Length; ii++) { if (slots[ii] != null) { // Already filled in. continue; } var parameter = parameters[ii]; var plannedArgument = new DefaultUnresolvedArgument(); if (parameter.ParameterType == typeof(ICurrentNode)) { plannedArgument.ArgumentType = UnresolvedArgumentType.CurrentNode; plannedArgument.CurrentNode = new DefaultCurrentNode(createdNode); } else if (parameter.ParameterType == typeof(INode)) { plannedArgument.ArgumentType = UnresolvedArgumentType.Node; plannedArgument.Node = createdNode; } else if (parameter.ParameterType == typeof(IHierarchy)) { plannedArgument.ArgumentType = UnresolvedArgumentType.Hierarchy; plannedArgument.Hierarchy = _hierarchy; } else if (parameter.ParameterType == typeof(IKernel)) { plannedArgument.ArgumentType = UnresolvedArgumentType.KnownValue; plannedArgument.KnownValue = this; } else { plannedArgument.ArgumentType = UnresolvedArgumentType.Type; plannedArgument.UnresolvedType = parameters[ii].ParameterType; plannedArgument.InjectionParameters = parameters[ii].GetCustomAttributes(true).OfType <IInjectionAttribute>().ToArray(); var namedAttribute = plannedArgument.InjectionParameters.OfType <NamedAttribute>().FirstOrDefault(); plannedArgument.Name = namedAttribute == null ? null : namedAttribute.Name; } slots[ii] = plannedArgument; } createdNode.PlannedConstructorArguments = new List <IUnresolvedArgument>(slots); foreach (var argument in createdNode.PlannedConstructorArguments) { switch (argument.ArgumentType) { case UnresolvedArgumentType.Type: if (argument.IsMultipleResult) { #if !PLATFORM_UNITY var children = await CreatePlans( #else var children = CreatePlans( #endif argument.MultipleResultElementType, createdNode, argument.Name, planName, localPlanRoot, argument.InjectionParameters, null, transientBindings); foreach (var child in children) { if (child.ParentPlan == createdNode) { _hierarchy.AddChildNode(createdNode, (INode)child); } } ((DefaultUnresolvedArgument)argument).PlannedTargets = children; } else { #if !PLATFORM_UNITY var child = await CreatePlan( #else var child = CreatePlan( #endif argument.UnresolvedType, createdNode, argument.Name, planName, localPlanRoot, argument.InjectionParameters, null, transientBindings); if (child.ParentPlan == createdNode) { _hierarchy.AddChildNode(createdNode, (INode)child); } ((DefaultUnresolvedArgument)argument).PlannedTarget = child; } break; } } if (createdNode.Parent == null) { _hierarchy.AddRootNode(createdNode); } else { _hierarchy.AddChildNode(scopeNode, createdNode); } plans[i] = createdNode; } finally { localPlanRoot.PlannedCreatedNodes.Add(createdNode); } } // If we are the plan root, go back through all of the nodes we deferred and try to // resolve them now that the plan has been fully created. if (planRoot != null && planRoot == current) { foreach (var deferred in planRoot.DeferredCreatedNodes) { // Search the deferred options. foreach (var searchOption in deferred.DeferredSearchOptions) { var type = searchOption.Key; var scopeNode = searchOption.Value; var existing = scopeNode.Children.FirstOrDefault(x => x.Type != null && type.IsAssignableFrom(x.Type)); if (existing != null) { if (existing.Planned && existing.PlanRoot != planRoot) { // Flag that the plan root is now dependent on the other // plan being resolved. if (planRoot != null) { planRoot.DependentOnPlans.Add(existing.PlanRoot); } } // Set the existing node as the deferred target. ((DefaultNode)deferred).DeferredResolvedTarget = existing; break; } } // If this deferred node doesn't have any deferred search options, give a // more tailored error. if (deferred.DeferredSearchOptions.Count == 0) { ((DefaultNode)deferred).InvalidHint = "This node was deferred because it depends on an existing node " + "being in the tree, however, no search options were provided on " + "the deferred node. This indicates that you have a parameter that " + "specifies [RequireExisting], but has no explicit scope set on the " + "parameter, and no explicit kernel bindings for '" + deferred.RequestedType + "' " + "which also provide scopes. The deferred node can not be resolved."; } // If we didn't find a resolved target for this deferred node, invalidate // the deferred node. if (deferred.DeferredResolvedTarget == null) { ((DefaultNode)deferred).InvalidHint = "This node was deferred because it depends on an existing node " + "being in the tree, however, no search options yielded a resolution " + "for the node. This usually indicates that an implementation was " + "expecting you to declare a dependent service elsewhere in the " + "hierarchy, but you haven't done so. The request was looking for one of: \r\n" + deferred.DeferredSearchOptions .Select(x => " * A '" + x.Key.FullName + "' within '" + x.Value.FullName + "'") .Aggregate((a, b) => a + "\r\n" + b); } } } return(plans); }