9阅网

您现在的位置是:首页 > 知识 > 正文

知识

c# - 如何在mongodb中多线程时正确更新嵌套数组?

admin2022-10-31知识16

我正在学习如何编程,在我的旅程中,我决定学习如何使用数据库,选择的数据库是mongodb。在我的数据库中,有一个叫WarClan的对象,WarClan里面有一个ClanUser的List,每个ClanUser都有一个GameItem的List,这个GameItem应该是玩家的库存。就像下面的代表。

public class WarClan
{
    [BsonId]
    public ObjectId Id { get; set; }
    public string ClanName { get; set; }
    public List<ClanUser> ClanUsers { get; set; }
}
public class ClanUser
{
    public int Cid { get; set; }
    public List<GameItem> Inventario {get;set;}
}

我有一个 "GameServer",它有多个通道实例在不同的线程中运行. 而玩家可以同时连接多个通道。游戏有一些功能,例如,物品赠送,物品掉落,库存命令等等。昨天我发现一个玩家在市场上卖了一个物品,但是物品并没有被拿走。现在我相信是我写代码的方式问题。以下是我写的 "更新库存 "的代码(更新整个玩家的数据,呵呵)。

public async Task UpdateUser(WarClan clan, ClanUser user) 
    {
        var collection = db.GetCollection<WarClan>("clans");
        var arrayFilter = Builders<WarClan>.Filter.Eq("ClanName", clan.ClanName)
                & Builders<WarClan>.Filter.Eq("ClanUsers.Cid", user.Cid);
        var arrayUpdate = Builders<WarClan>.Update.Set("ClanUsers.$", user);
        await collection.UpdateOneAsync(arrayFilter, arrayUpdate);
    }

我相信在出售物品的同时,他也做了一些需要调用更新用户的事情,我认为发生了类似的事情(我不确定,只是想了解)。

    //Thread1:
    var user = await GetUser(123456); //By CID
    user.Inventario.Add(Item3);
    await UpdateUser(clan, user);

    //Thread2:
    var user = await GetUser(123456); //By CID
    user.Inventario.Add(Item4);
    await UpdateUser(clan, user);

/*
* The expected result: 
* User.Inventario = Item3, Item4
* 
* Given result:
* User.Inventario = Item4 
* OR
* User.Inventario = Item3
*/

怎样才能避免这样的问题呢?我现在有点迷茫,不知道该怎么搜D。



【回答】:

我只是在更新文档的时候不替换整个文档就解决了。对于库存,因为按索引删除对我来说很麻烦,所以我为库存中的每一个物品都创建了一个ObjectId。所以,当玩家购买它时,我通过它的UniqueId来删除它。

public async Task RemoveInventoryItemAsync(ObjectId userId, ObjectId itemId)
    {
        var collection = db.GetCollection<ClanUser>("users");
        var arrayFilter = Builders<ClanUser>.Filter.Where(x => x._Id == userId);
        var arrayUpdate = Builders<ClanUser>.Update.PullFilter(x => x.Inventario, c => c.UniqueId == itemId);

        await collection.FindOneAndUpdateAsync(arrayFilter, arrayUpdate);
    }

我做了几百万次的测试,看起来很不错!另外,我还为用户创建了一个专用表,"WarClan.ClanUsers "现在只包含一个ObjectId的数组,也就是 "用户 "表中玩家的ObjectId。不知道是否推荐这样做。