* AppRouterState.cs: chore: 使用 await/async 替换 ContinueWith/OnSuccess
* QueryTest.cs: * ObjectTest.cs: * AVObject.cs: * ObjectControllerTests.cs: * AVFileController.cs: * AVObjectController.cs: * AVCloudCodeController.cs:
parent
eda7dd43bb
commit
f3ed814d96
|
@ -40,17 +40,17 @@ namespace LeanCloud.Storage.Internal {
|
|||
get; internal set;
|
||||
}
|
||||
|
||||
public DateTime FetchedAt {
|
||||
public DateTimeOffset FetchedAt {
|
||||
get; internal set;
|
||||
}
|
||||
|
||||
public AppRouterState() {
|
||||
FetchedAt = DateTime.Now;
|
||||
FetchedAt = DateTimeOffset.Now;
|
||||
}
|
||||
|
||||
public bool IsExpired {
|
||||
get {
|
||||
return DateTime.Now > FetchedAt + TimeSpan.FromSeconds(TTL);
|
||||
return DateTimeOffset.Now > FetchedAt.AddSeconds(TTL);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -33,6 +33,27 @@ namespace LeanCloudTests {
|
|||
TestContext.Out.WriteLine($"balance: {account["balance"]}");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task SaveWithPointer() {
|
||||
AVObject comment = AVObject.Create("Comment");
|
||||
comment["content"] = "Hello, Comment";
|
||||
|
||||
|
||||
AVObject post = AVObject.Create("Post");
|
||||
post["name"] = "New Post";
|
||||
|
||||
AVObject category = AVObject.Create("Category");
|
||||
post["category"] = category;
|
||||
|
||||
comment["post"] = post;
|
||||
|
||||
AVObject testPost = AVObject.Create("Post");
|
||||
testPost["name"] = "Test Post";
|
||||
comment["test_post"] = testPost;
|
||||
|
||||
await comment.SaveAsync();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task SaveBatch() {
|
||||
List<AVObject> objList = new List<AVObject>();
|
||||
|
|
|
@ -45,7 +45,7 @@ namespace LeanCloudTests {
|
|||
res.Dispose();
|
||||
TestContext.Out.WriteLine($"response at {Thread.CurrentThread.ManagedThreadId}");
|
||||
TestContext.Out.WriteLine(data);
|
||||
Assert.Pass();
|
||||
Assert.Pass();
|
||||
}
|
||||
|
||||
[Test]
|
||||
|
|
|
@ -8,12 +8,7 @@ namespace LeanCloudTests {
|
|||
public class QueryTest {
|
||||
[SetUp]
|
||||
public void SetUp() {
|
||||
AVClient.Initialize(new AVClient.Configuration {
|
||||
ApplicationId = "BMYV4RKSTwo8WSqt8q9ezcWF-gzGzoHsz",
|
||||
ApplicationKey = "pbf6Nk5seyjilexdpyrPwjSp",
|
||||
ApiServer = "https://avoscloud.com"
|
||||
});
|
||||
AVClient.HttpLog(TestContext.Out.WriteLine);
|
||||
Utils.InitNorthChina();
|
||||
}
|
||||
|
||||
[Test]
|
||||
|
|
|
@ -7,7 +7,7 @@ using System.Net.Http;
|
|||
|
||||
namespace LeanCloud.Storage.Internal {
|
||||
public class AVCloudCodeController {
|
||||
public Task<T> CallFunctionAsync<T>(string name,
|
||||
public async Task<T> CallFunctionAsync<T>(string name,
|
||||
IDictionary<string, object> parameters,
|
||||
CancellationToken cancellationToken) {
|
||||
var command = new EngineCommand {
|
||||
|
@ -15,28 +15,26 @@ namespace LeanCloud.Storage.Internal {
|
|||
Method = HttpMethod.Post,
|
||||
Content = parameters ?? new Dictionary<string, object>()
|
||||
};
|
||||
return AVPlugins.Instance.CommandRunner.RunCommandAsync<IDictionary<string, object>>(command, cancellationToken).OnSuccess(t => {
|
||||
var decoded = AVDecoder.Instance.Decode(t.Result.Item2) as IDictionary<string, object>;
|
||||
if (!decoded.ContainsKey("result")) {
|
||||
return default;
|
||||
}
|
||||
return Conversion.To<T>(decoded["result"]);
|
||||
});
|
||||
var data = await AVPlugins.Instance.CommandRunner.RunCommandAsync<IDictionary<string, object>>(command, cancellationToken);
|
||||
var decoded = AVDecoder.Instance.Decode(data.Item2) as IDictionary<string, object>;
|
||||
if (!decoded.ContainsKey("result")) {
|
||||
return default;
|
||||
}
|
||||
return Conversion.To<T>(decoded["result"]);
|
||||
}
|
||||
|
||||
public Task<T> RPCFunction<T>(string name, IDictionary<string, object> parameters, CancellationToken cancellationToken) {
|
||||
public async Task<T> RPCFunction<T>(string name, IDictionary<string, object> parameters, CancellationToken cancellationToken) {
|
||||
var command = new EngineCommand {
|
||||
Path = $"call/{Uri.EscapeUriString(name)}",
|
||||
Method = HttpMethod.Post,
|
||||
Content = parameters ?? new Dictionary<string, object>()
|
||||
};
|
||||
return AVPlugins.Instance.CommandRunner.RunCommandAsync<IDictionary<string, object>>(command, cancellationToken).OnSuccess(t => {
|
||||
var decoded = AVDecoder.Instance.Decode(t.Result.Item2) as IDictionary<string, object>;
|
||||
if (!decoded.ContainsKey("result")) {
|
||||
return default;
|
||||
}
|
||||
return Conversion.To<T>(decoded["result"]);
|
||||
});
|
||||
var data = await AVPlugins.Instance.CommandRunner.RunCommandAsync<IDictionary<string, object>>(command, cancellationToken);
|
||||
var decoded = AVDecoder.Instance.Decode(data.Item2) as IDictionary<string, object>;
|
||||
if (!decoded.ContainsKey("result")) {
|
||||
return default;
|
||||
}
|
||||
return Conversion.To<T>(decoded["result"]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,39 +17,36 @@ namespace LeanCloud.Storage.Internal {
|
|||
const string QCloud = "qcloud";
|
||||
const string AWS = "s3";
|
||||
|
||||
public Task<FileState> SaveAsync(FileState state,
|
||||
public async Task<FileState> SaveAsync(FileState state,
|
||||
Stream dataStream,
|
||||
IProgress<AVUploadProgressEventArgs> progress,
|
||||
CancellationToken cancellationToken = default) {
|
||||
if (state.Url != null) {
|
||||
return SaveWithUrl(state);
|
||||
return await SaveWithUrl(state);
|
||||
}
|
||||
|
||||
return GetFileToken(state, cancellationToken).OnSuccess(t => {
|
||||
// 根据 provider 区分 cdn
|
||||
var ret = t.Result;
|
||||
var fileToken = ret.Item2;
|
||||
var provider = fileToken["provider"] as string;
|
||||
switch (provider) {
|
||||
case QCloud:
|
||||
return new QCloudUploader().Upload(state, dataStream, fileToken, progress, cancellationToken);
|
||||
case AWS:
|
||||
return new AWSUploader().Upload(state, dataStream, fileToken, progress, cancellationToken);
|
||||
default:
|
||||
return new QiniuUploader().Upload(state, dataStream, fileToken, progress, cancellationToken);
|
||||
}
|
||||
}).Unwrap();
|
||||
var data = await GetFileToken(state, cancellationToken);
|
||||
var fileToken = data.Item2;
|
||||
var provider = fileToken["provider"] as string;
|
||||
switch (provider) {
|
||||
case QCloud:
|
||||
return await new QCloudUploader().Upload(state, dataStream, fileToken, progress, cancellationToken);
|
||||
case AWS:
|
||||
return await new AWSUploader().Upload(state, dataStream, fileToken, progress, cancellationToken);
|
||||
default:
|
||||
return await new QiniuUploader().Upload(state, dataStream, fileToken, progress, cancellationToken);
|
||||
}
|
||||
}
|
||||
|
||||
public Task DeleteAsync(FileState state, CancellationToken cancellationToken) {
|
||||
public async Task DeleteAsync(FileState state, CancellationToken cancellationToken) {
|
||||
var command = new AVCommand {
|
||||
Path = $"files/{state.ObjectId}",
|
||||
Method = HttpMethod.Delete
|
||||
};
|
||||
return AVPlugins.Instance.CommandRunner.RunCommandAsync<IDictionary<string, object>>(command, cancellationToken: cancellationToken);
|
||||
await AVPlugins.Instance.CommandRunner.RunCommandAsync<IDictionary<string, object>>(command, cancellationToken: cancellationToken);
|
||||
}
|
||||
|
||||
internal Task<FileState> SaveWithUrl(FileState state) {
|
||||
internal async Task<FileState> SaveWithUrl(FileState state) {
|
||||
Dictionary<string, object> strs = new Dictionary<string, object> {
|
||||
{ "url", state.Url.ToString() },
|
||||
{ "name", state.Name },
|
||||
|
@ -72,14 +69,13 @@ namespace LeanCloud.Storage.Internal {
|
|||
};
|
||||
}
|
||||
|
||||
return AVPlugins.Instance.CommandRunner.RunCommandAsync<IDictionary<string, object>>(cmd).OnSuccess(t => {
|
||||
var result = t.Result.Item2;
|
||||
state.ObjectId = result["objectId"].ToString();
|
||||
return state;
|
||||
});
|
||||
var data = await AVPlugins.Instance.CommandRunner.RunCommandAsync<IDictionary<string, object>>(cmd);
|
||||
var result = data.Item2;
|
||||
state.ObjectId = result["objectId"].ToString();
|
||||
return state;
|
||||
}
|
||||
|
||||
internal Task<Tuple<HttpStatusCode, IDictionary<string, object>>> GetFileToken(FileState fileState, CancellationToken cancellationToken) {
|
||||
internal async Task<Tuple<HttpStatusCode, IDictionary<string, object>>> GetFileToken(FileState fileState, CancellationToken cancellationToken) {
|
||||
string str = fileState.Name;
|
||||
IDictionary<string, object> parameters = new Dictionary<string, object> {
|
||||
{ "name", str },
|
||||
|
@ -94,24 +90,22 @@ namespace LeanCloud.Storage.Internal {
|
|||
Method = HttpMethod.Post,
|
||||
Content = parameters
|
||||
};
|
||||
return AVPlugins.Instance.CommandRunner.RunCommandAsync<IDictionary<string, object>>(command);
|
||||
return await AVPlugins.Instance.CommandRunner.RunCommandAsync<IDictionary<string, object>>(command);
|
||||
}
|
||||
|
||||
public Task<FileState> GetAsync(string objectId, CancellationToken cancellationToken) {
|
||||
public async Task<FileState> GetAsync(string objectId, CancellationToken cancellationToken) {
|
||||
var command = new AVCommand {
|
||||
Path = $"files/{objectId}",
|
||||
Method = HttpMethod.Get
|
||||
};
|
||||
return AVPlugins.Instance.CommandRunner.RunCommandAsync<IDictionary<string, object>>(command, cancellationToken: cancellationToken).OnSuccess(_ => {
|
||||
var result = _.Result;
|
||||
var jsonData = result.Item2;
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
return new FileState {
|
||||
ObjectId = jsonData["objectId"] as string,
|
||||
Name = jsonData["name"] as string,
|
||||
Url = new Uri(jsonData["url"] as string, UriKind.Absolute),
|
||||
};
|
||||
});
|
||||
var data = await AVPlugins.Instance.CommandRunner.RunCommandAsync<IDictionary<string, object>>(command, cancellationToken);
|
||||
var jsonData = data.Item2;
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
return new FileState {
|
||||
ObjectId = jsonData["objectId"] as string,
|
||||
Name = jsonData["name"] as string,
|
||||
Url = new Uri(jsonData["url"] as string, UriKind.Absolute),
|
||||
};
|
||||
}
|
||||
|
||||
internal static string GetUniqueName(FileState state) {
|
||||
|
|
|
@ -8,19 +8,19 @@ using System.Net.Http;
|
|||
|
||||
namespace LeanCloud.Storage.Internal {
|
||||
public class AVObjectController {
|
||||
public Task<IObjectState> FetchAsync(IObjectState state,
|
||||
public async Task<IObjectState> FetchAsync(IObjectState state,
|
||||
IDictionary<string, object> queryString,
|
||||
CancellationToken cancellationToken) {
|
||||
var command = new AVCommand {
|
||||
Path = $"classes/{Uri.EscapeDataString(state.ClassName)}/{Uri.EscapeDataString(state.ObjectId)}?{AVClient.BuildQueryString(queryString)}",
|
||||
Method = HttpMethod.Get
|
||||
};
|
||||
return AVPlugins.Instance.CommandRunner.RunCommandAsync<IDictionary<string, object>>(command, cancellationToken).OnSuccess(t => {
|
||||
return AVObjectCoder.Instance.Decode(t.Result.Item2, AVDecoder.Instance);
|
||||
});
|
||||
var data = await AVPlugins.Instance.CommandRunner.RunCommandAsync<IDictionary<string, object>>(command, cancellationToken);
|
||||
var objState = AVObjectCoder.Instance.Decode(data.Item2, AVDecoder.Instance);
|
||||
return objState;
|
||||
}
|
||||
|
||||
public Task<IObjectState> SaveAsync(IObjectState state,
|
||||
public async Task<IObjectState> SaveAsync(IObjectState state,
|
||||
IDictionary<string, IAVFieldOperation> operations,
|
||||
bool fetchWhenSave,
|
||||
AVQuery<AVObject> query,
|
||||
|
@ -44,13 +44,12 @@ namespace LeanCloud.Storage.Internal {
|
|||
string encode = AVClient.BuildQueryString(args);
|
||||
command.Path = $"{command.Path}?{encode}";
|
||||
}
|
||||
return AVPlugins.Instance.CommandRunner.RunCommandAsync<IDictionary<string, object>>(command, cancellationToken).OnSuccess(t => {
|
||||
var serverState = AVObjectCoder.Instance.Decode(t.Result.Item2, AVDecoder.Instance);
|
||||
serverState = serverState.MutatedClone(mutableClone => {
|
||||
mutableClone.IsNew = t.Result.Item1 == System.Net.HttpStatusCode.Created;
|
||||
});
|
||||
return serverState;
|
||||
var data = await AVPlugins.Instance.CommandRunner.RunCommandAsync<IDictionary<string, object>>(command, cancellationToken);
|
||||
var serverState = AVObjectCoder.Instance.Decode(data.Item2, AVDecoder.Instance);
|
||||
serverState = serverState.MutatedClone(mutableClone => {
|
||||
mutableClone.IsNew = data.Item1 == System.Net.HttpStatusCode.Created;
|
||||
});
|
||||
return serverState;
|
||||
}
|
||||
|
||||
public IList<Task<IObjectState>> SaveAllAsync(IList<IObjectState> states,
|
||||
|
@ -76,13 +75,13 @@ namespace LeanCloud.Storage.Internal {
|
|||
return stateTasks;
|
||||
}
|
||||
|
||||
public Task DeleteAsync(IObjectState state,
|
||||
public async Task DeleteAsync(IObjectState state,
|
||||
CancellationToken cancellationToken) {
|
||||
var command = new AVCommand {
|
||||
Path = $"classes/{state.ClassName}/{state.ObjectId}",
|
||||
Method = HttpMethod.Delete
|
||||
};
|
||||
return AVPlugins.Instance.CommandRunner.RunCommandAsync<IDictionary<string, object>>(command, cancellationToken);
|
||||
await AVPlugins.Instance.CommandRunner.RunCommandAsync<IDictionary<string, object>>(command, cancellationToken);
|
||||
}
|
||||
|
||||
public IList<Task> DeleteAllAsync(IList<IObjectState> states,
|
||||
|
@ -99,6 +98,7 @@ namespace LeanCloud.Storage.Internal {
|
|||
|
||||
// TODO (hallucinogen): move this out to a class to be used by Analytics
|
||||
private const int MaximumBatchSize = 50;
|
||||
|
||||
internal IList<Task<IDictionary<string, object>>> ExecuteBatchRequests(IList<AVCommand> requests,
|
||||
CancellationToken cancellationToken) {
|
||||
var tasks = new List<Task<IDictionary<string, object>>>();
|
||||
|
|
|
@ -814,49 +814,26 @@ string propertyName
|
|||
|
||||
#region Delete Object
|
||||
|
||||
internal Task DeleteAsync(Task toAwait, CancellationToken cancellationToken) {
|
||||
/// <summary>
|
||||
/// Deletes this object on the server.
|
||||
/// </summary>
|
||||
/// <param name="cancellationToken">The cancellation token.</param>
|
||||
public async Task DeleteAsync(CancellationToken cancellationToken = default) {
|
||||
if (ObjectId == null) {
|
||||
return Task.FromResult(0);
|
||||
return;
|
||||
}
|
||||
|
||||
return toAwait.OnSuccess(_ => {
|
||||
return ObjectController.DeleteAsync(State, cancellationToken);
|
||||
}).Unwrap().OnSuccess(_ => IsDirty = true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deletes this object on the server.
|
||||
/// </summary>
|
||||
public Task DeleteAsync() {
|
||||
return DeleteAsync(CancellationToken.None);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deletes this object on the server.
|
||||
/// </summary>
|
||||
/// <param name="cancellationToken">The cancellation token.</param>
|
||||
public Task DeleteAsync(CancellationToken cancellationToken) {
|
||||
return taskQueue.Enqueue(toAwait => DeleteAsync(toAwait, cancellationToken),
|
||||
cancellationToken);
|
||||
await ObjectController.DeleteAsync(State, cancellationToken);
|
||||
IsDirty = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deletes each object in the provided list.
|
||||
/// </summary>
|
||||
/// <param name="objects">The objects to delete.</param>
|
||||
public static Task DeleteAllAsync<T>(IEnumerable<T> objects) where T : AVObject {
|
||||
return DeleteAllAsync(objects, CancellationToken.None);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deletes each object in the provided list.
|
||||
/// </summary>
|
||||
/// <param name="objects">The objects to delete.</param>
|
||||
/// <param name="cancellationToken">The cancellation token.</param>
|
||||
public static Task DeleteAllAsync<T>(
|
||||
IEnumerable<T> objects, CancellationToken cancellationToken) where T : AVObject {
|
||||
public static Task DeleteAllAsync<T>(IEnumerable<T> objects, CancellationToken cancellationToken = default)
|
||||
where T : AVObject {
|
||||
var uniqueObjects = new HashSet<AVObject>(objects.OfType<AVObject>().ToList(),
|
||||
new IdentityEqualityComparer<AVObject>());
|
||||
new IdentityEqualityComparer<AVObject>());
|
||||
|
||||
return EnqueueForAll<object>(uniqueObjects, toAwait => {
|
||||
var states = uniqueObjects.Select(t => t.state).ToList();
|
||||
|
@ -1094,7 +1071,7 @@ string propertyName
|
|||
/// </summary>
|
||||
internal virtual void OnSettingValue(ref string key, ref object value) {
|
||||
if (key == null) {
|
||||
throw new ArgumentNullException("key");
|
||||
throw new ArgumentNullException(nameof(key));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1151,8 +1128,7 @@ string propertyName
|
|||
}
|
||||
|
||||
internal void SetIfDifferent<T>(string key, T value) {
|
||||
T current;
|
||||
bool hasCurrent = TryGetValue<T>(key, out current);
|
||||
bool hasCurrent = TryGetValue<T>(key, out T current);
|
||||
if (value == null) {
|
||||
if (hasCurrent) {
|
||||
PerformOperation(key, AVDeleteOperation.Instance);
|
||||
|
@ -1313,10 +1289,10 @@ string propertyName
|
|||
/// <returns></returns>
|
||||
public AVQuery<T> GetRelationRevserseQuery<T>(string parentClassName, string key) where T : AVObject {
|
||||
if (string.IsNullOrEmpty(parentClassName)) {
|
||||
throw new ArgumentNullException("parentClassName", "can not query a relation without parentClassName.");
|
||||
throw new ArgumentNullException(nameof(parentClassName), "can not query a relation without parentClassName.");
|
||||
}
|
||||
if (string.IsNullOrEmpty(key)) {
|
||||
throw new ArgumentNullException("key", "can not query a relation without key.");
|
||||
throw new ArgumentNullException(nameof(key), "can not query a relation without key.");
|
||||
}
|
||||
return new AVQuery<T>(parentClassName).WhereEqualTo(key, this);
|
||||
}
|
||||
|
@ -1604,7 +1580,7 @@ string propertyName
|
|||
// types.
|
||||
if (SubclassingController.GetType(className) != null) {
|
||||
throw new ArgumentException(
|
||||
"Use the class-specific query properties for class " + className, "className");
|
||||
"Use the class-specific query properties for class " + className, nameof(className));
|
||||
}
|
||||
return new AVQuery<AVObject>(className);
|
||||
}
|
||||
|
@ -1665,7 +1641,7 @@ string propertyName
|
|||
}
|
||||
|
||||
private SynchronizedEventHandler<PropertyUpdatedEventArgs> propertyUpdated =
|
||||
new SynchronizedEventHandler<PropertyUpdatedEventArgs>();
|
||||
new SynchronizedEventHandler<PropertyUpdatedEventArgs>();
|
||||
|
||||
public event PropertyUpdatedEventHandler PropertyUpdated {
|
||||
add {
|
||||
|
@ -1681,7 +1657,7 @@ string propertyName
|
|||
}
|
||||
|
||||
private SynchronizedEventHandler<CollectionPropertyUpdatedEventArgs> collectionUpdated =
|
||||
new SynchronizedEventHandler<CollectionPropertyUpdatedEventArgs>();
|
||||
new SynchronizedEventHandler<CollectionPropertyUpdatedEventArgs>();
|
||||
|
||||
public event CollectionPropertyUpdatedEventHandler CollectionPropertyUpdated {
|
||||
add {
|
||||
|
|
Loading…
Reference in New Issue