Skip to content

Commit

Permalink
CSHARP-4118: Limit propagation of known serializers.
Browse files Browse the repository at this point in the history
  • Loading branch information
rstam authored and BorisDog committed May 3, 2022
1 parent 389ae9a commit d2a5f59
Show file tree
Hide file tree
Showing 4 changed files with 96 additions and 7 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ public override Expression Visit(Expression node)
return null;
}

_currentKnownSerializersNode = new KnownSerializersNode(_currentKnownSerializersNode);
_currentKnownSerializersNode = new KnownSerializersNode(node, _currentKnownSerializersNode);

if (node == _root)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,25 +16,28 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using MongoDB.Bson.Serialization;
using MongoDB.Driver.Linq.Linq3Implementation.Misc;
using MongoDB.Driver.Support;

namespace MongoDB.Driver.Linq.Linq3Implementation.Serializers.KnownSerializers
{
internal class KnownSerializersNode
{
// private fields
private readonly Expression _expression;
private readonly Dictionary<Type, HashSet<IBsonSerializer>> _knownSerializers = new Dictionary<Type, HashSet<IBsonSerializer>>();
private readonly KnownSerializersNode _parent;

// constructors
public KnownSerializersNode(KnownSerializersNode parent)
public KnownSerializersNode(Expression expression, KnownSerializersNode parent)
{
_expression = expression;
_parent = parent; // will be null for the root node
}

// public properties
public Expression Expression => _expression;
public Dictionary<Type, HashSet<IBsonSerializer>> KnownSerializers => _knownSerializers;
public KnownSerializersNode Parent => _parent;

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

set.Add(serializer);

_parent?.AddKnownSerializer(type, serializer);
if (ShouldPropagateKnownSerializerToParent())
{
_parent.AddKnownSerializer(type, serializer);
}
}

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

return possibleSerializers;
}

private bool ShouldPropagateKnownSerializerToParent()
{
if (_parent == null)
{
return false;
}

return _parent.Expression.NodeType switch
{
ExpressionType.MemberInit => false,
ExpressionType.New => false,
_ => true
};
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,15 @@ internal class KnownSerializersRegistry
// public methods
public void Add(Expression expression, KnownSerializersNode knownSerializers)
{
if (_registry.ContainsKey(expression)) return;
if (knownSerializers.Expression != expression)
{
throw new ArgumentException($"Expression {expression} does not match knownSerializers.Expression {knownSerializers.Expression}.");
}

if (_registry.ContainsKey(expression))
{
return;
}

_registry.Add(expression, knownSerializers);
}
Expand All @@ -41,8 +49,8 @@ public IBsonSerializer GetSerializer(Expression expression, IBsonSerializer defa
return possibleSerializers.Count switch
{
0 => defaultSerializer ?? BsonSerializer.LookupSerializer(expressionType), // sometimes there is no known serializer from the context (e.g. CSHARP-4062)
> 1 => throw new InvalidOperationException($"More than one possible serializer found for {expression}."),
_ => possibleSerializers.First()
1 => possibleSerializers.First(),
_ => throw new InvalidOperationException($"More than one possible serializer found for {expression}.")
};
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
/* Copyright 2010-present MongoDB Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

using System.Linq;
using MongoDB.Bson;
using MongoDB.Bson.Serialization.Attributes;
using Xunit;

namespace MongoDB.Driver.Tests.Linq.Linq3ImplementationTests.Jira
{
public class CSharp4118Tests : Linq3IntegrationTest
{
[Fact]
public void Known_serializers_should_not_propagate_past_anonymous_class()
{
var collection = GetCollection<C>();
var queryable = collection.AsQueryable()
.Select(x => new { S = "abc", HasId = x.Id != "000000000000000000000000" });

var stages = Translate(collection, queryable);

AssertStages(stages, "{ $project : { S : 'abc', HasId : { $ne : ['$_id', ObjectId('000000000000000000000000')] }, _id : 0 } }");
}

[Fact]
public void Known_serializers_should_not_propagate_past_class_with_member_initializers()
{
var collection = GetCollection<C>();
var queryable = collection.AsQueryable()
.Select(x => new R { S = "abc", HasId = x.Id != "000000000000000000000000" });

var stages = Translate(collection, queryable);

AssertStages(stages, "{ $project : { S : 'abc', HasId : { $ne : ['$_id', ObjectId('000000000000000000000000')] }, _id : 0 } }");
}

private class C
{
[BsonRepresentation(BsonType.ObjectId)] public string Id { get; set; }
}

private class R
{
public string S { get; set; }
public bool HasId { get; set; }
}
}
}

0 comments on commit d2a5f59

Please sign in to comment.