diff --git a/src/MongoDB.Bson/Serialization/BsonClassMap.cs b/src/MongoDB.Bson/Serialization/BsonClassMap.cs index 87239a33bf5..2781d218d6b 100644 --- a/src/MongoDB.Bson/Serialization/BsonClassMap.cs +++ b/src/MongoDB.Bson/Serialization/BsonClassMap.cs @@ -1323,10 +1323,25 @@ internal IDiscriminatorConvention GetDiscriminatorConvention() var discriminatorConvention = _discriminatorConvention; if (discriminatorConvention == null) { - // it's possible but harmless for multiple threads to do the discriminator convention lookukp at the same time + // it's possible but harmless for multiple threads to do the discriminator convention lookup at the same time discriminatorConvention = LookupDiscriminatorConvention(); _discriminatorConvention = discriminatorConvention; + + if (discriminatorConvention != null) + { + var conflictingMemberMap = _allMemberMaps.FirstOrDefault(memberMap => memberMap.ElementName == discriminatorConvention.ElementName); + + if (conflictingMemberMap != null) + { + var fieldOrProperty = conflictingMemberMap.MemberInfo is FieldInfo ? "field" : "property"; + + throw new BsonSerializationException( + $"The discriminator element name cannot be {discriminatorConvention.ElementName} " + + $"because it is already being used by the {fieldOrProperty} {conflictingMemberMap.MemberName} of type {_classType.FullName}"); + } + } } + return discriminatorConvention; IDiscriminatorConvention LookupDiscriminatorConvention() diff --git a/src/MongoDB.Bson/Serialization/Conventions/StandardDiscriminatorConvention.cs b/src/MongoDB.Bson/Serialization/Conventions/StandardDiscriminatorConvention.cs index ca4d2cbc117..55148561676 100644 --- a/src/MongoDB.Bson/Serialization/Conventions/StandardDiscriminatorConvention.cs +++ b/src/MongoDB.Bson/Serialization/Conventions/StandardDiscriminatorConvention.cs @@ -39,14 +39,15 @@ public abstract class StandardDiscriminatorConvention : IDiscriminatorConvention /// The element name. protected StandardDiscriminatorConvention(string elementName) { - if (elementName == null) + if (string.IsNullOrEmpty(elementName)) { - throw new ArgumentNullException("elementName"); + throw new ArgumentException("Element names cannot be null or empty.", nameof(elementName)); } if (elementName.IndexOf('\0') != -1) { - throw new ArgumentException("Element names cannot contain nulls.", "elementName"); + throw new ArgumentException("Element names cannot contain nulls.", nameof(elementName)); } + _elementName = elementName; } diff --git a/tests/MongoDB.Bson.Tests/Serialization/Conventions/StandardDiscriminatorConventionTests.cs b/tests/MongoDB.Bson.Tests/Serialization/Conventions/StandardDiscriminatorConventionTests.cs index f1d633bd724..25fabd6b479 100644 --- a/tests/MongoDB.Bson.Tests/Serialization/Conventions/StandardDiscriminatorConventionTests.cs +++ b/tests/MongoDB.Bson.Tests/Serialization/Conventions/StandardDiscriminatorConventionTests.cs @@ -31,7 +31,7 @@ public void TestConstructorThrowsWhenElementNameContainsNulls() [Fact] public void TestConstructorThrowsWhenElementNameIsNull() { - Assert.Throws(() => new ScalarDiscriminatorConvention(null)); + Assert.Throws(() => new ScalarDiscriminatorConvention(null)); } [Fact] diff --git a/tests/MongoDB.Driver.Tests/Jira/CSharp4040Tests.cs b/tests/MongoDB.Driver.Tests/Jira/CSharp4040Tests.cs new file mode 100644 index 00000000000..7858491fd0a --- /dev/null +++ b/tests/MongoDB.Driver.Tests/Jira/CSharp4040Tests.cs @@ -0,0 +1,47 @@ +/* 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 FluentAssertions; +using MongoDB.Bson; +using MongoDB.Bson.Serialization.Attributes; +using Xunit; + +namespace MongoDB.Driver.Tests.Jira +{ + public class CSharp4040Tests + { + private class BaseDocument + { + [BsonId] public ObjectId Id { get; set; } = ObjectId.GenerateNewId(); + + [BsonElement("_t")] + public string Field1 { get; set; } + } + + private class DerivedDocument : BaseDocument {} + + [Fact] + public void BsonClassMapSerializer_serialization_when_using_field_with_same_element_name_as_discriminator_should_throw() + { + var obj = new DerivedDocument { Field1 = "field1" }; + + var recordedException = Record.Exception(() => obj.ToJson(typeof(BaseDocument))); + recordedException.Should().NotBeNull(); + recordedException.Should().BeOfType(); + recordedException.Message.Should().Be("The discriminator element name cannot be _t because it is already being used" + + " by the property Field1 of type MongoDB.Driver.Tests.Jira.CSharp4040Tests+DerivedDocument"); + } + } +}