如何通过一个调用使用mongo db C#驱动程序添加嵌套元素或更新属性

时间:2019-08-01 19:28:50

标签: c# mongodb

我试图进行一个数据库调用,以将元素添加到嵌套数组中,或者如果该元素已经存在,则增加一个计数器。在这种情况下,我们尝试查看是否存在匹配的子文档,如果找不到匹配的子文档,我们希望将该子文档添加到“子文档”列表中,如果找到一个子文档,我们希望增加在该列表中找到的子文档的计数属性。

我们之所以要寻找一个电话来这样做,是因为我们将有多种服务可能试图同时做同一件事。

    public class Document
    {
        [BsonId]
        [BsonIgnoreIfDefault]
        [BsonRepresentation(BsonType.ObjectId)]
        public string DocumentID { get; set; }

        public int Date { get; set; }
        public List<SubDocument> SubDocs { get; set; }
    }

    public class SubDocument
    {
        public string Count { get; set; }
        public string Name { get; set; }
    }

任何帮助将不胜感激。我们正在使用.NET驱动程序2.8.1

我可以使用查找来执行此操作,如果找到,则会更新计数器。但是到目前为止的测试显示,如果有多个服务这样做的话,计数将会被遗漏。

2 个答案:

答案 0 :(得分:2)

这可以通过单个bulkWrite命令来完成,该命令结合了2条这样的更新命令:

db.Document.bulkWrite([
    { updateOne: {
            filter: {
                "_id": ObjectId("5d455b8f2d686e2980829d1b"),
                "SubDocs": {
                    "$not": {
                        "$elemMatch": {
                            "Name": "iron man" } } } },
            update: {
                "$push": {
                    "SubDocs": {
                        "Count": NumberInt("0"),
                        "Name": "iron man" } } } }
    },
    { updateOne: {
            filter: {
                "_id": ObjectId("5d455b8f2d686e2980829d1b"),
                "SubDocs": {
                    "$elemMatch": {
                        "Name": "iron man"
                    } } },
            update: {
                "$inc": {
                    "SubDocs.$.Count": NumberInt("1")
                } } } }])

这是一个多线程测试程序,在我的开发环境中似乎没有任何并发​​问题。

using MongoDB.Entities; // PM> Install-Package MongoDB.Entities
using System.Linq;
using System.Threading.Tasks;

namespace StackOverflow
{
    public class Document : Entity
    {
        public SubDocument[] SubDocs { get; set; } = new SubDocument[] { };
    }

    public class SubDocument
    {
        public int Count { get; set; }
        public string Name { get; set; }
    }

    public class Program
    {
        private static void Main(string[] args)
        {
            new DB("test");

            var doc = new Document();
            doc.Save();

            var subDoc = new SubDocument { Name = "iron man" };

            Parallel.ForEach(Enumerable.Range(1, 20), _ =>
            {
                var bulk = DB.Update<Document>();

                bulk.Match(d =>
                           d.ID == doc.ID &&
                          !d.SubDocs.Any(s => s.Name == subDoc.Name))
                    .Modify(b => b.Push(d => d.SubDocs, subDoc))
                    .AddToQueue();

                bulk.Match(d =>
                          d.ID == doc.ID &&
                          d.SubDocs.Any(s => s.Name == subDoc.Name))
                    .Modify(b =>
                           b.Inc(d => d.SubDocs[-1].Count, 1))
                    .AddToQueue();

                bulk.Execute();
            });
        }
    }
}

答案 1 :(得分:0)

我相信Mongo 4 has support for atomic transactions,因此探索之路就是尝试并使用它们。

如果要在C#端执行此操作,则在执行检查文档是否存在/更新计数器的逻辑时,确保一次仅更新一项服务的一种方法是包装关键部分中的逻辑(即锁定)。

请查看https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/lock-statement,以获取有关操作方法的示例。