diff --git a/src/MongoDB.Bson/Serialization/Conventions/NamedIdMemberConvention.cs b/src/MongoDB.Bson/Serialization/Conventions/NamedIdMemberConvention.cs index 5f28ef57cf2..43b4bb08d8a 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 && !string.IsNullOrEmpty(bsonElement.ElementName) && 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..aa7d752b24c 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,12 +22,75 @@ namespace MongoDB.Bson.Tests.Serialization.Conventions { public class ElementNameConventionsTests { - private class TestClass + + [Fact] + public void TestBsonElementAttributeOverridesNamedIdMemberConvention() { - public string FirstName { get; set; } - public int Age { get; set; } - public string _DumbName { get; set; } - public string lowerCase { get; set; } + var classMap = new BsonClassMap(); + classMap.AutoMap(); + + classMap.Freeze(); + Assert.Null(classMap.IdMemberMap); + Assert.Equal("notId", classMap.GetMemberMap(x => x.Id).ElementName); + } + + [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); + } + + [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); + } + + [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); + } + + [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); + } + + [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); } [Fact] @@ -58,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; } + } } }