Skip to content

Commit 190d37c

Browse files
committed
CSHARP-4821: LINQ3 Where with enum filter followed by Select with cast throws System.ArgumentException.
1 parent 985f4ef commit 190d37c

File tree

3 files changed

+146
-12
lines changed

3 files changed

+146
-12
lines changed

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

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,17 @@ namespace MongoDB.Bson.Serialization.Serializers
2727
/// </summary>
2828
public class DateTimeSerializer : StructSerializerBase<DateTime>, IRepresentationConfigurable<DateTimeSerializer>
2929
{
30+
#region static
31+
// private static fields
32+
private static readonly DateTimeSerializer __instance = new DateTimeSerializer();
33+
34+
// public static properties
35+
/// <summary>
36+
/// Gets the default DateTimeSerializer.
37+
/// </summary>
38+
public static DateTimeSerializer Instance => __instance;
39+
#endregion
40+
3041
// private constants
3142
private static class Flags
3243
{

src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/ConvertExpressionToAggregationExpressionTranslator.cs

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -88,21 +88,21 @@ public static AggregationExpression Translate(TranslationContext context, UnaryE
8888
}
8989
else
9090
{
91-
var to = expressionType.FullName switch
91+
AstExpression to;
92+
switch (expressionType.FullName)
9293
{
93-
"MongoDB.Bson.ObjectId" => "objectId",
94-
"System.Boolean" => "bool",
95-
"System.DateTime" => "date",
96-
"System.Decimal" => "decimal",
97-
"System.Double" => "double",
98-
"System.Int32" => "int",
99-
"System.Int64" => "long",
100-
"System.String" => "string",
101-
_ => throw new ExpressionNotSupportedException(expression, because: $"conversion to {expressionType} is not supported")
102-
};
94+
case "MongoDB.Bson.ObjectId": to = "objectId"; serializer = ObjectIdSerializer.Instance; break;
95+
case "System.Boolean": to = "bool"; serializer = BooleanSerializer.Instance; break;
96+
case "System.DateTime": to = "date"; serializer = DateTimeSerializer.Instance; break;
97+
case "System.Decimal": to = "decimal"; serializer = DecimalSerializer.Decimal128Instance; break; // not the default representation
98+
case "System.Double": to = "double"; serializer = DoubleSerializer.Instance; break;
99+
case "System.Int32": to = "int"; serializer = Int32Serializer.Instance; break;
100+
case "System.Int64": to = "long"; serializer = Int64Serializer.Instance; break;
101+
case "System.String": to = "string"; serializer = StringSerializer.Instance; break;
102+
default: throw new ExpressionNotSupportedException(expression, because: $"conversion to {expressionType} is not supported");
103+
}
103104

104105
ast = AstExpression.Convert(ast, to);
105-
serializer = context.KnownSerializersRegistry.GetSerializer(expression);
106106
}
107107

108108
return new AggregationExpression(expression, ast, serializer);
Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
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;
17+
using FluentAssertions;
18+
using MongoDB.Driver.Core.Misc;
19+
using MongoDB.Driver.Core.TestHelpers.XunitExtensions;
20+
using MongoDB.Driver.Linq;
21+
using MongoDB.TestHelpers.XunitExtensions;
22+
using Xunit;
23+
24+
namespace MongoDB.Driver.Tests.Linq.Linq3Implementation.Jira
25+
{
26+
public class CSharp4821Tests : Linq3IntegrationTest
27+
{
28+
[Theory]
29+
[ParameterAttributeData]
30+
public void Where_should_work(
31+
[Values(LinqProvider.V2, LinqProvider.V3)] LinqProvider linqProvider)
32+
{
33+
var collection = GetCollection(linqProvider);
34+
35+
var queryable = collection.AsQueryable()
36+
.Where(x => x.Status == Status.Open);
37+
38+
var stages = Translate(collection, queryable);
39+
AssertStages(stages, "{ $match : { Status : 1 } }");
40+
41+
var results = queryable.ToList();
42+
results.Select(x => x.Id).Should().Equal(1);
43+
}
44+
45+
[Theory]
46+
[ParameterAttributeData]
47+
public void Select_should_work(
48+
[Values(LinqProvider.V2, LinqProvider.V3)] LinqProvider linqProvider)
49+
{
50+
RequireServer.Check().Supports(Feature.ToConversionOperators);
51+
var collection = GetCollection(linqProvider);
52+
53+
var queryable = collection.AsQueryable()
54+
.Select(x => new { Result = (int)x.Version });
55+
56+
var stages = Translate(collection, queryable);
57+
if (linqProvider == LinqProvider.V2)
58+
{
59+
AssertStages(stages, "{ $project : { Result : '$Version', _id : 0 } }");
60+
}
61+
else
62+
{
63+
AssertStages(stages, "{ $project : { Result : { $toInt : '$Version' }, _id : 0 } }");
64+
}
65+
66+
var results = queryable.ToList();
67+
results.Select(x => x.Result).Should().Equal(1, 2);
68+
}
69+
70+
[Theory]
71+
[ParameterAttributeData]
72+
public void Where_followed_by_Select_should_work(
73+
[Values(LinqProvider.V2, LinqProvider.V3)] LinqProvider linqProvider)
74+
{
75+
RequireServer.Check().Supports(Feature.ToConversionOperators);
76+
var collection = GetCollection(linqProvider);
77+
78+
var queryable = collection.AsQueryable()
79+
.Where(x => x.Status == Status.Open)
80+
.Select(x => new { Result = (int)x.Version });
81+
82+
var stages = Translate(collection, queryable);
83+
if (linqProvider == LinqProvider.V2)
84+
{
85+
AssertStages(
86+
stages,
87+
"{ $match : { Status : 1 } }",
88+
"{ $project : { Result : '$Version', _id : 0 } }");
89+
}
90+
else
91+
{
92+
AssertStages(
93+
stages,
94+
"{ $match : { Status : 1 } }",
95+
"{ $project : { Result : { $toInt : '$Version' }, _id : 0 } }");
96+
}
97+
98+
var results = queryable.ToList();
99+
results.Select(x => x.Result).Should().Equal(1);
100+
}
101+
102+
private IMongoCollection<C> GetCollection(LinqProvider linqProvider)
103+
{
104+
var collection = GetCollection<C>("test", linqProvider);
105+
CreateCollection(
106+
collection,
107+
new C { Id = 1, Status = Status.Open, Version = 1L },
108+
new C { Id = 2, Status = Status.Closed, Version = 2L });
109+
return collection;
110+
}
111+
112+
private class C
113+
{
114+
public int Id { get; set; }
115+
public Status Status { get; set; }
116+
public long Version { get; set; }
117+
}
118+
119+
#pragma warning disable CA1717 // Only FlagsAttribute enums should have plural names
120+
public enum Status { Closed, Open };
121+
#pragma warning restore CA1717 // Only FlagsAttribute enums should have plural names
122+
}
123+
}

0 commit comments

Comments
 (0)