Using type discriminator results in strange database behaviour

Mongo version: 5.0.5 (Docker image and Atlas cluster show same behaviour)
C# Driver version: 2.14.1

Here is the description of the strange behaviour:
I
n mongosh (or Compass):
Running test.find() returns two documents, both with _t: "Unknown".
Running test.find({_t: "Unknown"}) returns 0 documents.
Running test.find({_t: "X"}) or test.find({_t: "Y"}) returns 1 document, but the value of _t: "Unknown" is still shown in the returned document.

How to get into this state:

Have an instance of Mongo running. Create a new .NET 6 console project using the NuGet MongoDB.Driver 2.14.1. Replace everything in Program.cs with the code below (editing the connection string if necessary). If you don’t understand this code, do not run in locally, that’s dangerous!

using MongoDB.Bson;
using MongoDB.Bson.Serialization;
using MongoDB.Bson.Serialization.Attributes;
using MongoDB.Bson.Serialization.Conventions;
using MongoDB.Driver;

const string conventionName = "test";
var conventionPack = new ConventionPack
{
    new CamelCaseElementNameConvention(),
    new EnumRepresentationConvention(BsonType.String)
};

ConventionRegistry.Remove(conventionName);
ConventionRegistry.Register(conventionName, conventionPack, t => true);

BsonClassMap.RegisterClassMap<Document>();
BsonClassMap.RegisterClassMap<XDocument>(x =>
{
    x.AutoMap();
    x.SetDiscriminator(Type.X.ToString());
});
BsonClassMap.RegisterClassMap<YDocument>(x =>
{
    x.AutoMap();
    x.SetDiscriminator(Type.Y.ToString());
});

var client = new MongoClient("mongodb://localhost:27017");
var db = client.GetDatabase("test");
var coll = db.GetCollection<Document>("docs");

var x = new XDocument();
var y = new YDocument();

await coll.InsertOneAsync(x);
await coll.InsertOneAsync(y);

public class Document
{
    [BsonElement("_t")]
    public Type Type { get; set; }
}
public class XDocument : Document
{
    public string X { get; set; } = "hello x!";
}
public class YDocument : Document
{
    public string Y { get; set; } = "hello y!";
}

public enum Type
{
    Unknown = 0,
    X,
    Y
}

Hi, @Jonny_Powell,

Welcome to the MongoDB Community Forums. I understand that you’re having some challenges mapping polymorphic types using the .NET/C# driver.

The _t field is used by the .NET/C# driver’s discriminator conventions when mapping polymorphic types to a collection. See Polymorphism in our driver documentation for more information on discriminator conventions.

Investigating your problem, I found a previously unknown bug, which I’ve filed as CSHARP-4040. When creating a mapping, we verify that your mapping does not contain any duplicate field names. However we do not include the discriminator conventions (which use _t by default) in this check. When you insert documents using this mapping into the collection, the BSON contains two fields named _t - one that you mapped and one from the discriminator convention.

Although BSON permits duplicate field names, their behaviour is undefined. The legacy mongo shell will only display the first duplicate. Compass and mongosh will only display the second duplicate. In C#, loading it as a BsonDocument will throw because of the duplicate field names, but deserializing it into a XDocument will read the second _t value into the field. How a particular driver handles duplicate field names in BSON is driver-dependent.

We recommend avoiding duplicate field names and in most cases our drivers prevent you from creating documents with duplicate field names. You’ve encountered an edge case where the .NET/C# driver does not include the discriminator field name (by default _t) in this check. We recommend avoiding the use of the field name _t in your mappings with the .NET/C# driver due to its use by our discriminator conventions.

Please let us know if you have any additional questions and thank you for helping us identify CSHARP-4040.

Sincerely,
James