chore: 简化 InstallationController 逻辑
parent
c994aad93e
commit
265a34aa9b
|
@ -1,9 +1,7 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Newtonsoft.Json;
|
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
|
|
||||||
namespace LeanCloud.Storage.Internal {
|
namespace LeanCloud.Storage.Internal {
|
||||||
|
|
|
@ -5,43 +5,35 @@ using System.Threading.Tasks;
|
||||||
using LeanCloud.Utilities;
|
using LeanCloud.Utilities;
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
|
|
||||||
namespace LeanCloud.Storage.Internal
|
namespace LeanCloud.Storage.Internal {
|
||||||
{
|
public class AVCloudCodeController {
|
||||||
public class AVCloudCodeController
|
|
||||||
{
|
|
||||||
public Task<T> CallFunctionAsync<T>(String name,
|
public Task<T> CallFunctionAsync<T>(String name,
|
||||||
IDictionary<string, object> parameters,
|
IDictionary<string, object> parameters,
|
||||||
string sessionToken,
|
string sessionToken,
|
||||||
CancellationToken cancellationToken)
|
CancellationToken cancellationToken) {
|
||||||
{
|
|
||||||
var command = new EngineCommand {
|
var command = new EngineCommand {
|
||||||
Path = $"functions/{Uri.EscapeUriString(name)}",
|
Path = $"functions/{Uri.EscapeUriString(name)}",
|
||||||
Method = HttpMethod.Post,
|
Method = HttpMethod.Post,
|
||||||
Content = parameters
|
Content = parameters
|
||||||
};
|
};
|
||||||
return AVPlugins.Instance.CommandRunner.RunCommandAsync<IDictionary<string, object>>(command, cancellationToken: cancellationToken).OnSuccess(t =>
|
return AVPlugins.Instance.CommandRunner.RunCommandAsync<IDictionary<string, object>>(command, cancellationToken: cancellationToken).OnSuccess(t => {
|
||||||
{
|
|
||||||
var decoded = AVDecoder.Instance.Decode(t.Result.Item2) as IDictionary<string, object>;
|
var decoded = AVDecoder.Instance.Decode(t.Result.Item2) as IDictionary<string, object>;
|
||||||
if (!decoded.ContainsKey("result"))
|
if (!decoded.ContainsKey("result")) {
|
||||||
{
|
|
||||||
return default(T);
|
return default(T);
|
||||||
}
|
}
|
||||||
return Conversion.To<T>(decoded["result"]);
|
return Conversion.To<T>(decoded["result"]);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task<T> RPCFunction<T>(string name, IDictionary<string, object> parameters, string sessionToken, CancellationToken cancellationToken)
|
public Task<T> RPCFunction<T>(string name, IDictionary<string, object> parameters, string sessionToken, CancellationToken cancellationToken) {
|
||||||
{
|
|
||||||
var command = new EngineCommand {
|
var command = new EngineCommand {
|
||||||
Path = $"call/{Uri.EscapeUriString(name)}",
|
Path = $"call/{Uri.EscapeUriString(name)}",
|
||||||
Method = HttpMethod.Post,
|
Method = HttpMethod.Post,
|
||||||
Content = parameters
|
Content = parameters
|
||||||
};
|
};
|
||||||
return AVPlugins.Instance.CommandRunner.RunCommandAsync<IDictionary<string, object>>(command, cancellationToken: cancellationToken).OnSuccess(t =>
|
return AVPlugins.Instance.CommandRunner.RunCommandAsync<IDictionary<string, object>>(command, cancellationToken: cancellationToken).OnSuccess(t => {
|
||||||
{
|
|
||||||
var decoded = AVDecoder.Instance.Decode(t.Result.Item2) as IDictionary<string, object>;
|
var decoded = AVDecoder.Instance.Decode(t.Result.Item2) as IDictionary<string, object>;
|
||||||
if (!decoded.ContainsKey("result"))
|
if (!decoded.ContainsKey("result")) {
|
||||||
{
|
|
||||||
return default(T);
|
return default(T);
|
||||||
}
|
}
|
||||||
return Conversion.To<T>(decoded["result"]);
|
return Conversion.To<T>(decoded["result"]);
|
||||||
|
|
|
@ -36,7 +36,7 @@ namespace LeanCloud.Storage.Internal {
|
||||||
Content = new StringContent(JsonConvert.SerializeObject(command.Content))
|
Content = new StringContent(JsonConvert.SerializeObject(command.Content))
|
||||||
};
|
};
|
||||||
|
|
||||||
var headers = await GetHeadersAsync();
|
var headers = GetHeadersAsync();
|
||||||
foreach (var header in headers) {
|
foreach (var header in headers) {
|
||||||
if (!string.IsNullOrEmpty(header.Value)) {
|
if (!string.IsNullOrEmpty(header.Value)) {
|
||||||
request.Headers.Add(header.Key, header.Value);
|
request.Headers.Add(header.Key, header.Value);
|
||||||
|
@ -89,10 +89,10 @@ namespace LeanCloud.Storage.Internal {
|
||||||
|
|
||||||
private const string revocableSessionTokenTrueValue = "1";
|
private const string revocableSessionTokenTrueValue = "1";
|
||||||
|
|
||||||
async Task<Dictionary<string, string>> GetHeadersAsync() {
|
Dictionary<string, string> GetHeadersAsync() {
|
||||||
var headers = new Dictionary<string, string>();
|
var headers = new Dictionary<string, string>();
|
||||||
var installationId = await AVPlugins.Instance.InstallationIdController.GetAsync();
|
var installationId = AVPlugins.Instance.InstallationIdController.Get();
|
||||||
headers.Add("X-LC-Installation-Id", installationId.ToString());
|
headers.Add("X-LC-Installation-Id", installationId);
|
||||||
var conf = AVClient.CurrentConfiguration;
|
var conf = AVClient.CurrentConfiguration;
|
||||||
headers.Add("X-LC-Id", conf.ApplicationId);
|
headers.Add("X-LC-Id", conf.ApplicationId);
|
||||||
long timestamp = (long)(DateTime.UtcNow - new DateTime(1970, 1, 1)).TotalMilliseconds;
|
long timestamp = (long)(DateTime.UtcNow - new DateTime(1970, 1, 1)).TotalMilliseconds;
|
||||||
|
|
|
@ -1,59 +1,39 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Threading.Tasks;
|
using System.IO;
|
||||||
|
|
||||||
namespace LeanCloud.Storage.Internal {
|
namespace LeanCloud.Storage.Internal {
|
||||||
|
/// <summary>
|
||||||
|
/// 临时方案,后面将有 Android 和 iOS 提供 device token
|
||||||
|
/// </summary>
|
||||||
public class InstallationIdController {
|
public class InstallationIdController {
|
||||||
private const string InstallationIdKey = "InstallationId";
|
private string installationId;
|
||||||
private readonly object mutex = new object();
|
private readonly object mutex = new object();
|
||||||
private Guid? installationId;
|
|
||||||
|
|
||||||
public Task SetAsync(Guid? installationId) {
|
public string Get() {
|
||||||
lock (mutex) {
|
if (installationId == null) {
|
||||||
Task saveTask;
|
lock (mutex) {
|
||||||
|
if (installationId == null) {
|
||||||
if (installationId == null) {
|
string installationPath = "installation.conf";
|
||||||
saveTask = AVPlugins.Instance.StorageController
|
// 文件读取或从 Native 平台读取
|
||||||
.LoadAsync()
|
if (File.Exists(installationPath)) {
|
||||||
.OnSuccess(storage => storage.Result.RemoveAsync(InstallationIdKey))
|
using (StreamReader reader = new StreamReader(installationPath)) {
|
||||||
.Unwrap();
|
installationId = reader.ReadToEnd();
|
||||||
} else {
|
if (installationId != null) {
|
||||||
saveTask = AVPlugins.Instance.StorageController
|
return installationId;
|
||||||
.LoadAsync()
|
}
|
||||||
.OnSuccess(storage => storage.Result.AddAsync(InstallationIdKey, installationId.ToString()))
|
}
|
||||||
.Unwrap();
|
}
|
||||||
}
|
// 生成新的 device token
|
||||||
this.installationId = installationId;
|
Guid newInstallationId = Guid.NewGuid();
|
||||||
return saveTask;
|
installationId = newInstallationId.ToString();
|
||||||
}
|
// 写回文件
|
||||||
}
|
using (StreamWriter writer = new StreamWriter(installationPath)) {
|
||||||
|
writer.Write(installationId);
|
||||||
public Task<Guid?> GetAsync() {
|
}
|
||||||
lock (mutex) {
|
}
|
||||||
if (installationId != null) {
|
|
||||||
return Task.FromResult(installationId);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return installationId;
|
||||||
return AVPlugins.Instance.StorageController
|
|
||||||
.LoadAsync()
|
|
||||||
.OnSuccess(s => {
|
|
||||||
object id;
|
|
||||||
s.Result.TryGetValue(InstallationIdKey, out id);
|
|
||||||
try {
|
|
||||||
lock (mutex) {
|
|
||||||
installationId = new Guid((string)id);
|
|
||||||
return Task.FromResult(installationId);
|
|
||||||
}
|
|
||||||
} catch (Exception) {
|
|
||||||
var newInstallationId = Guid.NewGuid();
|
|
||||||
return SetAsync(newInstallationId).OnSuccess<Guid?>(_ => newInstallationId);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.Unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
public Task ClearAsync() {
|
|
||||||
return SetAsync(null);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,51 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.Threading;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace LeanCloud.Storage.Internal {
|
|
||||||
/// <summary>
|
|
||||||
/// <code>IAVObjectCurrentController</code> controls the single-instance <see cref="AVObject"/>
|
|
||||||
/// persistence used throughout the code-base. Sample usages are <see cref="AVUser.CurrentUser"/> and
|
|
||||||
/// <see cref="AVInstallation.CurrentInstallation"/>.
|
|
||||||
/// </summary>
|
|
||||||
/// <typeparam name="T">Type of object being persisted.</typeparam>
|
|
||||||
public interface IAVObjectCurrentController<T> where T : AVObject {
|
|
||||||
/// <summary>
|
|
||||||
/// Persists current <see cref="AVObject"/>.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="obj"><see cref="AVObject"/> to be persisted.</param>
|
|
||||||
/// <param name="cancellationToken">The cancellation token.</param>
|
|
||||||
Task SetAsync(T obj, CancellationToken cancellationToken);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the persisted current <see cref="AVObject"/>.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="cancellationToken">The cancellation token.</param>
|
|
||||||
Task<T> GetAsync(CancellationToken cancellationToken);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Returns a <see cref="Task"/> that resolves to <code>true</code> if current
|
|
||||||
/// <see cref="AVObject"/> exists.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="cancellationToken">The cancellation token.</param>
|
|
||||||
Task<bool> ExistsAsync(CancellationToken cancellationToken);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Returns <code>true</code> if the given <see cref="AVObject"/> is the persisted current
|
|
||||||
/// <see cref="AVObject"/>.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="obj">The object to check.</param>
|
|
||||||
/// <returns>True if <code>obj</code> is the current persisted <see cref="AVObject"/>.</returns>
|
|
||||||
bool IsCurrent(T obj);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Nullifies the current <see cref="AVObject"/> from memory.
|
|
||||||
/// </summary>
|
|
||||||
void ClearFromMemory();
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Clears current <see cref="AVObject"/> from disk.
|
|
||||||
/// </summary>
|
|
||||||
void ClearFromDisk();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -30,14 +30,11 @@ namespace LeanCloud.Storage.Internal {
|
||||||
}
|
}
|
||||||
|
|
||||||
public Type GetType(string className) {
|
public Type GetType(string className) {
|
||||||
ObjectSubclassInfo info = null;
|
|
||||||
mutex.EnterReadLock();
|
mutex.EnterReadLock();
|
||||||
registeredSubclasses.TryGetValue(className, out info);
|
registeredSubclasses.TryGetValue(className, out ObjectSubclassInfo info);
|
||||||
mutex.ExitReadLock();
|
mutex.ExitReadLock();
|
||||||
|
|
||||||
return info != null
|
return info?.TypeInfo.AsType();
|
||||||
? info.TypeInfo.AsType()
|
|
||||||
: null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool IsTypeValid(string className, Type type) {
|
public bool IsTypeValid(string className, Type type) {
|
||||||
|
|
|
@ -19,13 +19,13 @@ namespace LeanCloud.Storage.Internal {
|
||||||
dictionary = new Dictionary<string, object>();
|
dictionary = new Dictionary<string, object>();
|
||||||
}
|
}
|
||||||
|
|
||||||
internal Task SaveAsync() {
|
internal async Task SaveAsync() {
|
||||||
string json;
|
string json;
|
||||||
locker.EnterReadLock();
|
locker.EnterReadLock();
|
||||||
json = JsonConvert.SerializeObject(dictionary);
|
json = await JsonUtils.SerializeObjectAsync(dictionary);
|
||||||
locker.ExitReadLock();
|
locker.ExitReadLock();
|
||||||
using (var sw = new StreamWriter(filePath)) {
|
using (var sw = new StreamWriter(filePath)) {
|
||||||
return sw.WriteAsync(json);
|
await sw.WriteAsync(json);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -150,6 +150,7 @@ namespace LeanCloud.Storage.Internal {
|
||||||
private readonly TaskQueue taskQueue = new TaskQueue();
|
private readonly TaskQueue taskQueue = new TaskQueue();
|
||||||
private readonly Task<string> fileTask;
|
private readonly Task<string> fileTask;
|
||||||
private StorageDictionary storageDictionary;
|
private StorageDictionary storageDictionary;
|
||||||
|
private IDictionary<string, object> storage;
|
||||||
|
|
||||||
public StorageController(string fileNamePrefix) {
|
public StorageController(string fileNamePrefix) {
|
||||||
fileTask = taskQueue.Enqueue(t => t.ContinueWith(_ => {
|
fileTask = taskQueue.Enqueue(t => t.ContinueWith(_ => {
|
||||||
|
|
|
@ -3,17 +3,24 @@ using Newtonsoft.Json;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace LeanCloud.Storage.Internal {
|
namespace LeanCloud.Storage.Internal {
|
||||||
|
/// <summary>
|
||||||
|
/// 为 Json 解析提供异步接口
|
||||||
|
/// </summary>
|
||||||
public static class JsonUtils {
|
public static class JsonUtils {
|
||||||
public static Task<T> DeserializeObjectAsync<T>(string str) {
|
public static async Task<string> SerializeObjectAsync(object obj) {
|
||||||
var tcs = new TaskCompletionSource<T>();
|
string str = null;
|
||||||
Task.Run(() => {
|
await Task.Run(() => {
|
||||||
try {
|
str = JsonConvert.SerializeObject(obj);
|
||||||
tcs.SetResult(JsonConvert.DeserializeObject<T>(str));
|
|
||||||
} catch (Exception e) {
|
|
||||||
tcs.SetException(e);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
return tcs.Task;
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async Task<T> DeserializeObjectAsync<T>(string str, params JsonConverter[] converters) {
|
||||||
|
T obj = default;
|
||||||
|
await Task.Run(() => {
|
||||||
|
obj = JsonConvert.DeserializeObject<T>(str, converters);
|
||||||
|
});
|
||||||
|
return obj;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue