如何禁用 MongoDb 中的鉴别器字段(C# 驱动程序)

Avn*_*evy 4 c# mongodb

有没有办法完全禁用(对于所有类)将鉴别器(“_t”)字段添加到 bson 文档中?我指的是:mongo-csharp-driver/多态性

Kev*_*ith 6

假设我们有一个SquareRectangle继承自Shape

public abstract class Shape
{
    public ObjectId Id { get; set; }
}

public sealed class Square : Shape
{
    public int Size { get; set; }
}

public sealed class Rectangle : Shape
{
    public int Width { get; set; }
    public int Height { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

就像你说的,如果我们运行以下代码。

var client = new MongoClient();

var db = client.GetDatabase("test");

var shapes = db.GetCollection<Shape>("shapes");

await shapes.InsertManyAsync(new Shape[]
{
    new Square{Size = 10},
    new Rectangle{Height = 5, Width = 4}
});
Run Code Online (Sandbox Code Playgroud)

我们将把以下内容插入到 MongoDB 中

db.shapes.find()
{ "_id" : ObjectId("5f4e2affc23dde5a501bdf0b"), "_t" : "Square", "Size" : 10 }
{ "_id" : ObjectId("5f4e2affc23dde5a501bdf0c"), "_t" : "Rectangle", "Width" : 4, "Height" : 5 }
Run Code Online (Sandbox Code Playgroud)

最初,我认为我们能够DiscriminatorIsRequired在 上设置标志BsonClassMap并将其包装在约定中,但是,通过尝试此操作,由于 MongoDB C# 驱动程序中的以下逻辑位,它似乎失败了。

private bool ShouldSerializeDiscriminator(Type nominalType)
{
    return (nominalType != _classMap.ClassType || _classMap.DiscriminatorIsRequired || _classMap.HasRootClass) && !_classMap.IsAnonymous;
}
Run Code Online (Sandbox Code Playgroud)

https://github.com/mongodb/mongo-csharp-driver/blob/9e567e23615c8bb5c7ac1489427c2d15b2124522/src/MongoDB.Bson/Serialization/Serializers/BsonClassMapSerializer.cs#L722

因此,因为我们无法告诉序列化器我们不想包含鉴别器,所以我们必须给它一个约定,而不是不执行任何操作。如果我们创建一个IDiscriminatorConvention几乎不执行任何操作并为鉴别器返回 null 的值,那么驱动程序不会将其添加到文档中。

public class NullDiscriminatorConvention : IDiscriminatorConvention
{
    public static NullDiscriminatorConvention Instance { get; }
        = new NullDiscriminatorConvention();

    public Type GetActualType(IBsonReader bsonReader, Type nominalType)
        => nominalType;

    public BsonValue GetDiscriminator(Type nominalType, Type actualType)
        => null;

    public string ElementName { get; } = null;
}
Run Code Online (Sandbox Code Playgroud)

然后需要针对每种类型注册该鉴别器约定。

BsonSerializer.RegisterDiscriminatorConvention(typeof(Square), NullDiscriminatorConvention.Instance);
BsonSerializer.RegisterDiscriminatorConvention(typeof(Rectangle), NullDiscriminatorConvention.Instance);
Run Code Online (Sandbox Code Playgroud)

或者,如果我们希望它适用于所有类型,您可以做一些反思。

var shapeTypes = AppDomain.CurrentDomain.GetAssemblies()
    .SelectMany(domainAssembly => domainAssembly.GetTypes(),
        (domainAssembly, assemblyType) => new {domainAssembly, assemblyType})
    .Where(t => @t.assemblyType.IsSubclassOf(typeof(Shape)))
    .Select(t => @t.assemblyType).ToArray();

foreach (var shapeType in shapeTypes)
{
    BsonSerializer.RegisterDiscriminatorConvention(shapeType, NullDiscriminatorConvention.Instance);
}
Run Code Online (Sandbox Code Playgroud)

现在,如果我们重新运行我们的代码。

var shapeTypes = AppDomain.CurrentDomain.GetAssemblies()
    .SelectMany(domainAssembly => domainAssembly.GetTypes(),
        (domainAssembly, assemblyType) => new {domainAssembly, assemblyType})
    .Where(t => @t.assemblyType.IsSubclassOf(typeof(Shape)))
    .Select(t => @t.assemblyType).ToArray();

foreach (var shapeType in shapeTypes)
{
    BsonSerializer.RegisterDiscriminatorConvention(shapeType, NullDiscriminatorConvention.Instance);
}

var client = new MongoClient();

var db = client.GetDatabase("test");

var shapes = db.GetCollection<Shape>("shapes");

await shapes.InsertManyAsync(new Shape[]
{
    new Square{Size = 10},
    new Rectangle{Height = 5, Width = 4}
});
Run Code Online (Sandbox Code Playgroud)

我们将得到预期的输出。

db.shapes.find()
{ "_id" : ObjectId("5f4e2d63ed12d7c5d3638d36"), "Size" : 10 }
{ "_id" : ObjectId("5f4e2d63ed12d7c5d3638d37"), "Width" : 4, "Height" : 5 }
Run Code Online (Sandbox Code Playgroud)