Skip to content

Commit b802157

Browse files
committed
CSHARP-5349: Fix discriminator convention inheritance.
1 parent 944cc8b commit b802157

File tree

3 files changed

+169
-2
lines changed

3 files changed

+169
-2
lines changed

src/MongoDB.Bson/Serialization/BsonClassMap.cs

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1323,11 +1323,40 @@ internal IDiscriminatorConvention GetDiscriminatorConvention()
13231323
var discriminatorConvention = _discriminatorConvention;
13241324
if (discriminatorConvention == null)
13251325
{
1326-
// it's possible but harmless for multiple threads to do the field initialization at the same time
1327-
discriminatorConvention = _hasRootClass ? StandardDiscriminatorConvention.Hierarchical : StandardDiscriminatorConvention.Scalar;
1326+
// it's possible but harmless for multiple threads to do the discriminator convention lookukp at the same time
1327+
discriminatorConvention = LookupDiscriminatorConvention();
13281328
_discriminatorConvention = discriminatorConvention;
13291329
}
13301330
return discriminatorConvention;
1331+
1332+
IDiscriminatorConvention LookupDiscriminatorConvention()
1333+
{
1334+
var classMap = this;
1335+
while (classMap != null)
1336+
{
1337+
if (classMap._discriminatorConvention != null)
1338+
{
1339+
return classMap._discriminatorConvention;
1340+
}
1341+
1342+
if (BsonSerializer.IsDiscriminatorConventionRegisteredAtThisLevel(classMap._classType))
1343+
{
1344+
// in this case LookupDiscriminatorConvention below will find it
1345+
break;
1346+
}
1347+
1348+
if (classMap._isRootClass)
1349+
{
1350+
// in this case auto-register a hierarchical convention for the root class and look it up as usual below
1351+
BsonSerializer.GetOrRegisterDiscriminatorConvention(classMap._classType, StandardDiscriminatorConvention.Hierarchical);
1352+
break;
1353+
}
1354+
1355+
classMap = classMap._baseClassMap;
1356+
}
1357+
1358+
return BsonSerializer.LookupDiscriminatorConvention(_classType);
1359+
}
13311360
}
13321361

13331362
// private methods

src/MongoDB.Bson/Serialization/BsonSerializer.cs

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -269,6 +269,51 @@ public static object Deserialize(TextReader textReader, Type nominalType, Action
269269
}
270270
}
271271

272+
internal static IDiscriminatorConvention GetOrRegisterDiscriminatorConvention(Type type, IDiscriminatorConvention discriminatorConvention)
273+
{
274+
__configLock.EnterReadLock();
275+
try
276+
{
277+
if (__discriminatorConventions.TryGetValue(type, out var registeredDiscriminatorConvention))
278+
{
279+
return registeredDiscriminatorConvention;
280+
}
281+
}
282+
finally
283+
{
284+
__configLock.ExitReadLock();
285+
}
286+
287+
__configLock.EnterWriteLock();
288+
try
289+
{
290+
if (__discriminatorConventions.TryGetValue(type, out var registeredDiscrimantorConvention))
291+
{
292+
return registeredDiscrimantorConvention;
293+
}
294+
295+
RegisterDiscriminatorConvention(type, discriminatorConvention);
296+
return discriminatorConvention;
297+
}
298+
finally
299+
{
300+
__configLock.ExitWriteLock();
301+
}
302+
}
303+
304+
internal static bool IsDiscriminatorConventionRegisteredAtThisLevel(Type type)
305+
{
306+
__configLock.EnterReadLock();
307+
try
308+
{
309+
return __discriminatorConventions.ContainsKey(type);
310+
}
311+
finally
312+
{
313+
__configLock.ExitReadLock();
314+
}
315+
}
316+
272317
/// <summary>
273318
/// Returns whether the given type has any discriminators registered for any of its subclasses.
274319
/// </summary>
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
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.Bson.Serialization;
19+
using MongoDB.Bson.Serialization.Attributes;
20+
using MongoDB.Bson.Serialization.Conventions;
21+
using MongoDB.Bson.Serialization.Serializers;
22+
using MongoDB.Driver.Linq;
23+
using Xunit;
24+
25+
namespace MongoDB.Driver.Tests.Linq.Linq3Implementation.Jira
26+
{
27+
public class CSharp5349Tests : Linq3IntegrationTest
28+
{
29+
static CSharp5349Tests()
30+
{
31+
BsonSerializer.RegisterDiscriminatorConvention(typeof(BaseNoRoot), new ScalarDiscriminatorConvention("__type"));
32+
BsonSerializer.RegisterDiscriminatorConvention(typeof(BaseWithRoot), new HierarchicalDiscriminatorConvention("__type"));
33+
}
34+
35+
[Fact]
36+
public void InsertOne_BaseNoRoot_should_use_the_configured_discriminator()
37+
{
38+
var collection = GetCollectionOfBaseNoRoot();
39+
40+
var documents = collection.AsQueryable().As(BsonDocumentSerializer.Instance).ToList();
41+
42+
documents.Single().Should().Be("{ _id : 1, __type : 'DerivedNoRoot' }");
43+
}
44+
45+
[Fact]
46+
public void InsertOne_BaseWithRoot_should_use_the_configured_discriminator()
47+
{
48+
var collection = GetCollectionOfBaseWithRoot();
49+
50+
var documents = collection.AsQueryable().As(BsonDocumentSerializer.Instance).ToList();
51+
52+
documents.Single().Should().Be("{ _id : 1, __type : ['BaseWithRoot', 'DerivedWithRoot'] }");
53+
}
54+
55+
private IMongoCollection<BaseNoRoot> GetCollectionOfBaseNoRoot()
56+
{
57+
var collection = GetCollection<BaseNoRoot>("test");
58+
CreateCollection(
59+
collection,
60+
new DerivedNoRoot { Id = 1 });
61+
return collection;
62+
}
63+
64+
private IMongoCollection<BaseWithRoot> GetCollectionOfBaseWithRoot()
65+
{
66+
var collection = GetCollection<BaseWithRoot>("test");
67+
CreateCollection(
68+
collection,
69+
new DerivedWithRoot { Id = 1 });
70+
return collection;
71+
}
72+
73+
private class BaseNoRoot
74+
{
75+
public int Id { get; set; }
76+
}
77+
78+
private class DerivedNoRoot : BaseNoRoot
79+
{
80+
}
81+
82+
[BsonDiscriminator(RootClass = true)]
83+
[BsonKnownTypes(typeof(DerivedWithRoot))]
84+
private class BaseWithRoot
85+
{
86+
public int Id { get; set; }
87+
}
88+
89+
private class DerivedWithRoot : BaseWithRoot
90+
{
91+
}
92+
}
93+
}

0 commit comments

Comments
 (0)