@@ -547,8 +547,6 @@ private static Expression CollectSourcePropertyValue(IHttpActionDescriptor actio
547547 value = sourcePropertyExpression ;
548548 continue ;
549549 }
550-
551- nullCheckTarget = sourcePropertyExpression ;
552550 }
553551
554552 resultEnumerableType ??= GetEnumerableType ( sourcePropertyExpression . Type ) ;
@@ -562,15 +560,15 @@ private static Expression CollectSourcePropertyValue(IHttpActionDescriptor actio
562560 CollectSourcePropertyValue ( propertyName , previousPropertyName , isNestedEnumerablePair , nestedEnumerableAnchor , ref value , ref nullCheckTarget ) ;
563561 }
564562
565- if ( ensureNullPropagation && nullCheckTarget != null && i + 1 < parts . Length )
563+ if ( ensureNullPropagation && nullCheckTarget != null && ! reachedEnd )
566564 nullCheckTargets . Add ( nullCheckTarget ) ;
567565 }
568566
569567 if ( nullCheckTargets . Any ( ) )
570568 {
571569 Expression test = nullCheckTargets . Select ( x => Expression . NotEqual ( x , Expression . Constant ( null ) ) ) . Aggregate ( Expression . AndAlso /* Short-circuit behavior like && in C# */ ) ;
572570 bool hasDefaultValue = parameter . DefaultValue != DBNull . Value && parameter . DefaultValue != null ;
573- Expression fallbackValue = hasDefaultValue ? ( Expression ) Expression . Constant ( parameter . DefaultValue ) : Expression . Default ( parameter . ParameterType ) ;
571+ Expression fallbackValue = hasDefaultValue ? Expression . Constant ( parameter . DefaultValue ) : Expression . Default ( parameter . ParameterType ) ;
574572 value = EnsureCorrectType ( parameter . InternalParameterName , value , parameter . ParameterType , actionParameter ) ;
575573 value = Expression . Condition ( test , value , fallbackValue ) ;
576574 }
@@ -732,6 +730,11 @@ private static Expression EnsureCorrectType(string parameterName, Expression val
732730 if ( targetType == typeof ( object ) )
733731 return valueExpression ;
734732
733+ // Prefer Expression.Cast/Boxing over custom runtime type conversion
734+ // This scenario wraps int into Nullable<int>
735+ if ( valueExpression . Type == Nullable . GetUnderlyingType ( targetType ) )
736+ return Expression . Convert ( valueExpression , targetType ) ;
737+
735738 if ( TryStructuredTypeConversion ( parameterName , valueExpression , targetType , out Expression newValueExpression ) )
736739 return newValueExpression ;
737740
@@ -803,6 +806,7 @@ private static Expression BuildFlattenNestedEnumerableExpression(Expression valu
803806 Type parentGenericType = propertyEnumerableType ;
804807 ParameterExpression selectorParameter = Expression . Parameter ( propertyEnumerableType , "x" ) ;
805808 Expression selectorProperty = selectorParameter ;
809+ ICollection < Expression > nullCheckTargets = new List < Expression > ( ) ;
806810
807811 for ( int i = startIndex ; i < tokens . Length ; i ++ )
808812 {
@@ -819,13 +823,25 @@ private static Expression BuildFlattenNestedEnumerableExpression(Expression valu
819823 }
820824
821825 if ( enumerableType == null )
826+ {
827+ nullCheckTargets . Add ( selectorProperty ) ;
822828 continue ;
829+ }
823830
824831 Type childGenericType = enumerableType ;
825832 MethodInfo flattenMethod = typeof ( HttpParameterResolver ) . SafeGetMethod ( nameof ( FlattenNestedEnumerable ) , BindingFlags . NonPublic | BindingFlags . Static )
826833 . MakeGenericMethod ( parentGenericType , childGenericType ) ;
827834
828- Expression collectionSelector = Expression . Lambda ( selectorProperty , selectorParameter ) ;
835+ Expression collectionSelectorProperty = selectorProperty ;
836+ if ( nullCheckTargets . Any ( ) )
837+ {
838+ Expression test = nullCheckTargets . Select ( x => Expression . NotEqual ( x , Expression . Constant ( null ) ) ) . Aggregate ( Expression . AndAlso /* Short-circuit behavior like && in C# */ ) ;
839+ Expression fallbackValue = Expression . Convert ( Expression . Call ( typeof ( Array ) , nameof ( Array . Empty ) , [ enumerableType ] ) , collectionSelectorProperty . Type ) ;
840+ collectionSelectorProperty = Expression . Condition ( test , collectionSelectorProperty , fallbackValue ) ;
841+ nullCheckTargets . Clear ( ) ;
842+ }
843+
844+ Expression collectionSelector = Expression . Lambda ( collectionSelectorProperty , selectorParameter ) ;
829845 result = Expression . Call ( flattenMethod , result , collectionSelector ) ;
830846 parentGenericType = typeof ( NestedEnumerablePair < , > ) . MakeGenericType ( parentGenericType , childGenericType ) ;
831847 selectorParameter = Expression . Parameter ( parentGenericType , "x" ) ;
0 commit comments