c# - 如何在mongodb中多线程时正确更新嵌套数组?
我正在学习如何编程,在我的旅程中,我决定学习如何使用数据库,选择的数据库是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。不知道是否推荐这样做。