簡體   English   中英

修補 REST API 以在 .NET 中部分更新 MongoDB

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM