[英]Patch REST API to Partial Update MongoDB in .NET
我有一個對象
{
"_id": "testobject",
"A": "First line",
"B": "Second line",
"C": "Third line"
}
我想向我的 API 發送 REST PATCH 請求以僅更新這些屬性之一
{
"_id": "testobject",
"C": "Forth line"
}
這被解析為一個類
public class SomeObject {
public string A { get; set; }
public string B { get; set; }
public string C { get; set; }
}
我現在需要更新 MongoDB 中的現有文檔,但只更新屬性C
。
我可以為這條記錄創建一個更新定義
UpdateDefinition<SomeObject> update = Builders<SomeObject>.Update.Set(x => x.C, <value of property C>)
或者我可以對每個屬性進行硬編碼以查看它是否為空
IList<UpdateDefinition<SomeObject>> updates = new List<UpdateDefinition<SomeObject>>();
if (!string.IsNullOrEmpty(C)) {
updates.Add(UpdateDefinition<SomeObject> update = Builders<SomeObject>.Update.Set(x => x.C, <value of property C>));
}
if (!string.IsNullOrEmpty(C)) {
updates.Add(UpdateDefinition<SomeObject> update = Builders<SomeObject>.Update.Set(x => x.C, <value of property C>));
}
但是,如果我有很多屬性和很多子屬性,這可能會很快變得非常大。 另一個問題是,如果我有意將屬性的值設置為空,那么它根本不會更新記錄,因為它會查找非空字段。
如何在 .NET 中動態地對 MongoDB 文檔進行部分更新,以便我有一個通用的 PATCH API 調用,該調用可以采用文檔具有的任何參數並且只更新指定的屬性?
我建議您避免依賴 1.x 遺留 API,因為它在 2.x 中也得到了完美的支持,如下面的示例代碼所示。
var client = new MongoClient();
var database = client.GetDatabase("test");
var collection = database.GetCollection<BsonDocument>("test");
var changesJson = "{ a : 1, b : 2 }";
var changesDocument = BsonDocument.Parse(changesJson);
var filter = Builders<BsonDocument>.Filter.Eq("_id", 1);
UpdateDefinition<BsonDocument> update = null;
foreach (var change in changesDocument)
{
if (update == null)
{
var builder = Builders<BsonDocument>.Update;
update = builder.Set(change.Name, change.Value);
}
else
{
update = update.Set(change.Name, change.Value);
}
}
//following 3 lines are for debugging purposes only
//var registry = BsonSerializer.SerializerRegistry;
//var serializer = registry.GetSerializer<BsonDocument>();
//var rendered = update.Render(serializer, registry).ToJson();
//you can also use the simpler form below if you're OK with bypassing the UpdateDefinitionBuilder (and trust the JSON string to be fully correct)
update = new BsonDocumentUpdateDefinition<BsonDocument>(new BsonDocument("$set", changesDocument));
var result = collection.UpdateOne(filter, update);
感謝 Robert Stam 提供的代碼示例。
你可以使用
IMongoUpdate updateDoc = new UpdateDocument("$set", doc);
collection.Update(Query.EQ("_id",id), updateDoc);
但是,您應該小心。
如果您首先將文檔反序列化為 SomeObject,則所有字段都將獲得其默認值(字符串為 null,整數為 0 等)。 如果您使用該對象進行更新,則 json 字符串中不存在的字段將更新為其默認值。
如果你使用
var bsonDoc = BsonSerializer.Deserialize<BsonDocument>(jsonString);
IMongoUpdate updateDoc = new UpdateDocument("$set", bsonDoc);
collection.Update(Query.EQ("_id",id), updateDoc);
您在數據庫中的文檔將僅針對 jsonString 中存在的字段進行更新
不確定是否有人在這里 >= 20 年 6 月,但我執行了以下操作。 我正在使用 NewtonSoft JObject/JArray,我想創建一個 mongo 更新解析器/函數,它不知道傳入的模式並且也會構建嵌套文檔。 我必須習慣的另一件事(我是 Mongo 的新手)是 Bson 更新文檔中鍵的語法,即
{ "key.full.path.into.nested.document" : "valueToSet" }
因此,在嘗試了幾種方法來手動/遞歸地考慮傳入 JSON 文檔的嵌套/包含路徑后,我終於找到了並且可以完美地使用 JToken.Path 屬性。
無論如何,希望這是有人會覺得有用的東西。 這只是一個例子,對文檔結構做了一些假設,但在當前形式下非常有用。 而且,像我一樣,我認為它可能會幫助一些正在學習 Mongo 和他們的 C# 驅動程序的人,同時還使用 JSON.Net 來包裝傳入的 REST 請求。
public BsonDocument ParseUpdateRequest(JObject req)
{
BsonDocument bson = new BsonDocument();
Parse(req, ref bson);
BsonDocument parsedBson = new BsonDocument();
parsedBson["$set"] = bson;
return parsedBson;
}
private void Parse(JObject req, ref BsonDocument bson)
{
/**
* Could use a parent key/node in each recursion call or just use the JToken path
* string.IsNullOrEmpty(parentNode) ? field.Key : parentNode + "." + field.Key;
**/
string key;
JToken val;
foreach (var field in req)
{
key = field.Value.Path;
val = field.Value;
switch (val.Type)
{
case JTokenType.String:
bson.Add(key, (string)val);
break;
case JTokenType.Integer:
bson.Add(key, (int)val);
break;
case JTokenType.Float:
bson.Add(key, (float)val);
break;
case JTokenType.Date:
DateTime dt = (DateTime)val;
bson.Add(key, dt.ToUniversalTime());
break;
case JTokenType.Array:
BsonArray bsonArray = ParseArray((JArray)val);
bson.Add(key, bsonArray);
break;
case JTokenType.Object:
Parse((JObject)val, ref bson);
break;
}
}
return;
}
private BsonArray ParseArray(JArray source)
{
BsonArray bson = new BsonArray();
foreach (JToken field in source)
{
switch (field.Type)
{
case JTokenType.String:
bson.Add((string)field);
break;
case JTokenType.Date:
DateTime dt = (DateTime)field;
bson.Add(dt.ToUniversalTime());
break;
case JTokenType.Integer:
bson.Add((int)field);
break;
case JTokenType.Float:
bson.Add((float)field);
break;
case JTokenType.Object:
BsonDocument nestedDoc = new BsonDocument();
Parse((JObject)field, ref nestedDoc);
bson.Add(nestedDoc);
break;
}
}
return bson;
}
這是我寫的一些簡單的測試代碼:
ModelUser user = new ModelUser();
ControllerApp app = new ControllerApp();
ControllerApp.Instance.User = user;
JObject req = new JObject();
req["first"] = "First";
req["last"] = "Last";
req["usertype"] = "parent";
req["pw"] = "q345n3452345n2345";
req["array"] = JArray.Parse("[ '1', '2', '3' ]");
req["dateTest"] = DateTime.UtcNow;
req["profile"] = new JObject();
req["profile"]["name"] = new JObject();
req["profile"]["name"]["first"] = "testUpdateFirst";
BsonDocument bd;
bd = user.ParseUpdateRequest(req);
string s = bd.ToJson();
包含對象的數組將失敗:
"array": [{"test": "value"}]
將導致{array.test[0] : "value"}
,但 mongodb 需要{array.test.0 : "value"}
。
在 ASP.net core 3.1 中,您可以使用 JsonPatchDocument 並在 mongodb 中進行替換
ASP.NET Core Web API 中的 JsonPatch
[HttpPatch]
public IActionResult JsonPatchWithModelState(
[FromBody] JsonPatchDocument<Customer> patchDoc)
{
if (patchDoc != null)
{
var customer = CreateCustomer();
patchDoc.ApplyTo(customer, ModelState);
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
return new ObjectResult(customer);
}
else
{
return BadRequest(ModelState);
}
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.