Skip to content
Open
42 changes: 42 additions & 0 deletions src/MongoDB.Bson/Serialization/IBsonSerializerExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,48 @@ public static TValue Deserialize<TValue>(this IBsonSerializer<TValue> serializer
return serializer.Deserialize(context, args);
}

/// <summary>
/// Gets the serializer for a base type starting from a serializer for a derived type.
/// </summary>
/// <param name="derivedTypeSerializer">The serializer for the derived type.</param>
/// <param name="baseType">The base type.</param>
/// <returns>The serializer for the base type.</returns>
public static IBsonSerializer GetBaseTypeSerializer(this IBsonSerializer derivedTypeSerializer, Type baseType)
{
if (derivedTypeSerializer.ValueType == baseType)
{
return derivedTypeSerializer;
}

if (!baseType.IsAssignableFrom(derivedTypeSerializer.ValueType))
{
throw new ArgumentException($"{baseType} is not assignable from {derivedTypeSerializer.ValueType}.");
}

return BsonSerializer.LookupSerializer(baseType); // TODO: should be able to navigate from serializer
}

/// <summary>
/// Gets the serializer for a derived type starting from a serializer for a base type.
/// </summary>
/// <param name="baseTypeSerializer">The serializer for the base type.</param>
/// <param name="derivedType">The derived type.</param>
/// <returns>The serializer for the derived type.</returns>
public static IBsonSerializer GetDerivedTypeSerializer(this IBsonSerializer baseTypeSerializer, Type derivedType)
{
if (baseTypeSerializer.ValueType == derivedType)
{
return baseTypeSerializer;
}

if (!baseTypeSerializer.ValueType.IsAssignableFrom(derivedType))
{
throw new ArgumentException($"{baseTypeSerializer.ValueType} is not assignable from {derivedType}.");
}

return BsonSerializer.LookupSerializer(derivedType); // TODO: should be able to navigate from serializer
}

/// <summary>
/// Gets the discriminator convention for a serializer.
/// </summary>
Expand Down
19 changes: 19 additions & 0 deletions src/MongoDB.Bson/Serialization/Serializers/ArraySerializer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,29 @@
* limitations under the License.
*/

using System;
using System.Collections.Generic;

namespace MongoDB.Bson.Serialization.Serializers
{
/// <summary>
/// A static factory class for ArraySerializers.
/// </summary>
public static class ArraySerializer
{
/// <summary>
/// Creates an ArraySerializer.
/// </summary>
/// <param name="itemSerializer">The item serializer.</param>
/// <returns>An ArraySerializer.</returns>
public static IBsonSerializer Create(IBsonSerializer itemSerializer)
{
var itemType = itemSerializer.ValueType;
var arraySerializerType = typeof(ArraySerializer<>).MakeGenericType(itemType);
return (IBsonSerializer)Activator.CreateInstance(arraySerializerType, itemSerializer);
}
}

/// <summary>
/// Represents a serializer for one-dimensional arrays.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,15 @@ namespace MongoDB.Bson.Serialization.Serializers
/// </summary>
public sealed class CharSerializer : StructSerializerBase<char>, IRepresentationConfigurable<CharSerializer>
{
#region static
private static readonly CharSerializer __instance = new();

/// <summary>
/// Returns the default instance of CharSerializer.
/// </summary>
public static CharSerializer Instance => __instance;
#endregion

// private fields
private readonly BsonType _representation;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@ namespace MongoDB.Bson.Serialization.Serializers
/// <typeparam name="TDictionary">The type of the dictionary.</typeparam>
public abstract class DictionarySerializerBase<TDictionary> :
ClassSerializerBase<TDictionary>,
IBsonDocumentSerializer,
IBsonDictionarySerializer
where TDictionary : class, IDictionary
{
Expand Down Expand Up @@ -132,22 +131,6 @@ obj is DictionarySerializerBase<TDictionary> other &&
/// <inheritdoc/>
public override int GetHashCode() => 0;

/// <inheritdoc/>
public bool TryGetMemberSerializationInfo(string memberName, out BsonSerializationInfo serializationInfo)
{
if (_dictionaryRepresentation != DictionaryRepresentation.Document)
{
serializationInfo = null;
return false;
}

serializationInfo = new BsonSerializationInfo(
memberName,
_valueSerializer,
_valueSerializer.ValueType);
return true;
}

// protected methods
/// <summary>
/// Deserializes a value.
Expand Down Expand Up @@ -355,7 +338,6 @@ private string SerializeKeyString(object key)
public abstract class DictionarySerializerBase<TDictionary, TKey, TValue> :
ClassSerializerBase<TDictionary>,
IBsonArraySerializer,
IBsonDocumentSerializer,
IBsonDictionarySerializer
where TDictionary : class, IEnumerable<KeyValuePair<TKey, TValue>>
{
Expand Down Expand Up @@ -499,35 +481,14 @@ obj is DictionarySerializerBase<TDictionary, TKey, TValue> other &&
/// <inheritdoc/>
public bool TryGetItemSerializationInfo(out BsonSerializationInfo serializationInfo)
{
if (_dictionaryRepresentation is DictionaryRepresentation.ArrayOfArrays or DictionaryRepresentation.ArrayOfDocuments)
{
var representation = _dictionaryRepresentation == DictionaryRepresentation.ArrayOfArrays
? BsonType.Array
: BsonType.Document;
var keySerializer = _lazyKeySerializer.Value;
var valueSerializer = _lazyValueSerializer.Value;
var keyValuePairSerializer = new KeyValuePairSerializer<TKey, TValue>(representation, keySerializer, valueSerializer);
serializationInfo = new BsonSerializationInfo(null, keyValuePairSerializer, keyValuePairSerializer.ValueType);
return true;
}

serializationInfo = null;
return false;
}

/// <inheritdoc/>
public bool TryGetMemberSerializationInfo(string memberName, out BsonSerializationInfo serializationInfo)
{
if (_dictionaryRepresentation != DictionaryRepresentation.Document)
{
serializationInfo = null;
return false;
}
var representation = _dictionaryRepresentation == DictionaryRepresentation.ArrayOfArrays
? BsonType.Array
: BsonType.Document;
var keySerializer = _lazyKeySerializer.Value;
var valueSerializer = _lazyValueSerializer.Value;
var keyValuePairSerializer = new KeyValuePairSerializer<TKey, TValue>(representation, keySerializer, valueSerializer);

serializationInfo = new BsonSerializationInfo(
memberName,
_lazyValueSerializer.Value,
_lazyValueSerializer.Value.ValueType);
serializationInfo = new BsonSerializationInfo(null, keyValuePairSerializer, keyValuePairSerializer.ValueType);
return true;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,73 @@ public interface INullableSerializer
/// </summary>
public static class NullableSerializer
{
private readonly static IBsonSerializer __nullableBooleanInstance = new NullableSerializer<bool>(BooleanSerializer.Instance);
private readonly static IBsonSerializer __nullableDecimalInstance = new NullableSerializer<decimal>(DecimalSerializer.Instance);
private readonly static IBsonSerializer __nullableDecimal128Instance = new NullableSerializer<Decimal128>(Decimal128Serializer.Instance);
private readonly static IBsonSerializer __nullableDoubleInstance = new NullableSerializer<double>(DoubleSerializer.Instance);
private readonly static IBsonSerializer __nullableInt32Instance = new NullableSerializer<int>(Int32Serializer.Instance);
private readonly static IBsonSerializer __nullableInt64Instance = new NullableSerializer<long>(Int64Serializer.Instance);
private readonly static IBsonSerializer __nullableLocalDateTimeInstance = new NullableSerializer<DateTime>(DateTimeSerializer.LocalInstance);
private readonly static IBsonSerializer __nullableObjectIdInstance = new NullableSerializer<ObjectId>(ObjectIdSerializer.Instance);
private readonly static IBsonSerializer __nullableSingleInstance = new NullableSerializer<float>(SingleSerializer.Instance);
private readonly static IBsonSerializer __nullableStandardGuidInstance = new NullableSerializer<Guid>(GuidSerializer.StandardInstance);
private readonly static IBsonSerializer __nullableUtcDateTimeInstance = new NullableSerializer<DateTime>(DateTimeSerializer.UtcInstance);

/// <summary>
/// Gets a serializer for nullable bools.
/// </summary>
public static IBsonSerializer NullableBooleanInstance => __nullableBooleanInstance;

/// <summary>
/// Gets a serializer for nullable decimals.
/// </summary>
public static IBsonSerializer NullableDecimalInstance => __nullableDecimalInstance;

/// <summary>
/// Gets a serializer for nullable Decimal128s.
/// </summary>
public static IBsonSerializer NullableDecimal128Instance => __nullableDecimal128Instance;

/// <summary>
/// Gets a serializer for nullable doubles.
/// </summary>
public static IBsonSerializer NullableDoubleInstance => __nullableDoubleInstance;

/// <summary>
/// Gets a serializer for nullable ints.
/// </summary>
public static IBsonSerializer NullableInt32Instance => __nullableInt32Instance;

/// <summary>
/// Gets a serializer for nullable longs.
/// </summary>
public static IBsonSerializer NullableInt64Instance => __nullableInt64Instance;

/// <summary>
/// Gets a serializer for local DateTime.
/// </summary>
public static IBsonSerializer NullableLocalDateTimeInstance => __nullableLocalDateTimeInstance;

/// <summary>
/// Gets a serializer for nullable floats.
/// </summary>
public static IBsonSerializer NullableSingleInstance => __nullableSingleInstance;

/// <summary>
/// Gets a serializer for nullable ObjectIds.
/// </summary>
public static IBsonSerializer NullableObjectIdInstance => __nullableObjectIdInstance;

/// <summary>
/// Gets a serializer for nullable Guids with standard representation.
/// </summary>
public static IBsonSerializer NullableStandardGuidInstance => __nullableStandardGuidInstance;

/// <summary>
/// Gets a serializer for UTC DateTime.
/// </summary>
public static IBsonSerializer NullableUtcDateTimeInstance => __nullableUtcDateTimeInstance;

/// <summary>
/// Creates a NullableSerializer.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,5 +60,17 @@ public static TValue GetConstantValue<TValue>(this Expression expression, Expres
var message = $"Expression must be a constant: {expression} in {containingExpression}.";
throw new ExpressionNotSupportedException(message);
}

public static bool IsConvert(this Expression expression, out Expression operand)
{
if (expression is UnaryExpression { NodeType: ExpressionType.Convert } unaryExpression)
{
operand = unaryExpression.Operand;
return true;
}

operand = null;
return false;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,8 @@ private AstStage RenderProjectStage(
out IBsonSerializer<TOutput> outputSerializer)
{
var partiallyEvaluatedOutput = (Expression<Func<TGrouping, TOutput>>)LinqExpressionPreprocessor.Preprocess(_output);
var context = TranslationContext.Create(translationOptions);
var parameter = partiallyEvaluatedOutput.Parameters.Single();
var context = TranslationContext.Create(partiallyEvaluatedOutput, initialNode: parameter, initialSerializer: inputSerializer, translationOptions: translationOptions);
var outputTranslation = ExpressionToAggregationExpressionTranslator.TranslateLambdaBody(context, partiallyEvaluatedOutput, inputSerializer, asRoot: true);
var (projectStage, projectSerializer) = ProjectionHelper.CreateProjectStage(outputTranslation);
outputSerializer = (IBsonSerializer<TOutput>)projectSerializer;
Expand Down Expand Up @@ -106,7 +107,8 @@ protected override AstStage RenderGroupingStage(
out IBsonSerializer<IGrouping<TValue, TInput>> groupingOutputSerializer)
{
var partiallyEvaluatedGroupBy = (Expression<Func<TInput, TValue>>)LinqExpressionPreprocessor.Preprocess(_groupBy);
var context = TranslationContext.Create(translationOptions);
var parameter = partiallyEvaluatedGroupBy.Parameters.Single();
var context = TranslationContext.Create(partiallyEvaluatedGroupBy, initialNode: parameter, initialSerializer: inputSerializer, translationOptions: translationOptions);
var groupByTranslation = ExpressionToAggregationExpressionTranslator.TranslateLambdaBody(context, partiallyEvaluatedGroupBy, inputSerializer, asRoot: true);

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

var valueSerializer = (IBsonSerializer<TValue>)groupByTranslation.Serializer;
Expand Down Expand Up @@ -188,7 +191,8 @@ protected override AstStage RenderGroupingStage(
out IBsonSerializer<IGrouping<TValue, TInput>> groupingOutputSerializer)
{
var partiallyEvaluatedGroupBy = (Expression<Func<TInput, TValue>>)LinqExpressionPreprocessor.Preprocess(_groupBy);
var context = TranslationContext.Create(translationOptions);
var parameter = partiallyEvaluatedGroupBy.Parameters.Single();
var context = TranslationContext.Create(partiallyEvaluatedGroupBy, initialNode: parameter, initialSerializer: inputSerializer, translationOptions: translationOptions);
var groupByTranslation = ExpressionToAggregationExpressionTranslator.TranslateLambdaBody(context, partiallyEvaluatedGroupBy, inputSerializer, asRoot: true);
var pushElements = AstExpression.AccumulatorField("_elements", AstUnaryAccumulatorOperator.Push, AstExpression.RootVar);
var groupBySerializer = (IBsonSerializer<TValue>)groupByTranslation.Serializer;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/* Copyright 2010-present MongoDB Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

using MongoDB.Bson;

namespace MongoDB.Driver.Linq.Linq3Implementation.Misc;

internal static class BsonTypeExtensions
{
public static bool IsNumeric(this BsonType bsonType)
=> bsonType is BsonType.Decimal128 or BsonType.Double or BsonType.Int32 or BsonType.Int64;
}
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,8 @@ public static LambdaExpression UnquoteLambdaIfQueryableMethod(MethodInfo method,
Ensure.IsNotNull(method, nameof(method));
Ensure.IsNotNull(expression, nameof(expression));

if (method.DeclaringType == typeof(Queryable))
var declaringType = method.DeclaringType;
if (declaringType == typeof(Queryable) || declaringType == typeof(MongoQueryable))
{
return UnquoteLambda(expression);
}
Expand Down
Loading