元素 'id' 不匹配任何字段或 属性 嵌套 类 错误

Element 'id' does not match any field or property of error with nested classes

我有以下 mongodb 文档架构;

{ 
    "_id" : ObjectId("5c9d34ff781318afb9e8ab43"), 
    "name" : "Name", 
    "slug" : "slug",
    "services" : {
        "subservice" : {
            "id" : NumberInt(37030)
        }
    }
}

然后我将 classes 定义为;

public class MainModel
{
    public ObjectId Id { get; set; }

    [BsonElement("name")]
    public string Name { get; set; }

    [BsonElement("slug")]
    public string Slug { get; set; }

    [BsonElement("services")]
    public ServicesDef Services { get; set; }

    public class ServicesDef 
    {
        [BsonElement("subservice")]
        public SubServiceDef SubService{ get; set; }

        public class SubServiceDef 
        {
            [BsonElement("id")]
            public int Id { get; set; }
        }
    }
}

但是当我查询文档时不知何故;

var result = await Repository.FindAsync(x => x.Slug == slug);

services.subservice.id 未正确注册并获得

元素 'id' 与 class SubServiceDef 的任何字段或 属性 不匹配。

卡在这里并寻求建议。

我想我遇到了与 cannot deserialize with the "Id" attribute 相同的问题,但似乎还有解决方案。

长话短说:一切都与约定有关。 MongoDB .NET 驱动程序公开静态 class ConventionRegistry 允许您注册自己的约定(更多 here). Additionally there are two "built-in" conventions __defaults__ and __attributes__. Digging deeper (driver github)您会发现它注册了一个非常有趣的约定:

new NamedIdMemberConvention(new [] { "Id", "id", "_id" })

这意味着 id 成员将被视为常规 BSON _id 元素。

如何解决?

您可以摆脱默认约定

ConventionRegistry.Remove("__defaults__");

然而,您将自动放弃所有其他非常危险的驱动程序约定。或者,您可以创建一个始终为空的假 属性:

public class SubServiceDef
{
    [BsonElement("id")]
    public int Id { get; set; }

    [BsonId]
    public ObjectId FakeId { get; set; }
}

或者您可以只使用 BsonNoId 属性 which

Specifies that the class's IdMember should be null.

[BsonNoId]
public class SubServiceDef
{
    [BsonElement("id")]
    public int Id { get; set; }
}

因此约定会将您的 id 设置为 class 映射中的 IdMember,但随后在后处理过程中,此属性将强制 IdMember 为空,并且您的 class 将成功反序列化

我喜欢@mickl 的回答。我遇到的问题是无法更新模型和添加属性。我还需要原始的 Ids 而不是反序列化后的 nulls。

我尝试了 BsonClassMap 但我有太多的子模型要更新。

所以,我最终采用了你的想法,删除了默认约定。

public class MongoDbDefaultConventionPack : IConventionPack
{
    // private static fields
    private static readonly IConventionPack __defaultConventionPack = new MongoDbDefaultConventionPack();

    // private fields
    private readonly IEnumerable<IConvention> _conventions;

    // constructors
    /// <summary>
    /// Initializes a new instance of the <see cref="MongoDbDefaultConventionPack" /> class.
    /// </summary>
    private MongoDbDefaultConventionPack()
    {
        _conventions = new List<IConvention>
        {
            new ReadWriteMemberFinderConvention(),
            // new NamedIdMemberConvention(new [] { "Id", "id", "_id" }), changed to:
            new NamedIdMemberConvention(),
            new NamedExtraElementsMemberConvention(new [] { "ExtraElements" }),
            // new IgnoreExtraElementsConvention(false), changed to:
            new IgnoreExtraElementsConvention(true),
            new ImmutableTypeClassMapConvention(),
            new NamedParameterCreatorMapConvention(),
            new StringObjectIdIdGeneratorConvention(), // should be before LookupIdGeneratorConvention
            new LookupIdGeneratorConvention()
        };
    }

    // public static properties
    /// <summary>
    /// Gets the instance.
    /// </summary>
    public static IConventionPack Instance
    {
        get { return __defaultConventionPack; }
    }

    // public properties
    /// <summary>
    /// Gets the conventions.
    /// </summary>
    public IEnumerable<IConvention> Conventions
    {
        get { return _conventions; }
    }
}

然后替换配置:

ConventionRegistry.Remove("__defaults__");
ConventionRegistry.Register("__defaults__", MongoDbDefaultConventionPack.Instance, t => true);

作为默认约定,在我的案例中效果很好。没有更多的例外。可用的原始 ID