Skip to content

Commit 6632df2

Browse files
rstamDmitryLukyanov
authored andcommitted
CSHARP-4499: Support Convert calls to a base type in filter translators.
1 parent 0eb71aa commit 6632df2

File tree

3 files changed

+142
-0
lines changed

3 files changed

+142
-0
lines changed
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
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+
18+
namespace MongoDB.Bson.Serialization.Serializers
19+
{
20+
/// <summary>
21+
/// Static factory class for DowncastingSerializer.
22+
/// </summary>
23+
public static class DowncastingSerializer
24+
{
25+
/// <summary>
26+
/// Creates a new DowncastingSerializer.
27+
/// </summary>
28+
/// <param name="baseType">The base type.</param>
29+
/// <param name="derivedType">The derived type.</param>
30+
/// <param name="derivedTypeSerializer">The derived type serializer.</param>
31+
/// <returns></returns>
32+
public static IBsonSerializer Create(
33+
Type baseType,
34+
Type derivedType,
35+
IBsonSerializer derivedTypeSerializer)
36+
{
37+
var downcastingSerializerType = typeof(DowncastingSerializer<,>).MakeGenericType(baseType, derivedType);
38+
return (IBsonSerializer)Activator.CreateInstance(downcastingSerializerType, derivedTypeSerializer);
39+
}
40+
}
41+
42+
/// <summary>
43+
/// A serializer for TBase where the actual values are of type TDerived.
44+
/// </summary>
45+
/// <typeparam name="TBase">The base type.</typeparam>
46+
/// <typeparam name="TDerived">The derived type.</typeparam>
47+
public class DowncastingSerializer<TBase, TDerived> : SerializerBase<TBase>
48+
where TDerived : TBase
49+
{
50+
private readonly IBsonSerializer<TDerived> _derivedSerializer;
51+
52+
/// <summary>
53+
/// Initializes a new instance of DowncastingSerializer.
54+
/// </summary>
55+
/// <param name="derivedSerializer">The derived type serializer.</param>
56+
/// <exception cref="ArgumentNullException"></exception>
57+
public DowncastingSerializer(IBsonSerializer<TDerived> derivedSerializer)
58+
{
59+
_derivedSerializer = derivedSerializer ?? throw new ArgumentNullException(nameof(derivedSerializer));
60+
}
61+
62+
/// <inheritdoc/>
63+
public override TBase Deserialize(BsonDeserializationContext context, BsonDeserializationArgs args)
64+
{
65+
return _derivedSerializer.Deserialize(context, args);
66+
}
67+
68+
/// <inheritdoc/>
69+
public override void Serialize(BsonSerializationContext context, BsonSerializationArgs args, TBase value)
70+
{
71+
_derivedSerializer.Serialize(context, args, (TDerived)value);
72+
}
73+
}
74+
}

src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToFilterTranslators/ToFilterFieldTranslators/ConvertExpressionToFilterFieldTranslator.cs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,11 @@ public static AstFilterField Translate(TranslationContext context, UnaryExpressi
4949
return TranslateConvertToNullable(field);
5050
}
5151

52+
if (IsConvertToBaseType(fieldType, targetType))
53+
{
54+
return TranslateConvertToBaseType(field, targetType);
55+
}
56+
5257
if (IsConvertToDerivedType(fieldType, targetType))
5358
{
5459
return TranslateConvertToDerivedType(field, targetType);
@@ -65,6 +70,11 @@ private static bool IsConvertEnumToUnderlyingType(Type fieldType, Type targetTyp
6570
targetType.IsSameAsOrNullableOf(underlyingType);
6671
}
6772

73+
private static bool IsConvertToBaseType(Type fieldType, Type targetType)
74+
{
75+
return fieldType.IsSubclassOf(targetType);
76+
}
77+
6878
private static bool IsConvertToDerivedType(Type fieldType, Type targetType)
6979
{
7080
return targetType.IsSubclassOf(fieldType);
@@ -135,6 +145,14 @@ private static AstFilterField TranslateConvertEnumToUnderlyingType(AstFilterFiel
135145
return AstFilter.Field(field.Path, targetSerializer);
136146
}
137147

148+
private static AstFilterField TranslateConvertToBaseType(AstFilterField field, Type baseType)
149+
{
150+
var derivedTypeSerializer = field.Serializer;
151+
var derivedType = derivedTypeSerializer.ValueType;
152+
var targetSerializer = DowncastingSerializer.Create(baseType, derivedType, derivedTypeSerializer);
153+
return AstFilter.Field(field.Path, targetSerializer);
154+
}
155+
138156
private static AstFilterField TranslateConvertToDerivedType(AstFilterField field, Type targetType)
139157
{
140158
var targetSerializer = BsonSerializer.LookupSerializer(targetType);
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
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 MongoDB.Bson;
17+
using MongoDB.Driver.Linq;
18+
using MongoDB.TestHelpers.XunitExtensions;
19+
using Xunit;
20+
21+
namespace MongoDB.Driver.Tests.Linq.Linq3ImplementationTests.Jira
22+
{
23+
public class CSharp4499Tests : Linq3IntegrationTest
24+
{
25+
[Theory]
26+
[ParameterAttributeData]
27+
public void ExpressionFieldDefinition_should_work([Values(LinqProvider.V2, LinqProvider.V3)] LinqProvider linqProvider)
28+
{
29+
var collection = CreateCollection(linqProvider);
30+
var fieldDefinition = new ExpressionFieldDefinition<ConcreteClass, object>(x => x.InternalId);
31+
32+
var queryable = collection.Aggregate()
33+
.Sort(Builders<ConcreteClass>.Sort.Ascending(fieldDefinition));
34+
35+
var stages = Translate(collection, queryable);
36+
AssertStages(stages, "{ $sort : { InternalId : 1 } }");
37+
}
38+
39+
private IMongoCollection<ConcreteClass> CreateCollection(LinqProvider linqProvider)
40+
{
41+
var collection = GetCollection<ConcreteClass>("C", linqProvider);
42+
return collection;
43+
}
44+
45+
private class ConcreteClass
46+
{
47+
public ObjectId InternalId { get; set; }
48+
}
49+
}
50+
}

0 commit comments

Comments
 (0)