Skip to content

Commit 61f7077

Browse files
committed
CSHARP-1919: Render filters using underlying field serializer when non-nullable field is compared to nullable enum.
1 parent 5045d42 commit 61f7077

6 files changed

+853
-4
lines changed

src/MongoDB.Driver/FieldValueSerializerHelper.cs

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -70,11 +70,26 @@ private static IBsonSerializer GetSerializerForValueType(IBsonSerializer fieldSe
7070
}
7171

7272
// synthesize an EnumConvertingSerializer using the field serializer
73-
if (fieldTypeInfo.IsEnum && valueType.IsConvertibleToEnum())
73+
if (fieldTypeInfo.IsEnum)
7474
{
75-
var enumConvertingSerializerType = typeof(EnumConvertingSerializer<,>).MakeGenericType(valueType, fieldType);
76-
var enumConvertingSerializerConstructor = enumConvertingSerializerType.GetTypeInfo().GetConstructor(new[] { fieldSerializerInterfaceType });
77-
return (IBsonSerializer)enumConvertingSerializerConstructor.Invoke(new object[] { fieldSerializer });
75+
if (valueType.IsConvertibleToEnum())
76+
{
77+
var enumConvertingSerializerType = typeof(EnumConvertingSerializer<,>).MakeGenericType(valueType, fieldType);
78+
var enumConvertingSerializerConstructor = enumConvertingSerializerType.GetTypeInfo().GetConstructor(new[] { fieldSerializerInterfaceType });
79+
return (IBsonSerializer)enumConvertingSerializerConstructor.Invoke(new object[] { fieldSerializer });
80+
}
81+
82+
if (valueType.IsNullable() && valueType.GetNullableUnderlyingType().IsConvertibleToEnum())
83+
{
84+
var underlyingValueType = valueType.GetNullableUnderlyingType();
85+
var underlyingValueSerializerInterfaceType = typeof(IBsonSerializer<>).MakeGenericType(underlyingValueType);
86+
var enumConvertingSerializerType = typeof(EnumConvertingSerializer<,>).MakeGenericType(underlyingValueType, fieldType);
87+
var enumConvertingSerializerConstructor = enumConvertingSerializerType.GetTypeInfo().GetConstructor(new[] { fieldSerializerInterfaceType });
88+
var enumConvertingSerializer = enumConvertingSerializerConstructor.Invoke(new object[] { fieldSerializer });
89+
var nullableSerializerType = typeof(NullableSerializer<>).MakeGenericType(underlyingValueType);
90+
var nullableSerializerConstructor = nullableSerializerType.GetTypeInfo().GetConstructor(new[] { underlyingValueSerializerInterfaceType });
91+
return (IBsonSerializer)nullableSerializerConstructor.Invoke(new object[] { enumConvertingSerializer });
92+
}
7893
}
7994

8095
// synthesize a NullableEnumConvertingSerializer using the field serializer
Lines changed: 215 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,215 @@
1+
/* Copyright 2017 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 FluentAssertions;
17+
using MongoDB.Bson.Serialization;
18+
using Xunit;
19+
20+
namespace MongoDB.Driver.Tests
21+
{
22+
public class FilterDefinitionBuilderEnumComparedToNullableEnumTests
23+
{
24+
private static IBsonSerializerRegistry __registry = BsonSerializer.SerializerRegistry;
25+
private static IBsonSerializer<C> __serializer = BsonSerializer.SerializerRegistry.GetSerializer<C>();
26+
private static FilterDefinitionBuilder<C> __subject = Builders<C>.Filter;
27+
28+
public enum E { A, B }
29+
30+
public class C
31+
{
32+
public E E { get; set; }
33+
}
34+
35+
[Theory]
36+
[InlineData(E.A, "{ E : 0 }")]
37+
[InlineData(null, "{ E : null }")]
38+
public void Eq_with_field_name_should_render_correctly(E? value, string expectedFilter)
39+
{
40+
var filter = __subject.Eq("E", value);
41+
42+
filter.Render(__serializer, __registry).Should().Be(expectedFilter);
43+
}
44+
45+
[Theory]
46+
[InlineData(E.A, "{ E : 0 }")]
47+
[InlineData(null, "{ E : null }")]
48+
public void Eq_with_lambda_should_render_correctly(E? value, string expectedFilter)
49+
{
50+
var filter = __subject.Eq(x => x.E, value);
51+
52+
filter.Render(__serializer, __registry).Should().Be(expectedFilter);
53+
}
54+
55+
[Theory]
56+
[InlineData(E.A, "{ E : { $gt : 0 } }")]
57+
[InlineData(null, "{ E : { $gt : null } }")]
58+
public void Gt_with_field_name_should_render_correctly(E? value, string expectedFilter)
59+
{
60+
var filter = __subject.Gt("E", value);
61+
62+
filter.Render(__serializer, __registry).Should().Be(expectedFilter);
63+
}
64+
65+
[Theory]
66+
[InlineData(E.A, "{ E : { $gt : 0 } }")]
67+
[InlineData(null, "{ E : { $gt : null } }")]
68+
public void Gt_with_lambda_should_render_correctly(E? value, string expectedFilter)
69+
{
70+
var filter = __subject.Gt(x => x.E, value);
71+
72+
filter.Render(__serializer, __registry).Should().Be(expectedFilter);
73+
}
74+
75+
[Theory]
76+
[InlineData(E.A, "{ E : { $gte : 0 } }")]
77+
[InlineData(null, "{ E : { $gte : null } }")]
78+
public void Gte_with_field_name_should_render_correctly(E? value, string expectedFilter)
79+
{
80+
var filter = __subject.Gte("E", value);
81+
82+
filter.Render(__serializer, __registry).Should().Be(expectedFilter);
83+
}
84+
85+
[Theory]
86+
[InlineData(E.A, "{ E : { $gte : 0 } }")]
87+
[InlineData(null, "{ E : { $gte : null } }")]
88+
public void Gte_with_lambda_should_render_correctly(E? value, string expectedFilter)
89+
{
90+
var filter = __subject.Gte(x => x.E, value);
91+
92+
filter.Render(__serializer, __registry).Should().Be(expectedFilter);
93+
}
94+
95+
[Theory]
96+
[InlineData(E.A, "{ E : { $lt : 0 } }")]
97+
[InlineData(null, "{ E : { $lt : null } }")]
98+
public void Lt_with_field_name_should_render_correctly(E? value, string expectedFilter)
99+
{
100+
var filter = __subject.Lt("E", value);
101+
102+
filter.Render(__serializer, __registry).Should().Be(expectedFilter);
103+
}
104+
105+
[Theory]
106+
[InlineData(E.A, "{ E : { $lt : 0 } }")]
107+
[InlineData(null, "{ E : { $lt : null } }")]
108+
public void Lt_with_lambda_should_render_correctly(E? value, string expectedFilter)
109+
{
110+
var filter = __subject.Lt(x => x.E, value);
111+
112+
filter.Render(__serializer, __registry).Should().Be(expectedFilter);
113+
}
114+
115+
[Theory]
116+
[InlineData(E.A, "{ E : { $lte : 0 } }")]
117+
[InlineData(null, "{ E : { $lte : null } }")]
118+
public void Lte_with_field_name_should_render_correctly(E? value, string expectedFilter)
119+
{
120+
var filter = __subject.Lte("E", value);
121+
122+
filter.Render(__serializer, __registry).Should().Be(expectedFilter);
123+
}
124+
125+
[Theory]
126+
[InlineData(E.A, "{ E : { $lte : 0 } }")]
127+
[InlineData(null, "{ E : { $lte : null } }")]
128+
public void Lte_with_lambda_should_render_correctly(E? value, string expectedFilter)
129+
{
130+
var filter = __subject.Lte(x => x.E, value);
131+
132+
filter.Render(__serializer, __registry).Should().Be(expectedFilter);
133+
}
134+
135+
[Theory]
136+
[InlineData(E.A, "{ E : { $ne : 0 } }")]
137+
[InlineData(null, "{ E : { $ne : null } }")]
138+
public void Ne_with_field_name_should_render_correctly(E? value, string expectedFilter)
139+
{
140+
var filter = __subject.Ne("E", value);
141+
142+
filter.Render(__serializer, __registry).Should().Be(expectedFilter);
143+
}
144+
145+
[Theory]
146+
[InlineData(E.A, "{ E : { $ne : 0 } }")]
147+
[InlineData(null, "{ E : { $ne : null } }")]
148+
public void Ne_with_lambda_should_render_correctly(E? value, string expectedFilter)
149+
{
150+
var filter = __subject.Ne(x => x.E, value);
151+
152+
filter.Render(__serializer, __registry).Should().Be(expectedFilter);
153+
}
154+
155+
[Theory]
156+
[InlineData(E.A, "{ E : 0 }")]
157+
[InlineData(null, "{ E : null }")]
158+
public void Where_operator_equals_should_render_correctly(E? value, string expectedFilter)
159+
{
160+
var filter = __subject.Where(x => x.E == value);
161+
162+
filter.Render(__serializer, __registry).Should().Be(expectedFilter);
163+
}
164+
165+
[Theory]
166+
[InlineData(E.A, "{ E : { $gt : 0 } }")]
167+
[InlineData(null, "{ E : { $gt : null } }")]
168+
public void Where_operator_greater_than_should_render_correctly(E? value, string expectedFilter)
169+
{
170+
var filter = __subject.Where(x => x.E > value);
171+
172+
filter.Render(__serializer, __registry).Should().Be(expectedFilter);
173+
}
174+
175+
[Theory]
176+
[InlineData(E.A, "{ E : { $gte : 0 } }")]
177+
[InlineData(null, "{ E : { $gte : null } }")]
178+
public void Where_operator_greater_than_or_equal_should_render_correctly(E? value, string expectedFilter)
179+
{
180+
var filter = __subject.Where(x => x.E >= value);
181+
182+
filter.Render(__serializer, __registry).Should().Be(expectedFilter);
183+
}
184+
185+
[Theory]
186+
[InlineData(E.A, "{ E : { $lt : 0 } }")]
187+
[InlineData(null, "{ E : { $lt : null } }")]
188+
public void Where_operator_less_than_should_render_correctly(E? value, string expectedFilter)
189+
{
190+
var filter = __subject.Where(x => x.E < value);
191+
192+
filter.Render(__serializer, __registry).Should().Be(expectedFilter);
193+
}
194+
195+
[Theory]
196+
[InlineData(E.A, "{ E : { $lte : 0 } }")]
197+
[InlineData(null, "{ E : { $lte : null } }")]
198+
public void Where_operator_less_than_or_equal_should_render_correctly(E? value, string expectedFilter)
199+
{
200+
var filter = __subject.Where(x => x.E <= value);
201+
202+
filter.Render(__serializer, __registry).Should().Be(expectedFilter);
203+
}
204+
205+
[Theory]
206+
[InlineData(E.A, "{ E : { $ne : 0 } }")]
207+
[InlineData(null, "{ E : { $ne : null } }")]
208+
public void Where_operator_not_equal_should_render_correctly(E? value, string expectedFilter)
209+
{
210+
var filter = __subject.Where(x => x.E != value);
211+
212+
filter.Render(__serializer, __registry).Should().Be(expectedFilter);
213+
}
214+
}
215+
}

0 commit comments

Comments
 (0)