From 1dda26daf9047cff2559985a30ebd159ed7a7829 Mon Sep 17 00:00:00 2001 From: Kyra Ramesh Krishna Date: Mon, 6 Apr 2026 15:22:05 -0400 Subject: [PATCH 1/3] first pass at making sure NamedIdMemberConvention respects BsonElement attribute and also test that BsonId is respected --- .../Conventions/NamedIdMemberConvention.cs | 7 ++ .../ElementNameConventionsTests.cs | 114 ++++++++++++++++++ 2 files changed, 121 insertions(+) diff --git a/src/MongoDB.Bson/Serialization/Conventions/NamedIdMemberConvention.cs b/src/MongoDB.Bson/Serialization/Conventions/NamedIdMemberConvention.cs index 5f28ef57cf2..e67f1aade06 100644 --- a/src/MongoDB.Bson/Serialization/Conventions/NamedIdMemberConvention.cs +++ b/src/MongoDB.Bson/Serialization/Conventions/NamedIdMemberConvention.cs @@ -17,6 +17,7 @@ using System.Collections.Generic; using System.Linq; using System.Reflection; +using MongoDB.Bson.Serialization.Attributes; namespace MongoDB.Bson.Serialization.Conventions { @@ -119,6 +120,12 @@ private bool IsValidIdMember(BsonClassMap classMap, MemberInfo member) return false; } } + + var bsonElement = member.GetCustomAttribute(); + if (bsonElement != null && bsonElement.ElementName != "_id") + { + return false; + } return true; } } diff --git a/tests/MongoDB.Bson.Tests/Serialization/Conventions/ElementNameConventionsTests.cs b/tests/MongoDB.Bson.Tests/Serialization/Conventions/ElementNameConventionsTests.cs index 5f844ef5570..8f1210afa56 100644 --- a/tests/MongoDB.Bson.Tests/Serialization/Conventions/ElementNameConventionsTests.cs +++ b/tests/MongoDB.Bson.Tests/Serialization/Conventions/ElementNameConventionsTests.cs @@ -14,6 +14,7 @@ */ using MongoDB.Bson.Serialization; +using MongoDB.Bson.Serialization.Attributes; using MongoDB.Bson.Serialization.Conventions; using Xunit; @@ -21,6 +22,119 @@ namespace MongoDB.Bson.Tests.Serialization.Conventions { public class ElementNameConventionsTests { + + private class BookWithBsonElementAttribute + { + [BsonElement("notId")] + public ObjectId Id { get; set; } // should not be set to _id + } + + [Fact] + public void TestBsonElementAttributeOverridesNamedIdMemberConvention() + { + var classMap = new BsonClassMap(); + classMap.AutoMap(); + + classMap.Freeze(); + Assert.Null(classMap.IdMemberMap); + Assert.Equal("notId", classMap.GetMemberMap(x => x.Id).ElementName); + } + + private class BookWithBsonIdAttribute + { + public ObjectId id { get; set; } + + [BsonId] + public string Title { get; set; } // should be set to _id + } + + [Fact] + public void TestBsonIdAttributeOverridesNamedIdMemberConvention() + { + var classMap = new BsonClassMap(); + classMap.AutoMap(); + + classMap.Freeze(); + Assert.Equal("_id", classMap.GetMemberMap(x => x.Title).ElementName); + Assert.Equal("_id", classMap.IdMemberMap.ElementName); + Assert.Equal("Title", classMap.IdMemberMap.MemberName); + Assert.Equal("id", classMap.GetMemberMap(x => x.id).ElementName); + } + + private class BookWithBsonElementAndBsonId + { + [BsonElement("notId")] + public ObjectId Id { get; set; } + + [BsonId] + public string Title { get; set; } // should be set to _id + } + + [Fact] + public void TestBsonIdAttributeWinsWhenBsonElementSkipsIdProperty() + { + var classMap = new BsonClassMap(); + classMap.AutoMap(); + classMap.Freeze(); + + Assert.Equal("Title", classMap.IdMemberMap.MemberName); + Assert.Equal("notId", classMap.GetMemberMap(x => x.Id).ElementName); + } + + private class BookWithExplicitBsonElementId + { + [BsonElement("_id")] + public string Id { get; set; } // should not be set to _id + } + + [Fact] + public void TestBsonElementWithUnderscoreIdOnIdPropertyAlsoBecomesIdMember() + { + var classMap = new BsonClassMap(); + classMap.AutoMap(); + classMap.Freeze(); + + Assert.NotNull(classMap.IdMemberMap); + Assert.Equal("Id", classMap.IdMemberMap.MemberName); + Assert.Equal("_id", classMap.GetMemberMap(x => x.Id).ElementName); + } + + private class BookWithExplicitBsonElementIdButNoIdProperty + { + [BsonElement("_id")] + public string Title { get; set; } // should be set to _id + } + + [Fact] + public void TestBsonElementWithUnderscoreIdButNoIdPropertyDoesNotBecomeIdMember() + { + var classMap = new BsonClassMap(); + classMap.AutoMap(); + classMap.Freeze(); + + Assert.Null(classMap.IdMemberMap); + Assert.Equal("_id", classMap.GetMemberMap(x => x.Title).ElementName); + } + + private class BookWithBsonElementAndFallthrough + { + [BsonElement("notId")] + public ObjectId Id { get; set; } + public ObjectId id { get; set; } // should be set to _id + } + + [Fact] + public void TestBsonElementOnIdFallsThroughToLowercaseId() + { + var classMap = new BsonClassMap(); + classMap.AutoMap(); + classMap.Freeze(); + + Assert.Equal("id", classMap.IdMemberMap.MemberName); + Assert.Equal("_id", classMap.GetMemberMap(x => x.id).ElementName); + Assert.Equal("notId", classMap.GetMemberMap(x => x.Id).ElementName); + } + private class TestClass { public string FirstName { get; set; } From 344bd71a2aacb88182e4960a35bef302970afae4 Mon Sep 17 00:00:00 2001 From: Kyra Ramesh Krishna Date: Mon, 6 Apr 2026 16:55:02 -0400 Subject: [PATCH 2/3] add check for when BsonElement has no args, aka element name is null --- .../Serialization/Conventions/NamedIdMemberConvention.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/MongoDB.Bson/Serialization/Conventions/NamedIdMemberConvention.cs b/src/MongoDB.Bson/Serialization/Conventions/NamedIdMemberConvention.cs index e67f1aade06..43b4bb08d8a 100644 --- a/src/MongoDB.Bson/Serialization/Conventions/NamedIdMemberConvention.cs +++ b/src/MongoDB.Bson/Serialization/Conventions/NamedIdMemberConvention.cs @@ -122,7 +122,7 @@ private bool IsValidIdMember(BsonClassMap classMap, MemberInfo member) } var bsonElement = member.GetCustomAttribute(); - if (bsonElement != null && bsonElement.ElementName != "_id") + if (bsonElement != null && !string.IsNullOrEmpty(bsonElement.ElementName) && bsonElement.ElementName != "_id") { return false; } From 94ad7a1b2206edbcf8f8c8fc445874769b9f9867 Mon Sep 17 00:00:00 2001 From: Kyra Ramesh Krishna Date: Wed, 8 Apr 2026 11:03:02 -0400 Subject: [PATCH 3/3] move utility classes after all tests --- .../ElementNameConventionsTests.cs | 100 +++++++++--------- 1 file changed, 50 insertions(+), 50 deletions(-) diff --git a/tests/MongoDB.Bson.Tests/Serialization/Conventions/ElementNameConventionsTests.cs b/tests/MongoDB.Bson.Tests/Serialization/Conventions/ElementNameConventionsTests.cs index 8f1210afa56..aa7d752b24c 100644 --- a/tests/MongoDB.Bson.Tests/Serialization/Conventions/ElementNameConventionsTests.cs +++ b/tests/MongoDB.Bson.Tests/Serialization/Conventions/ElementNameConventionsTests.cs @@ -23,12 +23,6 @@ namespace MongoDB.Bson.Tests.Serialization.Conventions public class ElementNameConventionsTests { - private class BookWithBsonElementAttribute - { - [BsonElement("notId")] - public ObjectId Id { get; set; } // should not be set to _id - } - [Fact] public void TestBsonElementAttributeOverridesNamedIdMemberConvention() { @@ -40,14 +34,6 @@ public void TestBsonElementAttributeOverridesNamedIdMemberConvention() Assert.Equal("notId", classMap.GetMemberMap(x => x.Id).ElementName); } - private class BookWithBsonIdAttribute - { - public ObjectId id { get; set; } - - [BsonId] - public string Title { get; set; } // should be set to _id - } - [Fact] public void TestBsonIdAttributeOverridesNamedIdMemberConvention() { @@ -61,15 +47,6 @@ public void TestBsonIdAttributeOverridesNamedIdMemberConvention() Assert.Equal("id", classMap.GetMemberMap(x => x.id).ElementName); } - private class BookWithBsonElementAndBsonId - { - [BsonElement("notId")] - public ObjectId Id { get; set; } - - [BsonId] - public string Title { get; set; } // should be set to _id - } - [Fact] public void TestBsonIdAttributeWinsWhenBsonElementSkipsIdProperty() { @@ -81,12 +58,6 @@ public void TestBsonIdAttributeWinsWhenBsonElementSkipsIdProperty() Assert.Equal("notId", classMap.GetMemberMap(x => x.Id).ElementName); } - private class BookWithExplicitBsonElementId - { - [BsonElement("_id")] - public string Id { get; set; } // should not be set to _id - } - [Fact] public void TestBsonElementWithUnderscoreIdOnIdPropertyAlsoBecomesIdMember() { @@ -99,12 +70,6 @@ public void TestBsonElementWithUnderscoreIdOnIdPropertyAlsoBecomesIdMember() Assert.Equal("_id", classMap.GetMemberMap(x => x.Id).ElementName); } - private class BookWithExplicitBsonElementIdButNoIdProperty - { - [BsonElement("_id")] - public string Title { get; set; } // should be set to _id - } - [Fact] public void TestBsonElementWithUnderscoreIdButNoIdPropertyDoesNotBecomeIdMember() { @@ -116,13 +81,6 @@ public void TestBsonElementWithUnderscoreIdButNoIdPropertyDoesNotBecomeIdMember( Assert.Equal("_id", classMap.GetMemberMap(x => x.Title).ElementName); } - private class BookWithBsonElementAndFallthrough - { - [BsonElement("notId")] - public ObjectId Id { get; set; } - public ObjectId id { get; set; } // should be set to _id - } - [Fact] public void TestBsonElementOnIdFallsThroughToLowercaseId() { @@ -135,14 +93,6 @@ public void TestBsonElementOnIdFallsThroughToLowercaseId() Assert.Equal("notId", classMap.GetMemberMap(x => x.Id).ElementName); } - private class TestClass - { - public string FirstName { get; set; } - public int Age { get; set; } - public string _DumbName { get; set; } - public string lowerCase { get; set; } - } - [Fact] public void TestMemberNameElementNameConvention() { @@ -172,5 +122,55 @@ public void TestCamelCaseElementNameConvention() Assert.Equal("_DumbName", classMap.GetMemberMap(x => x._DumbName).ElementName); Assert.Equal("lowerCase", classMap.GetMemberMap(x => x.lowerCase).ElementName); } + + private class BookWithBsonElementAttribute + { + [BsonElement("notId")] + public ObjectId Id { get; set; } // should not be set to _id + } + + private class BookWithBsonIdAttribute + { + public ObjectId id { get; set; } + + [BsonId] + public string Title { get; set; } // should be set to _id + } + + private class BookWithBsonElementAndBsonId + { + [BsonElement("notId")] + public ObjectId Id { get; set; } + + [BsonId] + public string Title { get; set; } // should be set to _id + } + + private class BookWithExplicitBsonElementId + { + [BsonElement("_id")] + public string Id { get; set; } // should not be set to _id + } + + private class BookWithExplicitBsonElementIdButNoIdProperty + { + [BsonElement("_id")] + public string Title { get; set; } // should be set to _id + } + + private class BookWithBsonElementAndFallthrough + { + [BsonElement("notId")] + public ObjectId Id { get; set; } + public ObjectId id { get; set; } // should be set to _id + } + + private class TestClass + { + public string FirstName { get; set; } + public int Age { get; set; } + public string _DumbName { get; set; } + public string lowerCase { get; set; } + } } }