* ObjectTest.cs: chore: 完善 Object 模块和测试
* LCQuery.cs: * LCObject.cs: * LCDecoder.cs: * LCEncoder.cs: * LCHttpClient.cs: * ILCOperation.cs: * LCAddOperation.cs: * LCSetOperation.cs: * LCDeleteOperation.cs: * LCRemoveOperation.cs: * LCAddUniqueOperation.cs: * LCDecrementOperation.cs: * LCCompositionalCondition.cs: * LCAddRelationOperation.cs: * LCRemoveRelationOperation.cs:
parent
c775f612bc
commit
05047115d1
|
@ -1,16 +1,15 @@
|
|||
using NUnit.Framework;
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Collections.Generic;
|
||||
using LeanCloud.Common;
|
||||
using LeanCloud.Storage;
|
||||
|
||||
namespace LeanCloud.Test {
|
||||
public class ObjectTests {
|
||||
public class ObjectTest {
|
||||
[SetUp]
|
||||
public void SetUp() {
|
||||
Logger.LogDelegate += Utils.Print;
|
||||
Utils.InitNorthChina();
|
||||
LeanCloud.Initialize("ikGGdRE2YcVOemAaRbgp1xGJ-gzGzoHsz", "NUKmuRbdAhg1vrb2wexYo1jo", "https://ikggdre2.lc-cn-n1-shared.com");
|
||||
}
|
||||
|
||||
[TearDown]
|
||||
|
@ -19,296 +18,130 @@ namespace LeanCloud.Test {
|
|||
}
|
||||
|
||||
[Test]
|
||||
public async Task Save() {
|
||||
AVObject obj = AVObject.Create("Foo");
|
||||
obj["content"] = "hello, world";
|
||||
obj["list"] = new List<int> { 1, 1, 2, 3, 5, 8 };
|
||||
obj["dict"] = new Dictionary<string, int> {
|
||||
{ "hello", 1 },
|
||||
{ "world", 2 }
|
||||
public async Task CreateObject() {
|
||||
LCObject @object = new LCObject("Hello");
|
||||
@object["intValue"] = 123;
|
||||
@object["boolValue"] = true;
|
||||
@object["stringValue"] = "hello, world";
|
||||
@object["time"] = DateTime.Now;
|
||||
@object["intList"] = new List<int> { 1, 1, 2, 3, 5, 8 };
|
||||
@object["stringMap"] = new Dictionary<string, object> {
|
||||
{ "k1", 111 },
|
||||
{ "k2", true },
|
||||
{ "k3", "haha" }
|
||||
};
|
||||
await obj.SaveAsync(true);
|
||||
Assert.NotNull(obj.ObjectId);
|
||||
Assert.NotNull(obj.CreatedAt);
|
||||
Assert.NotNull(obj.UpdatedAt);
|
||||
LCObject nestedObj = new LCObject("World");
|
||||
nestedObj["content"] = "7788";
|
||||
@object["objectValue"] = nestedObj;
|
||||
@object["pointerList"] = new List<object> { new LCObject("World"), nestedObj };
|
||||
await @object.Save();
|
||||
|
||||
TestContext.WriteLine(@object.ClassName);
|
||||
TestContext.WriteLine(@object.ObjectId);
|
||||
TestContext.WriteLine(@object.CreatedAt);
|
||||
TestContext.WriteLine(@object.UpdatedAt);
|
||||
TestContext.WriteLine(@object["intValue"]);
|
||||
TestContext.WriteLine(@object["boolValue"]);
|
||||
TestContext.WriteLine(@object["stringValue"]);
|
||||
TestContext.WriteLine(@object["objectValue"]);
|
||||
TestContext.WriteLine(@object["time"]);
|
||||
|
||||
Assert.AreEqual(nestedObj, @object["objectValue"]);
|
||||
TestContext.WriteLine(nestedObj.ClassName);
|
||||
TestContext.WriteLine(nestedObj.ObjectId);
|
||||
|
||||
Assert.NotNull(@object.ObjectId);
|
||||
Assert.NotNull(@object.ClassName);
|
||||
Assert.NotNull(@object.CreatedAt);
|
||||
Assert.NotNull(@object.UpdatedAt);
|
||||
Assert.AreEqual(@object["intValue"], 123);
|
||||
Assert.AreEqual(@object["boolValue"], true);
|
||||
Assert.AreEqual(@object["stringValue"], "hello, world");
|
||||
|
||||
Assert.NotNull(nestedObj);
|
||||
Assert.NotNull(nestedObj.ClassName);
|
||||
Assert.NotNull(nestedObj.ObjectId);
|
||||
Assert.NotNull(nestedObj.CreatedAt);
|
||||
Assert.NotNull(nestedObj.UpdatedAt);
|
||||
|
||||
List<object> pointerList = @object["pointerList"] as List<object>;
|
||||
foreach (object pointerObj in pointerList) {
|
||||
LCObject pointer = pointerObj as LCObject;
|
||||
Assert.NotNull(pointer.ObjectId);
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task SaveWithOptions() {
|
||||
AVObject account = AVObject.CreateWithoutData("Account", "5d65fa5330863b008065e476");
|
||||
account["balance"] = 100;
|
||||
await account.SaveAsync();
|
||||
AVQuery<AVObject> query = new AVQuery<AVObject>("Account");
|
||||
query.WhereGreaterThan("balance", 80);
|
||||
account["balance"] = 50;
|
||||
await account.SaveAsync(true, query);
|
||||
TestContext.Out.WriteLine($"balance: {account["balance"]}");
|
||||
}
|
||||
|
||||
//[Test]
|
||||
//public async Task SaveWithPointer() {
|
||||
// AVObject comment = new AVObject("Comment") {
|
||||
// { "content", "Hello, Comment" }
|
||||
// };
|
||||
|
||||
// AVObject post = new AVObject("Post") {
|
||||
// { "name", "New Post" },
|
||||
// { "category", new AVObject("Category") {
|
||||
// { "name", "new post category" }
|
||||
// } }
|
||||
// };
|
||||
// comment["post"] = post;
|
||||
|
||||
// AVObject testPost = new AVObject("Post") {
|
||||
// { "name", "Test Post" },
|
||||
// { "category", new AVObject("Category") {
|
||||
// { "name", "test post category" }
|
||||
// } }
|
||||
// };
|
||||
// comment["test_post"] = testPost;
|
||||
|
||||
// await comment.Save();
|
||||
// TestContext.Out.WriteLine(post);
|
||||
// TestContext.Out.WriteLine(testPost);
|
||||
// TestContext.Out.WriteLine(comment);
|
||||
//}
|
||||
|
||||
[Test]
|
||||
public async Task SaveWithPointer() {
|
||||
AVObject parent = new AVObject("Parent");
|
||||
AVObject c1 = new AVObject("C1");
|
||||
AVObject c2 = new AVObject("C2");
|
||||
parent["c1"] = c1;
|
||||
parent["c2"] = c2;
|
||||
await parent.SaveAsync();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task SaveWithPointerArray() {
|
||||
AVObject parent = new AVObject("Parent");
|
||||
AVObject c1 = new AVObject("C1");
|
||||
AVObject c2 = new AVObject("C2");
|
||||
parent["iList"] = new List<int> { 1, 1, 2, 3 };
|
||||
parent["cList"] = new List<AVObject> { c1, c2 };
|
||||
parent["cDict"] = new Dictionary<string, AVObject> {
|
||||
{ "c1", c1 },
|
||||
{ "c2", c2 }
|
||||
};
|
||||
await parent.SaveAsync();
|
||||
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task SaveBatch() {
|
||||
List<AVObject> objList = new List<AVObject>();
|
||||
public async Task SaveAll() {
|
||||
List<LCObject> list = new List<LCObject>();
|
||||
for (int i = 0; i < 5; i++) {
|
||||
AVObject obj = AVObject.Create("Foo");
|
||||
obj["content"] = "batch object";
|
||||
objList.Add(obj);
|
||||
LCObject world = new LCObject("World");
|
||||
world["content"] = $"word_{i}";
|
||||
list.Add(world);
|
||||
}
|
||||
try {
|
||||
await objList.SaveAllAsync();
|
||||
objList.ForEach(obj => {
|
||||
Assert.NotNull(obj.ObjectId);
|
||||
});
|
||||
} catch (Exception e) {
|
||||
TestContext.Out.WriteLine(e.Message);
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Fetch() {
|
||||
AVObject obj = AVObject.CreateWithoutData("Todo", "5d5f6039d5de2b006cf29c8f");
|
||||
await obj.FetchAsync();
|
||||
Assert.NotNull(obj["title"]);
|
||||
Assert.NotNull(obj["content"]);
|
||||
TestContext.Out.WriteLine($"{obj["title"]}, {obj["content"]}");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task FetchWithKeys() {
|
||||
AVObject obj = AVObject.CreateWithoutData("Post", "5d3abfa530863b0068e1b326");
|
||||
await obj.FetchAsync(new List<string> { "pubUser" });
|
||||
TestContext.Out.WriteLine($"{obj["pubUser"]}");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task FetchWithIncludes() {
|
||||
AVObject obj = AVObject.CreateWithoutData("Post", "5d3abfa530863b0068e1b326");
|
||||
await obj.FetchAsync(includes: new List<string> { "tag" });
|
||||
AVObject tag = obj["tag"] as AVObject;
|
||||
TestContext.Out.WriteLine($"{tag["name"]}");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task FetchAll() {
|
||||
List<AVObject> objList = new List<AVObject> {
|
||||
AVObject.CreateWithoutData("Tag", "5d64e5ebc05a8000730340ba"),
|
||||
AVObject.CreateWithoutData("Tag", "5d64e5eb12215f0073db271c"),
|
||||
AVObject.CreateWithoutData("Tag", "5d64e57f43e78c0068a14315")
|
||||
};
|
||||
await objList.FetchAllAsync();
|
||||
objList.ForEach(obj => {
|
||||
await LCObject.SaveAll(list);
|
||||
foreach (LCObject obj in list) {
|
||||
Assert.NotNull(obj.ObjectId);
|
||||
TestContext.Out.WriteLine($"{obj.ObjectId}, {obj["name"]}");
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Delete() {
|
||||
AVObject obj = AVObject.Create("Foo");
|
||||
obj["content"] = "hello, world";
|
||||
await obj.SaveAsync();
|
||||
Assert.NotNull(obj);
|
||||
await obj.DeleteAsync();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task DeleteWithCondition() {
|
||||
AVObject account = new AVObject("Account") {
|
||||
{ "balance", 100 },
|
||||
};
|
||||
account.ACL = new AVACL {
|
||||
PublicWriteAccess = true,
|
||||
PublicReadAccess = true
|
||||
};
|
||||
await account.SaveAsync();
|
||||
AVQuery<AVObject> condition = new AVQuery<AVObject>();
|
||||
condition.WhereGreaterThan("balance", 10);
|
||||
await account.DeleteAsync(condition);
|
||||
|
||||
account = new AVObject("Account") {
|
||||
{ "name", "acl account" },
|
||||
{ "balance", 8 },
|
||||
};
|
||||
account.ACL = new AVACL {
|
||||
PublicWriteAccess = true,
|
||||
PublicReadAccess = true
|
||||
};
|
||||
await account.SaveAsync();
|
||||
condition = new AVQuery<AVObject>();
|
||||
condition.WhereGreaterThan("balance", 10);
|
||||
try {
|
||||
await account.DeleteAsync(condition);
|
||||
} catch (AVException e) {
|
||||
Assert.AreEqual(e.Code, AVException.ErrorCode.NoEffectOnUpdatingOrDeleting);
|
||||
}
|
||||
LCObject world = new LCObject("World");
|
||||
await world.Save();
|
||||
await world.Delete();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task DeleteAll() {
|
||||
List<AVObject> objList = new List<AVObject>();
|
||||
for (int i = 0; i < 5; i++) {
|
||||
AVObject obj = AVObject.Create("Foo");
|
||||
obj.ACL = new AVACL {
|
||||
PublicReadAccess = true,
|
||||
PublicWriteAccess = i % 2 == 0
|
||||
};
|
||||
obj["content"] = "batch object";
|
||||
objList.Add(obj);
|
||||
}
|
||||
await objList.SaveAllAsync();
|
||||
List<LCObject> list = new List<LCObject> {
|
||||
new LCObject("World"),
|
||||
new LCObject("World"),
|
||||
new LCObject("World"),
|
||||
new LCObject("World")
|
||||
};
|
||||
await LCObject.SaveAll(list);
|
||||
await LCObject.DeleteAll(list);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Fetch() {
|
||||
LCObject hello = LCObject.CreateWithoutData("Hello", "5e14392743c257006fb769d5");
|
||||
await hello.Fetch(includes: new List<string> { "objectValue" });
|
||||
LCObject world = hello["objectValue"] as LCObject;
|
||||
TestContext.WriteLine(world["content"]);
|
||||
Assert.AreEqual(world["content"], "7788");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task SaveWithOption() {
|
||||
LCObject account = new LCObject("Account");
|
||||
account["balance"] = 10;
|
||||
await account.Save();
|
||||
|
||||
account["balance"] = 1000;
|
||||
LCQuery<LCObject> q = new LCQuery<LCObject>("Account");
|
||||
q.WhereGreaterThan("balance", 100);
|
||||
try {
|
||||
await AVObject.DeleteAllAsync(objList);
|
||||
} catch (AggregateException e) {
|
||||
foreach (AVException ie in e.InnerExceptions) {
|
||||
TestContext.Out.WriteLine($"{ie.Code} : {ie.Message}");
|
||||
}
|
||||
await account.Save(fetchWhenSave: true, query: q);
|
||||
} catch(LCException e) {
|
||||
TestContext.WriteLine($"{e.Code} : {e.Message}");
|
||||
Assert.AreEqual(e.Code, 305);
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task TestMassiveRequest() {
|
||||
ThreadPool.SetMaxThreads(1, 1);
|
||||
await Task.Run(() => {
|
||||
for (int i = 0; i < 10; i++) {
|
||||
for (int j = 0; j < 50; j++) {
|
||||
AVObject obj = AVObject.Create("Foo");
|
||||
obj.SaveAsync().ContinueWith(_ => {
|
||||
TestContext.Out.WriteLine($"{obj.ObjectId} saved at {Thread.CurrentThread.ManagedThreadId}");
|
||||
});
|
||||
}
|
||||
Thread.Sleep(1000);
|
||||
}
|
||||
});
|
||||
}
|
||||
public async Task Unset() {
|
||||
LCObject hello = new LCObject("Hello");
|
||||
hello["content"] = "hello, world";
|
||||
await hello.Save();
|
||||
TestContext.WriteLine(hello["content"]);
|
||||
Assert.AreEqual(hello["content"], "hello, world");
|
||||
|
||||
[Test]
|
||||
public void SimpleCircleReference() {
|
||||
AVObject a = new AVObject("A");
|
||||
AVObject b = new AVObject("B");
|
||||
a["b"] = b;
|
||||
b["a"] = a;
|
||||
|
||||
Assert.ThrowsAsync<AVException>(async () => await a.SaveAsync());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void IndirectCircleReference() {
|
||||
AVObject a = new AVObject("A");
|
||||
AVObject b = new AVObject("B");
|
||||
AVObject c = new AVObject("C");
|
||||
a["b"] = b;
|
||||
b["c"] = c;
|
||||
c["a"] = a;
|
||||
|
||||
Assert.ThrowsAsync<AVException>(async () => await a.SaveAsync());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void SimpleCollectionPointerCircleReference() {
|
||||
AVObject a = new AVObject("A");
|
||||
AVObject b = new AVObject("B");
|
||||
a["children"] = new List<object> { 1, b };
|
||||
b["children"] = new Dictionary<string, object> {
|
||||
{ "c", a }
|
||||
};
|
||||
|
||||
Assert.ThrowsAsync<AVException>(async () => await a.SaveAsync());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void IndirectCollectionPointerCircleReference() {
|
||||
AVObject a = new AVObject("A");
|
||||
AVObject b = new AVObject("B");
|
||||
AVObject c = new AVObject("C");
|
||||
|
||||
a["children"] = new List<object> { 1, b };
|
||||
b["children"] = new List<object> { 2, c };
|
||||
c["children"] = new Dictionary<string, object> {
|
||||
{ "c", a }
|
||||
};
|
||||
|
||||
Assert.ThrowsAsync<AVException>(async () => await a.SaveAsync());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task SimpleSavePointerCollection() {
|
||||
AVObject p = new AVObject("P");
|
||||
AVObject c1 = new AVObject("C1");
|
||||
AVObject c2 = new AVObject("C2");
|
||||
p["cList"] = new List<AVObject> { c1, c2 };
|
||||
p["cDict"] = new Dictionary<string, object> {
|
||||
{ "c1", c1 },
|
||||
{ "c2", c2 }
|
||||
};
|
||||
await p.SaveAsync();
|
||||
Assert.NotNull(p.ObjectId);
|
||||
Assert.NotNull(p.CreatedAt);
|
||||
Assert.NotNull(c1.ObjectId);
|
||||
Assert.NotNull(c2.ObjectId);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task SaveWithExistedObject() {
|
||||
AVObject p = new AVObject("P");
|
||||
AVObject c1 = AVObject.CreateWithoutData("C1", "5dea05578a84ab00680b7ae5");
|
||||
AVObject c2 = new AVObject("C2");
|
||||
p["c"] = c1;
|
||||
c1["c"] = c2;
|
||||
await p.SaveAsync();
|
||||
Assert.NotNull(p.ObjectId);
|
||||
Assert.NotNull(p.CreatedAt);
|
||||
hello.Unset("content");
|
||||
await hello.Save();
|
||||
TestContext.WriteLine(hello["content"]);
|
||||
Assert.IsNull(hello["content"]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -56,8 +56,7 @@ namespace LeanCloud.Storage.Internal.Codec {
|
|||
string className = dict["className"].ToString();
|
||||
LCObject obj = LCObject.Create(className);
|
||||
LCObjectData objectData = LCObjectData.Decode(dict as Dictionary<string, object>);
|
||||
// TODO merge
|
||||
|
||||
obj.Merge(objectData);
|
||||
return obj;
|
||||
}
|
||||
|
||||
|
|
|
@ -58,10 +58,10 @@ namespace LeanCloud.Storage.Internal.Codec {
|
|||
|
||||
static object EncodeDictionary(IDictionary dict) {
|
||||
Dictionary<string, object> d = new Dictionary<string, object>();
|
||||
foreach (KeyValuePair<string, object> kv in dict) {
|
||||
string key = kv.Key;
|
||||
object value = kv.Value;
|
||||
d[key] = value;
|
||||
foreach (DictionaryEntry entry in dict) {
|
||||
string key = entry.Key.ToString();
|
||||
object value = entry.Value;
|
||||
d[key] = Encode(value);
|
||||
}
|
||||
return d;
|
||||
}
|
||||
|
@ -75,11 +75,11 @@ namespace LeanCloud.Storage.Internal.Codec {
|
|||
}
|
||||
|
||||
static object EncodeOperation(ILCOperation operation) {
|
||||
return null;
|
||||
return operation.Encode();
|
||||
}
|
||||
|
||||
static object EncodeQueryCondition(ILCQueryCondition cond) {
|
||||
return null;
|
||||
return cond.Encode();
|
||||
}
|
||||
|
||||
static object EncodeACL(LCACL acl) {
|
||||
|
|
|
@ -38,7 +38,7 @@ namespace LeanCloud.Storage.Internal.Http {
|
|||
client.DefaultRequestHeaders.Add("X-LC-Key", appKey);
|
||||
}
|
||||
|
||||
internal async Task<Dictionary<string, object>> Get(string path,
|
||||
internal async Task<T> Get<T>(string path,
|
||||
Dictionary<string, object> headers = null,
|
||||
Dictionary<string, object> queryParams = null) {
|
||||
|
||||
|
@ -53,6 +53,11 @@ namespace LeanCloud.Storage.Internal.Http {
|
|||
RequestUri = new Uri(url),
|
||||
Method = HttpMethod.Get
|
||||
};
|
||||
if (headers != null) {
|
||||
foreach (KeyValuePair<string, object> kv in headers) {
|
||||
request.Headers.Add(kv.Key, kv.Value.ToString());
|
||||
}
|
||||
}
|
||||
HttpUtils.PrintRequest(client, request);
|
||||
HttpResponseMessage response = await client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead);
|
||||
request.Dispose();
|
||||
|
@ -63,7 +68,7 @@ namespace LeanCloud.Storage.Internal.Http {
|
|||
|
||||
HttpStatusCode statusCode = response.StatusCode;
|
||||
if (response.IsSuccessStatusCode) {
|
||||
Dictionary<string, object> ret = JsonConvert.DeserializeObject<Dictionary<string, object>>(resultString, new LeanCloudJsonConverter());
|
||||
T ret = JsonConvert.DeserializeObject<T>(resultString, new LeanCloudJsonConverter());
|
||||
return ret;
|
||||
}
|
||||
int code = (int)statusCode;
|
||||
|
@ -109,7 +114,7 @@ namespace LeanCloud.Storage.Internal.Http {
|
|||
try {
|
||||
// 尝试获取 LeanCloud 返回错误信息
|
||||
Dictionary<string, object> error = JsonConvert.DeserializeObject<Dictionary<string, object>>(resultString, new LeanCloudJsonConverter());
|
||||
code = (int)error["code"];
|
||||
code = (int)(long)error["code"];
|
||||
message = error["error"].ToString();
|
||||
} catch (Exception e) {
|
||||
Logger.Error(e.Message);
|
||||
|
@ -118,13 +123,19 @@ namespace LeanCloud.Storage.Internal.Http {
|
|||
}
|
||||
}
|
||||
|
||||
internal async Task<Dictionary<string, object>> Put(string path,
|
||||
internal async Task<T> Put<T>(string path,
|
||||
Dictionary<string, object> headers = null,
|
||||
Dictionary<string, object> data = null,
|
||||
Dictionary<string, object> queryParams = null) {
|
||||
string url = $"{server}/{apiVersion}/{path}";
|
||||
if (queryParams != null) {
|
||||
IEnumerable<string> queryPairs = queryParams.Select(kv => $"{kv.Key}={kv.Value}");
|
||||
string queries = string.Join("&", queryPairs);
|
||||
url = $"{url}?{queries}";
|
||||
}
|
||||
string content = (data != null) ? JsonConvert.SerializeObject(data) : null;
|
||||
HttpRequestMessage request = new HttpRequestMessage {
|
||||
RequestUri = new Uri($"{server}/{apiVersion}/{path}"),
|
||||
RequestUri = new Uri(url),
|
||||
Method = HttpMethod.Put,
|
||||
Content = new StringContent(content)
|
||||
};
|
||||
|
@ -139,11 +150,43 @@ namespace LeanCloud.Storage.Internal.Http {
|
|||
|
||||
HttpStatusCode statusCode = response.StatusCode;
|
||||
if (response.IsSuccessStatusCode) {
|
||||
Dictionary<string, object> ret = JsonConvert.DeserializeObject<Dictionary<string, object>>(resultString, new LeanCloudJsonConverter());
|
||||
T ret = JsonConvert.DeserializeObject<T>(resultString, new LeanCloudJsonConverter());
|
||||
return ret;
|
||||
}
|
||||
int code = (int)statusCode;
|
||||
string message = resultString;
|
||||
try {
|
||||
// 尝试获取 LeanCloud 返回错误信息
|
||||
Dictionary<string, object> error = JsonConvert.DeserializeObject<Dictionary<string, object>>(resultString, new LeanCloudJsonConverter());
|
||||
code = (int)(long)error["code"];
|
||||
message = error["error"].ToString();
|
||||
} catch (Exception e) {
|
||||
Logger.Error(e.Message);
|
||||
} finally {
|
||||
throw new LCException(code, message);
|
||||
}
|
||||
}
|
||||
|
||||
internal async Task Delete(string path) {
|
||||
HttpRequestMessage request = new HttpRequestMessage {
|
||||
RequestUri = new Uri($"{server}/{apiVersion}/{path}"),
|
||||
Method = HttpMethod.Delete
|
||||
};
|
||||
HttpUtils.PrintRequest(client, request);
|
||||
HttpResponseMessage response = await client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead);
|
||||
request.Dispose();
|
||||
|
||||
string resultString = await response.Content.ReadAsStringAsync();
|
||||
response.Dispose();
|
||||
HttpUtils.PrintResponse(response, resultString);
|
||||
|
||||
HttpStatusCode statusCode = response.StatusCode;
|
||||
if (response.IsSuccessStatusCode) {
|
||||
Dictionary<string, object> ret = JsonConvert.DeserializeObject<Dictionary<string, object>>(resultString, new LeanCloudJsonConverter());
|
||||
return;
|
||||
}
|
||||
int code = (int)statusCode;
|
||||
string message = resultString;
|
||||
try {
|
||||
// 尝试获取 LeanCloud 返回错误信息
|
||||
Dictionary<string, object> error = JsonConvert.DeserializeObject<Dictionary<string, object>>(resultString, new LeanCloudJsonConverter());
|
||||
|
|
|
@ -5,7 +5,7 @@ namespace LeanCloud.Storage.Internal.Operation {
|
|||
internal interface ILCOperation {
|
||||
ILCOperation MergeWithPrevious(ILCOperation previousOp);
|
||||
|
||||
Dictionary<string, object> Encode();
|
||||
object Encode();
|
||||
|
||||
object Apply(object oldValue, string key);
|
||||
|
||||
|
|
|
@ -26,7 +26,7 @@ namespace LeanCloud.Storage.Internal.Operation {
|
|||
throw new ArgumentException("Operation is invalid after previous operation.");
|
||||
}
|
||||
|
||||
Dictionary<string, object> ILCOperation.Encode() {
|
||||
object ILCOperation.Encode() {
|
||||
return new Dictionary<string, object> {
|
||||
{ "op", "Add" },
|
||||
{ "objects", LCEncoder.Encode(valueList) }
|
||||
|
|
|
@ -23,7 +23,7 @@ namespace LeanCloud.Storage.Internal.Operation {
|
|||
throw new ArgumentException("Operation is invalid after previous operation.");
|
||||
}
|
||||
|
||||
public Dictionary<string, object> Encode() {
|
||||
public object Encode() {
|
||||
return new Dictionary<string, object> {
|
||||
{ "__op", "AddRelation" },
|
||||
{ "objects", LCEncoder.Encode(valueList) }
|
||||
|
|
|
@ -23,7 +23,7 @@ namespace LeanCloud.Storage.Internal.Operation {
|
|||
throw new ArgumentException("Operation is invalid after previous operation.");
|
||||
}
|
||||
|
||||
public Dictionary<string, object> Encode() {
|
||||
public object Encode() {
|
||||
return new Dictionary<string, object> {
|
||||
{ "__op", "AddUnique" },
|
||||
{ "objects", LCEncoder.Encode(values.ToList()) }
|
||||
|
|
|
@ -13,7 +13,7 @@ namespace LeanCloud.Storage.Internal.Operation {
|
|||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public Dictionary<string, object> Encode() {
|
||||
public object Encode() {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
|
|
|
@ -11,7 +11,7 @@ namespace LeanCloud.Storage.Internal.Operation {
|
|||
return this;
|
||||
}
|
||||
|
||||
public Dictionary<string, object> Encode() {
|
||||
public object Encode() {
|
||||
return new Dictionary<string, object> {
|
||||
{ "__op", "Delete" }
|
||||
};
|
||||
|
|
|
@ -24,7 +24,7 @@ namespace LeanCloud.Storage.Internal.Operation {
|
|||
throw new ArgumentException("Operation is invalid after previous operation.");
|
||||
}
|
||||
|
||||
public Dictionary<string, object> Encode() {
|
||||
public object Encode() {
|
||||
return new Dictionary<string, object> {
|
||||
{ "__op", "Remove" },
|
||||
{ "objects", LCEncoder.Encode(valueList) }
|
||||
|
|
|
@ -22,7 +22,7 @@ namespace LeanCloud.Storage.Internal.Operation {
|
|||
throw new ArgumentException("Operation is invalid after previous operation.");
|
||||
}
|
||||
|
||||
public Dictionary<string, object> Encode() {
|
||||
public object Encode() {
|
||||
return new Dictionary<string, object> {
|
||||
{ "__op", "RemoveRelation" },
|
||||
{ "objects", LCEncoder.Encode(valueList) }
|
||||
|
|
|
@ -15,8 +15,8 @@ namespace LeanCloud.Storage.Internal.Operation {
|
|||
return this;
|
||||
}
|
||||
|
||||
public Dictionary<string, object> Encode() {
|
||||
return LCEncoder.Encode(value) as Dictionary<string, object>;
|
||||
public object Encode() {
|
||||
return LCEncoder.Encode(value);
|
||||
}
|
||||
|
||||
public object Apply(object oldValue, string key) {
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using Newtonsoft.Json;
|
||||
using LeanCloud.Storage.Internal.Codec;
|
||||
|
||||
namespace LeanCloud.Storage.Internal.Query {
|
||||
|
@ -189,9 +190,7 @@ namespace LeanCloud.Storage.Internal.Query {
|
|||
if (conditionList == null || conditionList.Count == 0) {
|
||||
return null;
|
||||
}
|
||||
// TODO
|
||||
|
||||
return null;
|
||||
return JsonConvert.SerializeObject(Encode());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -87,7 +87,7 @@ namespace LeanCloud.Storage {
|
|||
LCObject obj = new LCObject(className);
|
||||
obj.data.ObjectId = objectId;
|
||||
obj.isNew = false;
|
||||
return null;
|
||||
return obj;
|
||||
}
|
||||
|
||||
internal static LCObject Create(string className) {
|
||||
|
@ -106,12 +106,14 @@ namespace LeanCloud.Storage {
|
|||
|
||||
public object this[string key] {
|
||||
get {
|
||||
object value = estimatedData[key];
|
||||
if (value is LCRelation<LCObject> relation) {
|
||||
relation.Key = key;
|
||||
relation.Parent = this;
|
||||
if (estimatedData.TryGetValue(key, out object value)) {
|
||||
if (value is LCRelation<LCObject> relation) {
|
||||
relation.Key = key;
|
||||
relation.Parent = this;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
return value;
|
||||
return null;
|
||||
}
|
||||
set {
|
||||
if (string.IsNullOrEmpty(key)) {
|
||||
|
@ -161,11 +163,13 @@ namespace LeanCloud.Storage {
|
|||
|
||||
List<Dictionary<string, object>> results = await LeanCloud.HttpClient.Post<List<Dictionary<string, object>>>("batch", data: data);
|
||||
List<LCObjectData> resultList = results.Select(item => {
|
||||
if (item.TryGetValue("error", out object message)) {
|
||||
int code = (int)item["code"];
|
||||
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 LCException(code, message as string);
|
||||
}
|
||||
return LCObjectData.Decode(item);
|
||||
return LCObjectData.Decode(item["success"] as IDictionary);
|
||||
}).ToList();
|
||||
|
||||
for (int i = 0; i < dirtyObjects.Count; i++) {
|
||||
|
@ -196,12 +200,68 @@ namespace LeanCloud.Storage {
|
|||
}
|
||||
Dictionary<string, object> response = ObjectId == null ?
|
||||
await LeanCloud.HttpClient.Post<Dictionary<string, object>>(path, data: LCEncoder.Encode(operationDict) as Dictionary<string, object>, queryParams: queryParams) :
|
||||
await LeanCloud.HttpClient.Put(path, data: LCEncoder.Encode(operationDict) as Dictionary<string, object>, queryParams: queryParams);
|
||||
await LeanCloud.HttpClient.Put<Dictionary<string, object>>(path, data: LCEncoder.Encode(operationDict) as Dictionary<string, object>, queryParams: queryParams);
|
||||
LCObjectData data = LCObjectData.Decode(response);
|
||||
Merge(data);
|
||||
return this;
|
||||
}
|
||||
|
||||
public static async Task<List<LCObject>> SaveAll(List<LCObject> objectList) {
|
||||
if (objectList == null) {
|
||||
throw new ArgumentNullException(nameof(objectList));
|
||||
}
|
||||
foreach (LCObject obj in objectList) {
|
||||
if (LCBatch.HasCircleReference(obj, new HashSet<LCObject>())) {
|
||||
throw new ArgumentException("Found a circle dependency when save.");
|
||||
}
|
||||
}
|
||||
Stack<LCBatch> batches = LCBatch.BatchObjects(objectList, true);
|
||||
await SaveBatches(batches);
|
||||
return objectList;
|
||||
}
|
||||
|
||||
public async Task Delete() {
|
||||
if (ObjectId == null) {
|
||||
return;
|
||||
}
|
||||
string path = $"classes/{ClassName}/{ObjectId}";
|
||||
await LeanCloud.HttpClient.Delete(path);
|
||||
}
|
||||
|
||||
public static async Task DeleteAll(List<LCObject> objectList) {
|
||||
if (objectList == null || objectList.Count == 0) {
|
||||
throw new ArgumentNullException(nameof(objectList));
|
||||
}
|
||||
IEnumerable<LCObject> objects = objectList.Where(item => item.ObjectId != null);
|
||||
HashSet<LCObject> objectSet = new HashSet<LCObject>(objects);
|
||||
List<Dictionary<string, object>> requestList = objectSet.Select(item => {
|
||||
string path = $"/{LeanCloud.APIVersion}/classes/{item.ClassName}/{item.ObjectId}";
|
||||
return new Dictionary<string, object> {
|
||||
{ "path", path },
|
||||
{ "method", "DELETE" }
|
||||
};
|
||||
}).ToList();
|
||||
Dictionary<string, object> data = new Dictionary<string, object> {
|
||||
{ "requests", LCEncoder.Encode(requestList) }
|
||||
};
|
||||
await LeanCloud.HttpClient.Post<List<object>>("batch", data: data);
|
||||
}
|
||||
|
||||
public async Task<LCObject> Fetch(IEnumerable<string> keys = null, IEnumerable<string> includes = null) {
|
||||
Dictionary<string, object> queryParams = new Dictionary<string, object>();
|
||||
if (keys != null) {
|
||||
queryParams["keys"] = string.Join(",", keys);
|
||||
}
|
||||
if (includes != null) {
|
||||
queryParams["include"] = string.Join(",", includes);
|
||||
}
|
||||
string path = $"classes/{ClassName}/{ObjectId}";
|
||||
Dictionary<string, object> response = await LeanCloud.HttpClient.Get<Dictionary<string, object>>(path, queryParams: queryParams);
|
||||
LCObjectData objectData = LCObjectData.Decode(response);
|
||||
Merge(objectData);
|
||||
return this;
|
||||
}
|
||||
|
||||
public static void RegisterSubclass(string className, Type type, Func<LCObject> constructor) {
|
||||
LCSubclassInfo subclassInfo = new LCSubclassInfo(className, type, constructor);
|
||||
subclassNameDict[className] = subclassInfo;
|
||||
|
@ -217,8 +277,11 @@ namespace LeanCloud.Storage {
|
|||
if (op is LCDeleteOperation) {
|
||||
estimatedData.Remove(key);
|
||||
} else {
|
||||
object oldValue = estimatedData[key];
|
||||
estimatedData[key] = op.Apply(oldValue, key);
|
||||
if (estimatedData.TryGetValue(key, out object oldValue)) {
|
||||
estimatedData[key] = op.Apply(oldValue, key);
|
||||
} else {
|
||||
estimatedData[key] = op.Apply(null, key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -274,7 +274,7 @@ namespace LeanCloud.Storage {
|
|||
Dictionary<string, object> parameters = BuildParams();
|
||||
parameters["limit"] = 0;
|
||||
parameters["count"] = 1;
|
||||
Dictionary<string, object> ret = await LeanCloud.HttpClient.Get(path, queryParams: parameters);
|
||||
Dictionary<string, object> ret = await LeanCloud.HttpClient.Get<Dictionary<string, object>>(path, queryParams: parameters);
|
||||
return (int)ret["count"];
|
||||
}
|
||||
|
||||
|
@ -297,7 +297,7 @@ namespace LeanCloud.Storage {
|
|||
public async Task<List<T>> Find() {
|
||||
string path = $"classes/{ClassName}";
|
||||
Dictionary<string, object> parameters = BuildParams();
|
||||
Dictionary<string, object> response = await LeanCloud.HttpClient.Get(path, queryParams: parameters);
|
||||
Dictionary<string, object> response = await LeanCloud.HttpClient.Get<Dictionary<string, object>>(path, queryParams: parameters);
|
||||
List<object> results = response["results"] as List<object>;
|
||||
List<T> list = new List<T>();
|
||||
foreach (object item in results) {
|
||||
|
|
Loading…
Reference in New Issue