C# Driver updating BsonDocument

Hello i got an issue with MongoDB model builder like example i have UserModel for adding or updating existing document in mongo
here is one BsonDocument

изображение_2022-11-15_214653166

 public BsonDocument Premium = new BsonDocument()
    .Add("isPremium", false)
    .Add("PremiumEnds", DateTime.Now)
    .Add("Level", int.Parse("0"))
    .Add("RatioText", double.Parse("1.0"))
    .Add("RatioVoice", double.Parse("1.0"));

and if document like this exist in user document like in first screenshot here is an issue
if i add to this BsonDocument new Key/Value like

 public BsonDocument Premium = new BsonDocument()
  .Add("isPremium", false)
  .Add("PremiumEnds", DateTime.Now)
  .Add("Level", int.Parse("0"))
  .Add("RatioText", double.Parse("1.0"))
  .Add("RatioVoice", double.Parse("1.0"))
  .Add("Key", "Value");

after getting user info and updating some values this will not change the Existing BsonDocument
but if i add to UserModel like public int Prestige { get; set; } outside BsonDocument after updating user new row will appears in document

Getting User Function

public static async Task<UserMongo> GetUserAsync(ulong id)
{
  var user = Users.Find(a => a.Id == id).FirstOrDefault();
  if (user != null) return user;
  user = await SignUp(id);
  return user;
}

Updating User Function

public static async Task<bool> UpdateAsync(UserMongo user)
{
  await Users.ReplaceOneAsync(a => a.Id == user.Id, user);
  return await Task.FromResult(true);
}

What is the problem maybe someone has issue like this?

also tryed building model like this

[BsonIgnoreIfDefault] 
public BsonDocument Premiums = new BsonDocument {
  { "isPremium", false },
  { "PremiumEnds", DateTime.Now },
  { "Level", int.Parse("0") },
  { "RatioText", double.Parse("1.0") },
  { "RatioVoice", double.Parse("1.0") }
};

After inserting for first time and adding new Key/Value to this BsonDocument after updating this document not apply new Key/Value inside BsonDocument

You might be missing the complimentary field _id in your BsonDocument, or it is not in the required format. if this is the case, your replace function will return false when you try to compare it.

var id = new ObjectId("123....abc");
var filter = new BsonDocument { { "_id", id } };

when you define a class model, you define Id property mapped to _id so it is easy to miss it while using BsonDocument.

It’s not a full model, it’s only peace of one BsonDocument, I have 2 unique keys it’s BsodId _id and userid which is long number that’s generated by discord and model is big like 7 nested BsonDocumets its only one of them.

If it’s needed I can show full model file

I am not a pro for C#, so I can’t promise a resolution. But there are some considerations to help to identify the problem. unless it is a bug in the driver, we may find a solution by trying a few things.

  • peek into your data and select a few of them as samples. for example, the ones you are having trouble with.
  • then make a new project, only to operate on these data. forget the full app logic, you need only data access parts.
  • MongoDB document model is flexible. If your code works with a few fields, it is highly possible to work with a full schema. Strip your code so you work on only a few fields including _id of a document and Id of the model class. try not to use any nested objects.
  • create new test collections on the database (or on a new M0 cluster) for this test.

this may help you identify the problem by yourself. if not, share this stripped-down version so we here may peek into it.

The problem is it’s working pretty well but after adding new key value to BsonDocument it’s not adding new key value to document but if I wanna modify some values inside created keys this working good and also adding new bson documents is also updating existed model

Actually, I am still failing to see what the actual problem you are facing. But it comes to my mind that you might be using your filtering wrong for BsonDocument.

I have a running code to use Builders for both “update” and “replace” operation. Can you please check if it helps (my document is simple {id:number,class:tring} plus automatic _id):

Click here for example code to update/replace
{
    MongoClient dbClient = new MongoClient(URI);

    var database = dbClient.GetDatabase("testme");
    var collection = database.GetCollection<BsonDocument>("grades");

    var filterbuilder = Builders<BsonDocument>.Filter;
    var updatebuilder = Builders<BsonDocument>.Update;

    // get a document
    var firstuserfilter = filterbuilder.Eq(doc => doc["id"], 2);
    var firstuser = collection.Find<BsonDocument>(firstuserfilter).First();
    Console.WriteLine(firstuser.ToJson());

    // updating some fields
    var updatefilter = filterbuilder.Eq("_id", firstuser["_id"]);
    var update = updatebuilder.Set("class", "3D");
    collection.UpdateOne(updatefilter, update);
    Console.WriteLine(collection.Find<BsonDocument>(firstuserfilter).First().ToJson());

    //replacing whole document
    firstuser["class"] = "3E";
    var replacefilter = filterbuilder.Eq("_id", firstuser["_id"]);
    collection.ReplaceOne(replacefilter, firstuser);
    Console.WriteLine(collection.Find<BsonDocument>(firstuserfilter).First().ToJson());
}

Here is my Full model for User

using System;
using System.Collections.Generic;
using MongoDB.Bson;

namespace InteractionFramework.Models
{
    public class UserMongo
    {
            public ulong Id { get; set; }
            public ulong PheonixCoin { get; set; }
            public ulong CSCoin { get; set; }
            public int Prestige { get; set; }
            public ulong XP { get; set; }
            public int Level
            {
                get
                {
                    return (int)Math.Sqrt(XP / 115);
                }
            }
            public string QiwiBillID { get; set; }
            public ulong VoiceActive { get; set; }
            public ulong Messages { get; set; }
            
            public BsonDocument Penalty  { get; set; }  = new BsonDocument()
                .Add("violations", new BsonArray())
                .Add("warns", new BsonArray())
                .Add("mute", new BsonArray())
                .Add("ban", new BsonArray());
            
            public BsonDocument Admin { get; set; } = new BsonDocument()
                .Add("isAdmin", false)
                .Add("stats", new BsonDocument()
                    .Add("violations", new BsonArray())
                    .Add("warns", new BsonArray())
                    .Add("mute", new BsonArray())
                    .Add("ban", new BsonArray())
                    .Add("ticket", new BsonArray()))
                .Add("admin_warns", new BsonArray())
                .Add("admin_at", DateTime.Now)
                .Add("start_at", long.Parse("0"))
                .Add("online_today", long.Parse("0"))
                .Add("online_week", long.Parse("0"))
                .Add("online_month", long.Parse("0"))
                .Add("online_total", long.Parse("0"));
            
            public BsonDocument Premium = new BsonDocument()
                .Add("isPremium", false)
                .Add("PremiumEnds", DateTime.Now)
                .Add("Level", 0)
                .Add("RatioText", double.Parse("1"))
                .Add("RatioVoice", double.Parse("1"));

            public BsonDocument Cases { get; set; } = new BsonDocument()
                .Add("Bronze", int.Parse("0"))
                .Add("Silver", int.Parse("0"))
                .Add("Gold", int.Parse("0"))
                .Add("Platinum", int.Parse("0"))
                .Add("Emerald", int.Parse("0"))
                .Add("Donate", int.Parse("0"));
            
            public BsonDocument lfg  { get; set; }  =  new BsonDocument()
                .Add("SteamID32", uint.Parse("0"))
                .Add("FaceitUrl", string.Empty)
                .Add("MMRank", int.Parse("0"))
                .Add("WGRank", int.Parse("0"))
                .Add("DZRank", int.Parse("0"))
                .Add("FCRank", int.Parse("0"))
                .Add("PEmoji", int.Parse("0"));
            
            public BsonDocument personalRole  { get; set; }  = new BsonDocument()
                .Add("DonateRoleID", long.Parse("0"))
                .Add("RoleEnds", DateTime.Now)
                .Add("AutoRenew", int.Parse("0"));
            
            public BsonDocument profileSettings  { get; set; }  = new BsonDocument()
                .Add("HideBalance", int.Parse("0"))
                .Add("HidePheonix", int.Parse("0"))
                .Add("AgentId", int.Parse("1"))
                .Add("BackgroundId", int.Parse("1"))
                .Add("CardId", int.Parse("1"))
                .Add("Icon1", int.Parse("0"))
                .Add("Icon2", int.Parse("0"))
                .Add("Icon3", int.Parse("0"))
                .Add("Icon4", int.Parse("0"));
            
            public List<InventoryEntry> Inventory  { get; set; }  = new List<InventoryEntry>();
            
            public class InventoryEntry
            {
                public int Id;
                public int Category;
                public DateTime EndTime;
                
                public InventoryEntry(int id, int category, DateTime endTime)
                {
                    Id = id;
                    Category = category;
                    EndTime = endTime;
                }
            }
            public class Punishment
            {
                public ulong AdminID;
                public ulong Date;
                public ulong Unmute;
                public string Reason;

                public Punishment(ulong adminId, ulong date, ulong unmute, string reason)
                {
                    AdminID = adminId;
                    Date = date;
                    Unmute = unmute;
                    Reason = reason;
                }
            }

    }
}

I have used your code and created following working code where I create 2 players with all default values except _id field.

It works fine and changes the users’ data by two methods: partial update and full replacement.

Please check it out and tell me if this reflects the workings of your app. I suspect either the “_id” field is the culprit or your update function has a flaw.

{
    MongoClient dbClient = new MongoClient(URI);

    var database = dbClient.GetDatabase("testme");
    var collection = database.GetCollection<UserMongo>("player");

    var filterbuilder = Builders<UserMongo>.Filter;
    var updatebuilder = Builders<UserMongo>.Update;

    // create 2 users , comment after first run
    var firstuser=new UserMongo();
    firstuser.Id=1;
    collection.InsertOne(firstuser);
    var seconduser=new UserMongo();
    seconduser.Id=2;
    collection.InsertOne(seconduser);
    // read users
    Console.WriteLine(collection.Find<UserMongo>(_=>true).FirstOrDefault().ToJson());

    // read first user back
    var firstuserfilter = filterbuilder.Eq<ulong>(doc => doc.Id, 1);
    var firstuserU = collection.Find<UserMongo>(firstuserfilter).FirstOrDefault();
    Console.WriteLine(firstuserU.ToJson());
    // read second user back
    var seconduserfilter = filterbuilder.Eq<ulong>(doc => doc.Id, 2);
    var seconduserR = collection.Find<UserMongo>(seconduserfilter).FirstOrDefault();
    Console.WriteLine(seconduserR.ToJson());

    // change first user by updating some fields "on the database"
    // var updatefilter = filterbuilder.Eq<ulong>("_id", 1);
    var updatefilter = filterbuilder.Eq<ulong>(doc=>doc.Id, 1);
    var update = updatebuilder.Set("XP", 100);
    collection.UpdateOne(updatefilter, update);
    Console.WriteLine(collection.Find<UserMongo>(firstuserfilter).FirstOrDefault().ToJson());

    // change document "in app" and change it on database replacing the whole document
    seconduserR.QiwiBillID = "42-7";
    // var replacefilter = filterbuilder.Eq("_id", seconduserR.Id);
    var replacefilter = filterbuilder.Eq(doc=>doc.Id, seconduserR.Id);
    collection.ReplaceOne(replacefilter, seconduserR);
    Console.WriteLine(collection.Find<UserMongo>(seconduserfilter).FirstOrDefault().ToJson());
}