MongoDB:只更新特定字段

14
我正在尝试使用C#驱动程序更新MongoDB中一个已定义类型的集合中的一行。当处理特定类型为>的集合数据时,我倾向于避免从集合中检索敏感数据(如盐、密码哈希等)。
现在我正在尝试更新一个User实例。然而,我实际上从未在第一次检索中检索到敏感数据,所以在应用修改并将新数据提交给集合之前,我猜这些数据在检索的模型实例中将是default(byte[])(就我所知)。
也许我在MongoDB C#驱动程序中忽略了某些微不足道的东西,可以使用MongoCollection.Save(T item)更新记录而不更新特定属性,比如User.PasswordHash或User.PasswordSalt?我应该先检索完整记录,然后在那里更新“安全”属性,并将其写回吗?还是有一种花哨的选项可以排除更新中的某些字段?
提前感谢。
4个回答

21

Save(someValue)用于当您希望生成的记录成为或变为您传入的完整对象(someValue)的情况。

您可以使用

var query = Query.EQ("_id","123");
var sortBy = SortBy.Null;
var update = Update.Inc("LoginCount",1).Set("LastLogin",DateTime.UtcNow); // some update, you can chain a series of update commands here

MongoCollection<User>.FindAndModify(query,sortby,update); 

使用FindAndModify方法,您可以指定要更改现有记录中的哪些字段,而将其他字段保持不变。

您可以在这里看到一个示例。

您所需要的现有记录仅为其_id,不必加载或映射回您的POCO对象中的2个秘密字段。


2
谢谢指出。但我的根本问题仍然存在:我希望使用“MongoCollection<T>”类型的所有属性更新“T”实例的值,除了一组特定已知的字段。类似于“Update<T>.EverythingFrom(someObject).Except(x => x.ExceptThis).Except(x => x.ExceptThat)”这样的东西。 - Manny
2
这是一个自定义的工作,你需要编写 (EverythingFrom..)。在客户端上不太难做,通过遍历BsonMemberMap并获取所有映射的成员,然后仅从每个成员构建Update.Set(),如果你知道该值已更改。但不能保证一致性,因为DB可能已经在读取记录到内存后发生了更改。 - Nuk Nuk San

6

可以在Where语句中添加更多的条件。像这样:

var db = ReferenceTreeDb.Database;
var packageCol = db.GetCollection<Package>("dotnetpackage");
var filter = Builders<Package>.Filter.Where(_ => _.packageName == packageItem.PackageName.ToLower() && _.isLatestVersion);
var update = Builders<Package>.Update.Set(_ => _.isLatestVersion, false);
var options = new FindOneAndUpdateOptions<Package>();
packageCol.FindOneAndUpdate(filter, update, options);

5

我曾经遇到过同样的问题,由于我想为所有类型创建一个通用方法,又不想使用反射来创建自己的实现,因此我最终采用了以下通用解决方案(为了简化,将所有内容都显示在一个方法中):

Task<bool> Update(string Id, T item)
{
    var serializerSettings = new JsonSerializerSettings()
            {
                NullValueHandling = NullValueHandling.Ignore,
                DefaultValueHandling = DefaultValueHandling.Ignore
            };
    var bson = new BsonDocument() { { "$set", BsonDocument.Parse(JsonConvert.SerializeObject(item, serializerSettings)) } };
    await database.GetCollection<T>(collectionName).UpdateOneAsync(Builders<T>.Filter.Eq("Id", Id), bson);
}

注意:

  • 确保所有不需要更新的字段都设置为默认值。

  • 如果您需要将字段设置为默认值,您需要使用DefaultValueHandling.Include或编写自定义方法进行更新。

  • 当性能很重要时,请使用Builders<T>.Update编写自定义更新方法。

    P.S.:明显应该由MongoDB .Net驱动程序实现,但我在文档中找不到它的任何位置,也许我只是看错了。


帮了我很多忙,但最好使用item.ToBsonDocument()而不是BsonDocument.Parse(JsonConvert.SerializeObject(item, serializerSettings)) - Vladislav Vazhenin
我记得我无法使ToBsonDocument排除默认值,所以我使用了JsonConvert。也许在更新的版本中它会更好用,或者我忽略了一些设置。 - Vitaly
我在模型属性上使用了装饰器 [BsonIgnoreIfDefault],所以很可能这就是为什么它对我起作用的原因。 - Vladislav Vazhenin

2

在mongodb中更新值有很多种方法。

以下是我选择的最简单的一种方法,用于更新mongodb集合中的字段值。

public string UpdateData()
        {               
            string data = string.Empty;
            string param= "{$set: { name:'Developerrr New' } }";
            string filter= "{ 'name' : 'Developerrr '}";
            try
            {  
               //******get connections values from web.config file*****
                var connectionString = ConfigurationManager.AppSettings["connectionString"];
                var databseName = ConfigurationManager.AppSettings["database"];
                var tableName = ConfigurationManager.AppSettings["table"];

                //******Connect to mongodb**********
                var client = new MongoClient(connectionString);
                var dataBases = client.GetDatabase(databseName);
                var dataCollection = dataBases.GetCollection<BsonDocument>(tableName);       

                //****** convert filter and updating value to BsonDocument*******
                BsonDocument filterDoc = BsonDocument.Parse(filter);
                BsonDocument document = BsonDocument.Parse(param);

                //********Update value using UpdateOne method*****
                dataCollection.UpdateOne(filterDoc, document);                   
                data = "Success";
            }
            catch (Exception err)
            {
                data = "Failed - " + err;
            }
            return data;    
        }

希望这能帮到你 :)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接