@@ -19,7 +19,7 @@ namespace ConfigurationProcessor.Core.Implementation
1919{
2020 internal static class Extensions
2121 {
22- public static readonly MethodInfo BindMappableValuesMethod = ReflectionUtil . GetMethodInfo < object > ( o => BindMappableValues ( default ! , default ! , default ! , default ! , default ! , default ! ) ) ;
22+ public static readonly MethodInfo BindMappableValuesMethod = ReflectionUtil . GetMethodInfo < object > ( o => BindMappableValues < object > ( default ! , default ! , default ! , default ! , default ! , default ! ) ) . MakeGenericMethod ( typeof ( object ) ) ;
2323 private const string GenericTypePattern = "(?<typename>[a-zA-Z][a-zA-Z0-9\\ .]+)<(?<genparam>.+)>" ;
2424 private static readonly Regex GenericTypeRegex = new Regex ( GenericTypePattern , RegexOptions . Compiled ) ;
2525 private const char GenericTypeMarker = '`' ;
@@ -423,9 +423,9 @@ private static TypeResolver ReadGenericType(this ResolutionContext resolutionCon
423423 }
424424 }
425425
426- public static void BindMappableValues (
426+ public static void BindMappableValues < T > (
427427 this ResolutionContext resolutionContext ,
428- object target ,
428+ T target ,
429429 Type targetType ,
430430 MethodInfo configurationMethod ,
431431 IConfigurationSection sourceConfigurationSection ,
@@ -455,7 +455,7 @@ public static void BindMappableValues(
455455 if ( methodInfo . IsStatic )
456456 {
457457 var nargs = arguments . ToList ( ) ;
458- nargs . Insert ( 0 , target ) ;
458+ nargs . Insert ( 0 , target ! ) ;
459459 methodInfo . Invoke ( null , nargs . ToArray ( ) ) ;
460460 }
461461 else
@@ -472,45 +472,98 @@ internal static Delegate GenerateLambda(
472472 Type argumentType ,
473473 string ? originalKey )
474474 {
475- var typeParameter = Expression . Parameter ( argumentType ) ;
476- Expression bodyExpression ;
477- if ( sourceConfigurationSection ? . Exists ( ) == true )
475+ if ( argumentType . Name . StartsWith ( "Action`2" , StringComparison . Ordinal ) )
476+ {
477+ var genArgs = argumentType . GetGenericArguments ( ) ;
478+ var ( parameterExpression1 , bodyExpression1 ) = BuildExpressionWithParam ( genArgs [ 0 ] , true ) ;
479+ var ( parameterExpression2 , bodyExpression2 ) = BuildExpressionWithParam ( genArgs [ 1 ] , true ) ;
480+ var combinedArgType = typeof ( ValueTuple < , > ) . MakeGenericType ( genArgs ) ;
481+ var combinedArg = Expression . New ( combinedArgType . GetConstructor ( genArgs ) , parameterExpression1 , parameterExpression2 ) ;
482+ var bodyExpression3 = BuildExpression ( combinedArg , combinedArgType , true ) ;
483+ var combinedBody = Expression . Block ( bodyExpression1 , bodyExpression2 , bodyExpression3 ) ;
484+ var lambda = Expression . Lambda ( argumentType , combinedBody , parameterExpression1 , parameterExpression2 ) . Compile ( ) ;
485+ return lambda ;
486+ }
487+ else if ( argumentType . Name . StartsWith ( "Action`3" , StringComparison . Ordinal ) )
488+ {
489+ var genArgs = argumentType . GetGenericArguments ( ) ;
490+ var ( parameterExpression1 , bodyExpression1 ) = BuildExpressionWithParam ( genArgs [ 0 ] , true ) ;
491+ var ( parameterExpression2 , bodyExpression2 ) = BuildExpressionWithParam ( genArgs [ 1 ] , true ) ;
492+ var ( parameterExpression3 , bodyExpression3 ) = BuildExpressionWithParam ( genArgs [ 2 ] , true ) ;
493+ var combinedArgType = typeof ( ValueTuple < , , > ) . MakeGenericType ( genArgs ) ;
494+ var combinedArg = Expression . New ( combinedArgType . GetConstructor ( genArgs ) , parameterExpression1 , parameterExpression2 , parameterExpression3 ) ;
495+ var bodyExpression4 = BuildExpression ( combinedArg , combinedArgType , true ) ;
496+ var combinedBody = Expression . Block ( bodyExpression1 , bodyExpression2 , bodyExpression3 , bodyExpression4 ) ;
497+ var lambda = Expression . Lambda ( argumentType , combinedBody , parameterExpression1 , parameterExpression2 , parameterExpression3 ) . Compile ( ) ;
498+ return lambda ;
499+ }
500+ else
478501 {
479- var methodExpressions = new List < Expression > ( ) ;
502+ var ( parameterExpression , bodyExpression ) = BuildExpressionWithParam ( argumentType ) ;
503+
504+ var lambda = Expression . Lambda ( typeof ( Action < > ) . MakeGenericType ( argumentType ) , bodyExpression , parameterExpression ) . Compile ( ) ;
505+ return lambda ;
506+ }
480507
481- var childResolutionContext = new ResolutionContext ( resolutionContext . AssemblyFinder , resolutionContext . RootConfiguration , sourceConfigurationSection , resolutionContext . AdditionalMethods , resolutionContext . OnExtensionMethodNotFound , argumentType ) ;
508+ ( ParameterExpression , Expression ) BuildExpressionWithParam ( Type argumentType , bool handleMissing = false )
509+ {
510+ var parameterExpression = Expression . Parameter ( argumentType ) ;
511+ return ( parameterExpression , BuildExpression ( parameterExpression , argumentType , handleMissing ) ) ;
512+ }
482513
483- var keysToExclude = originalKey != null ? new List < string > { originalKey } : new List < string > ( ) ;
484- if ( int . TryParse ( sourceConfigurationSection . Key , out _ ) )
514+ Expression BuildExpression ( Expression parameterExpression , Type argumentType , bool handleMissing = false )
515+ {
516+ Expression bodyExpression ;
517+ if ( sourceConfigurationSection ? . Exists ( ) == true )
485518 {
486- // integer key indicates that this is from an array
487- keysToExclude . Add ( "Name" ) ;
488- }
519+ var methodExpressions = new List < Expression > ( ) ;
520+
521+ var childResolutionContext = new ResolutionContext (
522+ resolutionContext . AssemblyFinder ,
523+ resolutionContext . RootConfiguration ,
524+ sourceConfigurationSection ,
525+ resolutionContext . AdditionalMethods ,
526+ handleMissing ? e => e . Handled = true : resolutionContext . OnExtensionMethodNotFound ,
527+ argumentType ) ;
528+
529+ var keysToExclude = originalKey != null ? new List < string > { originalKey } : new List < string > ( ) ;
530+ if ( int . TryParse ( sourceConfigurationSection . Key , out _ ) )
531+ {
532+ // integer key indicates that this is from an array
533+ keysToExclude . Add ( "Name" ) ;
534+ }
489535
490- // we want to return a generic lambda that calls bind c => configuration.Bind(c)
491- Expression < Action < object > > bindExpression = c => sourceConfigurationSection . Bind ( c ) ;
492- var bindMethodExpression = ( MethodCallExpression ) bindExpression . Body ;
493- methodExpressions . Add ( Expression . Call ( bindMethodExpression . Method , bindMethodExpression . Arguments [ 0 ] , typeParameter ) ) ;
536+ // we want to return a generic lambda that calls bind c => configuration.Bind(c)
537+ if ( ! parameterExpression . Type . IsValueType )
538+ {
539+ Expression < Action < object > > bindExpression = c => sourceConfigurationSection . Bind ( c ) ;
540+ var bindMethodExpression = ( MethodCallExpression ) bindExpression . Body ;
541+ methodExpressions . Add ( Expression . Call ( bindMethodExpression . Method , bindMethodExpression . Arguments [ 0 ] , parameterExpression ) ) ;
542+ }
494543
495- methodExpressions . Add (
496- Expression . Call (
497- BindMappableValuesMethod ,
498- Expression . Constant ( childResolutionContext ) ,
499- typeParameter ,
500- Expression . Constant ( argumentType ) ,
501- Expression . Constant ( configurationMethod ) ,
502- Expression . Constant ( sourceConfigurationSection ) ,
503- Expression . Constant ( keysToExclude . ToArray ( ) ) ) ) ;
544+ var bindMethod = ! parameterExpression . Type . IsValueType ?
545+ BindMappableValuesMethod :
546+ ReflectionUtil . GetGenericMethodInfo ( ( ) => BindMappableValues < object > ( default ! , default ! , default ! , default ! , default ! , default ! ) ) . MakeGenericMethod ( parameterExpression . Type ) ;
547+
548+ methodExpressions . Add (
549+ Expression . Call (
550+ bindMethod ,
551+ Expression . Constant ( childResolutionContext ) ,
552+ parameterExpression ,
553+ Expression . Constant ( argumentType ) ,
554+ Expression . Constant ( configurationMethod ) ,
555+ Expression . Constant ( sourceConfigurationSection ) ,
556+ Expression . Constant ( keysToExclude . ToArray ( ) ) ) ) ;
557+
558+ bodyExpression = Expression . Block ( methodExpressions ) ;
559+ }
560+ else
561+ {
562+ bodyExpression = Expression . Empty ( ) ;
563+ }
504564
505- bodyExpression = Expression . Block ( methodExpressions ) ;
565+ return bodyExpression ;
506566 }
507- else
508- {
509- bodyExpression = Expression . Empty ( ) ;
510- }
511-
512- var lambda = Expression . Lambda ( typeof ( Action < > ) . MakeGenericType ( argumentType ) , bodyExpression , typeParameter ) . Compile ( ) ;
513- return lambda ;
514567 }
515568
516569 private static object ? GetImplicitValueForNotSpecifiedKey (
@@ -845,18 +898,22 @@ private static bool IsConfigurationOptionsBuilder(this ParameterInfo paramInfo,
845898
846899 internal static bool IsConfigurationOptionsBuilder ( this Type type , [ NotNullWhen ( true ) ] out Type ? argumentType )
847900 {
848- if ( type . IsGenericType && type . GetGenericTypeDefinition ( ) == typeof ( Action < > ) )
849- {
850- argumentType = type . GenericTypeArguments [ 0 ] ;
851-
852- // we only accept class types that contain a parameterless public constructor
853- return true ;
854- }
855- else
901+ if ( type . IsGenericType )
856902 {
857- argumentType = null ;
858- return false ;
903+ if ( type . GetGenericTypeDefinition ( ) == typeof ( Action < > ) )
904+ {
905+ argumentType = type . GenericTypeArguments [ 0 ] ;
906+ return true ;
907+ }
908+ else if ( type . GetGenericTypeDefinition ( ) == typeof ( Action < , > ) || type . GetGenericTypeDefinition ( ) == typeof ( Action < , , > ) )
909+ {
910+ argumentType = type ;
911+ return true ;
912+ }
859913 }
914+
915+ argumentType = null ;
916+ return false ;
860917 }
861918
862919 private static bool ParameterTypeHasPropertyMatches ( this Type parameterType , IEnumerable < string > suppliedNames )
0 commit comments