c# - 如何使用 MongoDB 访问深层嵌套数组(ASP.NET Core 2.2)

标签 c# asp.net arrays mongodb ienumerable

我正在使用 MongoDB 设计一个库存管理系统。我有以下数据库结构:

inventory
└─storage_slots
└─storage_locations
...etc...

每次添加新的Slot时,都会在storage_locations集合中添加一棵表示slot在层次结构中位置的树来表示它的位置(根据location,room ,部分,货架)。到目前为止,我已经成功地添加了一个新项目,其中没有任何位置字段已被使用:(该插槽也被添加到 storage_slots 集合中)

{
"_id" : ObjectId("5c57169f0863d665c7f13d27"),
"CreatedUtc" : {
    "$date" : 1549211298017
},
"UpdatedUtc" : {
    "$date" : 1549211298017
},
"Description" : null,
"Address" : null,
"StorageRooms" : [
    {
        "_id" : ObjectId("5c57169f0863d665c7f13d28"),
        "CreatedUtc" : {
            "$date" : 1549211297719
        },
        "UpdatedUtc" : {
            "$date" : 1549211297719
        },
        "Description" : null,
        "StorageSections" : [
            {
                "_id" : ObjectId("5c57169f0863d665c7f13d29"),
                "CreatedUtc" : {
                    "$date" : 1549211297719
                },
                "UpdatedUtc" : {
                    "$date" : 1549211297719
                },
                "Description" : null,
                "StorageShelves" : [
                    {
                        "_id" : ObjectId("5c57169f0863d665c7f13d2a"),
                        "CreatedUtc" : {
                            "$date" : 1549211297719
                        },
                        "UpdatedUtc" : {
                            "$date" : 1549211297719
                        },
                        "Description" : null,
                        "StorageSlotIds" : [
                            ObjectId("5c57169f0863d665c7f13d26")
                        ]
                    }
                ]
            }
        ]
    }
]
}

要清楚,storage_locations 是上面的层次结构,而 storage_slots 只是插槽的集合。

但是,如果字段已经存在于层次结构中,则会运行以下代码:(我从 this 帖子中得到启发)

var filter = Builders<StorageLocation>.Filter.And(
            Builders<StorageLocation>.Filter.Where(location => location.Id == id),
            Builders<StorageLocation>.Filter.Eq("StorageRooms.Id", roomId),
            Builders<StorageLocation>.Filter.Eq("StorageRooms.$.StorageSections.Id", sectionId),
            Builders<StorageLocation>.Filter.Eq("StorageRooms.$.StorageSections.$.StorageShelves.Id", shelfId));
        var update =
            Builders<StorageLocation>.Update.Push("StorageRooms.$.StorageSections.$.StorageShelves.$.StorageSlotIds",
                storageSlotIds);
        return await UpdateAsync(filter, update, cancellationToken);

此外,如果只定义了其中一些,那么我会混合使用这两种方法,但我决定不在此处显示,因为它们基于相同的原则构建,不会对问题做出贡献。

问题

每当运行上面的代码时。我收到以下错误:

InvalidCastException: Unable to cast object of type 'MongoDB.Bson.ObjectId[]' to type 'MongoDB.Bson.ObjectId'.

MongoDB.Bson.Serialization.Serializers.SerializerBase<TValue>.MongoDB.Bson.Serialization.IBsonSerializer.Serialize(BsonSerializationContext context, BsonSerializationArgs args, object value)

//annoying scrollbar

错误发生在这一行:

return await UpdateAsync(filter, update, cancellationToken);

方法是:

public Task<UpdateResult> UpdateAsync(FilterDefinition<T> filter, UpdateDefinition<T> updateDefinition,
        string database, string collection, CancellationToken cancellationToken)
    {
        return _mongoContext.MongoClient.GetDatabase(database).GetCollection<T>(collection)
            .UpdateOneAsync(filter, updateDefinition.Set(o => o.UpdatedUtc, DateTime.UtcNow),
                cancellationToken: cancellationToken);
    }

额外内容

这里有一些与问题相关的类:

public class StorageLocation : Dbo
{
    public string Description { get; set; }
    public Address Address { get; set; }
    public IEnumerable<StorageRoom> StorageRooms { get; set; }
}
public class StorageRoom : Dbo
{
    public string Description { get; set; }
    public IEnumerable<StorageSection> StorageSections { get; set; }
}
public class StorageSection : Dbo
{
    public string Description { get; set; }
    public IEnumerable<StorageShelf> StorageShelves { get; set; }
}
public class StorageShelf : Dbo
{
    public string Description { get; set; }
    public IEnumerable<ObjectId> StorageSlotIds { get; set; }
}
public class StorageSlot : Dbo
{
    public string Description { get; set; }

    public ObjectId LocationId { get; set; }
    public ObjectId RoomId { get; set; }
    public ObjectId SectionId { get; set; }
    public ObjectId ShelfId { get; set; }

    ...etc...
}

最佳答案

您收到此错误是因为 $ positional operator只能使用一次,而在您的情况下有多层嵌套数组。来自文档:

The positional $ operator cannot be used for queries which traverse more than one array, such as queries that traverse arrays nested within other arrays, because the replacement for the $ placeholder is a single value

要解决这个问题,您可以使用 filtered positional MongoDB 3.6 中引入的运算符。它允许您在更新路径中指定多个占位符,然后您可以使用 arrayFilters 为这些占位符定义条件。

var filter = Builders<StorageLocation>.Filter.And(
   Builders<StorageLocation>.Filter.Where(location => location.Id == id),
   Builders<StorageLocation>.Filter.Eq("StorageRooms._id", roomId));

var arrayFilters = new List<ArrayFilterDefinition>();
ArrayFilterDefinition<BsonDocument> sectionFilter = new BsonDocument("section._id", new BsonDocument("$eq", sectionId));
ArrayFilterDefinition<BsonDocument> shelfFilter = new BsonDocument("shelf._id", new BsonDocument("$eq", shelfId));
arrayFilters.Add(sectionFilter);
arrayFilters.Add(shelfFilter);

var updateOptions = new UpdateOptions { ArrayFilters = arrayFilters };


var update =
    Builders<StorageLocation>.Update.Push("StorageRooms.$.StorageSections.$[section].StorageShelves.$[shelf].StorageSlotIds",
        storageSlotIds);

await Col.UpdateOneAsync(filter, update, updateOptions);

关于c# - 如何使用 MongoDB 访问深层嵌套数组(ASP.NET Core 2.2),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54505373/

相关文章:

c# - 从 shim 方法调用原始方法

c# - 未使用异步函数时出现异步错误?

c# - 如何使用 WCF 发送用户凭据以在远程计算机上使用?

asp.net - 将静态数据存储在数据库或文件系统中

php - 在 PHP 的 DLL(.net) 中运行函数 - 似乎没有任何效果

JavaScript 按索引对数组进行垂直排序

javascript - 循环日期并添加对象(如果 JSON 数据中不存在)

c# - 加载 Zumero 模块依赖项时遇到问题

asp.net - 为什么在调用返回 JSON 结果的基于外部 IIS 的 MVC Controller 时会收到 403 禁止消息

java - 将字符串子集转换为数组变量的最佳方法是什么?