Skip to content

Commit adf84bd

Browse files
committed
CSHARP-5572: Implement new KnownSerializerFinder
1 parent 8c51ba0 commit adf84bd

File tree

100 files changed

+8142
-414
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

100 files changed

+8142
-414
lines changed

src/MongoDB.Bson/Serialization/IBsonSerializerExtensions.cs

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,48 @@ public static TValue Deserialize<TValue>(this IBsonSerializer<TValue> serializer
5050
return serializer.Deserialize(context, args);
5151
}
5252

53+
/// <summary>
54+
/// Gets the serializer for a base type starting from a serializer for a derived type.
55+
/// </summary>
56+
/// <param name="derivedTypeSerializer">The serializer for the derived type.</param>
57+
/// <param name="baseType">The base type.</param>
58+
/// <returns>The serializer for the base type.</returns>
59+
public static IBsonSerializer GetBaseTypeSerializer(this IBsonSerializer derivedTypeSerializer, Type baseType)
60+
{
61+
if (derivedTypeSerializer.ValueType == baseType)
62+
{
63+
return derivedTypeSerializer;
64+
}
65+
66+
if (!baseType.IsAssignableFrom(derivedTypeSerializer.ValueType))
67+
{
68+
throw new ArgumentException($"{baseType} is not assignable from {derivedTypeSerializer.ValueType}.");
69+
}
70+
71+
return BsonSerializer.LookupSerializer(baseType); // TODO: should be able to navigate from serializer
72+
}
73+
74+
/// <summary>
75+
/// Gets the serializer for a derived type starting from a serializer for a base type.
76+
/// </summary>
77+
/// <param name="baseTypeSerializer">The serializer for the base type.</param>
78+
/// <param name="derivedType">The derived type.</param>
79+
/// <returns>The serializer for the derived type.</returns>
80+
public static IBsonSerializer GetDerivedTypeSerializer(this IBsonSerializer baseTypeSerializer, Type derivedType)
81+
{
82+
if (baseTypeSerializer.ValueType == derivedType)
83+
{
84+
return baseTypeSerializer;
85+
}
86+
87+
if (!baseTypeSerializer.ValueType.IsAssignableFrom(derivedType))
88+
{
89+
throw new ArgumentException($"{baseTypeSerializer.ValueType} is not assignable from {derivedType}.");
90+
}
91+
92+
return BsonSerializer.LookupSerializer(derivedType); // TODO: should be able to navigate from serializer
93+
}
94+
5395
/// <summary>
5496
/// Gets the discriminator convention for a serializer.
5597
/// </summary>

src/MongoDB.Bson/Serialization/Serializers/ArraySerializer.cs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,29 @@
1313
* limitations under the License.
1414
*/
1515

16+
using System;
1617
using System.Collections.Generic;
1718

1819
namespace MongoDB.Bson.Serialization.Serializers
1920
{
21+
/// <summary>
22+
/// A static factory class for ArraySerializers.
23+
/// </summary>
24+
public static class ArraySerializer
25+
{
26+
/// <summary>
27+
/// Creates an ArraySerializer.
28+
/// </summary>
29+
/// <param name="itemSerializer">The item serializer.</param>
30+
/// <returns>An ArraySerializer.</returns>
31+
public static IBsonSerializer Create(IBsonSerializer itemSerializer)
32+
{
33+
var itemType = itemSerializer.ValueType;
34+
var arraySerializerType = typeof(ArraySerializer<>).MakeGenericType(itemType);
35+
return (IBsonSerializer)Activator.CreateInstance(arraySerializerType, itemSerializer);
36+
}
37+
}
38+
2039
/// <summary>
2140
/// Represents a serializer for one-dimensional arrays.
2241
/// </summary>

src/MongoDB.Bson/Serialization/Serializers/CharSerializer.cs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,15 @@ namespace MongoDB.Bson.Serialization.Serializers
2222
/// </summary>
2323
public sealed class CharSerializer : StructSerializerBase<char>, IRepresentationConfigurable<CharSerializer>
2424
{
25+
#region static
26+
private static readonly CharSerializer __instance = new();
27+
28+
/// <summary>
29+
/// Returns the default instance of CharSerializer.
30+
/// </summary>
31+
public static CharSerializer Instance => __instance;
32+
#endregion
33+
2534
// private fields
2635
private readonly BsonType _representation;
2736

src/MongoDB.Bson/Serialization/Serializers/DictionarySerializerBase.cs

Lines changed: 7 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,6 @@ namespace MongoDB.Bson.Serialization.Serializers
2727
/// <typeparam name="TDictionary">The type of the dictionary.</typeparam>
2828
public abstract class DictionarySerializerBase<TDictionary> :
2929
ClassSerializerBase<TDictionary>,
30-
IBsonDocumentSerializer,
3130
IBsonDictionarySerializer
3231
where TDictionary : class, IDictionary
3332
{
@@ -132,22 +131,6 @@ obj is DictionarySerializerBase<TDictionary> other &&
132131
/// <inheritdoc/>
133132
public override int GetHashCode() => 0;
134133

135-
/// <inheritdoc/>
136-
public bool TryGetMemberSerializationInfo(string memberName, out BsonSerializationInfo serializationInfo)
137-
{
138-
if (_dictionaryRepresentation != DictionaryRepresentation.Document)
139-
{
140-
serializationInfo = null;
141-
return false;
142-
}
143-
144-
serializationInfo = new BsonSerializationInfo(
145-
memberName,
146-
_valueSerializer,
147-
_valueSerializer.ValueType);
148-
return true;
149-
}
150-
151134
// protected methods
152135
/// <summary>
153136
/// Deserializes a value.
@@ -355,7 +338,6 @@ private string SerializeKeyString(object key)
355338
public abstract class DictionarySerializerBase<TDictionary, TKey, TValue> :
356339
ClassSerializerBase<TDictionary>,
357340
IBsonArraySerializer,
358-
IBsonDocumentSerializer,
359341
IBsonDictionarySerializer
360342
where TDictionary : class, IEnumerable<KeyValuePair<TKey, TValue>>
361343
{
@@ -499,35 +481,14 @@ obj is DictionarySerializerBase<TDictionary, TKey, TValue> other &&
499481
/// <inheritdoc/>
500482
public bool TryGetItemSerializationInfo(out BsonSerializationInfo serializationInfo)
501483
{
502-
if (_dictionaryRepresentation is DictionaryRepresentation.ArrayOfArrays or DictionaryRepresentation.ArrayOfDocuments)
503-
{
504-
var representation = _dictionaryRepresentation == DictionaryRepresentation.ArrayOfArrays
505-
? BsonType.Array
506-
: BsonType.Document;
507-
var keySerializer = _lazyKeySerializer.Value;
508-
var valueSerializer = _lazyValueSerializer.Value;
509-
var keyValuePairSerializer = new KeyValuePairSerializer<TKey, TValue>(representation, keySerializer, valueSerializer);
510-
serializationInfo = new BsonSerializationInfo(null, keyValuePairSerializer, keyValuePairSerializer.ValueType);
511-
return true;
512-
}
513-
514-
serializationInfo = null;
515-
return false;
516-
}
517-
518-
/// <inheritdoc/>
519-
public bool TryGetMemberSerializationInfo(string memberName, out BsonSerializationInfo serializationInfo)
520-
{
521-
if (_dictionaryRepresentation != DictionaryRepresentation.Document)
522-
{
523-
serializationInfo = null;
524-
return false;
525-
}
484+
var representation = _dictionaryRepresentation == DictionaryRepresentation.ArrayOfArrays
485+
? BsonType.Array
486+
: BsonType.Document;
487+
var keySerializer = _lazyKeySerializer.Value;
488+
var valueSerializer = _lazyValueSerializer.Value;
489+
var keyValuePairSerializer = new KeyValuePairSerializer<TKey, TValue>(representation, keySerializer, valueSerializer);
526490

527-
serializationInfo = new BsonSerializationInfo(
528-
memberName,
529-
_lazyValueSerializer.Value,
530-
_lazyValueSerializer.Value.ValueType);
491+
serializationInfo = new BsonSerializationInfo(null, keyValuePairSerializer, keyValuePairSerializer.ValueType);
531492
return true;
532493
}
533494

src/MongoDB.Bson/Serialization/Serializers/NullableSerializer.cs

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,73 @@ public interface INullableSerializer
3333
/// </summary>
3434
public static class NullableSerializer
3535
{
36+
private readonly static IBsonSerializer __nullableBooleanInstance = new NullableSerializer<bool>(BooleanSerializer.Instance);
37+
private readonly static IBsonSerializer __nullableDecimalInstance = new NullableSerializer<decimal>(DecimalSerializer.Instance);
38+
private readonly static IBsonSerializer __nullableDecimal128Instance = new NullableSerializer<Decimal128>(Decimal128Serializer.Instance);
39+
private readonly static IBsonSerializer __nullableDoubleInstance = new NullableSerializer<double>(DoubleSerializer.Instance);
40+
private readonly static IBsonSerializer __nullableInt32Instance = new NullableSerializer<int>(Int32Serializer.Instance);
41+
private readonly static IBsonSerializer __nullableInt64Instance = new NullableSerializer<long>(Int64Serializer.Instance);
42+
private readonly static IBsonSerializer __nullableLocalDateTimeInstance = new NullableSerializer<DateTime>(DateTimeSerializer.LocalInstance);
43+
private readonly static IBsonSerializer __nullableObjectIdInstance = new NullableSerializer<ObjectId>(ObjectIdSerializer.Instance);
44+
private readonly static IBsonSerializer __nullableSingleInstance = new NullableSerializer<float>(SingleSerializer.Instance);
45+
private readonly static IBsonSerializer __nullableStandardGuidInstance = new NullableSerializer<Guid>(GuidSerializer.StandardInstance);
46+
private readonly static IBsonSerializer __nullableUtcDateTimeInstance = new NullableSerializer<DateTime>(DateTimeSerializer.UtcInstance);
47+
48+
/// <summary>
49+
/// Gets a serializer for nullable bools.
50+
/// </summary>
51+
public static IBsonSerializer NullableBooleanInstance => __nullableBooleanInstance;
52+
53+
/// <summary>
54+
/// Gets a serializer for nullable decimals.
55+
/// </summary>
56+
public static IBsonSerializer NullableDecimalInstance => __nullableDecimalInstance;
57+
58+
/// <summary>
59+
/// Gets a serializer for nullable Decimal128s.
60+
/// </summary>
61+
public static IBsonSerializer NullableDecimal128Instance => __nullableDecimal128Instance;
62+
63+
/// <summary>
64+
/// Gets a serializer for nullable doubles.
65+
/// </summary>
66+
public static IBsonSerializer NullableDoubleInstance => __nullableDoubleInstance;
67+
68+
/// <summary>
69+
/// Gets a serializer for nullable ints.
70+
/// </summary>
71+
public static IBsonSerializer NullableInt32Instance => __nullableInt32Instance;
72+
73+
/// <summary>
74+
/// Gets a serializer for nullable longs.
75+
/// </summary>
76+
public static IBsonSerializer NullableInt64Instance => __nullableInt64Instance;
77+
78+
/// <summary>
79+
/// Gets a serializer for local DateTime.
80+
/// </summary>
81+
public static IBsonSerializer NullableLocalDateTimeInstance => __nullableLocalDateTimeInstance;
82+
83+
/// <summary>
84+
/// Gets a serializer for nullable floats.
85+
/// </summary>
86+
public static IBsonSerializer NullableSingleInstance => __nullableSingleInstance;
87+
88+
/// <summary>
89+
/// Gets a serializer for nullable ObjectIds.
90+
/// </summary>
91+
public static IBsonSerializer NullableObjectIdInstance => __nullableObjectIdInstance;
92+
93+
/// <summary>
94+
/// Gets a serializer for nullable Guids with standard representation.
95+
/// </summary>
96+
public static IBsonSerializer NullableStandardGuidInstance => __nullableStandardGuidInstance;
97+
98+
/// <summary>
99+
/// Gets a serializer for UTC DateTime.
100+
/// </summary>
101+
public static IBsonSerializer NullableUtcDateTimeInstance => __nullableUtcDateTimeInstance;
102+
36103
/// <summary>
37104
/// Creates a NullableSerializer.
38105
/// </summary>

src/MongoDB.Driver/Linq/Linq3Implementation/ExtensionMethods/ExpressionExtensions.cs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,5 +60,17 @@ public static TValue GetConstantValue<TValue>(this Expression expression, Expres
6060
var message = $"Expression must be a constant: {expression} in {containingExpression}.";
6161
throw new ExpressionNotSupportedException(message);
6262
}
63+
64+
public static bool IsConvert(this Expression expression, out Expression operand)
65+
{
66+
if (expression is UnaryExpression { NodeType: ExpressionType.Convert } unaryExpression)
67+
{
68+
operand = unaryExpression.Operand;
69+
return true;
70+
}
71+
72+
operand = null;
73+
return false;
74+
}
6375
}
6476
}

src/MongoDB.Driver/Linq/Linq3Implementation/GroupingWithOutputExpressionStageDefinitions.cs

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,8 @@ private AstStage RenderProjectStage(
6464
out IBsonSerializer<TOutput> outputSerializer)
6565
{
6666
var partiallyEvaluatedOutput = (Expression<Func<TGrouping, TOutput>>)LinqExpressionPreprocessor.Preprocess(_output);
67-
var context = TranslationContext.Create(translationOptions);
67+
var parameter = partiallyEvaluatedOutput.Parameters.Single();
68+
var context = TranslationContext.Create(partiallyEvaluatedOutput, initialNode: parameter, initialSerializer: inputSerializer, translationOptions: translationOptions);
6869
var outputTranslation = ExpressionToAggregationExpressionTranslator.TranslateLambdaBody(context, partiallyEvaluatedOutput, inputSerializer, asRoot: true);
6970
var (projectStage, projectSerializer) = ProjectionHelper.CreateProjectStage(outputTranslation);
7071
outputSerializer = (IBsonSerializer<TOutput>)projectSerializer;
@@ -106,7 +107,8 @@ protected override AstStage RenderGroupingStage(
106107
out IBsonSerializer<IGrouping<TValue, TInput>> groupingOutputSerializer)
107108
{
108109
var partiallyEvaluatedGroupBy = (Expression<Func<TInput, TValue>>)LinqExpressionPreprocessor.Preprocess(_groupBy);
109-
var context = TranslationContext.Create(translationOptions);
110+
var parameter = partiallyEvaluatedGroupBy.Parameters.Single();
111+
var context = TranslationContext.Create(partiallyEvaluatedGroupBy, initialNode: parameter, initialSerializer: inputSerializer, translationOptions: translationOptions);
110112
var groupByTranslation = ExpressionToAggregationExpressionTranslator.TranslateLambdaBody(context, partiallyEvaluatedGroupBy, inputSerializer, asRoot: true);
111113

112114
var valueSerializer = (IBsonSerializer<TValue>)groupByTranslation.Serializer;
@@ -150,7 +152,8 @@ protected override AstStage RenderGroupingStage(
150152
out IBsonSerializer<IGrouping<AggregateBucketAutoResultId<TValue>, TInput>> groupingOutputSerializer)
151153
{
152154
var partiallyEvaluatedGroupBy = (Expression<Func<TInput, TValue>>)LinqExpressionPreprocessor.Preprocess(_groupBy);
153-
var context = TranslationContext.Create(translationOptions);
155+
var parameter = partiallyEvaluatedGroupBy.Parameters.Single();
156+
var context = TranslationContext.Create(partiallyEvaluatedGroupBy, initialNode: parameter, initialSerializer: inputSerializer, translationOptions: translationOptions);
154157
var groupByTranslation = ExpressionToAggregationExpressionTranslator.TranslateLambdaBody(context, partiallyEvaluatedGroupBy, inputSerializer, asRoot: true);
155158

156159
var valueSerializer = (IBsonSerializer<TValue>)groupByTranslation.Serializer;
@@ -188,7 +191,8 @@ protected override AstStage RenderGroupingStage(
188191
out IBsonSerializer<IGrouping<TValue, TInput>> groupingOutputSerializer)
189192
{
190193
var partiallyEvaluatedGroupBy = (Expression<Func<TInput, TValue>>)LinqExpressionPreprocessor.Preprocess(_groupBy);
191-
var context = TranslationContext.Create(translationOptions);
194+
var parameter = partiallyEvaluatedGroupBy.Parameters.Single();
195+
var context = TranslationContext.Create(partiallyEvaluatedGroupBy, initialNode: parameter, initialSerializer: inputSerializer, translationOptions: translationOptions);
192196
var groupByTranslation = ExpressionToAggregationExpressionTranslator.TranslateLambdaBody(context, partiallyEvaluatedGroupBy, inputSerializer, asRoot: true);
193197
var pushElements = AstExpression.AccumulatorField("_elements", AstUnaryAccumulatorOperator.Push, AstExpression.RootVar);
194198
var groupBySerializer = (IBsonSerializer<TValue>)groupByTranslation.Serializer;
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
/* Copyright 2010-present MongoDB Inc.
2+
*
3+
* Licensed under the Apache License, Version 2.0 (the "License");
4+
* you may not use this file except in compliance with the License.
5+
* You may obtain a copy of the License at
6+
*
7+
* http://www.apache.org/licenses/LICENSE-2.0
8+
*
9+
* Unless required by applicable law or agreed to in writing, software
10+
* distributed under the License is distributed on an "AS IS" BASIS,
11+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
* See the License for the specific language governing permissions and
13+
* limitations under the License.
14+
*/
15+
16+
using System.Linq.Expressions;
17+
18+
namespace MongoDB.Driver.Linq.Linq3Implementation.KnownSerializerFinders;
19+
20+
internal static class KnownSerializerFinder
21+
{
22+
public static void FindKnownSerializers(
23+
Expression expression,
24+
ExpressionTranslationOptions translationOptions,
25+
KnownSerializerMap knownSerializers)
26+
{
27+
var visitor = new KnownSerializerFinderVisitor(translationOptions, knownSerializers);
28+
29+
do
30+
{
31+
visitor.StartPass();
32+
visitor.Visit(expression);
33+
visitor.EndPass();
34+
}
35+
while (visitor.IsMakingProgress);
36+
37+
//#if DEBUG
38+
var expressionWithUnknownSerializer = UnknownSerializerFinder.FindExpressionWithUnknownSerializer(expression, knownSerializers);
39+
if (expressionWithUnknownSerializer != null)
40+
{
41+
throw new ExpressionNotSupportedException(expressionWithUnknownSerializer, because: "we were unable to determine which serializer to use for the result");
42+
}
43+
//#endif
44+
}
45+
}

0 commit comments

Comments
 (0)