Skip to content

Commit 53b886a

Browse files
committed
Various fixes
1 parent 2a0829b commit 53b886a

File tree

3 files changed

+107
-85
lines changed

3 files changed

+107
-85
lines changed

src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MemberExpressionToAggregationExpressionTranslator.cs

+1-2
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,6 @@ public static TranslatedExpression Translate(TranslationContext context, MemberE
4343
{
4444
case "HasValue": return HasValuePropertyToAggregationExpressionTranslator.Translate(context, expression);
4545
case "Value": return ValuePropertyToAggregationExpressionTranslator.Translate(context, expression);
46-
default: break;
4746
}
4847
}
4948

@@ -66,7 +65,7 @@ public static TranslatedExpression Translate(TranslationContext context, MemberE
6665

6766
if (!DocumentSerializerHelper.AreMembersRepresentedAsFields(containerTranslation.Serializer, out _))
6867
{
69-
if (member is PropertyInfo propertyInfo && propertyInfo.Name == "Length")
68+
if (member is PropertyInfo propertyInfo && propertyInfo.Name == "Length")
7069
{
7170
return LengthPropertyToAggregationExpressionTranslator.Translate(context, expression);
7271
}

src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslator.cs

+88-78
Original file line numberDiff line numberDiff line change
@@ -36,98 +36,108 @@ public static TranslatedExpression Translate(TranslationContext context, MethodC
3636
throw new ExpressionNotSupportedException(expression);
3737
}
3838

39-
ByteOrder? byteOrder = null;
40-
string format = null;
41-
AstExpression onErrorAst = null;
42-
AstExpression onNullAst = null;
43-
BsonBinarySubType? subType = null;
44-
4539
var fieldAst = ExpressionToAggregationExpressionTranslator.Translate(context, arguments[0]).Ast;
4640

47-
var optionExpression = arguments[1];
41+
ByteOrder? byteOrder;
42+
string format;
43+
AstExpression onErrorAst;
44+
AstExpression onNullAst;
45+
BsonBinarySubType? subType;
4846

49-
if (optionExpression is ConstantExpression constantExpression)
47+
var optionExpression = arguments[1];
48+
switch (optionExpression)
5049
{
51-
var options = (ConvertOptions)constantExpression.Value;
50+
case ConstantExpression constantExpression:
51+
ExtractOptionsFromConstantExpression(constantExpression, out byteOrder, out format, out onErrorAst, out onNullAst, out subType);
52+
break;
53+
case MemberInitExpression memberInitExpression:
54+
ExtractOptionsFromMemberInitExpression(memberInitExpression, context, out byteOrder, out format, out onErrorAst, out onNullAst, out subType);
55+
break;
56+
default:
57+
throw new ExpressionNotSupportedException("The 'Options' argument can be either a constant expression or a member initialization expression");
58+
}
5259

53-
if (options.OnErrorWasSet)
54-
{
55-
onErrorAst = options.GetOnError();
56-
}
60+
var toType = method.GetGenericArguments()[1];
61+
var toBsonType = GetBsonType(toType).Render();
62+
var serializer = BsonSerializer.LookupSerializer(toType);
5763

58-
if (options.OnNullWasSet)
59-
{
60-
onNullAst = options.GetOnNull();
61-
}
64+
var ast = AstExpression.Convert(fieldAst, toBsonType, subType: subType, byteOrder: byteOrder, format: format, onError: onErrorAst, onNull: onNullAst);
65+
return new TranslatedExpression(expression, ast, serializer);
66+
}
67+
68+
private static void ExtractOptionsFromConstantExpression(ConstantExpression constantExpression, out ByteOrder? byteOrder, out string format, out AstExpression onErrorAst, out AstExpression onNullAst, out BsonBinarySubType? subType)
69+
{
70+
byteOrder = null;
71+
format = null;
72+
onErrorAst = null;
73+
onNullAst = null;
74+
subType = null;
6275

63-
subType = options.SubType;
64-
format = options.Format;
65-
byteOrder = options.ByteOrder;
76+
var options = (ConvertOptions)constantExpression.Value;
77+
78+
if (options is null)
79+
{
80+
return;
6681
}
67-
else if (optionExpression is MemberInitExpression memberInitExpression)
82+
83+
if (options.OnErrorWasSet)
6884
{
69-
foreach (var binding in memberInitExpression.Bindings)
70-
{
71-
if (binding is not MemberAssignment memberAssignment) continue;
72-
73-
var memberName = memberAssignment.Member.Name;
74-
75-
switch (memberName)
76-
{
77-
case nameof(ConvertOptions.ByteOrder):
78-
{
79-
if (memberAssignment.Expression is not ConstantExpression byteOrderExpression)
80-
{
81-
throw new ExpressionNotSupportedException($"The {nameof(ConvertOptions.ByteOrder)} field must be a constant expression"); //TODO Improve message?
82-
}
83-
84-
byteOrder = (ByteOrder?)byteOrderExpression.Value;
85-
break;
86-
}
87-
case nameof(ConvertOptions.Format):
88-
{
89-
if (memberAssignment.Expression is not ConstantExpression formatExpression)
90-
{
91-
throw new ExpressionNotSupportedException($"The {nameof(ConvertOptions.Format)} field must be a constant expression"); //TODO Improve message?
92-
}
93-
94-
format = (string)formatExpression.Value;
95-
break;
96-
}
97-
case nameof(ConvertOptions<object>.OnError):
98-
{
99-
onErrorAst = ExpressionToAggregationExpressionTranslator.Translate(context, memberAssignment.Expression).Ast;
100-
break;
101-
}
102-
case nameof(ConvertOptions<object>.OnNull):
103-
{
104-
onNullAst = ExpressionToAggregationExpressionTranslator.Translate(context, memberAssignment.Expression).Ast;
105-
break;
106-
}
107-
case nameof(ConvertOptions.SubType):
108-
{
109-
if (memberAssignment.Expression is not ConstantExpression subTypeExpression)
110-
{
111-
throw new ExpressionNotSupportedException($"The {nameof(ConvertOptions.SubType)} field must be a constant expression"); //TODO Improve message?
112-
}
113-
114-
subType = (BsonBinarySubType?)subTypeExpression.Value;
115-
break;
116-
}
117-
}
118-
}
85+
onErrorAst = options.GetOnError();
11986
}
120-
else
87+
88+
if (options.OnNullWasSet)
12189
{
122-
throw new ExpressionNotSupportedException("The 'Options' argument can be either a constant expression or a member initialization expression"); //TODO Improve message?
90+
onNullAst = options.GetOnNull();
12391
}
12492

125-
var toType = method.GetGenericArguments()[1];
126-
var toBsonType = GetBsonType(toType).Render();
127-
var serializer = BsonSerializer.LookupSerializer(toType);
93+
subType = options.SubType;
94+
format = options.Format;
95+
byteOrder = options.ByteOrder;
96+
}
12897

129-
var ast = AstExpression.Convert(fieldAst, toBsonType, subType: subType, byteOrder: byteOrder, format: format, onError: onErrorAst, onNull: onNullAst);
130-
return new TranslatedExpression(expression, ast, serializer);
98+
private static void ExtractOptionsFromMemberInitExpression(MemberInitExpression memberInitExpression, TranslationContext context, out ByteOrder? byteOrder, out string format, out AstExpression onErrorAst, out AstExpression onNullAst, out BsonBinarySubType? subType)
99+
{
100+
byteOrder = null;
101+
format = null;
102+
onErrorAst = null;
103+
onNullAst = null;
104+
subType = null;
105+
106+
foreach (var binding in memberInitExpression.Bindings)
107+
{
108+
if (binding is not MemberAssignment memberAssignment) continue;
109+
110+
var memberName = memberAssignment.Member.Name;
111+
var expression = memberAssignment.Expression;
112+
113+
switch (memberName)
114+
{
115+
case nameof(ConvertOptions.ByteOrder):
116+
byteOrder = GetConstantValue<ByteOrder?>(expression, nameof(ConvertOptions.ByteOrder));
117+
break;
118+
case nameof(ConvertOptions.Format):
119+
format = GetConstantValue<string>(expression, nameof(ConvertOptions.Format));
120+
break;
121+
case nameof(ConvertOptions<object>.OnError):
122+
onErrorAst = ExpressionToAggregationExpressionTranslator.Translate(context, expression).Ast;
123+
break;
124+
case nameof(ConvertOptions<object>.OnNull):
125+
onNullAst = ExpressionToAggregationExpressionTranslator.Translate(context, expression).Ast;
126+
break;
127+
case nameof(ConvertOptions.SubType):
128+
subType = GetConstantValue<BsonBinarySubType?>(expression, nameof(ConvertOptions.SubType));
129+
break;
130+
}
131+
}
132+
}
133+
134+
private static T GetConstantValue<T>(Expression expression, string fieldName)
135+
{
136+
if (expression is not ConstantExpression constantExpression)
137+
{
138+
throw new ExpressionNotSupportedException($"The {fieldName} field must be a constant expression.");
139+
}
140+
return (T)constantExpression.Value;
131141
}
132142

133143
private static BsonType GetBsonType(Type type) //TODO Do we have this kind of info somewhere else...?

src/MongoDB.Driver/Mql.cs

+18-5
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
using MongoDB.Driver.Linq.Linq3Implementation.Misc;
1818
using MongoDB.Bson;
1919
using MongoDB.Bson.Serialization;
20+
using MongoDB.Driver.Linq.Linq3Implementation.Serializers;
2021

2122
namespace MongoDB.Driver
2223
{
@@ -51,7 +52,7 @@ public static TValue Constant<TValue>(TValue value, BsonType representaion)
5152

5253

5354
/// <summary>
54-
/// Converts a value from one type to another using the specified options.
55+
/// Converts a value from one type to another using the $convert aggregation operator.
5556
/// </summary>
5657
/// <typeparam name="TFrom">The type of the input value.</typeparam>
5758
/// <typeparam name="TTo">The type of the output value.</typeparam>
@@ -185,7 +186,7 @@ public enum ByteOrder
185186
}
186187

187188
/// <summary>
188-
/// Represents the options parameter for the conversion methods in the Mql static class.
189+
/// Represents the typed options parameter for <see cref="Mql.Convert{TFrom, TTo}(TFrom, ConvertOptions{TTo})"/>.
189190
/// </summary>
190191
public abstract class ConvertOptions
191192
{
@@ -229,8 +230,9 @@ public BsonBinarySubType? SubType
229230
internal abstract BsonValue GetOnNull();
230231
}
231232

233+
232234
/// <summary>
233-
/// Represents the typed options parameter for the conversion methods in the Mql static class.
235+
/// Represents the typed options parameter for <see cref="Mql.Convert{TFrom, TTo}(TFrom, ConvertOptions{TTo})"/>.
234236
/// This class allows to set 'onError' and 'onNull'.
235237
/// </summary>
236238
/// <typeparam name="TTo"> The type of 'onError' and 'onNull'.</typeparam>
@@ -240,6 +242,17 @@ public class ConvertOptions<TTo> : ConvertOptions
240242
private bool _onErrorWasSet;
241243
private TTo _onNull;
242244
private bool _onNullWasSet;
245+
private readonly IBsonSerializer _serializer;
246+
247+
/// <summary>
248+
/// Initializes a new instance of the <see cref="ConvertOptions{TTo}"/> class.
249+
/// </summary>
250+
public ConvertOptions()
251+
{
252+
_serializer = StandardSerializers.TryGetSerializer(typeof(TTo), out var serializer)
253+
? serializer
254+
: BsonSerializer.LookupSerializer(typeof(TTo));
255+
}
243256

244257
/// <summary>
245258
/// The onError parameter.
@@ -270,8 +283,8 @@ public TTo OnNull
270283
internal override bool OnErrorWasSet => _onErrorWasSet;
271284
internal override bool OnNullWasSet => _onNullWasSet;
272285

273-
internal override BsonValue GetOnError() => BsonValue.Create(_onError);
286+
internal override BsonValue GetOnError() => _serializer.ToBsonValue(_onError);
274287

275-
internal override BsonValue GetOnNull() => BsonValue.Create(_onNull);
288+
internal override BsonValue GetOnNull() => _serializer.ToBsonValue(_onNull);
276289
}
277290
}

0 commit comments

Comments
 (0)