Как вставить значение в список, вложенный в словарь (массив документов) в MongoDb с помощью драйвера C #? - PullRequest
0 голосов
/ 14 декабря 2018

У меня есть следующие классы:

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

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

class 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" уже существует.

Это может быть связано: Невозможно определить информацию сериализации для ошибки выражения

1 Ответ

0 голосов
/ 14 декабря 2018

Я мог бы наконец-то заработать.Вот мой код (пояснение ниже):

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!"}));

Дайте мне знать, если это сработало для вас!

...