static async Task SaveBatches(Stack <ParseBatch> batches)
        {
            while (batches.Count > 0)
            {
                ParseBatch         batch        = batches.Pop();
                List <ParseObject> dirtyObjects = batch.objects.Where(item => item.IsDirty)
                                                  .ToList();

                List <Dictionary <string, object> > requestList = dirtyObjects.Select(item => {
                    string path = item.ObjectId == null ?
                                  $"/parse/classes/{item.ClassName}" :
                                  $"/parse/classes/{item.ClassName}/{item.ClassName}";
                    string method = item.ObjectId == null ? "POST" : "PUT";
                    Dictionary <string, object> body = ParseEncoder.Encode(item.operationDict) as Dictionary <string, object>;
                    return(new Dictionary <string, object> {
                        { "path", path },
                        { "method", method },
                        { "body", body }
                    });
                }).ToList();

                Dictionary <string, object> data = new Dictionary <string, object> {
                    { "requests", ParseEncoder.Encode(requestList) }
                };

                List <Dictionary <string, object> > results = await ParseClient.HttpClient.Post <List <Dictionary <string, object> > >("batch", data : data);

                List <ParseObjectData> resultList = results.Select(item => {
                    if (item.TryGetValue("error", out object error))
                    {
                        Dictionary <string, object> err = error as Dictionary <string, object>;
                        int code       = (int)err["code"];
                        string message = (string)err["error"];
                        throw new ParseException(code, message as string);
                    }
                    return(ParseObjectData.Decode(item["success"] as IDictionary));
                }).ToList();

                for (int i = 0; i < dirtyObjects.Count; i++)
                {
                    ParseObject     obj     = dirtyObjects[i];
                    ParseObjectData objData = resultList[i];
                    obj.Merge(objData);
                }
            }
        }
        public static async Task <List <ParseObject> > SaveAll(List <ParseObject> objectList)
        {
            if (objectList == null)
            {
                throw new ArgumentNullException(nameof(objectList));
            }
            foreach (ParseObject obj in objectList)
            {
                if (ParseBatch.HasCircleReference(obj, new HashSet <ParseObject>()))
                {
                    throw new ArgumentException("Found a circle dependency when save.");
                }
            }
            Stack <ParseBatch> batches = ParseBatch.BatchObjects(objectList, true);

            await SaveBatches(batches);

            return(objectList);
        }
        public async Task <ParseObject> Save(bool fetchWhenSave = false, ParseQuery <ParseObject> query = null)
        {
            if (ParseBatch.HasCircleReference(this, new HashSet <ParseObject>()))
            {
                throw new ArgumentException("Found a circle dependency when save.");
            }

            Stack <ParseBatch> batches = ParseBatch.BatchObjects(new List <ParseObject> {
                this
            }, false);

            if (batches.Count > 0)
            {
                await SaveBatches(batches);
            }

            string path = ObjectId == null ? $"classes/{ClassName}" : $"classes/{ClassName}/{ObjectId}";
            Dictionary <string, object> queryParams = new Dictionary <string, object>();

            if (fetchWhenSave)
            {
                queryParams["fetchWhenSave"] = true;
            }
            if (query != null)
            {
                queryParams["where"] = query.BuildWhere();
            }
            Dictionary <string, object> response = ObjectId == null ?
                                                   await ParseClient.HttpClient.Post <Dictionary <string, object> >(path, data : ParseEncoder.Encode(operationDict) as Dictionary <string, object>, queryParams : queryParams) :
                                                   await ParseClient.HttpClient.Put <Dictionary <string, object> >(path, data : ParseEncoder.Encode(operationDict) as Dictionary <string, object>, queryParams : queryParams);

            ParseObjectData data = ParseObjectData.Decode(response);

            Merge(data);
            return(this);
        }