@@ -27,37 +27,62 @@ internal static class NewExpressionToAggregationExpressionTranslator
2727 {
2828 public static AggregationExpression Translate ( TranslationContext context , NewExpression expression )
2929 {
30- if ( expression . Type == typeof ( DateTime ) )
30+ var expressionType = expression . Type ;
31+ var constructorInfo = expression . Constructor ;
32+ var arguments = expression . Arguments . ToArray ( ) ;
33+ var members = expression . Members ;
34+
35+ if ( expressionType == typeof ( DateTime ) )
3136 {
3237 return NewDateTimeExpressionToAggregationExpressionTranslator . Translate ( context , expression ) ;
3338 }
34- if ( expression . Type . IsConstructedGenericType && expression . Type . GetGenericTypeDefinition ( ) == typeof ( HashSet < > ) )
39+ if ( expressionType . IsConstructedGenericType && expressionType . GetGenericTypeDefinition ( ) == typeof ( HashSet < > ) )
3540 {
3641 return NewHashSetExpressionToAggregationExpressionTranslator . Translate ( context , expression ) ;
3742 }
38- if ( expression . Type . IsConstructedGenericType && expression . Type . GetGenericTypeDefinition ( ) == typeof ( List < > ) )
43+ if ( expressionType . IsConstructedGenericType && expressionType . GetGenericTypeDefinition ( ) == typeof ( List < > ) )
3944 {
4045 return NewListExpressionToAggregationExpressionTranslator . Translate ( context , expression ) ;
4146 }
4247
43- var classMapType = typeof ( BsonClassMap < > ) . MakeGenericType ( expression . Type ) ;
48+ var classMapType = typeof ( BsonClassMap < > ) . MakeGenericType ( expressionType ) ;
4449 var classMap = ( BsonClassMap ) Activator . CreateInstance ( classMapType ) ;
4550 var computedFields = new List < AstComputedField > ( ) ;
4651
47- for ( var i = 0 ; i < expression . Members . Count ; i ++ )
52+ string [ ] propertyNames ;
53+ if ( members != null )
54+ {
55+ // if Members is not null then trust Members more than the constructor parameter names (which are compiler generated for anonymous types)
56+ propertyNames = members . Select ( member => member . Name ) . ToArray ( ) ;
57+ }
58+ else
4859 {
49- var member = expression . Members [ i ] ;
50- var fieldExpression = expression . Arguments [ i ] ;
51- var fieldTranslation = ExpressionToAggregationExpressionTranslator . Translate ( context , fieldExpression ) ;
52- var memberSerializer = fieldTranslation . Serializer ?? BsonSerializer . LookupSerializer ( fieldExpression . Type ) ;
53- var defaultValue = GetDefaultValue ( memberSerializer . ValueType ) ;
54- classMap . MapProperty ( member . Name ) . SetSerializer ( memberSerializer ) . SetDefaultValue ( defaultValue ) ;
55- computedFields . Add ( AstExpression . ComputedField ( member . Name , fieldTranslation . Ast ) ) ;
60+ propertyNames = constructorInfo . GetParameters ( ) . Select ( p => GetMatchingPropertyName ( expression , p . Name ) ) . ToArray ( ) ;
5661 }
5762
58- var constructorInfo = expression . Constructor ;
59- var constructorArgumentNames = expression . Members . Select ( m => m . Name ) . ToArray ( ) ;
60- classMap . MapConstructor ( constructorInfo , constructorArgumentNames ) ;
63+ for ( var i = 0 ; i < arguments . Length ; i ++ )
64+ {
65+ var propertyName = propertyNames [ i ] ;
66+ var valueExpression = arguments [ i ] ;
67+ var valueTranslation = ExpressionToAggregationExpressionTranslator . Translate ( context , valueExpression ) ;
68+ var valueSerializer = valueTranslation . Serializer ?? BsonSerializer . LookupSerializer ( valueExpression . Type ) ;
69+ var defaultValue = GetDefaultValue ( valueSerializer . ValueType ) ;
70+ classMap . MapProperty ( propertyName ) . SetSerializer ( valueSerializer ) . SetDefaultValue ( defaultValue ) ;
71+ computedFields . Add ( AstExpression . ComputedField ( propertyName , valueTranslation . Ast ) ) ;
72+ }
73+
74+ // map any properties that didn't match a constructor argument
75+ foreach ( var property in expressionType . GetProperties ( ) )
76+ {
77+ if ( ! propertyNames . Contains ( property . Name ) )
78+ {
79+ var valueSerializer = context . KnownSerializersRegistry . GetSerializer ( expression , property . PropertyType ) ;
80+ var defaultValue = GetDefaultValue ( valueSerializer . ValueType ) ;
81+ classMap . MapProperty ( property . Name ) . SetSerializer ( valueSerializer ) . SetDefaultValue ( defaultValue ) ;
82+ }
83+ }
84+
85+ classMap . MapConstructor ( constructorInfo , propertyNames ) ;
6186 classMap . Freeze ( ) ;
6287
6388 var ast = AstExpression . ComputedDocument ( computedFields ) ;
@@ -82,5 +107,18 @@ private static object GetDefaultValue(Type type)
82107 return null ;
83108 }
84109 }
110+
111+ private static string GetMatchingPropertyName ( NewExpression expression , string constructorParameterName )
112+ {
113+ foreach ( var property in expression . Type . GetProperties ( ) )
114+ {
115+ if ( property . Name . Equals ( constructorParameterName , StringComparison . OrdinalIgnoreCase ) )
116+ {
117+ return property . Name ;
118+ }
119+ }
120+
121+ throw new ExpressionNotSupportedException ( expression , because : $ "constructor parameter { constructorParameterName } does not match any property") ;
122+ }
85123 }
86124}
0 commit comments