@@ -27,37 +27,62 @@ internal static class NewExpressionToAggregationExpressionTranslator
27
27
{
28
28
public static AggregationExpression Translate ( TranslationContext context , NewExpression expression )
29
29
{
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 ) )
31
36
{
32
37
return NewDateTimeExpressionToAggregationExpressionTranslator . Translate ( context , expression ) ;
33
38
}
34
- if ( expression . Type . IsConstructedGenericType && expression . Type . GetGenericTypeDefinition ( ) == typeof ( HashSet < > ) )
39
+ if ( expressionType . IsConstructedGenericType && expressionType . GetGenericTypeDefinition ( ) == typeof ( HashSet < > ) )
35
40
{
36
41
return NewHashSetExpressionToAggregationExpressionTranslator . Translate ( context , expression ) ;
37
42
}
38
- if ( expression . Type . IsConstructedGenericType && expression . Type . GetGenericTypeDefinition ( ) == typeof ( List < > ) )
43
+ if ( expressionType . IsConstructedGenericType && expressionType . GetGenericTypeDefinition ( ) == typeof ( List < > ) )
39
44
{
40
45
return NewListExpressionToAggregationExpressionTranslator . Translate ( context , expression ) ;
41
46
}
42
47
43
- var classMapType = typeof ( BsonClassMap < > ) . MakeGenericType ( expression . Type ) ;
48
+ var classMapType = typeof ( BsonClassMap < > ) . MakeGenericType ( expressionType ) ;
44
49
var classMap = ( BsonClassMap ) Activator . CreateInstance ( classMapType ) ;
45
50
var computedFields = new List < AstComputedField > ( ) ;
46
51
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
48
59
{
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 ( ) ;
56
61
}
57
62
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 ) ;
61
86
classMap . Freeze ( ) ;
62
87
63
88
var ast = AstExpression . ComputedDocument ( computedFields ) ;
@@ -82,5 +107,18 @@ private static object GetDefaultValue(Type type)
82
107
return null ;
83
108
}
84
109
}
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
+ }
85
123
}
86
124
}
0 commit comments