* AVCorePlugins.cs: chore: 将 AVObject 中的属性 ACL 单独处理

* AVObject.cs:
* IObjectState.cs:
* MutableObjectState.cs:
* PointerOrLocalIdEncoder.cs:
* AVUserController.cs:
* AVObjectController.cs:
oneRain 2019-12-16 14:40:16 +08:00
parent fcb513aee3
commit 148c4d6ae2
7 changed files with 83 additions and 164 deletions

View File

@ -22,7 +22,6 @@ namespace LeanCloud.Storage.Internal {
private AVCloudCodeController cloudCodeController; private AVCloudCodeController cloudCodeController;
private AVFileController fileController; private AVFileController fileController;
private AVObjectController objectController;
private AVQueryController queryController; private AVQueryController queryController;
private AVUserController userController; private AVUserController userController;
private ObjectSubclassingController subclassingController; private ObjectSubclassingController subclassingController;
@ -42,7 +41,6 @@ namespace LeanCloud.Storage.Internal {
CloudCodeController = null; CloudCodeController = null;
FileController = null; FileController = null;
ObjectController = null;
UserController = null; UserController = null;
SubclassingController = 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 { public AVQueryController QueryController {
get { get {
lock (mutex) { lock (mutex) {

View File

@ -46,24 +46,21 @@ namespace LeanCloud.Storage.Internal
var operations = value.GetCurrentOperations(); var operations = value.GetCurrentOperations();
var operationJSON = AVObject.ToJSONObjectForSaving(operations); var operationJSON = AVObject.ToJSONObjectForSaving(operations);
var objectJSON = value.ToDictionary(kvp => kvp.Key, kvp => PointerOrLocalIdEncoder.Instance.Encode(kvp.Value)); 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; objectJSON[kvp.Key] = kvp.Value;
} }
if (value.CreatedAt.HasValue) if (value.CreatedAt.HasValue) {
{ objectJSON["createdAt"] = value.CreatedAt.Value.ToString(AVClient.DateFormatStrings.First(), CultureInfo.InvariantCulture);
objectJSON["createdAt"] = value.CreatedAt.Value.ToString(AVClient.DateFormatStrings.First(),
CultureInfo.InvariantCulture);
} }
if (value.UpdatedAt.HasValue) if (value.UpdatedAt.HasValue) {
{ objectJSON["updatedAt"] = value.UpdatedAt.Value.ToString(AVClient.DateFormatStrings.First(), CultureInfo.InvariantCulture);
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; objectJSON["objectId"] = value.ObjectId;
} }
if (value.ACL != null) {
objectJSON["acl"] = Encode(value.ACL);
}
objectJSON["className"] = value.ClassName; objectJSON["className"] = value.ClassName;
objectJSON["__type"] = "Object"; objectJSON["__type"] = "Object";
return objectJSON; return objectJSON;

View File

@ -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;
}
}
}

View File

@ -1,13 +1,11 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
namespace LeanCloud.Storage.Internal namespace LeanCloud.Storage.Internal {
{ public interface IObjectState : IEnumerable<KeyValuePair<string, object>> {
public interface IObjectState : IEnumerable<KeyValuePair<string, object>>
{
bool IsNew { get; }
string ClassName { get; } string ClassName { get; }
string ObjectId { get; } string ObjectId { get; }
AVACL ACL { get; set; }
DateTime? UpdatedAt { get; } DateTime? UpdatedAt { get; }
DateTime? CreatedAt { get; } DateTime? CreatedAt { get; }
object this[string key] { get; } object this[string key] { get; }

View File

@ -3,7 +3,6 @@ using System.Collections.Generic;
namespace LeanCloud.Storage.Internal { namespace LeanCloud.Storage.Internal {
public class MutableObjectState : IObjectState { public class MutableObjectState : IObjectState {
public bool IsNew { get; set; }
public string ClassName { get; set; } public string ClassName { get; set; }
public string ObjectId { get; set; } public string ObjectId { get; set; }
public AVACL ACL { get; set; } public AVACL ACL { get; set; }
@ -38,13 +37,12 @@ namespace LeanCloud.Storage.Internal {
} }
public void Apply(IObjectState other) { public void Apply(IObjectState other) {
IsNew = other.IsNew;
if (other.ObjectId != null) { if (other.ObjectId != null) {
ObjectId = other.ObjectId; ObjectId = other.ObjectId;
} }
//if (other.ACL != null) { if (other.ACL != null) {
ACL = other.ACL;
//} }
if (other.UpdatedAt != null) { if (other.UpdatedAt != null) {
UpdatedAt = other.UpdatedAt; UpdatedAt = other.UpdatedAt;
} }
@ -65,7 +63,6 @@ namespace LeanCloud.Storage.Internal {
protected virtual MutableObjectState MutableClone() { protected virtual MutableObjectState MutableClone() {
return new MutableObjectState { return new MutableObjectState {
IsNew = IsNew,
ClassName = ClassName, ClassName = ClassName,
ObjectId = ObjectId, ObjectId = ObjectId,
CreatedAt = CreatedAt, CreatedAt = CreatedAt,

View File

@ -14,9 +14,6 @@ namespace LeanCloud.Storage.Internal {
}; };
var ret = await AVPlugins.Instance.CommandRunner.RunCommandAsync<IDictionary<string, object>>(command); var ret = await AVPlugins.Instance.CommandRunner.RunCommandAsync<IDictionary<string, object>>(command);
var serverState = AVObjectCoder.Instance.Decode(ret.Item2, AVDecoder.Instance); var serverState = AVObjectCoder.Instance.Decode(ret.Item2, AVDecoder.Instance);
serverState = serverState.MutatedClone(mutableClone => {
mutableClone.IsNew = true;
});
return serverState; return serverState;
} }
@ -37,9 +34,6 @@ namespace LeanCloud.Storage.Internal {
}; };
var ret = await AVPlugins.Instance.CommandRunner.RunCommandAsync<IDictionary<string, object>>(command); var ret = await AVPlugins.Instance.CommandRunner.RunCommandAsync<IDictionary<string, object>>(command);
var serverState = AVObjectCoder.Instance.Decode(ret.Item2, AVDecoder.Instance); var serverState = AVObjectCoder.Instance.Decode(ret.Item2, AVDecoder.Instance);
serverState = serverState.MutatedClone(mutableClone => {
mutableClone.IsNew = ret.Item1 == System.Net.HttpStatusCode.Created;
});
return serverState; return serverState;
} }
@ -57,9 +51,6 @@ namespace LeanCloud.Storage.Internal {
}; };
var ret = await AVPlugins.Instance.CommandRunner.RunCommandAsync<IDictionary<string, object>>(command); var ret = await AVPlugins.Instance.CommandRunner.RunCommandAsync<IDictionary<string, object>>(command);
var serverState = AVObjectCoder.Instance.Decode(ret.Item2, AVDecoder.Instance); var serverState = AVObjectCoder.Instance.Decode(ret.Item2, AVDecoder.Instance);
serverState = serverState.MutatedClone(mutableClone => {
mutableClone.IsNew = ret.Item1 == System.Net.HttpStatusCode.Created;
});
return serverState; return serverState;
} }
@ -94,9 +85,6 @@ namespace LeanCloud.Storage.Internal {
}; };
var ret = await AVPlugins.Instance.CommandRunner.RunCommandAsync<IDictionary<string, object>>(command); var ret = await AVPlugins.Instance.CommandRunner.RunCommandAsync<IDictionary<string, object>>(command);
var serverState = AVObjectCoder.Instance.Decode(ret.Item2, AVDecoder.Instance); var serverState = AVObjectCoder.Instance.Decode(ret.Item2, AVDecoder.Instance);
serverState = serverState.MutatedClone(mutableClone => {
mutableClone.IsNew = ret.Item1 == System.Net.HttpStatusCode.Created;
});
return serverState; return serverState;
} }

View File

@ -35,16 +35,16 @@ namespace LeanCloud {
} }
} }
internal AVACL acl;
[AVFieldName("ACL")] [AVFieldName("ACL")]
public AVACL ACL { public AVACL ACL {
// 设置 IsDirty
get { get {
return GetProperty<AVACL>(null, "ACL"); return acl;
} } set {
set { acl = value;
IsDirty = true; 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 { internal static ObjectSubclassingController SubclassingController {
get { get {
return AVPlugins.Instance.SubclassingController; return AVPlugins.Instance.SubclassingController;
@ -261,6 +255,17 @@ namespace LeanCloud {
RebuildEstimatedData(); 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) { public static IDictionary<string, object> ToJSONObjectForSaving(IDictionary<string, IAVFieldOperation> operations) {
var result = new Dictionary<string, object>(); var result = new Dictionary<string, object>();
foreach (var pair in operations) { foreach (var pair in operations) {
@ -296,8 +301,29 @@ namespace LeanCloud {
} }
Stack<Batch> batches = BatchObjects(new List<AVObject> { this }, false); Stack<Batch> batches = BatchObjects(new List<AVObject> { this }, false);
await SaveBatches(batches, cancellationToken); 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) public static async Task SaveAllAsync<T>(IEnumerable<T> objects, CancellationToken cancellationToken = default)
@ -315,10 +341,28 @@ namespace LeanCloud {
while (batches.Any()) { while (batches.Any()) {
Batch batch = batches.Pop(); Batch batch = batches.Pop();
IList<AVObject> dirtyObjects = batch.Objects.Where(o => o.IsDirty).ToList(); 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 { 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); pair.item.HandleSave(pair.state);
} }
} catch (Exception e) { } catch (Exception e) {
@ -334,7 +378,14 @@ namespace LeanCloud {
if (queryString == null) { if (queryString == null) {
queryString = new Dictionary<string, object>(); 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); HandleFetchResult(objectState);
return this; return this;
} }