首页 > 解决方案 > 如何使用 C# 驱动程序在 MongoDb 中嵌套在字典(文档数组)中的列表中插入值?

问题描述

我有以下课程:

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

    [BsonDictionaryOptions(Representation = DictionaryRepresentation.ArrayOfDocuments)]
    public IDictionary<string, List<Child>> Children { get; set; }
}

class Child { }

我知道想在 Dictionary 的一个条目中添加一个新的 Child:

using MongoDB.Driver;

namespace ConsoleApp1
{
    class Program
    {
        static void Main(string[] args)
        {
            var collection = new MongoClient("mongodb://localhost:27017")
                .GetDatabase("Test").GetCollection<Parent>("Parents");
            var parent = new Parent();
            collection.InsertOne(parent);

            collection.UpdateOne(p => p.Id == parent.Id, 
                Builders<Parent>.Update.Push(p => p.Children["string1"], new Child()));
        }
    }
}

我得到一个System.InvalidOperationException

无法确定 p => p.Children.get_Item("string1") 的序列化信息。

我设法让它像这样工作:

collection.UpdateOne(p => p.Id == parent.Id && p.Children.Any(a => a.Key == "string1"),
    Builders<Parent>.Update.Push($"{nameof(Parent.Children)}.$.v", new Child()));

但这非常难看,只有在条目"string1"已经存在时才有效。

这可能是相关的:无法确定表达式错误的序列化信息

标签: c#.netmongodbmongodb-.net-driver

解决方案


我终于可以得到一些工作。这是我的代码(下面的解释):

class Parent
{
    public ObjectId Id;

    //Remove the attribute. This field needs to be initialized
    public IDictionary<string, List<Child>> Children = new Dictionary<string, List<Child>>();
}

class Child 
{ 
    public string ChildName;  // This isn't needed
}

class Program
{
    static void Main(string[] args)
    {
        var collection = new MongoClient("mongodb://localhost:27017")
            .GetDatabase("Test").GetCollection<Parent>("Parents");

        var parent = new Parent();

        collection.InsertOne(parent);

        collection.UpdateOne(p => p.Id == parent.Id, 
            Builders<Parent>.Update
                .Push(p => p.Children["string1"], new Child {ChildName = "I am the new guy!"}));
    }
}

这里有两件事。首先,我删除了该属性。其次,该字段需要用空字典初始化,否则会报错。如果您设置的键尚不存在,将自动创建关联列表。我还向 Child 类添加了一个字段,但这不是必需的,它可以保持为空。

你甚至可以在同一个 $push 中创建多个键:

collection.UpdateOne(p => p.Id == parent.Id, 
    Builders<Parent>.Update
        .Push(p => p.Children["string1"], new Child {ChildName = "I am the new guy!"})
        .Push(p => p.Children["string2"], new Child {ChildName = "I am a child in string2!"}));

但是,这很重要,但是,如果您尝试按下相同的键,则只有最后一次按下才会生效。这不起作用:

collection.UpdateOne(p => p.Id == parent.Id, 
    Builders<Parent>.Update
        .Push(p => p.Children["string1"], new Child {ChildName = "I wont be pushed to string1"})
        .Push(p => p.Children["string1"], new Child {ChildName = "I will overwrite the previous push"}));

在这些情况下,您必须使用PushEach并传递一组项目。您可以PushEach结合Push

collection.UpdateOne(p => p.Id == parent.Id, 
    Builders<Parent>.Update
        .PushEach(p => p.Children["string1"], new [] {new Child {ChildName = "I am the first child!"}, new Child {ChildName = "I am the second child!"}})
        .Push(p => p.Children["string2"], new Child {ChildName = "I am a child in string2!"}));

让我知道这是否对您有用!


推荐阅读