Exemple #1
0
        private (ConstructorInfo, Func <ParameterInfo[], IEnumerable <(IConfigurationSection, Object)> >) DeduceConstructor(
            TConfigurationTypeLoader configurationTypeLoader,
            TypeInfo loadedType,
            IConfigurationSection ctorConfig
            )
        {
            ConstructorInfo suitableCtor;
            Func <ParameterInfo[], IEnumerable <(IConfigurationSection, Object)> > ctorArgs;
            var  possibleCtors = loadedType.DeclaredConstructors.Where(ctor => ctor.IsPublic && !ctor.IsStatic);
            Type singleConfigType;

            if ((singleConfigType = configurationTypeLoader(loadedType)) != null)
            {
                suitableCtor = possibleCtors.FirstOrDefault(ctor =>
                {
                    var paramz = ctor.GetParameters();
                    return(paramz.Length == 1 && paramz[0].ParameterType.GetTypeInfo().IsAssignableFrom(singleConfigType.GetTypeInfo()));
                });
                ctorArgs = paramz => Enumerable.Repeat((ctorConfig, ctorConfig.Get(singleConfigType)), 1);
            }
            else
            {
                var children = ctorConfig.GetChildren().ToArray();
                if (children.Length > 0)
                {
                    if (children.All(child => Int32.TryParse(child.Key, out var ignored)))
                    {
                        // This is an array - need to find constructor which accepts given amount of parameters
                        suitableCtor = possibleCtors
                                       .Where(ctor =>
                        {
                            var paramz = ctor.GetParameters();
                            return(paramz.Length >= children.Length &&
                                   paramz.Skip(children.Length).All(p => p.IsOptional));
                        })
                                       .OrderBy(c => c.GetParameters().Length)
                                       .FirstOrDefault();
                        ctorArgs = paramz => children.Select((c, idx) => (c, c.Get(paramz[idx].ParameterType)));
                    }
                    else
                    {
                        // This is an object - try to find constructor with all required parameters
                        suitableCtor = possibleCtors
                                       .Where(ctor =>
                        {
                            var paramz = ctor.GetParameters();
                            return(paramz.All(p => !String.IsNullOrEmpty(ctorConfig.GetSection(p.Name)?.Value)));
                        })
                                       .FirstOrDefault();
                        ctorArgs = paramz => paramz.Select(p =>
                        {
                            var thisSection = ctorConfig.GetSection(p.Name);
                            return(thisSection, thisSection.Get(p.ParameterType));
                        });
                    }
                }
Exemple #2
0
        /// <summary>
        /// Potentially asynchronously instantiates an object from type information provided in given <see cref="IConfiguration"/>.
        /// </summary>
        /// <param name="config">The configuration containing type information.</param>
        /// <param name="targetType">The type which should be parent type of the object to be instantiated.</param>
        /// <param name="configurationTypeLoader">The optional custom callback to load configuration type for given type. By default, the value of <see cref="ConfigurationTypeAttribute.ConfigurationType"/> property of the applied <see cref="ConfigurationTypeAttribute"/> is used.</param>
        /// <returns>The potentially asynchronous operation which results in instantiated object, or <c>null</c>.</returns>
        /// <remarks>
        /// This method takes <see cref="ConfigurationTypeAttribute"/> and <see cref="NestedDynamicConfigurationAttribute"/> attributes into account when instantiating and traversing object.
        /// This method is only asynchronous if <see cref="TypeLoaderDelegate"/> callback provided to this <see cref="DynamicConfigurableTypeLoader"/> is asynchronous.
        /// </remarks>
        /// <seealso cref="DefaultConfigurationTypeLoaderCallback"/>
        public async ValueTask <Object> InstantiateWithConfiguration(
            IConfiguration config,
            TypeInfo targetType,
            TConfigurationTypeLoader configurationTypeLoader = null
            )
        {
            var type = await this._typeLoader(config, targetType);

            if (configurationTypeLoader == null)
            {
                configurationTypeLoader = DefaultConfigurationTypeLoaderCallback;
            }

            Object retVal = null;

            if (type != null && !type.IsInterface && !type.IsAbstract)
            {
                // Type load successful - now figure out constructor and arguments to it
                (var ctor, var ctorArgs) = this.DeduceConstructor(configurationTypeLoader, type, this._constructorArgumentsLoader(config, type));
                if (ctor != null)
                {
                    Object[] actualCtorArgs;
                    if (ctorArgs != null)
                    {
                        var tuples = ctorArgs(ctor.GetParameters()).ToArray();
                        await Task.WhenAll(tuples
                                           .Select( tuple => this.ScanForNestedConfigs(configurationTypeLoader, tuple.Item1, tuple.Item2).AsTask())
                                           .ToArray());

                        actualCtorArgs = tuples.Select(t => t.Item2).ToArray();
                    }
                    else
                    {
                        actualCtorArgs = null;
                    }

                    retVal = ctor.Invoke(actualCtorArgs);
                }
            }

            return(retVal);
        }