diff --git a/src/MongoDB.Bson/Serialization/BsonClassMap.cs b/src/MongoDB.Bson/Serialization/BsonClassMap.cs
index 7fe7e0c6b98..87239a33bf5 100644
--- a/src/MongoDB.Bson/Serialization/BsonClassMap.cs
+++ b/src/MongoDB.Bson/Serialization/BsonClassMap.cs
@@ -1323,11 +1323,40 @@ internal IDiscriminatorConvention GetDiscriminatorConvention()
var discriminatorConvention = _discriminatorConvention;
if (discriminatorConvention == null)
{
- // it's possible but harmless for multiple threads to do the field initialization at the same time
- discriminatorConvention = _hasRootClass ? StandardDiscriminatorConvention.Hierarchical : StandardDiscriminatorConvention.Scalar;
+ // it's possible but harmless for multiple threads to do the discriminator convention lookukp at the same time
+ discriminatorConvention = LookupDiscriminatorConvention();
_discriminatorConvention = discriminatorConvention;
}
return discriminatorConvention;
+
+ IDiscriminatorConvention LookupDiscriminatorConvention()
+ {
+ var classMap = this;
+ while (classMap != null)
+ {
+ if (classMap._discriminatorConvention != null)
+ {
+ return classMap._discriminatorConvention;
+ }
+
+ if (BsonSerializer.IsDiscriminatorConventionRegisteredAtThisLevel(classMap._classType))
+ {
+ // in this case LookupDiscriminatorConvention below will find it
+ break;
+ }
+
+ if (classMap._isRootClass)
+ {
+ // in this case auto-register a hierarchical convention for the root class and look it up as usual below
+ BsonSerializer.GetOrRegisterDiscriminatorConvention(classMap._classType, StandardDiscriminatorConvention.Hierarchical);
+ break;
+ }
+
+ classMap = classMap._baseClassMap;
+ }
+
+ return BsonSerializer.LookupDiscriminatorConvention(_classType);
+ }
}
// private methods
diff --git a/src/MongoDB.Bson/Serialization/BsonSerializer.cs b/src/MongoDB.Bson/Serialization/BsonSerializer.cs
index 064565f6d64..b10676062f4 100644
--- a/src/MongoDB.Bson/Serialization/BsonSerializer.cs
+++ b/src/MongoDB.Bson/Serialization/BsonSerializer.cs
@@ -269,6 +269,51 @@ public static object Deserialize(TextReader textReader, Type nominalType, Action
}
}
+ internal static IDiscriminatorConvention GetOrRegisterDiscriminatorConvention(Type type, IDiscriminatorConvention discriminatorConvention)
+ {
+ __configLock.EnterReadLock();
+ try
+ {
+ if (__discriminatorConventions.TryGetValue(type, out var registeredDiscriminatorConvention))
+ {
+ return registeredDiscriminatorConvention;
+ }
+ }
+ finally
+ {
+ __configLock.ExitReadLock();
+ }
+
+ __configLock.EnterWriteLock();
+ try
+ {
+ if (__discriminatorConventions.TryGetValue(type, out var registeredDiscrimantorConvention))
+ {
+ return registeredDiscrimantorConvention;
+ }
+
+ RegisterDiscriminatorConvention(type, discriminatorConvention);
+ return discriminatorConvention;
+ }
+ finally
+ {
+ __configLock.ExitWriteLock();
+ }
+ }
+
+ internal static bool IsDiscriminatorConventionRegisteredAtThisLevel(Type type)
+ {
+ __configLock.EnterReadLock();
+ try
+ {
+ return __discriminatorConventions.ContainsKey(type);
+ }
+ finally
+ {
+ __configLock.ExitReadLock();
+ }
+ }
+
///
/// Returns whether the given type has any discriminators registered for any of its subclasses.
///
diff --git a/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Jira/CSharp5349Tests.cs b/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Jira/CSharp5349Tests.cs
new file mode 100644
index 00000000000..596d0724be0
--- /dev/null
+++ b/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Jira/CSharp5349Tests.cs
@@ -0,0 +1,93 @@
+/* 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 FluentAssertions;
+using MongoDB.Bson.Serialization;
+using MongoDB.Bson.Serialization.Attributes;
+using MongoDB.Bson.Serialization.Conventions;
+using MongoDB.Bson.Serialization.Serializers;
+using MongoDB.Driver.Linq;
+using Xunit;
+
+namespace MongoDB.Driver.Tests.Linq.Linq3Implementation.Jira
+{
+ public class CSharp5349Tests : Linq3IntegrationTest
+ {
+ static CSharp5349Tests()
+ {
+ BsonSerializer.RegisterDiscriminatorConvention(typeof(BaseNoRoot), new ScalarDiscriminatorConvention("__type"));
+ BsonSerializer.RegisterDiscriminatorConvention(typeof(BaseWithRoot), new HierarchicalDiscriminatorConvention("__type"));
+ }
+
+ [Fact]
+ public void InsertOne_BaseNoRoot_should_use_the_configured_discriminator()
+ {
+ var collection = GetCollectionOfBaseNoRoot();
+
+ var documents = collection.AsQueryable().As(BsonDocumentSerializer.Instance).ToList();
+
+ documents.Single().Should().Be("{ _id : 1, __type : 'DerivedNoRoot' }");
+ }
+
+ [Fact]
+ public void InsertOne_BaseWithRoot_should_use_the_configured_discriminator()
+ {
+ var collection = GetCollectionOfBaseWithRoot();
+
+ var documents = collection.AsQueryable().As(BsonDocumentSerializer.Instance).ToList();
+
+ documents.Single().Should().Be("{ _id : 1, __type : ['BaseWithRoot', 'DerivedWithRoot'] }");
+ }
+
+ private IMongoCollection GetCollectionOfBaseNoRoot()
+ {
+ var collection = GetCollection("test");
+ CreateCollection(
+ collection,
+ new DerivedNoRoot { Id = 1 });
+ return collection;
+ }
+
+ private IMongoCollection GetCollectionOfBaseWithRoot()
+ {
+ var collection = GetCollection("test");
+ CreateCollection(
+ collection,
+ new DerivedWithRoot { Id = 1 });
+ return collection;
+ }
+
+ private class BaseNoRoot
+ {
+ public int Id { get; set; }
+ }
+
+ private class DerivedNoRoot : BaseNoRoot
+ {
+ }
+
+ [BsonDiscriminator(RootClass = true)]
+ [BsonKnownTypes(typeof(DerivedWithRoot))]
+ private class BaseWithRoot
+ {
+ public int Id { get; set; }
+ }
+
+ private class DerivedWithRoot : BaseWithRoot
+ {
+ }
+ }
+}