Skip to content

Commit 052d218

Browse files
committed
CSHARP-4118: Limit propagation of known serializers.
1 parent 1458b9f commit 052d218

File tree

4 files changed

+96
-7
lines changed

4 files changed

+96
-7
lines changed

src/MongoDB.Driver/Linq/Linq3Implementation/Serializers/KnownSerializers/KnownSerializerFinder.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ public override Expression Visit(Expression node)
5858
return null;
5959
}
6060

61-
_currentKnownSerializersNode = new KnownSerializersNode(_currentKnownSerializersNode);
61+
_currentKnownSerializersNode = new KnownSerializersNode(node, _currentKnownSerializersNode);
6262

6363
if (node == _root)
6464
{

src/MongoDB.Driver/Linq/Linq3Implementation/Serializers/KnownSerializers/KnownSerializersNode.cs

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,25 +16,28 @@
1616
using System;
1717
using System.Collections.Generic;
1818
using System.Linq;
19+
using System.Linq.Expressions;
1920
using MongoDB.Bson.Serialization;
2021
using MongoDB.Driver.Linq.Linq3Implementation.Misc;
21-
using MongoDB.Driver.Support;
2222

2323
namespace MongoDB.Driver.Linq.Linq3Implementation.Serializers.KnownSerializers
2424
{
2525
internal class KnownSerializersNode
2626
{
2727
// private fields
28+
private readonly Expression _expression;
2829
private readonly Dictionary<Type, HashSet<IBsonSerializer>> _knownSerializers = new Dictionary<Type, HashSet<IBsonSerializer>>();
2930
private readonly KnownSerializersNode _parent;
3031

3132
// constructors
32-
public KnownSerializersNode(KnownSerializersNode parent)
33+
public KnownSerializersNode(Expression expression, KnownSerializersNode parent)
3334
{
35+
_expression = expression;
3436
_parent = parent; // will be null for the root node
3537
}
3638

3739
// public properties
40+
public Expression Expression => _expression;
3841
public Dictionary<Type, HashSet<IBsonSerializer>> KnownSerializers => _knownSerializers;
3942
public KnownSerializersNode Parent => _parent;
4043

@@ -49,7 +52,10 @@ public void AddKnownSerializer(Type type, IBsonSerializer serializer)
4952

5053
set.Add(serializer);
5154

52-
_parent?.AddKnownSerializer(type, serializer);
55+
if (ShouldPropagateKnownSerializerToParent())
56+
{
57+
_parent.AddKnownSerializer(type, serializer);
58+
}
5359
}
5460

5561
public HashSet<IBsonSerializer> GetPossibleSerializers(Type type)
@@ -109,5 +115,20 @@ private HashSet<IBsonSerializer> GetPossibleSerializersAtThisLevel(Type type)
109115

110116
return possibleSerializers;
111117
}
118+
119+
private bool ShouldPropagateKnownSerializerToParent()
120+
{
121+
if (_parent == null)
122+
{
123+
return false;
124+
}
125+
126+
return _parent.Expression.NodeType switch
127+
{
128+
ExpressionType.MemberInit => false,
129+
ExpressionType.New => false,
130+
_ => true
131+
};
132+
}
112133
}
113134
}

src/MongoDB.Driver/Linq/Linq3Implementation/Serializers/KnownSerializers/KnownSerializersRegistry.cs

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,15 @@ internal class KnownSerializersRegistry
2929
// public methods
3030
public void Add(Expression expression, KnownSerializersNode knownSerializers)
3131
{
32-
if (_registry.ContainsKey(expression)) return;
32+
if (knownSerializers.Expression != expression)
33+
{
34+
throw new ArgumentException($"Expression {expression} does not match knownSerializers.Expression {knownSerializers.Expression}.");
35+
}
36+
37+
if (_registry.ContainsKey(expression))
38+
{
39+
return;
40+
}
3341

3442
_registry.Add(expression, knownSerializers);
3543
}
@@ -41,8 +49,8 @@ public IBsonSerializer GetSerializer(Expression expression, IBsonSerializer defa
4149
return possibleSerializers.Count switch
4250
{
4351
0 => defaultSerializer ?? BsonSerializer.LookupSerializer(expressionType), // sometimes there is no known serializer from the context (e.g. CSHARP-4062)
44-
> 1 => throw new InvalidOperationException($"More than one possible serializer found for {expression}."),
45-
_ => possibleSerializers.First()
52+
1 => possibleSerializers.First(),
53+
_ => throw new InvalidOperationException($"More than one possible serializer found for {expression}.")
4654
};
4755
}
4856
}
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
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 MongoDB.Bson;
18+
using MongoDB.Bson.Serialization.Attributes;
19+
using Xunit;
20+
21+
namespace MongoDB.Driver.Tests.Linq.Linq3ImplementationTests.Jira
22+
{
23+
public class CSharp4118Tests : Linq3IntegrationTest
24+
{
25+
[Fact]
26+
public void Known_serializers_should_not_propagate_past_anonymous_class()
27+
{
28+
var collection = GetCollection<C>();
29+
var queryable = collection.AsQueryable()
30+
.Select(x => new { S = "abc", HasId = x.Id != "000000000000000000000000" });
31+
32+
var stages = Translate(collection, queryable);
33+
34+
AssertStages(stages, "{ $project : { S : 'abc', HasId : { $ne : ['$_id', ObjectId('000000000000000000000000')] }, _id : 0 } }");
35+
}
36+
37+
[Fact]
38+
public void Known_serializers_should_not_propagate_past_class_with_member_initializers()
39+
{
40+
var collection = GetCollection<C>();
41+
var queryable = collection.AsQueryable()
42+
.Select(x => new R { S = "abc", HasId = x.Id != "000000000000000000000000" });
43+
44+
var stages = Translate(collection, queryable);
45+
46+
AssertStages(stages, "{ $project : { S : 'abc', HasId : { $ne : ['$_id', ObjectId('000000000000000000000000')] }, _id : 0 } }");
47+
}
48+
49+
private class C
50+
{
51+
[BsonRepresentation(BsonType.ObjectId)] public string Id { get; set; }
52+
}
53+
54+
private class R
55+
{
56+
public string S { get; set; }
57+
public bool HasId { get; set; }
58+
}
59+
}
60+
}

0 commit comments

Comments
 (0)