Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 067ddb6

Browse files
committed
CSHARP-5506: Support Dictionary get_Item for keys that aren't strings but are serialized as strings.
1 parent 693ea35 commit 067ddb6

File tree

4 files changed

+172
-23
lines changed

4 files changed

+172
-23
lines changed

‎src/MongoDB.Driver/Linq/Linq3Implementation/Reflection/DictionaryMethod.cs‎

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,16 +21,15 @@ namespace MongoDB.Driver.Linq.Linq3Implementation.Reflection
2121
internal static class DictionaryMethod
2222
{
2323
// public static methods
24-
public static bool IsGetItemWithStringMethod(MethodInfo method)
24+
public static bool IsGetItemWithKeyMethod(MethodInfo method)
2525
{
2626
return
2727
!method.IsStatic &&
2828
method.Name == "get_Item" &&
29+
method.DeclaringType.ImplementsDictionaryInterface(out var keyType, out var valueType) &&
2930
method.GetParameters() is var parameters &&
3031
parameters.Length == 1 &&
31-
parameters[0].ParameterType == typeof(string) &&
32-
method.DeclaringType.ImplementsDictionaryInterface(out var keyType, out var valueType) &&
33-
keyType == typeof(string) &&
32+
parameters[0].ParameterType == keyType &&
3433
method.ReturnType == valueType;
3534
}
3635
}

‎src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/GetItemMethodToAggregationExpressionTranslator.cs‎

Lines changed: 51 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,9 @@
1919
using System.Reflection;
2020
using MongoDB.Bson;
2121
using MongoDB.Bson.Serialization;
22+
using MongoDB.Bson.Serialization.Options;
2223
using MongoDB.Bson.Serialization.Serializers;
2324
using MongoDB.Driver.Linq.Linq3Implementation.Ast.Expressions;
24-
using MongoDB.Driver.Linq.Linq3Implementation.ExtensionMethods;
2525
using MongoDB.Driver.Linq.Linq3Implementation.Misc;
2626
using MongoDB.Driver.Linq.Linq3Implementation.Reflection;
2727

@@ -55,9 +55,9 @@ public static TranslatedExpression Translate(TranslationContext context, Express
5555
return TranslateIListGetItemWithInt(context, expression, sourceExpression, arguments[0]);
5656
}
5757

58-
if (DictionaryMethod.IsGetItemWithStringMethod(method))
58+
if (DictionaryMethod.IsGetItemWithKeyMethod(method))
5959
{
60-
return TranslateIDictionaryGetItemWithString(context, expression, sourceExpression, arguments[0]);
60+
return TranslateIDictionaryGetItemWithKey(context, expression, sourceExpression, arguments[0]);
6161
}
6262

6363
throw new ExpressionNotSupportedException(expression);
@@ -112,13 +112,55 @@ private static TranslatedExpression TranslateIListGetItemWithInt(TranslationCont
112112
return new TranslatedExpression(expression, ast, serializer);
113113
}
114114

115-
private static TranslatedExpression TranslateIDictionaryGetItemWithString(TranslationContext context, Expression expression, Expression sourceExpression, Expression keyExpression)
115+
private static TranslatedExpression TranslateIDictionaryGetItemWithKey(TranslationContext context, Expression expression, Expression dictionaryExpression, Expression keyExpression)
116116
{
117-
var sourceTranslation = ExpressionToAggregationExpressionTranslator.Translate(context, sourceExpression);
118-
var key = keyExpression.GetConstantValue<string>(containingExpression: expression);
119-
var ast = AstExpression.GetField(sourceTranslation.Ast, key); // TODO: verify that dictionary is using Document representation
120-
var valueSerializer = GetDictionaryValueSerializer(sourceTranslation.Serializer);
121-
return new TranslatedExpression(expression, ast, valueSerializer);
117+
var dictionaryTranslation = ExpressionToAggregationExpressionTranslator.Translate(context, dictionaryExpression);
118+
if (!(dictionaryTranslation.Serializer is IBsonDictionarySerializer dictionarySerializer))
119+
{
120+
throw new ExpressionNotSupportedException(expression, because: $"dictionary serializer class {dictionaryTranslation.Serializer.GetType()} does not implement {nameof(IBsonDictionarySerializer)}");
121+
}
122+
if (dictionarySerializer.DictionaryRepresentation != DictionaryRepresentation.Document)
123+
{
124+
throw new ExpressionNotSupportedException(expression, because: "dictionary is not represented as a document");
125+
}
126+
127+
var keySerializer = dictionarySerializer.KeySerializer;
128+
AstExpression keyFieldNameAst;
129+
130+
if (keyExpression is ConstantExpression constantKeyExpression)
131+
{
132+
var key = constantKeyExpression.Value;
133+
var serializedKey = SerializationHelper.SerializeValue(keySerializer, key);
134+
135+
if (!(serializedKey is BsonString))
136+
{
137+
throw new ExpressionNotSupportedException(expression, because: "key did not serialize as a string");
138+
}
139+
140+
keyFieldNameAst = AstExpression.Constant(serializedKey);
141+
}
142+
else
143+
{
144+
if (!(keySerializer is IHasRepresentationSerializer hasRepresentationSerializer))
145+
{
146+
throw new ExpressionNotSupportedException(expression, because: $"key serializer class {keySerializer.GetType()} does not implement {nameof(IHasRepresentationSerializer)}");
147+
}
148+
if (hasRepresentationSerializer.Representation != BsonType.String)
149+
{
150+
throw new ExpressionNotSupportedException(expression, because: $"key serializer class {keySerializer.GetType()} does not serialize as a string");
151+
}
152+
153+
var keyTranslation = ExpressionToAggregationExpressionTranslator.Translate(context, keyExpression);
154+
if (!keyTranslation.Serializer.Equals(keySerializer))
155+
{
156+
throw new ExpressionNotSupportedException(expression, because: "key expression serializer is not equal to the key serializer");
157+
}
158+
159+
keyFieldNameAst = keyTranslation.Ast;
160+
}
161+
162+
var ast = AstExpression.GetField(dictionaryTranslation.Ast, keyFieldNameAst);
163+
return new TranslatedExpression(expression, ast, dictionarySerializer.ValueSerializer);
122164
}
123165
}
124166
}

‎src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToFilterTranslators/ToFilterFieldTranslators/GetItemMethodToFilterFieldTranslator.cs‎

Lines changed: 22 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,12 @@
1616
using System.Collections.ObjectModel;
1717
using System.Linq.Expressions;
1818
using System.Reflection;
19+
using MongoDB.Bson;
1920
using MongoDB.Bson.Serialization;
2021
using MongoDB.Bson.Serialization.Options;
2122
using MongoDB.Bson.Serialization.Serializers;
22-
using MongoDB.Driver.Linq.Linq3Implementation.Ast.Filters;
2323
using MongoDB.Driver.Linq.Linq3Implementation.ExtensionMethods;
24+
using MongoDB.Driver.Linq.Linq3Implementation.Misc;
2425
using MongoDB.Driver.Linq.Linq3Implementation.Reflection;
2526

2627
namespace MongoDB.Driver.Linq.Linq3Implementation.Translators.ExpressionToFilterTranslators.ToFilterFieldTranslators
@@ -53,9 +54,9 @@ public static TranslatedFilterField Translate(TranslationContext context, Expres
5354
return TranslateIListGetItemWithInt(context, expression, fieldExpression, arguments[0]);
5455
}
5556

56-
if (DictionaryMethod.IsGetItemWithStringMethod(method))
57+
if (DictionaryMethod.IsGetItemWithKeyMethod(method))
5758
{
58-
return TranslateDictionaryGetItemWithString(context, expression, fieldExpression, arguments[0]);
59+
return TranslateDictionaryGetItemWithKey(context, expression, fieldExpression, arguments[0]);
5960
}
6061

6162
throw new ExpressionNotSupportedException(expression);
@@ -79,19 +80,30 @@ private static TranslatedFilterField TranslateIListGetItemWithInt(TranslationCon
7980
return ArrayIndexExpressionToFilterFieldTranslator.Translate(context, expression, fieldExpression, indexExpression);
8081
}
8182

82-
private static TranslatedFilterField TranslateDictionaryGetItemWithString(TranslationContext context, Expression expression, Expression fieldExpression, Expression keyExpression)
83+
private static TranslatedFilterField TranslateDictionaryGetItemWithKey(TranslationContext context, Expression expression, Expression fieldExpression, Expression keyExpression)
8384
{
8485
var fieldTranslation = ExpressionToFilterFieldTranslator.Translate(context, fieldExpression);
85-
var key = keyExpression.GetConstantValue<string>(containingExpression: expression);
86+
var key = keyExpression.GetConstantValue<object>(containingExpression: expression);
8687

87-
if (fieldTranslation.Serializer is IBsonDictionarySerializer dictionarySerializer &&
88-
dictionarySerializer.DictionaryRepresentation == DictionaryRepresentation.Document)
88+
if (!(fieldTranslation.Serializer is IBsonDictionarySerializer dictionarySerializer))
89+
{
90+
throw new ExpressionNotSupportedException(expression, because: $"dictionary serializer class {fieldTranslation.Serializer.GetType()} does not implement {nameof(IBsonDictionarySerializer)}");
91+
}
92+
if (dictionarySerializer.DictionaryRepresentation != DictionaryRepresentation.Document)
8993
{
90-
var valueSerializer = dictionarySerializer.ValueSerializer;
91-
return fieldTranslation.SubField(key, valueSerializer);
94+
throw new ExpressionNotSupportedException(expression, because: "dictionary is not represented as a document");
9295
}
9396

94-
throw new ExpressionNotSupportedException(expression);
97+
var keySerializer = dictionarySerializer.KeySerializer;
98+
var valueSerializer = dictionarySerializer.ValueSerializer;
99+
100+
var serializedKey = SerializationHelper.SerializeValue(keySerializer, key);
101+
if (serializedKey is not BsonString)
102+
{
103+
throw new ExpressionNotSupportedException(expression, because: "key did not serialize as a string");
104+
}
105+
106+
return fieldTranslation.SubField(serializedKey.AsString, valueSerializer);
95107
}
96108
}
97109
}
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
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+
using System.Collections.Generic;
18+
using System.Linq;
19+
using FluentAssertions;
20+
using MongoDB.Bson;
21+
using MongoDB.Bson.Serialization;
22+
using MongoDB.Bson.Serialization.Options;
23+
using MongoDB.Bson.Serialization.Serializers;
24+
using MongoDB.Driver.TestHelpers;
25+
using Xunit;
26+
27+
namespace MongoDB.Driver.Tests.Linq.Linq3Implementation.Jira;
28+
29+
public class CSharp5506Tests : LinqIntegrationTest<CSharp5506Tests.ClassFixture>
30+
{
31+
static CSharp5506Tests()
32+
{
33+
var keySerializer = new GuidSerializer(BsonType.String);
34+
var valueSerializer = Int32Serializer.Instance;
35+
var dictionarySerializer = new DictionaryInterfaceImplementerSerializer<Dictionary<Guid, int>>(
36+
DictionaryRepresentation.Document,
37+
keySerializer,
38+
valueSerializer);
39+
40+
BsonClassMap.RegisterClassMap<C>(cm =>
41+
{
42+
cm.AutoMap();
43+
cm.MapMember(x => x.Dictionary).SetSerializer(dictionarySerializer);
44+
});
45+
}
46+
47+
public CSharp5506Tests(ClassFixture fixture)
48+
: base(fixture)
49+
{
50+
}
51+
52+
[Fact]
53+
public void Where_Dictionary_item_equals_value_should_work()
54+
{
55+
var collection = Fixture.Collection;
56+
var guid = Guid.Parse("01020304-0506-0708-090a-0b0c0d0e0f10");
57+
58+
var queryable = collection.AsQueryable().Where(x => x.Dictionary[guid] == 2);
59+
60+
var stages = Translate(collection, queryable);
61+
AssertStages(stages, "{ $match : { 'Dictionary.01020304-0506-0708-090a-0b0c0d0e0f10' : 2 } }");
62+
63+
var result = Queryable.Single(queryable);
64+
result.Id.Should().Be(1);
65+
}
66+
67+
[Fact]
68+
public void Select_Dictionary_item_equals_value_should_work()
69+
{
70+
var collection = Fixture.Collection;
71+
var guid = Guid.Parse("01020304-0506-0708-090a-0b0c0d0e0f10");
72+
73+
var queryable = collection.AsQueryable()
74+
.Select(x => x.Dictionary[guid]);
75+
76+
var stages = Translate(collection, queryable);
77+
AssertStages(stages, "{ $project : { _v : '$Dictionary.01020304-0506-0708-090a-0b0c0d0e0f10', _id : 0 } }");
78+
79+
var result = Queryable.Single(queryable);
80+
result.Should().Be(2);
81+
}
82+
83+
public class C
84+
{
85+
public int Id { get; set; }
86+
public Dictionary<Guid, int> Dictionary { get; set; }
87+
}
88+
89+
public sealed class ClassFixture : MongoCollectionFixture<C>
90+
{
91+
protected override IEnumerable<C> InitialData =>
92+
[
93+
new C { Id = 1, Dictionary = new Dictionary<Guid, int> { [Guid.Parse("01020304-0506-0708-090a-0b0c0d0e0f10")] = 2 } },
94+
];
95+
}
96+
}

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /