* ObjectTest.cs: chore: 继续简化代码

* AVObject.cs:
* AVObjectCoder.cs:
* MutableObjectState.cs:
* AVUserController.cs:
oneRain 2019-12-12 17:11:17 +08:00
parent a9ce795c45
commit fcb513aee3
5 changed files with 66 additions and 103 deletions

View File

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

View File

@ -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"))
{
serverData["ACL"] = extractFromDictionary<AVACL>(mutableData, "ACL", (obj) =>
AVACL acl = ExtractFromDictionary(mutableData, "ACL", (obj) =>
{
return new AVACL(obj as IDictionary<string, object>);
});
}
string className = extractFromDictionary<string>(mutableData, "className", obj =>
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);
}

View File

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

View File

@ -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;
}
}
public static AVObject Create(string className) {
@ -137,8 +133,6 @@ 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;
@ -146,9 +140,6 @@ namespace LeanCloud {
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;
}
}
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);