繁体   English   中英

如何使用 C# MongoDb 驱动程序更新深度嵌套数组中的“类型安全”字段?

[英]How to update a field “type-safe” in a deeply nested array with C# MongoDb driver?

我正在尝试更新深度嵌套数组中的各个字段。

    public class Maker
    {
        public int Id { get; set; }
        public List<Vehicle> Vehicles { get; set; }
    }

    public class Vehicle
    {
        public int Id { get; set; }
        public int Price { get; set; }
        public List<Part> Parts { get; set; }
    }

    public class Part
    {
        public int Id { get; set; }
        public string Name { get; set; }
    }

更新 1 级数组中的字段就像一个魅力。 更新 2 级数组中的字段失败。

 public void UpdateNestedArrayFields()
    {
        var client = new MongoClient();
        var db = client.GetDatabase("test");
        var makers = db.GetCollection<Maker>("makers");

        // Create a maker and add a vehicle
        var newPart = new Part() { Id = 34, Name = "Wheel" };
        var newVehicle = new Vehicle() { Id = 17, Price = 1000, Parts = new List<Part>() { newPart } };
        var newMaker = new Maker() { Id = 5, Vehicles = new List<Vehicle>() { newVehicle } };

        // Update vehicle's price (WORKS)
        makers.FindOneAndUpdate(
            m => m.Id == newMaker.Id && m.Vehicles.Any(v => v.Id == newVehicle.Id),
            Builders<Maker>.Update.Set(m => m.Vehicles[-1].Price, 2000));

        // Update part's name (**FAILS**)
        makers.FindOneAndUpdate(
            m => m.Id == newMaker.Id && m.Vehicles.Single(v => v.Id == newVehicle.Id).Parts.Any(p=> p.Id == newPart.Id),
            Builders<Maker>.Update.Set(m => m.Vehicles[-1].Parts[-1].Name, "Tire"));
    }       

我知道不支持多个位置运算符,所以我的最后一行 (m.Vehicles[-1].Parts[-1].Name) 有点废话。

显然我不是唯一一个遇到这个问题的人,我找到了类似这样那样的不同解决方案。 但是,它们都不是类型安全的 生成的代码非常丑陋且容易出错。

所以我想知道:有没有办法重新设计我的代码,使其类型安全?

我正在使用 MongoDb 4.0.9 和 C# 驱动程序 2.11.5。

编辑(01-11-2020):对于任何感兴趣的人,我发现 Massimiliano Kraus 的这个非常好的扩展,它有点过时(2017 年),但仍在工作,结果证明是一个巨大的帮助。 谢谢你,马西米利亚诺! MongoDB.DeepUpdater.CSharp

为了像这样更新嵌套的 arrays,您需要使用 mongodb 的阵列过滤器功能。

所需的 mongo 查询是这样的:

db.Maker.update(
    {
        "_id": 5
    },
    {
        $set: { 'Vehicles.$[v].Parts.$[p].Name': 'Tyre' }
    },
    {
        arrayFilters: [
            { 'v._id': { $eq: 17 } },
            { 'p._id': { $eq: 34 } }
        ]
    }
)

c# 驱动程序无法以强类型方式生成上述内容。 查看这篇文章,了解运行上述高级查询的替代半类型解决方案。

还有一个类型化嵌套数组更新的其他可能且类型更多的版本。 它没有完全输入,但它更接近你的需要。

它也使用ArrayFilters但以更多类型的方式

此代码直接来自我正在处理的应用程序。

var filter = Builders<EventModel>.Filter.Eq(s => s.Id, eventId);

var settingsPath = @$"{nameof(EventModel.EventLayout)}.{nameof(EventLayoutSetting.Pages)}.$[page].{nameof(Page.Components)}.$[component].{nameof(Component.Settings)}";
var update = Builders<EventModel>.Update.
                     Set(settingsPath, settings);

var dbResult = await eventsCollection.UpdateOneAsync(filter, update, new UpdateOptions 
            {
                ArrayFilters = new List<ArrayFilterDefinition> 
                {
                    new BsonDocumentArrayFilterDefinition<BsonDocument> (new BsonDocument($"page.{nameof(Page.PageId)}",pageId)),
                    new BsonDocumentArrayFilterDefinition<BsonDocument> (new BsonDocument($"component.{nameof(Component.ComponentId)}",componentId))
                }
            });

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM