/// <summary> /// Adds a service to the collection specifying what named dependencies to assign to named parameters. /// </summary> /// <typeparam name="TService">The type of service to add.</typeparam> /// <typeparam name="TImplementation">The implementation type for the service.</typeparam> /// <param name="serviceCollection">The collection of service descriptors.</param> /// <param name="lifetime">The lifetime of the service.</param> /// <param name="dependencies"> /// The collection of named dependencies. /// {registeredType, registeredName, parameterName} /// </param> /// <returns>The <see cref="IServiceCollection"/>.</returns> public static IServiceCollection AddServiceWithNamedDependencies <TService, TImplementation>( this IServiceCollection serviceCollection, ServiceLifetime lifetime, params NamedDependency[] dependencies) where TImplementation : class { INamedServiceFactory[] factories = serviceCollection.Where(x => typeof(INamedServiceFactory).IsAssignableFrom(x.ServiceType)) .Select(x => x.ImplementationInstance).Cast <INamedServiceFactory>().ToArray(); // Gather a set of parameters from our target ttype that best match our named dependencies. ParameterInfo[] parameters = GetMatchingConstructorParameters <TImplementation>(out ConstructorInfo constructorInfo, dependencies); // Create an array of parameter factories that we can pass to our target factory. // We can reuse this for every request keeping overheads low and performance high. var argsFactory = new Func <IServiceProvider, object> [parameters.Length]; for (int i = 0; i < parameters.Length; i++) { ParameterInfo parameter = parameters[i]; string parameterName = parameter.Name; Type parameterType = parameter.ParameterType; NamedDependency dependency = Array.Find(dependencies, x => x.ParameterName == parameterName && parameterType.IsAssignableFrom(x.ServiceType)); if (dependency != default) { INamedServiceFactory factory = Array.Find(factories, x => x.ServiceType == dependency.ServiceType); if (factory != null) { argsFactory[i] = p => factory.Resolve(dependency.ServiceName, p); continue; } } argsFactory[i] = p => p.GetService(parameterType); } serviceCollection.Add(new ServiceDescriptor(typeof(TService), provider => { object[] args = new object[argsFactory.Length]; for (int i = 0; i < argsFactory.Length; i++) { args[i] = argsFactory[i].Invoke(provider); } return(constructorInfo.Invoke(args)); }, lifetime)); return(serviceCollection); }
private static ParameterInfo[] GetMatchingConstructorParameters <T>(out ConstructorInfo constructorInfo, params NamedDependency[] dependencies) { int bestMatch = 0; constructorInfo = null; ParameterInfo[] parameters = Array.Empty <ParameterInfo>(); ConstructorInfo[] constructorInfos = typeof(T).GetConstructors(); // Loop through the constructors. // We look for the constructor that has largest number of matching parameters. for (int i = 0; i < constructorInfos.Length; i++) { int currentMatch = 0; ParameterInfo[] currentParameters = constructorInfos[i].GetParameters(); for (int j = 0; j < currentParameters.Length; j++) { ParameterInfo parameter = currentParameters[j]; for (int k = 0; k < dependencies.Length; k++) { NamedDependency dependency = dependencies[k]; if (parameter.Name == dependency.ParameterName && parameter.ParameterType.IsAssignableFrom(dependency.ServiceType)) { currentMatch++; } } if (currentMatch > bestMatch) { bestMatch = currentMatch; parameters = currentParameters; constructorInfo = constructorInfos[i]; } } } return(parameters); }