From 5f080464665c18404cccbe05d3611f3e6e7768ea Mon Sep 17 00:00:00 2001 From: rstam Date: Mon, 24 Nov 2025 13:26:45 -0800 Subject: [PATCH 1/3] CSHARP-4040: Use AppContext switch to disable CSHARP-4040 validation --- .../Serialization/BsonClassMap.cs | 5 + .../Jira/CSharp4040SwitchTests.cs | 183 ++++++++++++++++++ 2 files changed, 188 insertions(+) create mode 100644 tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Jira/CSharp4040SwitchTests.cs diff --git a/src/MongoDB.Bson/Serialization/BsonClassMap.cs b/src/MongoDB.Bson/Serialization/BsonClassMap.cs index e81624f0026..b447bf6671c 100644 --- a/src/MongoDB.Bson/Serialization/BsonClassMap.cs +++ b/src/MongoDB.Bson/Serialization/BsonClassMap.cs @@ -1337,6 +1337,11 @@ internal IDiscriminatorConvention GetDiscriminatorConvention() void EnsureNoMemberMapConflicts(string elementName) { + if (AppContext.TryGetSwitch("DisableCSharp4040Validation", out bool disableCSharp4040Validation) && disableCSharp4040Validation) + { + return; + } + var conflictingMemberMap = _allMemberMaps.FirstOrDefault(memberMap => memberMap.ElementName == elementName); if (conflictingMemberMap != null) diff --git a/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Jira/CSharp4040SwitchTests.cs b/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Jira/CSharp4040SwitchTests.cs new file mode 100644 index 00000000000..c1f243b5ac7 --- /dev/null +++ b/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Jira/CSharp4040SwitchTests.cs @@ -0,0 +1,183 @@ +/* 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; +using System.Collections.Generic; +using System.Linq; +using MongoDB.Driver.TestHelpers; +using FluentAssertions; +using MongoDB.Bson.Serialization; +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 CSharp4040SwitchTests : LinqIntegrationTest, IDisposable +{ + static CSharp4040SwitchTests() + { + AppContext.SetSwitch("DisableCSharp4040Validation", true); + + var discriminatorConvention = new HierarchicalDiscriminatorConvention("TypeNames"); + + BsonClassMap.RegisterClassMap(cm => + { + cm.AutoMap(); + cm.SetIsRootClass(true); + cm.SetDiscriminatorIsRequired(true); + cm.MapMember(x => x.TypeNames).SetShouldSerializeMethod(_ => false); + cm.SetDiscriminatorConvention(discriminatorConvention); + }); + } + + public void Dispose() + { + AppContext.SetSwitch("DisableCSharp4040Validation", false); + } + + public CSharp4040SwitchTests(ClassFixture fixture) + : base(fixture) + { + } + + [Fact] + public void Documents_should_serializer_as_expected() + { + var collection = Fixture.Collection; + + var seralizedDocuments = collection.AsQueryable().As(BsonDocumentSerializer.Instance).ToList(); + + seralizedDocuments.Count.Should().Be(2); + seralizedDocuments[0].Should().Be("{ _id : 1, TypeNames : ['C', 'D'] }"); + seralizedDocuments[1].Should().Be("{ _id : 2, TypeNames : ['C', 'D', 'E'] }"); + } + + [Fact] + public void OfType_C_should_work() + { + var collection = Fixture.Collection; + + var queryable = collection.AsQueryable() + .OfType(); + + var stages = Translate(collection, queryable); + AssertStages(stages, []); + + var results = queryable.ToList(); + results.Select(x => x.Id).Should().Equal(1, 2); + } + + [Fact] + public void OfType_D_should_work() + { + var collection = Fixture.Collection; + + var queryable = collection.AsQueryable() + .OfType(); + + var stages = Translate(collection, queryable); + AssertStages(stages, "{ $match : { TypeNames : 'D' } }"); + + var results = queryable.ToList(); + results.Select(x => x.Id).Should().Equal(1, 2); + } + + [Fact] + public void OfType_E_should_work() + { + var collection = Fixture.Collection; + + var queryable = collection.AsQueryable() + .OfType(); + + var stages = Translate(collection, queryable); + AssertStages(stages, "{ $match : { TypeNames : 'E' } }"); + + var results = queryable.ToList(); + results.Select(x => x.Id).Should().Equal(2); + } + + [Fact] + public void Where_TypeNames_Contains_C_should_work() + { + var collection = Fixture.Collection; + + var queryable = collection.AsQueryable() + .Where(x => x.TypeNames.Contains("C")); + + var stages = Translate(collection, queryable); + AssertStages(stages, "{ $match : { TypeNames : 'C' } }"); + + var results = queryable.ToList(); + results.Select(x => x.Id).Should().Equal(1, 2); + } + + [Fact] + public void Where_TypeNames_Contains_D_should_work() + { + var collection = Fixture.Collection; + + var queryable = collection.AsQueryable() + .Where(x => x.TypeNames.Contains("D")); + + var stages = Translate(collection, queryable); + AssertStages(stages, "{ $match : { TypeNames : 'D' } }"); + + var results = queryable.ToList(); + results.Select(x => x.Id).Should().Equal(1, 2); + } + + [Fact] + public void Where_TypeNames_Contains_E_should_work() + { + var collection = Fixture.Collection; + + var queryable = collection.AsQueryable() + .Where(x => x.TypeNames.Contains("E")); + + var stages = Translate(collection, queryable); + AssertStages(stages, "{ $match : { TypeNames : 'E' } }"); + + var results = queryable.ToList(); + results.Select(x => x.Id).Should().Equal(2); + } + + public abstract class C + { + public int Id { get; set; } + public virtual IReadOnlyList TypeNames => ["C"]; + } + + public class D : C + { + public override IReadOnlyList TypeNames => ["C", "D"]; + } + + public class E : D + { + public override IReadOnlyList TypeNames => ["C", "D", "E"]; + } + + public sealed class ClassFixture : MongoCollectionFixture + { + protected override IEnumerable InitialData => + [ + new D { Id = 1 }, + new E { Id = 2 } + ]; + } +} From 726afb87e3e195ef3df219edf629c0769e5ae459 Mon Sep 17 00:00:00 2001 From: rstam Date: Mon, 24 Nov 2025 14:11:43 -0800 Subject: [PATCH 2/3] CSHARP-4040: Rename switch --- src/MongoDB.Bson/Serialization/BsonClassMap.cs | 2 +- .../Linq/Linq3Implementation/Jira/CSharp4040SwitchTests.cs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/MongoDB.Bson/Serialization/BsonClassMap.cs b/src/MongoDB.Bson/Serialization/BsonClassMap.cs index b447bf6671c..3e257d2259f 100644 --- a/src/MongoDB.Bson/Serialization/BsonClassMap.cs +++ b/src/MongoDB.Bson/Serialization/BsonClassMap.cs @@ -1337,7 +1337,7 @@ internal IDiscriminatorConvention GetDiscriminatorConvention() void EnsureNoMemberMapConflicts(string elementName) { - if (AppContext.TryGetSwitch("DisableCSharp4040Validation", out bool disableCSharp4040Validation) && disableCSharp4040Validation) + if (AppContext.TryGetSwitch("Switch.MongoDB.Driver.DisableDiscriminatorFieldConflictCheck", out bool disableConflictCheck) && disableConflictCheck) { return; } diff --git a/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Jira/CSharp4040SwitchTests.cs b/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Jira/CSharp4040SwitchTests.cs index c1f243b5ac7..079f26d53e5 100644 --- a/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Jira/CSharp4040SwitchTests.cs +++ b/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Jira/CSharp4040SwitchTests.cs @@ -30,7 +30,7 @@ public class CSharp4040SwitchTests : LinqIntegrationTest Date: Mon, 24 Nov 2025 18:04:48 -0800 Subject: [PATCH 3/3] CSHARP-4040: Set and clear switch in ClassFixture --- .../Jira/CSharp4040SwitchTests.cs | 47 ++++++++++--------- 1 file changed, 24 insertions(+), 23 deletions(-) diff --git a/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Jira/CSharp4040SwitchTests.cs b/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Jira/CSharp4040SwitchTests.cs index 079f26d53e5..7a8a16cef30 100644 --- a/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Jira/CSharp4040SwitchTests.cs +++ b/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Jira/CSharp4040SwitchTests.cs @@ -26,36 +26,15 @@ namespace MongoDB.Driver.Tests.Linq.Linq3Implementation.Jira; -public class CSharp4040SwitchTests : LinqIntegrationTest, IDisposable +public class CSharp4040SwitchTests : LinqIntegrationTest { - static CSharp4040SwitchTests() - { - AppContext.SetSwitch("Switch.MongoDB.Driver.DisableDiscriminatorFieldConflictCheck", true); - - var discriminatorConvention = new HierarchicalDiscriminatorConvention("TypeNames"); - - BsonClassMap.RegisterClassMap(cm => - { - cm.AutoMap(); - cm.SetIsRootClass(true); - cm.SetDiscriminatorIsRequired(true); - cm.MapMember(x => x.TypeNames).SetShouldSerializeMethod(_ => false); - cm.SetDiscriminatorConvention(discriminatorConvention); - }); - } - - public void Dispose() - { - AppContext.SetSwitch("Switch.MongoDB.Driver.DisableDiscriminatorFieldConflictCheck", false); - } - public CSharp4040SwitchTests(ClassFixture fixture) : base(fixture) { } [Fact] - public void Documents_should_serializer_as_expected() + public void Documents_should_serialize_as_expected() { var collection = Fixture.Collection; @@ -174,10 +153,32 @@ public class E : D public sealed class ClassFixture : MongoCollectionFixture { + public ClassFixture() + { + AppContext.SetSwitch("Switch.MongoDB.Driver.DisableDiscriminatorFieldConflictCheck", true); + + var discriminatorConvention = new HierarchicalDiscriminatorConvention("TypeNames"); + BsonSerializer.RegisterDiscriminatorConvention(typeof(C), discriminatorConvention); + + BsonClassMap.RegisterClassMap(cm => + { + cm.AutoMap(); + cm.SetIsRootClass(true); + cm.SetDiscriminatorIsRequired(true); + cm.MapMember(x => x.TypeNames).SetShouldSerializeMethod(_ => false); + }); + } + protected override IEnumerable InitialData => [ new D { Id = 1 }, new E { Id = 2 } ]; + + public override void Dispose() + { + base.Dispose(); + AppContext.SetSwitch("Switch.MongoDB.Driver.DisableDiscriminatorFieldConflictCheck", false); + } } }