* AVCorePlugins.cs: chore: 将 AVObject 中的属性 ACL 单独处理
* AVObject.cs: * IObjectState.cs: * MutableObjectState.cs: * PointerOrLocalIdEncoder.cs: * AVUserController.cs: * AVObjectController.cs:
parent
fcb513aee3
commit
148c4d6ae2
|
@ -22,7 +22,6 @@ namespace LeanCloud.Storage.Internal {
|
|||
|
||||
private AVCloudCodeController cloudCodeController;
|
||||
private AVFileController fileController;
|
||||
private AVObjectController objectController;
|
||||
private AVQueryController queryController;
|
||||
private AVUserController userController;
|
||||
private ObjectSubclassingController subclassingController;
|
||||
|
@ -42,7 +41,6 @@ namespace LeanCloud.Storage.Internal {
|
|||
|
||||
CloudCodeController = null;
|
||||
FileController = null;
|
||||
ObjectController = null;
|
||||
UserController = null;
|
||||
SubclassingController = null;
|
||||
|
||||
|
@ -108,20 +106,6 @@ namespace LeanCloud.Storage.Internal {
|
|||
}
|
||||
}
|
||||
|
||||
public AVObjectController ObjectController {
|
||||
get {
|
||||
lock (mutex) {
|
||||
objectController = objectController ?? new AVObjectController();
|
||||
return objectController;
|
||||
}
|
||||
}
|
||||
set {
|
||||
lock (mutex) {
|
||||
objectController = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public AVQueryController QueryController {
|
||||
get {
|
||||
lock (mutex) {
|
||||
|
|
|
@ -46,24 +46,21 @@ namespace LeanCloud.Storage.Internal
|
|||
var operations = value.GetCurrentOperations();
|
||||
var operationJSON = AVObject.ToJSONObjectForSaving(operations);
|
||||
var objectJSON = value.ToDictionary(kvp => kvp.Key, kvp => PointerOrLocalIdEncoder.Instance.Encode(kvp.Value));
|
||||
foreach (var kvp in operationJSON)
|
||||
{
|
||||
foreach (var kvp in operationJSON) {
|
||||
objectJSON[kvp.Key] = kvp.Value;
|
||||
}
|
||||
if (value.CreatedAt.HasValue)
|
||||
{
|
||||
objectJSON["createdAt"] = value.CreatedAt.Value.ToString(AVClient.DateFormatStrings.First(),
|
||||
CultureInfo.InvariantCulture);
|
||||
if (value.CreatedAt.HasValue) {
|
||||
objectJSON["createdAt"] = value.CreatedAt.Value.ToString(AVClient.DateFormatStrings.First(), CultureInfo.InvariantCulture);
|
||||
}
|
||||
if (value.UpdatedAt.HasValue)
|
||||
{
|
||||
objectJSON["updatedAt"] = value.UpdatedAt.Value.ToString(AVClient.DateFormatStrings.First(),
|
||||
CultureInfo.InvariantCulture);
|
||||
if (value.UpdatedAt.HasValue) {
|
||||
objectJSON["updatedAt"] = value.UpdatedAt.Value.ToString(AVClient.DateFormatStrings.First(), CultureInfo.InvariantCulture);
|
||||
}
|
||||
if(!string.IsNullOrEmpty(value.ObjectId))
|
||||
{
|
||||
if(!string.IsNullOrEmpty(value.ObjectId)) {
|
||||
objectJSON["objectId"] = value.ObjectId;
|
||||
}
|
||||
if (value.ACL != null) {
|
||||
objectJSON["acl"] = Encode(value.ACL);
|
||||
}
|
||||
objectJSON["className"] = value.ClassName;
|
||||
objectJSON["__type"] = "Object";
|
||||
return objectJSON;
|
||||
|
|
|
@ -1,96 +0,0 @@
|
|||
using System;
|
||||
using System.Linq;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Net.Http;
|
||||
|
||||
namespace LeanCloud.Storage.Internal {
|
||||
public class AVObjectController {
|
||||
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
|
||||
};
|
||||
var data = await AVPlugins.Instance.CommandRunner.RunCommandAsync<IDictionary<string, object>>(command, cancellationToken);
|
||||
var objState = AVObjectCoder.Instance.Decode(data.Item2, AVDecoder.Instance);
|
||||
return objState;
|
||||
}
|
||||
|
||||
public async Task<IObjectState> SaveAsync(IObjectState state,
|
||||
IDictionary<string, IAVFieldOperation> operations,
|
||||
bool fetchWhenSave,
|
||||
AVQuery<AVObject> query,
|
||||
CancellationToken cancellationToken) {
|
||||
var objectJSON = AVObject.ToJSONObjectForSaving(operations);
|
||||
|
||||
var command = new AVCommand {
|
||||
Path = state.ObjectId == null ? $"classes/{Uri.EscapeDataString(state.ClassName)}" : $"classes/{Uri.EscapeDataString(state.ClassName)}/{state.ObjectId}",
|
||||
Method = state.ObjectId == null ? HttpMethod.Post : HttpMethod.Put,
|
||||
Content = objectJSON
|
||||
};
|
||||
Dictionary<string, object> args = new Dictionary<string, object>();
|
||||
if (fetchWhenSave) {
|
||||
args.Add("fetchWhenSave", fetchWhenSave);
|
||||
}
|
||||
// 查询条件
|
||||
if (query != null) {
|
||||
args.Add("where", query.BuildWhere());
|
||||
}
|
||||
if (args.Count > 0) {
|
||||
string encode = AVClient.BuildQueryString(args);
|
||||
command.Path = $"{command.Path}?{encode}";
|
||||
}
|
||||
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 async Task<IList<IObjectState>> SaveAllAsync(IList<IObjectState> states,
|
||||
IList<IDictionary<string, IAVFieldOperation>> operationsList,
|
||||
CancellationToken cancellationToken) {
|
||||
var requests = states
|
||||
.Zip(operationsList, (item, ops) => new AVCommand {
|
||||
Path = item.ObjectId == null ? $"classes/{Uri.EscapeDataString(item.ClassName)}" : $"classes/{Uri.EscapeDataString(item.ClassName)}/{Uri.EscapeDataString(item.ObjectId)}",
|
||||
Method = item.ObjectId == null ? HttpMethod.Post : HttpMethod.Put,
|
||||
Content = AVObject.ToJSONObjectForSaving(ops)
|
||||
})
|
||||
.ToList();
|
||||
IList<IObjectState> list = new List<IObjectState>();
|
||||
var result = await AVPlugins.Instance.CommandRunner.ExecuteBatchRequests(requests, cancellationToken);
|
||||
foreach (var data in result) {
|
||||
if (data.TryGetValue("success", out object val)) {
|
||||
IObjectState obj = AVObjectCoder.Instance.Decode(val as IDictionary<string, object>, AVDecoder.Instance);
|
||||
list.Add(obj);
|
||||
}
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
public async Task<IList<IObjectState>> SaveAllAsync(IList<AVObject> avObjects, CancellationToken cancellationToken) {
|
||||
List<AVCommand> commandList = new List<AVCommand>();
|
||||
foreach (AVObject avObj in avObjects) {
|
||||
AVCommand command = new AVCommand {
|
||||
Path = avObj.ObjectId == null ? $"classes/{Uri.EscapeDataString(avObj.ClassName)}" : $"classes/{Uri.EscapeDataString(avObj.ClassName)}/{Uri.EscapeDataString(avObj.ObjectId)}",
|
||||
Method = avObj.ObjectId == null ? HttpMethod.Post : HttpMethod.Put,
|
||||
Content = AVObject.ToJSONObjectForSaving(avObj.operationDict)
|
||||
};
|
||||
commandList.Add(command);
|
||||
}
|
||||
IList<IObjectState> list = new List<IObjectState>();
|
||||
var result = await AVPlugins.Instance.CommandRunner.ExecuteBatchRequests(commandList, cancellationToken);
|
||||
foreach (var data in result) {
|
||||
if (data.TryGetValue("success", out object val)) {
|
||||
IObjectState obj = AVObjectCoder.Instance.Decode(val as IDictionary<string, object>, AVDecoder.Instance);
|
||||
list.Add(obj);
|
||||
}
|
||||
}
|
||||
return list;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,13 +1,11 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace LeanCloud.Storage.Internal
|
||||
{
|
||||
public interface IObjectState : IEnumerable<KeyValuePair<string, object>>
|
||||
{
|
||||
bool IsNew { get; }
|
||||
namespace LeanCloud.Storage.Internal {
|
||||
public interface IObjectState : IEnumerable<KeyValuePair<string, object>> {
|
||||
string ClassName { get; }
|
||||
string ObjectId { get; }
|
||||
AVACL ACL { get; set; }
|
||||
DateTime? UpdatedAt { get; }
|
||||
DateTime? CreatedAt { get; }
|
||||
object this[string key] { get; }
|
||||
|
|
|
@ -3,7 +3,6 @@ using System.Collections.Generic;
|
|||
|
||||
namespace LeanCloud.Storage.Internal {
|
||||
public class MutableObjectState : IObjectState {
|
||||
public bool IsNew { get; set; }
|
||||
public string ClassName { get; set; }
|
||||
public string ObjectId { get; set; }
|
||||
public AVACL ACL { get; set; }
|
||||
|
@ -38,13 +37,12 @@ namespace LeanCloud.Storage.Internal {
|
|||
}
|
||||
|
||||
public void Apply(IObjectState other) {
|
||||
IsNew = other.IsNew;
|
||||
if (other.ObjectId != null) {
|
||||
ObjectId = other.ObjectId;
|
||||
}
|
||||
//if (other.ACL != null) {
|
||||
|
||||
//}
|
||||
if (other.ACL != null) {
|
||||
ACL = other.ACL;
|
||||
}
|
||||
if (other.UpdatedAt != null) {
|
||||
UpdatedAt = other.UpdatedAt;
|
||||
}
|
||||
|
@ -65,7 +63,6 @@ namespace LeanCloud.Storage.Internal {
|
|||
|
||||
protected virtual MutableObjectState MutableClone() {
|
||||
return new MutableObjectState {
|
||||
IsNew = IsNew,
|
||||
ClassName = ClassName,
|
||||
ObjectId = ObjectId,
|
||||
CreatedAt = CreatedAt,
|
||||
|
|
|
@ -14,9 +14,6 @@ namespace LeanCloud.Storage.Internal {
|
|||
};
|
||||
var ret = await AVPlugins.Instance.CommandRunner.RunCommandAsync<IDictionary<string, object>>(command);
|
||||
var serverState = AVObjectCoder.Instance.Decode(ret.Item2, AVDecoder.Instance);
|
||||
serverState = serverState.MutatedClone(mutableClone => {
|
||||
mutableClone.IsNew = true;
|
||||
});
|
||||
return serverState;
|
||||
}
|
||||
|
||||
|
@ -37,9 +34,6 @@ namespace LeanCloud.Storage.Internal {
|
|||
};
|
||||
var ret = await AVPlugins.Instance.CommandRunner.RunCommandAsync<IDictionary<string, object>>(command);
|
||||
var serverState = AVObjectCoder.Instance.Decode(ret.Item2, AVDecoder.Instance);
|
||||
serverState = serverState.MutatedClone(mutableClone => {
|
||||
mutableClone.IsNew = ret.Item1 == System.Net.HttpStatusCode.Created;
|
||||
});
|
||||
return serverState;
|
||||
}
|
||||
|
||||
|
@ -57,9 +51,6 @@ namespace LeanCloud.Storage.Internal {
|
|||
};
|
||||
var ret = await AVPlugins.Instance.CommandRunner.RunCommandAsync<IDictionary<string, object>>(command);
|
||||
var serverState = AVObjectCoder.Instance.Decode(ret.Item2, AVDecoder.Instance);
|
||||
serverState = serverState.MutatedClone(mutableClone => {
|
||||
mutableClone.IsNew = ret.Item1 == System.Net.HttpStatusCode.Created;
|
||||
});
|
||||
return serverState;
|
||||
}
|
||||
|
||||
|
@ -94,9 +85,6 @@ namespace LeanCloud.Storage.Internal {
|
|||
};
|
||||
var ret = await AVPlugins.Instance.CommandRunner.RunCommandAsync<IDictionary<string, object>>(command);
|
||||
var serverState = AVObjectCoder.Instance.Decode(ret.Item2, AVDecoder.Instance);
|
||||
serverState = serverState.MutatedClone(mutableClone => {
|
||||
mutableClone.IsNew = ret.Item1 == System.Net.HttpStatusCode.Created;
|
||||
});
|
||||
return serverState;
|
||||
}
|
||||
|
||||
|
|
|
@ -35,16 +35,16 @@ namespace LeanCloud {
|
|||
}
|
||||
}
|
||||
|
||||
internal AVACL acl;
|
||||
|
||||
[AVFieldName("ACL")]
|
||||
public AVACL ACL {
|
||||
// 设置 IsDirty
|
||||
get {
|
||||
return GetProperty<AVACL>(null, "ACL");
|
||||
}
|
||||
set {
|
||||
return acl;
|
||||
} set {
|
||||
acl = value;
|
||||
IsDirty = true;
|
||||
MutateState(mutableClone => {
|
||||
mutableClone.ACL = value;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -89,12 +89,6 @@ namespace LeanCloud {
|
|||
}
|
||||
}
|
||||
|
||||
internal static AVObjectController ObjectController {
|
||||
get {
|
||||
return AVPlugins.Instance.ObjectController;
|
||||
}
|
||||
}
|
||||
|
||||
internal static ObjectSubclassingController SubclassingController {
|
||||
get {
|
||||
return AVPlugins.Instance.SubclassingController;
|
||||
|
@ -261,6 +255,17 @@ namespace LeanCloud {
|
|||
RebuildEstimatedData();
|
||||
}
|
||||
|
||||
internal IDictionary<string, object> ToJSONObject() {
|
||||
IDictionary<string, object> result = new Dictionary<string, object>();
|
||||
if (ACL != null) {
|
||||
result["ACL"] = PointerOrLocalIdEncoder.Instance.Encode(ACL);
|
||||
}
|
||||
foreach (KeyValuePair<string, IAVFieldOperation> kv in operationDict) {
|
||||
result[kv.Key] = PointerOrLocalIdEncoder.Instance.Encode(kv.Value);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public static IDictionary<string, object> ToJSONObjectForSaving(IDictionary<string, IAVFieldOperation> operations) {
|
||||
var result = new Dictionary<string, object>();
|
||||
foreach (var pair in operations) {
|
||||
|
@ -296,8 +301,29 @@ namespace LeanCloud {
|
|||
}
|
||||
Stack<Batch> batches = BatchObjects(new List<AVObject> { this }, false);
|
||||
await SaveBatches(batches, cancellationToken);
|
||||
IObjectState result = await ObjectController.SaveAsync(state, operationDict, fetchWhenSave, query, cancellationToken);
|
||||
HandleSave(result);
|
||||
|
||||
IDictionary<string, object> objectJSON = ToJSONObject();
|
||||
var command = new AVCommand {
|
||||
Path = ObjectId == null ? $"classes/{Uri.EscapeDataString(ClassName)}" :
|
||||
$"classes/{Uri.EscapeDataString(ClassName)}/{ObjectId}",
|
||||
Method = ObjectId == null ? HttpMethod.Post : HttpMethod.Put,
|
||||
Content = objectJSON
|
||||
};
|
||||
Dictionary<string, object> args = new Dictionary<string, object>();
|
||||
if (fetchWhenSave) {
|
||||
args.Add("fetchWhenSave", fetchWhenSave);
|
||||
}
|
||||
// 查询条件
|
||||
if (query != null) {
|
||||
args.Add("where", query.BuildWhere());
|
||||
}
|
||||
if (args.Count > 0) {
|
||||
string encode = AVClient.BuildQueryString(args);
|
||||
command.Path = $"{command.Path}?{encode}";
|
||||
}
|
||||
var data = await AVPlugins.Instance.CommandRunner.RunCommandAsync<IDictionary<string, object>>(command, cancellationToken);
|
||||
IObjectState serverState = AVObjectCoder.Instance.Decode(data.Item2, AVDecoder.Instance);
|
||||
HandleSave(serverState);
|
||||
}
|
||||
|
||||
public static async Task SaveAllAsync<T>(IEnumerable<T> objects, CancellationToken cancellationToken = default)
|
||||
|
@ -315,10 +341,28 @@ namespace LeanCloud {
|
|||
while (batches.Any()) {
|
||||
Batch batch = batches.Pop();
|
||||
IList<AVObject> dirtyObjects = batch.Objects.Where(o => o.IsDirty).ToList();
|
||||
var serverStates = await ObjectController.SaveAllAsync(dirtyObjects, cancellationToken);
|
||||
|
||||
List<AVCommand> commandList = new List<AVCommand>();
|
||||
foreach (AVObject avObj in dirtyObjects) {
|
||||
AVCommand command = new AVCommand {
|
||||
Path = avObj.ObjectId == null ? $"classes/{Uri.EscapeDataString(avObj.ClassName)}" :
|
||||
$"classes/{Uri.EscapeDataString(avObj.ClassName)}/{Uri.EscapeDataString(avObj.ObjectId)}",
|
||||
Method = avObj.ObjectId == null ? HttpMethod.Post : HttpMethod.Put,
|
||||
Content = avObj.ToJSONObject()
|
||||
};
|
||||
commandList.Add(command);
|
||||
}
|
||||
IList<IObjectState> list = new List<IObjectState>();
|
||||
var result = await AVPlugins.Instance.CommandRunner.ExecuteBatchRequests(commandList, cancellationToken);
|
||||
foreach (var data in result) {
|
||||
if (data.TryGetValue("success", out object val)) {
|
||||
IObjectState obj = AVObjectCoder.Instance.Decode(val as IDictionary<string, object>, AVDecoder.Instance);
|
||||
list.Add(obj);
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
foreach (var pair in dirtyObjects.Zip(serverStates, (item, state) => new { item, state })) {
|
||||
foreach (var pair in dirtyObjects.Zip(list, (item, state) => new { item, state })) {
|
||||
pair.item.HandleSave(pair.state);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
|
@ -334,7 +378,14 @@ namespace LeanCloud {
|
|||
if (queryString == null) {
|
||||
queryString = new Dictionary<string, object>();
|
||||
}
|
||||
IObjectState objectState = await ObjectController.FetchAsync(state, queryString, cancellationToken);
|
||||
|
||||
var command = new AVCommand {
|
||||
Path = $"classes/{Uri.EscapeDataString(state.ClassName)}/{Uri.EscapeDataString(state.ObjectId)}?{AVClient.BuildQueryString(queryString)}",
|
||||
Method = HttpMethod.Get
|
||||
};
|
||||
var data = await AVPlugins.Instance.CommandRunner.RunCommandAsync<IDictionary<string, object>>(command, cancellationToken);
|
||||
IObjectState objectState = AVObjectCoder.Instance.Decode(data.Item2, AVDecoder.Instance);
|
||||
|
||||
HandleFetchResult(objectState);
|
||||
return this;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue