* ObjectTest.cs: chore: 继续简化代码
* AVObject.cs: * AVObjectCoder.cs: * MutableObjectState.cs: * AVUserController.cs:
parent
a9ce795c45
commit
fcb513aee3
|
@ -27,7 +27,7 @@ namespace LeanCloud.Test {
|
|||
{ "hello", 1 },
|
||||
{ "world", 2 }
|
||||
};
|
||||
await obj.SaveAsync(fetchWhenSave: true);
|
||||
await obj.SaveAsync(true);
|
||||
Assert.NotNull(obj.ObjectId);
|
||||
Assert.NotNull(obj.CreatedAt);
|
||||
Assert.NotNull(obj.UpdatedAt);
|
||||
|
|
|
@ -39,30 +39,29 @@ namespace LeanCloud.Storage.Internal
|
|||
{
|
||||
IDictionary<string, object> serverData = new Dictionary<string, object>();
|
||||
var mutableData = new Dictionary<string, object>(data);
|
||||
string objectId = extractFromDictionary<string>(mutableData, "objectId", (obj) =>
|
||||
string objectId = ExtractFromDictionary<string>(mutableData, "objectId", (obj) =>
|
||||
{
|
||||
return obj as string;
|
||||
});
|
||||
DateTime? createdAt = extractFromDictionary<DateTime?>(mutableData, "createdAt", (obj) =>
|
||||
DateTime? createdAt = ExtractFromDictionary<DateTime?>(mutableData, "createdAt", (obj) =>
|
||||
{
|
||||
return (DateTime)obj;
|
||||
});
|
||||
DateTime? updatedAt = extractFromDictionary<DateTime?>(mutableData, "updatedAt", (obj) =>
|
||||
DateTime? updatedAt = ExtractFromDictionary<DateTime?>(mutableData, "updatedAt", (obj) =>
|
||||
{
|
||||
return (DateTime)obj;
|
||||
});
|
||||
|
||||
if (mutableData.ContainsKey("ACL"))
|
||||
AVACL acl = ExtractFromDictionary(mutableData, "ACL", (obj) =>
|
||||
{
|
||||
serverData["ACL"] = extractFromDictionary<AVACL>(mutableData, "ACL", (obj) =>
|
||||
{
|
||||
return new AVACL(obj as IDictionary<string, object>);
|
||||
});
|
||||
}
|
||||
string className = extractFromDictionary<string>(mutableData, "className", obj =>
|
||||
return new AVACL(obj as IDictionary<string, object>);
|
||||
});
|
||||
|
||||
string className = ExtractFromDictionary(mutableData, "className", obj =>
|
||||
{
|
||||
return obj as string;
|
||||
});
|
||||
|
||||
if (createdAt != null && updatedAt == null)
|
||||
{
|
||||
updatedAt = createdAt;
|
||||
|
@ -83,6 +82,7 @@ namespace LeanCloud.Storage.Internal
|
|||
return new MutableObjectState
|
||||
{
|
||||
ObjectId = objectId,
|
||||
ACL = acl,
|
||||
CreatedAt = createdAt,
|
||||
UpdatedAt = updatedAt,
|
||||
ServerData = serverData,
|
||||
|
@ -90,12 +90,11 @@ namespace LeanCloud.Storage.Internal
|
|||
};
|
||||
}
|
||||
|
||||
private T extractFromDictionary<T>(IDictionary<string, object> data, string key, Func<object, T> action)
|
||||
private T ExtractFromDictionary<T>(IDictionary<string, object> data, string key, Func<object, T> action)
|
||||
{
|
||||
T result = default(T);
|
||||
if (data.ContainsKey(key))
|
||||
{
|
||||
result = action(data[key]);
|
||||
T result = default;
|
||||
if (data.TryGetValue(key, out object val)) {
|
||||
result = action(val);
|
||||
data.Remove(key);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
using System;
|
||||
using System.Linq;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace LeanCloud.Storage.Internal {
|
||||
|
@ -7,6 +6,7 @@ namespace LeanCloud.Storage.Internal {
|
|||
public bool IsNew { get; set; }
|
||||
public string ClassName { get; set; }
|
||||
public string ObjectId { get; set; }
|
||||
public AVACL ACL { get; set; }
|
||||
public DateTime? UpdatedAt { get; set; }
|
||||
public DateTime? CreatedAt { get; set; }
|
||||
|
||||
|
@ -42,6 +42,9 @@ namespace LeanCloud.Storage.Internal {
|
|||
if (other.ObjectId != null) {
|
||||
ObjectId = other.ObjectId;
|
||||
}
|
||||
//if (other.ACL != null) {
|
||||
|
||||
//}
|
||||
if (other.UpdatedAt != null) {
|
||||
UpdatedAt = other.UpdatedAt;
|
||||
}
|
||||
|
@ -67,7 +70,7 @@ namespace LeanCloud.Storage.Internal {
|
|||
ObjectId = ObjectId,
|
||||
CreatedAt = CreatedAt,
|
||||
UpdatedAt = UpdatedAt,
|
||||
ServerData = this.ToDictionary(t => t.Key, t => t.Value)
|
||||
ServerData = new Dictionary<string, object>(ServerData)
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -52,7 +52,7 @@ namespace LeanCloud.Storage.Internal {
|
|||
Path = path,
|
||||
Method = HttpMethod.Post,
|
||||
Content = new Dictionary<string, object> {
|
||||
{ "authData", authData}
|
||||
{ "authData", authData }
|
||||
}
|
||||
};
|
||||
var ret = await AVPlugins.Instance.CommandRunner.RunCommandAsync<IDictionary<string, object>>(command);
|
||||
|
|
|
@ -12,6 +12,10 @@ using System.Collections.Concurrent;
|
|||
|
||||
namespace LeanCloud {
|
||||
public class AVObject : IEnumerable<KeyValuePair<string, object>> {
|
||||
static readonly HashSet<string> RESERVED_KEYS = new HashSet<string> {
|
||||
"objectId", "ACL", "createdAt", "updatedAt"
|
||||
};
|
||||
|
||||
public string ClassName {
|
||||
get {
|
||||
return state.ClassName;
|
||||
|
@ -25,7 +29,9 @@ namespace LeanCloud {
|
|||
}
|
||||
set {
|
||||
IsDirty = true;
|
||||
SetObjectIdInternal(value);
|
||||
MutateState(mutableClone => {
|
||||
mutableClone.ObjectId = value;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -35,7 +41,10 @@ namespace LeanCloud {
|
|||
return GetProperty<AVACL>(null, "ACL");
|
||||
}
|
||||
set {
|
||||
SetProperty(value, "ACL");
|
||||
IsDirty = true;
|
||||
MutateState(mutableClone => {
|
||||
mutableClone.ACL = value;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -65,9 +74,6 @@ namespace LeanCloud {
|
|||
private readonly ConcurrentDictionary<string, object> serverData = new ConcurrentDictionary<string, object>();
|
||||
private readonly ConcurrentDictionary<string, object> estimatedData = new ConcurrentDictionary<string, object>();
|
||||
|
||||
private static readonly ThreadLocal<bool> isCreatingPointer = new ThreadLocal<bool>(() => false);
|
||||
|
||||
private bool hasBeenFetched;
|
||||
private bool dirty;
|
||||
|
||||
private IObjectState state;
|
||||
|
@ -106,9 +112,6 @@ namespace LeanCloud {
|
|||
}
|
||||
|
||||
public AVObject(string className) {
|
||||
var isPointer = isCreatingPointer.Value;
|
||||
isCreatingPointer.Value = false;
|
||||
|
||||
if (className == null) {
|
||||
throw new ArgumentException("You must specify a LeanCloud class name when creating a new AVObject.");
|
||||
}
|
||||
|
@ -122,14 +125,7 @@ namespace LeanCloud {
|
|||
state = new MutableObjectState {
|
||||
ClassName = className
|
||||
};
|
||||
|
||||
if (!isPointer) {
|
||||
hasBeenFetched = true;
|
||||
IsDirty = true;
|
||||
} else {
|
||||
IsDirty = false;
|
||||
hasBeenFetched = false;
|
||||
}
|
||||
IsDirty = true;
|
||||
}
|
||||
|
||||
public static AVObject Create(string className) {
|
||||
|
@ -137,18 +133,13 @@ namespace LeanCloud {
|
|||
}
|
||||
|
||||
public static AVObject CreateWithoutData(string className, string objectId) {
|
||||
isCreatingPointer.Value = true;
|
||||
try {
|
||||
var result = SubclassingController.Instantiate(className);
|
||||
result.ObjectId = objectId;
|
||||
result.IsDirty = false;
|
||||
if (result.IsDirty) {
|
||||
throw new InvalidOperationException("A AVObject subclass default constructor must not make changes to the object that cause it to be dirty.");
|
||||
}
|
||||
return result;
|
||||
} finally {
|
||||
isCreatingPointer.Value = false;
|
||||
var result = SubclassingController.Instantiate(className);
|
||||
result.ObjectId = objectId;
|
||||
result.IsDirty = false;
|
||||
if (result.IsDirty) {
|
||||
throw new InvalidOperationException("A AVObject subclass default constructor must not make changes to the object that cause it to be dirty.");
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public static T Create<T>() where T : AVObject {
|
||||
|
@ -231,9 +222,6 @@ namespace LeanCloud {
|
|||
// Make a new serverData with fetched values.
|
||||
var newServerData = serverState.ToDictionary(t => t.Key, t => t.Value);
|
||||
|
||||
// Trigger handler based on serverState
|
||||
hasBeenFetched |= serverState.ObjectId != null;
|
||||
|
||||
// We cache the fetched object because subsequent Save operation might flush
|
||||
// the fetched objects into Pointers.
|
||||
//IDictionary<string, AVObject> fetchedObject = CollectFetchedObjects();
|
||||
|
@ -367,21 +355,13 @@ namespace LeanCloud {
|
|||
private static Task<IEnumerable<T>> FetchAllInternalAsync<T>(
|
||||
IEnumerable<T> objects, bool force, CancellationToken cancellationToken) where T : AVObject {
|
||||
|
||||
if (objects.Any(obj => { return obj.state.ObjectId == null; })) {
|
||||
if (objects.Any(obj => obj.state.ObjectId == null)) {
|
||||
throw new InvalidOperationException("You cannot fetch objects that haven't already been saved.");
|
||||
}
|
||||
|
||||
var objectsToFetch = (from obj in objects
|
||||
where force || !obj.IsDataAvailable
|
||||
select obj).ToList();
|
||||
|
||||
if (objectsToFetch.Count == 0) {
|
||||
return Task.FromResult(objects);
|
||||
}
|
||||
|
||||
// Do one Find for each class.
|
||||
var findsByClass =
|
||||
(from obj in objectsToFetch
|
||||
(from obj in objects
|
||||
group obj.ObjectId by obj.ClassName into classGroup
|
||||
where classGroup.Any()
|
||||
select new {
|
||||
|
@ -398,13 +378,12 @@ namespace LeanCloud {
|
|||
}
|
||||
|
||||
// Merge the data from the Finds into the input objects.
|
||||
var pairs = from obj in objectsToFetch
|
||||
var pairs = from obj in objects
|
||||
from result in findsByClass[obj.ClassName].Result
|
||||
where result.ObjectId == obj.ObjectId
|
||||
select new { obj, result };
|
||||
foreach (var pair in pairs) {
|
||||
pair.obj.MergeFromObject(pair.result);
|
||||
pair.obj.hasBeenFetched = true;
|
||||
}
|
||||
|
||||
return objects;
|
||||
|
@ -456,7 +435,7 @@ namespace LeanCloud {
|
|||
#endregion
|
||||
|
||||
public virtual void Remove(string key) {
|
||||
CheckKeyIsMutable(key);
|
||||
CheckKeyValid(key);
|
||||
PerformOperation(key, AVDeleteOperation.Instance);
|
||||
}
|
||||
|
||||
|
@ -504,7 +483,7 @@ namespace LeanCloud {
|
|||
|
||||
virtual public object this[string key] {
|
||||
get {
|
||||
CheckGetAccess(key);
|
||||
CheckKeyValid(key);
|
||||
|
||||
if (!estimatedData.TryGetValue(key, out object value)) {
|
||||
value = state[key];
|
||||
|
@ -518,7 +497,7 @@ namespace LeanCloud {
|
|||
return value;
|
||||
}
|
||||
set {
|
||||
CheckKeyIsMutable(key);
|
||||
CheckKeyValid(key);
|
||||
Set(key, value);
|
||||
}
|
||||
}
|
||||
|
@ -535,40 +514,54 @@ namespace LeanCloud {
|
|||
}
|
||||
|
||||
public void Increment(string key, long amount) {
|
||||
CheckKeyIsMutable(key);
|
||||
CheckKeyValid(key);
|
||||
PerformOperation(key, new AVIncrementOperation(amount));
|
||||
}
|
||||
|
||||
public void Increment(string key, double amount) {
|
||||
CheckKeyIsMutable(key);
|
||||
CheckKeyValid(key);
|
||||
PerformOperation(key, new AVIncrementOperation(amount));
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
public void AddToList(string key, object value) {
|
||||
CheckKeyValid(key);
|
||||
AddRangeToList(key, new[] { value });
|
||||
}
|
||||
|
||||
public void AddRangeToList<T>(string key, IEnumerable<T> values) {
|
||||
CheckKeyIsMutable(key);
|
||||
CheckKeyValid(key);
|
||||
PerformOperation(key, new AVAddOperation(values.Cast<object>()));
|
||||
}
|
||||
|
||||
public void AddUniqueToList(string key, object value) {
|
||||
CheckKeyValid(key);
|
||||
AddRangeUniqueToList(key, new object[] { value });
|
||||
}
|
||||
|
||||
public void AddRangeUniqueToList<T>(string key, IEnumerable<T> values) {
|
||||
CheckKeyIsMutable(key);
|
||||
CheckKeyValid(key);
|
||||
PerformOperation(key, new AVAddUniqueOperation(values.Cast<object>()));
|
||||
}
|
||||
|
||||
public void RemoveAllFromList<T>(string key, IEnumerable<T> values) {
|
||||
CheckKeyIsMutable(key);
|
||||
CheckKeyValid(key);
|
||||
PerformOperation(key, new AVRemoveOperation(values.Cast<object>()));
|
||||
}
|
||||
|
||||
void CheckKeyValid(string key) {
|
||||
if (string.IsNullOrEmpty(key)) {
|
||||
throw new ArgumentNullException(nameof(key));
|
||||
}
|
||||
if (key.StartsWith("_", StringComparison.CurrentCulture)) {
|
||||
throw new ArgumentException("key should not start with _");
|
||||
}
|
||||
if (RESERVED_KEYS.Contains(key)) {
|
||||
throw new ArgumentException($"key: {key} is reserved by LeanCloud");
|
||||
}
|
||||
}
|
||||
|
||||
public bool ContainsKey(string key) {
|
||||
return estimatedData.ContainsKey(key) || state.ContainsKey(key);
|
||||
}
|
||||
|
@ -608,32 +601,6 @@ namespace LeanCloud {
|
|||
return false;
|
||||
}
|
||||
|
||||
public bool IsDataAvailable {
|
||||
get {
|
||||
return hasBeenFetched;
|
||||
}
|
||||
}
|
||||
|
||||
private bool CheckIsDataAvailable(string key) {
|
||||
return IsDataAvailable || estimatedData.ContainsKey(key);
|
||||
}
|
||||
|
||||
private void CheckGetAccess(string key) {
|
||||
if (!CheckIsDataAvailable(key)) {
|
||||
throw new InvalidOperationException("AVObject has no data for this key. Call FetchIfNeededAsync() to get the data.");
|
||||
}
|
||||
}
|
||||
|
||||
private void CheckKeyIsMutable(string key) {
|
||||
if (!IsKeyMutable(key)) {
|
||||
throw new InvalidOperationException($"Cannot change the `{key}` property of a `{ClassName}` object.");
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual bool IsKeyMutable(string key) {
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool HasSameId(AVObject other) {
|
||||
return other != null &&
|
||||
object.Equals(ClassName, other.ClassName) &&
|
||||
|
@ -657,12 +624,6 @@ namespace LeanCloud {
|
|||
return dirty || operationDict.Count > 0;
|
||||
}
|
||||
|
||||
private void SetObjectIdInternal(string objectId) {
|
||||
MutateState(mutableClone => {
|
||||
mutableClone.ObjectId = objectId;
|
||||
});
|
||||
}
|
||||
|
||||
public void Add(string key, object value) {
|
||||
if (ContainsKey(key)) {
|
||||
throw new ArgumentException("Key already exists", key);
|
||||
|
|
Loading…
Reference in New Issue