Skip to content

Commit 3661812

Browse files
committed
CSHARP-4609: Replace DowncastingSerializer with more permissive ConvertIfPossibleSerializer when rendering values associated with a FieldDefnition.
1 parent c20033d commit 3661812

File tree

4 files changed

+118
-6
lines changed

4 files changed

+118
-6
lines changed

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

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,27 @@
1717

1818
namespace MongoDB.Bson.Serialization.Serializers
1919
{
20+
/// <summary>
21+
/// An interface implemented by DowncastingSerializer.
22+
/// </summary>
23+
public interface IDowncastingSerializer
24+
{
25+
/// <summary>
26+
/// The base type that the serializer will downcast from.
27+
/// </summary>
28+
Type BaseType { get; }
29+
30+
/// <summary>
31+
/// The serializer for the derived type.
32+
/// </summary>
33+
IBsonSerializer DerivedSerializer { get; }
34+
35+
/// <summary>
36+
/// The derived type that the serializer will downcast to.
37+
/// </summary>
38+
Type DerivedType { get; }
39+
}
40+
2041
/// <summary>
2142
/// Static factory class for DowncastingSerializer.
2243
/// </summary>
@@ -44,7 +65,7 @@ public static IBsonSerializer Create(
4465
/// </summary>
4566
/// <typeparam name="TBase">The base type.</typeparam>
4667
/// <typeparam name="TDerived">The derived type.</typeparam>
47-
public class DowncastingSerializer<TBase, TDerived> : SerializerBase<TBase>, IBsonDocumentSerializer
68+
public class DowncastingSerializer<TBase, TDerived> : SerializerBase<TBase>, IBsonDocumentSerializer, IDowncastingSerializer
4869
where TDerived : TBase
4970
{
5071
private readonly IBsonSerializer<TDerived> _derivedSerializer;
@@ -59,6 +80,17 @@ public DowncastingSerializer(IBsonSerializer<TDerived> derivedSerializer)
5980
_derivedSerializer = derivedSerializer ?? throw new ArgumentNullException(nameof(derivedSerializer));
6081
}
6182

83+
/// <inheritdoc/>
84+
public Type BaseType => typeof(TBase);
85+
86+
/// <inheritdoc/>
87+
public IBsonSerializer<TDerived> DerivedSerializer => _derivedSerializer;
88+
89+
IBsonSerializer IDowncastingSerializer.DerivedSerializer => _derivedSerializer;
90+
91+
/// <inheritdoc/>
92+
public Type DerivedType => typeof(TDerived);
93+
6294
/// <inheritdoc/>
6395
public override TBase Deserialize(BsonDeserializationContext context, BsonDeserializationArgs args)
6496
{

src/MongoDB.Driver/FieldValueSerializerHelper.cs

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,13 @@ public static IBsonSerializer GetSerializerForValueType(IBsonSerializer fieldSer
3838
// these will normally be equal unless we've removed some Convert(s) that the compiler put in
3939
if (fieldType == valueType)
4040
{
41+
if (fieldSerializer is IDowncastingSerializer downcastingSerializer)
42+
{
43+
var derivedSerializer = downcastingSerializer.DerivedSerializer;
44+
var derivedType = derivedSerializer.ValueType;
45+
return ConvertIfPossibleSerializer.Create(valueType, derivedType, derivedSerializer, serializerRegistry);
46+
}
47+
4148
return fieldSerializer;
4249
}
4350

@@ -54,7 +61,6 @@ public static IBsonSerializer GetSerializerForValueType(IBsonSerializer fieldSer
5461

5562
var fieldTypeInfo = fieldType.GetTypeInfo();
5663
var fieldSerializerInterfaceType = typeof(IBsonSerializer<>).MakeGenericType(fieldType);
57-
var valueTypeInfo = valueType.GetTypeInfo();
5864

5965
// synthesize a NullableSerializer using the field serializer
6066
if (valueType.IsNullable() && valueType.GetNullableUnderlyingType() == fieldType)
@@ -132,9 +138,7 @@ public static IBsonSerializer GetSerializerForValueType(IBsonSerializer fieldSer
132138
}
133139

134140
// if we can't return a value serializer based on the field serializer return a converting serializer
135-
var convertIfPossibleSerializerType = typeof(ConvertIfPossibleSerializer<,>).MakeGenericType(valueType, fieldType);
136-
var convertIfPossibleSerializerConstructor = convertIfPossibleSerializerType.GetTypeInfo().GetConstructor(new[] { fieldSerializerInterfaceType, typeof(IBsonSerializerRegistry) });
137-
return (IBsonSerializer)convertIfPossibleSerializerConstructor.Invoke(new object[] { fieldSerializer, serializerRegistry });
141+
return ConvertIfPossibleSerializer.Create(valueType, fieldType, fieldSerializer, serializerRegistry);
138142
}
139143

140144
public static IBsonSerializer GetSerializerForValueType(IBsonSerializer fieldSerializer, IBsonSerializerRegistry serializerRegistry, Type valueType, object value)
@@ -177,6 +181,20 @@ private static IBsonSerializer WithStringRepresentation(IBsonSerializer serializ
177181
}
178182

179183
// nested types
184+
private static class ConvertIfPossibleSerializer
185+
{
186+
public static IBsonSerializer Create(
187+
Type valueType,
188+
Type fieldType,
189+
IBsonSerializer fieldSerializer,
190+
IBsonSerializerRegistry serializerRegistry)
191+
{
192+
var convertIfPossibleSerializerType = typeof(ConvertIfPossibleSerializer<,>).MakeGenericType(valueType, fieldType);
193+
var convertIfPossibleSerializerConstructor = convertIfPossibleSerializerType.GetTypeInfo().GetConstructor(new[] { fieldSerializer.GetType(), typeof(IBsonSerializerRegistry) });
194+
return (IBsonSerializer)convertIfPossibleSerializerConstructor.Invoke(new object[] { fieldSerializer, serializerRegistry });
195+
}
196+
}
197+
180198
private class ConvertIfPossibleSerializer<TFrom, TTo> : SerializerBase<TFrom>
181199
{
182200
private readonly IBsonSerializer<TTo> _serializer;
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
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;
17+
using System.Linq;
18+
using System.Linq.Expressions;
19+
using FluentAssertions;
20+
using MongoDB.Driver.Linq;
21+
using MongoDB.TestHelpers.XunitExtensions;
22+
using Xunit;
23+
24+
namespace MongoDB.Driver.Tests.Linq.Linq3ImplementationTests.Jira
25+
{
26+
public class CSharp4609Tests : Linq3IntegrationTest
27+
{
28+
[Theory]
29+
[ParameterAttributeData]
30+
public void Project_dictionary_value_should_work(
31+
[Values(LinqProvider.V2, LinqProvider.V3)] LinqProvider linqProvider)
32+
{
33+
var collection = CreateCollection(linqProvider);
34+
Expression<Func<MeasurementDetails, object>> field = x => x.CreationDate;
35+
var myValue = "2023-04-13T01:02:03Z";
36+
var filter = Builders<MeasurementDetails>.Filter.Lt(field, myValue);
37+
38+
var find = collection.Find(filter);
39+
40+
var translatedFilter = TranslateFilter(collection, filter);
41+
translatedFilter.Should().Be("{ CreationDate : { $lt : ISODate('2023-04-13T01:02:03Z') } }");
42+
43+
var results = find.ToList();
44+
results.Select(r => r.Id).Should().Equal(1);
45+
}
46+
47+
private IMongoCollection<MeasurementDetails> CreateCollection(LinqProvider linqProvider)
48+
{
49+
var collection = GetCollection<MeasurementDetails>("test", linqProvider);
50+
CreateCollection(
51+
collection,
52+
new MeasurementDetails { Id = 1, CreationDate = new DateTime(2023, 04, 13, 1, 2, 2, DateTimeKind.Utc) },
53+
new MeasurementDetails { Id = 2, CreationDate = new DateTime(2023, 04, 13, 1, 2, 3, DateTimeKind.Utc) });
54+
return collection;
55+
}
56+
57+
private class MeasurementDetails
58+
{
59+
public int Id { get; set; }
60+
public DateTime CreationDate { get; set; }
61+
}
62+
}
63+
}

tests/MongoDB.Driver.Tests/Linq/Linq3ImplementationTests/Linq3IntegrationTest.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@
2020
using MongoDB.Bson.Serialization;
2121
using MongoDB.Driver.Linq;
2222
using MongoDB.Driver.Linq.Linq3Implementation;
23-
using MongoDB.Driver.Linq.Linq3Implementation.Misc;
2423
using MongoDB.Driver.Linq.Linq3Implementation.Translators.ExpressionToExecutableQueryTranslators;
2524

2625
namespace MongoDB.Driver.Tests.Linq.Linq3ImplementationTests

0 commit comments

Comments
 (0)