* ObjectControllerTests.cs: chore: 格式化

* AVObject.cs:
* AVAddOperation.cs:
* AVSetOperation.cs:
* AVDeleteOperation.cs:
* AVFieldOperations.cs:
* AVRemoveOperation.cs:
* IAVFieldOperation.cs:
* AVRelationOperation.cs:
* AVAddUniqueOperation.cs:
* AVIncrementOperation.cs:
oneRain 2019-09-19 17:59:16 +08:00
parent afebf78444
commit d118935f21
11 changed files with 400 additions and 439 deletions

View File

@ -47,16 +47,24 @@ namespace LeanCloud.Test {
AVObject post = new AVObject("Post") {
{ "name", "New Post" },
{ "category", new AVObject("Category") }
{ "category", new AVObject("Category") {
{ "name", "new post category" }
} }
};
comment["post"] = post;
AVObject testPost = new AVObject("Post") {
{ "name", "Test Post" }
{ "name", "Test Post" },
{ "category", new AVObject("Category") {
{ "name", "test post category" }
} }
};
comment["test_post"] = testPost;
await comment.SaveAsync();
TestContext.Out.WriteLine(post);
TestContext.Out.WriteLine(testPost);
TestContext.Out.WriteLine(comment);
}
[Test]
@ -137,9 +145,12 @@ namespace LeanCloud.Test {
}
[Test]
public void Set() {
public async Task Set() {
AVObject obj = AVObject.Create("Foo");
obj["hello"] = "world";
await obj.SaveAsync();
obj["world"] = "aaa";
obj.Revert();
TestContext.Out.WriteAsync(obj["hello"] as string);
}
}

View File

@ -5,49 +5,49 @@ using System.Linq;
using LeanCloud.Utilities;
namespace LeanCloud.Storage.Internal {
public class AVAddOperation : IAVFieldOperation {
private ReadOnlyCollection<object> objects;
public AVAddOperation(IEnumerable<object> objects) {
this.objects = new ReadOnlyCollection<object>(objects.ToList());
}
public class AVAddOperation : IAVFieldOperation {
private readonly ReadOnlyCollection<object> objects;
public object Encode() {
return new Dictionary<string, object> {
{"__op", "Add"},
{"objects", PointerOrLocalIdEncoder.Instance.Encode(objects)}
};
}
public AVAddOperation(IEnumerable<object> objects) {
this.objects = new ReadOnlyCollection<object>(objects.ToList());
}
public IAVFieldOperation MergeWithPrevious(IAVFieldOperation previous) {
if (previous == null) {
return this;
}
if (previous is AVDeleteOperation) {
return new AVSetOperation(objects.ToList());
}
if (previous is AVSetOperation) {
var setOp = (AVSetOperation)previous;
var oldList = Conversion.To<IList<object>>(setOp.Value);
return new AVSetOperation(oldList.Concat(objects).ToList());
}
if (previous is AVAddOperation) {
return new AVAddOperation(((AVAddOperation)previous).Objects.Concat(objects));
}
throw new InvalidOperationException("Operation is invalid after previous operation.");
}
public object Encode() {
return new Dictionary<string, object> {
{ "__op", "Add" },
{ "objects", PointerOrLocalIdEncoder.Instance.Encode(objects) }
};
}
public object Apply(object oldValue, string key) {
if (oldValue == null) {
return objects.ToList();
}
var oldList = Conversion.To<IList<object>>(oldValue);
return oldList.Concat(objects).ToList();
}
public IAVFieldOperation MergeWithPrevious(IAVFieldOperation previous) {
if (previous == null) {
return this;
}
if (previous is AVDeleteOperation) {
return new AVSetOperation(objects.ToList());
}
if (previous is AVSetOperation setOp) {
var oldList = Conversion.To<IList<object>>(setOp.Value);
return new AVSetOperation(oldList.Concat(objects).ToList());
}
if (previous is AVAddOperation) {
return new AVAddOperation(((AVAddOperation)previous).Objects.Concat(objects));
}
throw new InvalidOperationException("Operation is invalid after previous operation.");
}
public IEnumerable<object> Objects {
get {
return objects;
}
public object Apply(object oldValue, string key) {
if (oldValue == null) {
return objects.ToList();
}
var oldList = Conversion.To<IList<object>>(oldValue);
return oldList.Concat(objects).ToList();
}
public IEnumerable<object> Objects {
get {
return objects;
}
}
}
}
}

View File

@ -5,65 +5,64 @@ using System.Linq;
using LeanCloud.Utilities;
namespace LeanCloud.Storage.Internal {
public class AVAddUniqueOperation : IAVFieldOperation {
private ReadOnlyCollection<object> objects;
public AVAddUniqueOperation(IEnumerable<object> objects) {
this.objects = new ReadOnlyCollection<object>(objects.Distinct().ToList());
}
public object Encode() {
return new Dictionary<string, object> {
{"__op", "AddUnique"},
{"objects", PointerOrLocalIdEncoder.Instance.Encode(objects)}
};
}
public IAVFieldOperation MergeWithPrevious(IAVFieldOperation previous) {
if (previous == null) {
return this;
}
if (previous is AVDeleteOperation) {
return new AVSetOperation(objects.ToList());
}
if (previous is AVSetOperation) {
var setOp = (AVSetOperation)previous;
var oldList = Conversion.To<IList<object>>(setOp.Value);
var result = this.Apply(oldList, null);
return new AVSetOperation(result);
}
if (previous is AVAddUniqueOperation) {
var oldList = ((AVAddUniqueOperation)previous).Objects;
return new AVAddUniqueOperation((IList<object>)this.Apply(oldList, null));
}
throw new InvalidOperationException("Operation is invalid after previous operation.");
}
public object Apply(object oldValue, string key) {
if (oldValue == null) {
return objects.ToList();
}
var newList = Conversion.To<IList<object>>(oldValue).ToList();
var comparer = AVFieldOperations.AVObjectComparer;
foreach (var objToAdd in objects) {
if (objToAdd is AVObject) {
var matchedObj = newList.FirstOrDefault(listObj => comparer.Equals(objToAdd, listObj));
if (matchedObj == null) {
newList.Add(objToAdd);
} else {
var index = newList.IndexOf(matchedObj);
newList[index] = objToAdd;
}
} else if (!newList.Contains<object>(objToAdd, comparer)) {
newList.Add(objToAdd);
public class AVAddUniqueOperation : IAVFieldOperation {
private ReadOnlyCollection<object> objects;
public AVAddUniqueOperation(IEnumerable<object> objects) {
this.objects = new ReadOnlyCollection<object>(objects.Distinct().ToList());
}
}
return newList;
}
public IEnumerable<object> Objects {
get {
return objects;
}
public object Encode() {
return new Dictionary<string, object> {
{ "__op", "AddUnique" },
{ "objects", PointerOrLocalIdEncoder.Instance.Encode(objects) }
};
}
public IAVFieldOperation MergeWithPrevious(IAVFieldOperation previous) {
if (previous == null) {
return this;
}
if (previous is AVDeleteOperation) {
return new AVSetOperation(objects.ToList());
}
if (previous is AVSetOperation setOp) {
var oldList = Conversion.To<IList<object>>(setOp.Value);
var result = this.Apply(oldList, null);
return new AVSetOperation(result);
}
if (previous is AVAddUniqueOperation) {
var oldList = ((AVAddUniqueOperation)previous).Objects;
return new AVAddUniqueOperation((IList<object>)this.Apply(oldList, null));
}
throw new InvalidOperationException("Operation is invalid after previous operation.");
}
public object Apply(object oldValue, string key) {
if (oldValue == null) {
return objects.ToList();
}
var newList = Conversion.To<IList<object>>(oldValue).ToList();
var comparer = AVFieldOperations.AVObjectComparer;
foreach (var objToAdd in objects) {
if (objToAdd is AVObject) {
var matchedObj = newList.FirstOrDefault(listObj => comparer.Equals(objToAdd, listObj));
if (matchedObj == null) {
newList.Add(objToAdd);
} else {
var index = newList.IndexOf(matchedObj);
newList[index] = objToAdd;
}
} else if (!newList.Contains<object>(objToAdd, comparer)) {
newList.Add(objToAdd);
}
}
return newList;
}
public IEnumerable<object> Objects {
get {
return objects;
}
}
}
}
}

View File

@ -1,37 +1,32 @@
using System.Collections.Generic;
namespace LeanCloud.Storage.Internal
{
namespace LeanCloud.Storage.Internal {
/// <summary>
/// An operation where a field is deleted from the object.
/// </summary>
public class AVDeleteOperation : IAVFieldOperation
{
public class AVDeleteOperation : IAVFieldOperation {
internal static readonly object DeleteToken = new object();
private static AVDeleteOperation _Instance = new AVDeleteOperation();
public static AVDeleteOperation Instance
{
get
{
public static AVDeleteOperation Instance {
get {
return _Instance;
}
}
private AVDeleteOperation() { }
public object Encode()
{
public object Encode() {
return new Dictionary<string, object> {
{"__op", "Delete"}
};
}
public IAVFieldOperation MergeWithPrevious(IAVFieldOperation previous)
{
public IAVFieldOperation MergeWithPrevious(IAVFieldOperation previous) {
return this;
}
public object Apply(object oldValue, string key)
{
public object Apply(object oldValue, string key) {
return DeleteToken;
}
}

View File

@ -2,39 +2,36 @@
using System.Collections.Generic;
namespace LeanCloud.Storage.Internal {
public class AVObjectIdComparer : IEqualityComparer<object> {
bool IEqualityComparer<object>.Equals(object p1, object p2) {
var avObj1 = p1 as AVObject;
var avObj2 = p2 as AVObject;
if (avObj1 != null && avObj2 != null) {
return object.Equals(avObj1.ObjectId, avObj2.ObjectId);
}
return object.Equals(p1, p2);
}
public int GetHashCode(object p) {
var avObject = p as AVObject;
if (avObject != null) {
return avObject.ObjectId.GetHashCode();
}
return p.GetHashCode();
}
}
static class AVFieldOperations {
private static AVObjectIdComparer comparer;
public static IAVFieldOperation Decode(IDictionary<string, object> json) {
throw new NotImplementedException();
}
public static IEqualityComparer<object> AVObjectComparer {
get {
if (comparer == null) {
comparer = new AVObjectIdComparer();
public class AVObjectIdComparer : IEqualityComparer<object> {
bool IEqualityComparer<object>.Equals(object p1, object p2) {
if (p1 is AVObject avObj1 && p2 is AVObject avObj2) {
return object.Equals(avObj1.ObjectId, avObj2.ObjectId);
}
return object.Equals(p1, p2);
}
public int GetHashCode(object p) {
if (p is AVObject avObject) {
return avObject.ObjectId.GetHashCode();
}
return p.GetHashCode();
}
}
static class AVFieldOperations {
private static AVObjectIdComparer comparer;
public static IAVFieldOperation Decode(IDictionary<string, object> json) {
throw new NotImplementedException();
}
public static IEqualityComparer<object> AVObjectComparer {
get {
if (comparer == null) {
comparer = new AVObjectIdComparer();
}
return comparer;
}
}
return comparer;
}
}
}
}

View File

@ -2,85 +2,80 @@
using System.Collections.Generic;
using System.Linq;
namespace LeanCloud.Storage.Internal
{
public class AVIncrementOperation : IAVFieldOperation
{
namespace LeanCloud.Storage.Internal {
public class AVIncrementOperation : IAVFieldOperation {
private static readonly IDictionary<Tuple<Type, Type>, Func<object, object, object>> adders;
static AVIncrementOperation()
{
static AVIncrementOperation() {
// Defines adders for all of the implicit conversions: http://msdn.microsoft.com/en-US/library/y5b434w4(v=vs.80).aspx
adders = new Dictionary<Tuple<Type, Type>, Func<object, object, object>> {
{new Tuple<Type, Type>(typeof(sbyte), typeof(sbyte)), (left, right) => (sbyte)left + (sbyte)right},
{new Tuple<Type, Type>(typeof(sbyte), typeof(short)), (left, right) => (sbyte)left + (short)right},
{new Tuple<Type, Type>(typeof(sbyte), typeof(int)), (left, right) => (sbyte)left + (int)right},
{new Tuple<Type, Type>(typeof(sbyte), typeof(long)), (left, right) => (sbyte)left + (long)right},
{new Tuple<Type, Type>(typeof(sbyte), typeof(float)), (left, right) => (sbyte)left + (float)right},
{new Tuple<Type, Type>(typeof(sbyte), typeof(double)), (left, right) => (sbyte)left + (double)right},
{new Tuple<Type, Type>(typeof(sbyte), typeof(decimal)), (left, right) => (sbyte)left + (decimal)right},
{new Tuple<Type, Type>(typeof(byte), typeof(byte)), (left, right) => (byte)left + (byte)right},
{new Tuple<Type, Type>(typeof(byte), typeof(short)), (left, right) => (byte)left + (short)right},
{new Tuple<Type, Type>(typeof(byte), typeof(ushort)), (left, right) => (byte)left + (ushort)right},
{new Tuple<Type, Type>(typeof(byte), typeof(int)), (left, right) => (byte)left + (int)right},
{new Tuple<Type, Type>(typeof(byte), typeof(uint)), (left, right) => (byte)left + (uint)right},
{new Tuple<Type, Type>(typeof(byte), typeof(long)), (left, right) => (byte)left + (long)right},
{new Tuple<Type, Type>(typeof(byte), typeof(ulong)), (left, right) => (byte)left + (ulong)right},
{new Tuple<Type, Type>(typeof(byte), typeof(float)), (left, right) => (byte)left + (float)right},
{new Tuple<Type, Type>(typeof(byte), typeof(double)), (left, right) => (byte)left + (double)right},
{new Tuple<Type, Type>(typeof(byte), typeof(decimal)), (left, right) => (byte)left + (decimal)right},
{new Tuple<Type, Type>(typeof(short), typeof(short)), (left, right) => (short)left + (short)right},
{new Tuple<Type, Type>(typeof(short), typeof(int)), (left, right) => (short)left + (int)right},
{new Tuple<Type, Type>(typeof(short), typeof(long)), (left, right) => (short)left + (long)right},
{new Tuple<Type, Type>(typeof(short), typeof(float)), (left, right) => (short)left + (float)right},
{new Tuple<Type, Type>(typeof(short), typeof(double)), (left, right) => (short)left + (double)right},
{new Tuple<Type, Type>(typeof(short), typeof(decimal)), (left, right) => (short)left + (decimal)right},
{new Tuple<Type, Type>(typeof(ushort), typeof(ushort)), (left, right) => (ushort)left + (ushort)right},
{new Tuple<Type, Type>(typeof(ushort), typeof(int)), (left, right) => (ushort)left + (int)right},
{new Tuple<Type, Type>(typeof(ushort), typeof(uint)), (left, right) => (ushort)left + (uint)right},
{new Tuple<Type, Type>(typeof(ushort), typeof(long)), (left, right) => (ushort)left + (long)right},
{new Tuple<Type, Type>(typeof(ushort), typeof(ulong)), (left, right) => (ushort)left + (ulong)right},
{new Tuple<Type, Type>(typeof(ushort), typeof(float)), (left, right) => (ushort)left + (float)right},
{new Tuple<Type, Type>(typeof(ushort), typeof(double)), (left, right) => (ushort)left + (double)right},
{new Tuple<Type, Type>(typeof(ushort), typeof(decimal)), (left, right) => (ushort)left + (decimal)right},
{new Tuple<Type, Type>(typeof(int), typeof(int)), (left, right) => (int)left + (int)right},
{new Tuple<Type, Type>(typeof(int), typeof(long)), (left, right) => (int)left + (long)right},
{new Tuple<Type, Type>(typeof(int), typeof(float)), (left, right) => (int)left + (float)right},
{new Tuple<Type, Type>(typeof(int), typeof(double)), (left, right) => (int)left + (double)right},
{new Tuple<Type, Type>(typeof(int), typeof(decimal)), (left, right) => (int)left + (decimal)right},
{new Tuple<Type, Type>(typeof(uint), typeof(uint)), (left, right) => (uint)left + (uint)right},
{new Tuple<Type, Type>(typeof(uint), typeof(long)), (left, right) => (uint)left + (long)right},
{new Tuple<Type, Type>(typeof(uint), typeof(ulong)), (left, right) => (uint)left + (ulong)right},
{new Tuple<Type, Type>(typeof(uint), typeof(float)), (left, right) => (uint)left + (float)right},
{new Tuple<Type, Type>(typeof(uint), typeof(double)), (left, right) => (uint)left + (double)right},
{new Tuple<Type, Type>(typeof(uint), typeof(decimal)), (left, right) => (uint)left + (decimal)right},
{new Tuple<Type, Type>(typeof(long), typeof(long)), (left, right) => (long)left + (long)right},
{new Tuple<Type, Type>(typeof(long), typeof(float)), (left, right) => (long)left + (float)right},
{new Tuple<Type, Type>(typeof(long), typeof(double)), (left, right) => (long)left + (double)right},
{new Tuple<Type, Type>(typeof(long), typeof(decimal)), (left, right) => (long)left + (decimal)right},
{new Tuple<Type, Type>(typeof(char), typeof(char)), (left, right) => (char)left + (char)right},
{new Tuple<Type, Type>(typeof(char), typeof(ushort)), (left, right) => (char)left + (ushort)right},
{new Tuple<Type, Type>(typeof(char), typeof(int)), (left, right) => (char)left + (int)right},
{new Tuple<Type, Type>(typeof(char), typeof(uint)), (left, right) => (char)left + (uint)right},
{new Tuple<Type, Type>(typeof(char), typeof(long)), (left, right) => (char)left + (long)right},
{new Tuple<Type, Type>(typeof(char), typeof(ulong)), (left, right) => (char)left + (ulong)right},
{new Tuple<Type, Type>(typeof(char), typeof(float)), (left, right) => (char)left + (float)right},
{new Tuple<Type, Type>(typeof(char), typeof(double)), (left, right) => (char)left + (double)right},
{new Tuple<Type, Type>(typeof(char), typeof(decimal)), (left, right) => (char)left + (decimal)right},
{new Tuple<Type, Type>(typeof(float), typeof(float)), (left, right) => (float)left + (float)right},
{new Tuple<Type, Type>(typeof(float), typeof(double)), (left, right) => (float)left + (double)right},
{new Tuple<Type, Type>(typeof(ulong), typeof(ulong)), (left, right) => (ulong)left + (ulong)right},
{new Tuple<Type, Type>(typeof(ulong), typeof(float)), (left, right) => (ulong)left + (float)right},
{new Tuple<Type, Type>(typeof(ulong), typeof(double)), (left, right) => (ulong)left + (double)right},
{new Tuple<Type, Type>(typeof(ulong), typeof(decimal)), (left, right) => (ulong)left + (decimal)right},
{new Tuple<Type, Type>(typeof(double), typeof(double)), (left, right) => (double)left + (double)right},
{new Tuple<Type, Type>(typeof(decimal), typeof(decimal)), (left, right) => (decimal)left + (decimal)right}
};
{new Tuple<Type, Type>(typeof(sbyte), typeof(sbyte)), (left, right) => (sbyte)left + (sbyte)right},
{new Tuple<Type, Type>(typeof(sbyte), typeof(short)), (left, right) => (sbyte)left + (short)right},
{new Tuple<Type, Type>(typeof(sbyte), typeof(int)), (left, right) => (sbyte)left + (int)right},
{new Tuple<Type, Type>(typeof(sbyte), typeof(long)), (left, right) => (sbyte)left + (long)right},
{new Tuple<Type, Type>(typeof(sbyte), typeof(float)), (left, right) => (sbyte)left + (float)right},
{new Tuple<Type, Type>(typeof(sbyte), typeof(double)), (left, right) => (sbyte)left + (double)right},
{new Tuple<Type, Type>(typeof(sbyte), typeof(decimal)), (left, right) => (sbyte)left + (decimal)right},
{new Tuple<Type, Type>(typeof(byte), typeof(byte)), (left, right) => (byte)left + (byte)right},
{new Tuple<Type, Type>(typeof(byte), typeof(short)), (left, right) => (byte)left + (short)right},
{new Tuple<Type, Type>(typeof(byte), typeof(ushort)), (left, right) => (byte)left + (ushort)right},
{new Tuple<Type, Type>(typeof(byte), typeof(int)), (left, right) => (byte)left + (int)right},
{new Tuple<Type, Type>(typeof(byte), typeof(uint)), (left, right) => (byte)left + (uint)right},
{new Tuple<Type, Type>(typeof(byte), typeof(long)), (left, right) => (byte)left + (long)right},
{new Tuple<Type, Type>(typeof(byte), typeof(ulong)), (left, right) => (byte)left + (ulong)right},
{new Tuple<Type, Type>(typeof(byte), typeof(float)), (left, right) => (byte)left + (float)right},
{new Tuple<Type, Type>(typeof(byte), typeof(double)), (left, right) => (byte)left + (double)right},
{new Tuple<Type, Type>(typeof(byte), typeof(decimal)), (left, right) => (byte)left + (decimal)right},
{new Tuple<Type, Type>(typeof(short), typeof(short)), (left, right) => (short)left + (short)right},
{new Tuple<Type, Type>(typeof(short), typeof(int)), (left, right) => (short)left + (int)right},
{new Tuple<Type, Type>(typeof(short), typeof(long)), (left, right) => (short)left + (long)right},
{new Tuple<Type, Type>(typeof(short), typeof(float)), (left, right) => (short)left + (float)right},
{new Tuple<Type, Type>(typeof(short), typeof(double)), (left, right) => (short)left + (double)right},
{new Tuple<Type, Type>(typeof(short), typeof(decimal)), (left, right) => (short)left + (decimal)right},
{new Tuple<Type, Type>(typeof(ushort), typeof(ushort)), (left, right) => (ushort)left + (ushort)right},
{new Tuple<Type, Type>(typeof(ushort), typeof(int)), (left, right) => (ushort)left + (int)right},
{new Tuple<Type, Type>(typeof(ushort), typeof(uint)), (left, right) => (ushort)left + (uint)right},
{new Tuple<Type, Type>(typeof(ushort), typeof(long)), (left, right) => (ushort)left + (long)right},
{new Tuple<Type, Type>(typeof(ushort), typeof(ulong)), (left, right) => (ushort)left + (ulong)right},
{new Tuple<Type, Type>(typeof(ushort), typeof(float)), (left, right) => (ushort)left + (float)right},
{new Tuple<Type, Type>(typeof(ushort), typeof(double)), (left, right) => (ushort)left + (double)right},
{new Tuple<Type, Type>(typeof(ushort), typeof(decimal)), (left, right) => (ushort)left + (decimal)right},
{new Tuple<Type, Type>(typeof(int), typeof(int)), (left, right) => (int)left + (int)right},
{new Tuple<Type, Type>(typeof(int), typeof(long)), (left, right) => (int)left + (long)right},
{new Tuple<Type, Type>(typeof(int), typeof(float)), (left, right) => (int)left + (float)right},
{new Tuple<Type, Type>(typeof(int), typeof(double)), (left, right) => (int)left + (double)right},
{new Tuple<Type, Type>(typeof(int), typeof(decimal)), (left, right) => (int)left + (decimal)right},
{new Tuple<Type, Type>(typeof(uint), typeof(uint)), (left, right) => (uint)left + (uint)right},
{new Tuple<Type, Type>(typeof(uint), typeof(long)), (left, right) => (uint)left + (long)right},
{new Tuple<Type, Type>(typeof(uint), typeof(ulong)), (left, right) => (uint)left + (ulong)right},
{new Tuple<Type, Type>(typeof(uint), typeof(float)), (left, right) => (uint)left + (float)right},
{new Tuple<Type, Type>(typeof(uint), typeof(double)), (left, right) => (uint)left + (double)right},
{new Tuple<Type, Type>(typeof(uint), typeof(decimal)), (left, right) => (uint)left + (decimal)right},
{new Tuple<Type, Type>(typeof(long), typeof(long)), (left, right) => (long)left + (long)right},
{new Tuple<Type, Type>(typeof(long), typeof(float)), (left, right) => (long)left + (float)right},
{new Tuple<Type, Type>(typeof(long), typeof(double)), (left, right) => (long)left + (double)right},
{new Tuple<Type, Type>(typeof(long), typeof(decimal)), (left, right) => (long)left + (decimal)right},
{new Tuple<Type, Type>(typeof(char), typeof(char)), (left, right) => (char)left + (char)right},
{new Tuple<Type, Type>(typeof(char), typeof(ushort)), (left, right) => (char)left + (ushort)right},
{new Tuple<Type, Type>(typeof(char), typeof(int)), (left, right) => (char)left + (int)right},
{new Tuple<Type, Type>(typeof(char), typeof(uint)), (left, right) => (char)left + (uint)right},
{new Tuple<Type, Type>(typeof(char), typeof(long)), (left, right) => (char)left + (long)right},
{new Tuple<Type, Type>(typeof(char), typeof(ulong)), (left, right) => (char)left + (ulong)right},
{new Tuple<Type, Type>(typeof(char), typeof(float)), (left, right) => (char)left + (float)right},
{new Tuple<Type, Type>(typeof(char), typeof(double)), (left, right) => (char)left + (double)right},
{new Tuple<Type, Type>(typeof(char), typeof(decimal)), (left, right) => (char)left + (decimal)right},
{new Tuple<Type, Type>(typeof(float), typeof(float)), (left, right) => (float)left + (float)right},
{new Tuple<Type, Type>(typeof(float), typeof(double)), (left, right) => (float)left + (double)right},
{new Tuple<Type, Type>(typeof(ulong), typeof(ulong)), (left, right) => (ulong)left + (ulong)right},
{new Tuple<Type, Type>(typeof(ulong), typeof(float)), (left, right) => (ulong)left + (float)right},
{new Tuple<Type, Type>(typeof(ulong), typeof(double)), (left, right) => (ulong)left + (double)right},
{new Tuple<Type, Type>(typeof(ulong), typeof(decimal)), (left, right) => (ulong)left + (decimal)right},
{new Tuple<Type, Type>(typeof(double), typeof(double)), (left, right) => (double)left + (double)right},
{new Tuple<Type, Type>(typeof(decimal), typeof(decimal)), (left, right) => (decimal)left + (decimal)right}
};
// Generate the adders in the other direction
foreach (var pair in adders.Keys.ToList())
{
if (pair.Item1.Equals(pair.Item2))
{
foreach (var pair in adders.Keys.ToList()) {
if (pair.Item1.Equals(pair.Item2)) {
continue;
}
var reversePair = new Tuple<Type, Type>(pair.Item2, pair.Item1);
@ -91,52 +86,42 @@ namespace LeanCloud.Storage.Internal
private object amount;
public AVIncrementOperation(object amount)
{
public AVIncrementOperation(object amount) {
this.amount = amount;
}
public object Encode()
{
return new Dictionary<string, object>
public object Encode() {
return new Dictionary<string, object>
{
{"__op", "Increment"},
{"amount", amount}
};
}
private static object Add(object obj1, object obj2)
{
private static object Add(object obj1, object obj2) {
Func<object, object, object> adder;
if (adders.TryGetValue(new Tuple<Type, Type>(obj1.GetType(), obj2.GetType()), out adder))
{
if (adders.TryGetValue(new Tuple<Type, Type>(obj1.GetType(), obj2.GetType()), out adder)) {
return adder(obj1, obj2);
}
throw new InvalidCastException("Cannot add " + obj1.GetType() + " to " + obj2.GetType());
}
public IAVFieldOperation MergeWithPrevious(IAVFieldOperation previous)
{
if (previous == null)
{
public IAVFieldOperation MergeWithPrevious(IAVFieldOperation previous) {
if (previous == null) {
return this;
}
if (previous is AVDeleteOperation)
{
if (previous is AVDeleteOperation) {
return new AVSetOperation(amount);
}
if (previous is AVSetOperation)
{
if (previous is AVSetOperation) {
var otherAmount = ((AVSetOperation)previous).Value;
if (otherAmount is string)
{
if (otherAmount is string) {
throw new InvalidOperationException("Cannot increment a non-number type.");
}
var myAmount = amount;
return new AVSetOperation(Add(otherAmount, myAmount));
}
if (previous is AVIncrementOperation)
{
if (previous is AVIncrementOperation) {
object otherAmount = ((AVIncrementOperation)previous).Amount;
object myAmount = amount;
return new AVIncrementOperation(Add(otherAmount, myAmount));
@ -144,10 +129,8 @@ namespace LeanCloud.Storage.Internal
throw new InvalidOperationException("Operation is invalid after previous operation.");
}
public object Apply(object oldValue, string key)
{
if (oldValue is string)
{
public object Apply(object oldValue, string key) {
if (oldValue is string) {
throw new InvalidOperationException("Cannot increment a non-number type.");
}
object otherAmount = oldValue ?? 0;
@ -155,10 +138,8 @@ namespace LeanCloud.Storage.Internal
return Add(otherAmount, myAmount);
}
public object Amount
{
get
{
public object Amount {
get {
return amount;
}
}

View File

@ -6,113 +6,113 @@ using System.Text;
using System.Threading.Tasks;
namespace LeanCloud.Storage.Internal {
public class AVRelationOperation : IAVFieldOperation {
private readonly IList<string> adds;
private readonly IList<string> removes;
private readonly string targetClassName;
public class AVRelationOperation : IAVFieldOperation {
private readonly IList<string> adds;
private readonly IList<string> removes;
private readonly string targetClassName;
private AVRelationOperation(IEnumerable<string> adds,
IEnumerable<string> removes,
string targetClassName) {
this.targetClassName = targetClassName;
this.adds = new ReadOnlyCollection<string>(adds.ToList());
this.removes = new ReadOnlyCollection<string>(removes.ToList());
}
public AVRelationOperation(IEnumerable<AVObject> adds,
IEnumerable<AVObject> removes) {
adds = adds ?? new AVObject[0];
removes = removes ?? new AVObject[0];
this.targetClassName = adds.Concat(removes).Select(o => o.ClassName).FirstOrDefault();
this.adds = new ReadOnlyCollection<string>(IdsFromObjects(adds).ToList());
this.removes = new ReadOnlyCollection<string>(IdsFromObjects(removes).ToList());
}
public object Encode() {
var adds = this.adds
.Select(id => PointerOrLocalIdEncoder.Instance.Encode(
AVObject.CreateWithoutData(targetClassName, id)))
.ToList();
var removes = this.removes
.Select(id => PointerOrLocalIdEncoder.Instance.Encode(
AVObject.CreateWithoutData(targetClassName, id)))
.ToList();
var addDict = adds.Count == 0 ? null : new Dictionary<string, object> {
{"__op", "AddRelation"},
{"objects", adds}
};
var removeDict = removes.Count == 0 ? null : new Dictionary<string, object> {
{"__op", "RemoveRelation"},
{"objects", removes}
};
if (addDict != null && removeDict != null) {
return new Dictionary<string, object> {
{"__op", "Batch"},
{"ops", new[] {addDict, removeDict}}
};
}
return addDict ?? removeDict;
}
public IAVFieldOperation MergeWithPrevious(IAVFieldOperation previous) {
if (previous == null) {
return this;
}
if (previous is AVDeleteOperation) {
throw new InvalidOperationException("You can't modify a relation after deleting it.");
}
var other = previous as AVRelationOperation;
if (other != null) {
if (other.TargetClassName != TargetClassName) {
throw new InvalidOperationException(
string.Format("Related object must be of class {0}, but {1} was passed in.",
other.TargetClassName,
TargetClassName));
private AVRelationOperation(IEnumerable<string> adds,
IEnumerable<string> removes,
string targetClassName) {
this.targetClassName = targetClassName;
this.adds = new ReadOnlyCollection<string>(adds.ToList());
this.removes = new ReadOnlyCollection<string>(removes.ToList());
}
var newAdd = adds.Union(other.adds.Except(removes)).ToList();
var newRemove = removes.Union(other.removes.Except(adds)).ToList();
return new AVRelationOperation(newAdd, newRemove, TargetClassName);
}
throw new InvalidOperationException("Operation is invalid after previous operation.");
}
public object Apply(object oldValue, string key) {
if (adds.Count == 0 && removes.Count == 0) {
return null;
}
if (oldValue == null) {
return AVRelationBase.CreateRelation(null, key, targetClassName);
}
if (oldValue is AVRelationBase) {
var oldRelation = (AVRelationBase)oldValue;
var oldClassName = oldRelation.TargetClassName;
if (oldClassName != null && oldClassName != targetClassName) {
throw new InvalidOperationException("Related object must be a " + oldClassName
+ ", but a " + targetClassName + " was passed in.");
public AVRelationOperation(IEnumerable<AVObject> adds,
IEnumerable<AVObject> removes) {
adds = adds ?? new AVObject[0];
removes = removes ?? new AVObject[0];
this.targetClassName = adds.Concat(removes).Select(o => o.ClassName).FirstOrDefault();
this.adds = new ReadOnlyCollection<string>(IdsFromObjects(adds).ToList());
this.removes = new ReadOnlyCollection<string>(IdsFromObjects(removes).ToList());
}
oldRelation.TargetClassName = targetClassName;
return oldRelation;
}
throw new InvalidOperationException("Operation is invalid after previous operation.");
}
public string TargetClassName { get { return targetClassName; } }
public object Encode() {
var adds = this.adds
.Select(id => PointerOrLocalIdEncoder.Instance.Encode(
AVObject.CreateWithoutData(targetClassName, id)))
.ToList();
var removes = this.removes
.Select(id => PointerOrLocalIdEncoder.Instance.Encode(
AVObject.CreateWithoutData(targetClassName, id)))
.ToList();
var addDict = adds.Count == 0 ? null : new Dictionary<string, object> {
{"__op", "AddRelation"},
{"objects", adds}
};
var removeDict = removes.Count == 0 ? null : new Dictionary<string, object> {
{"__op", "RemoveRelation"},
{"objects", removes}
};
private IEnumerable<string> IdsFromObjects(IEnumerable<AVObject> objects) {
foreach (var obj in objects) {
if (obj.ObjectId == null) {
throw new ArgumentException(
"You can't add an unsaved AVObject to a relation.");
if (addDict != null && removeDict != null) {
return new Dictionary<string, object> {
{"__op", "Batch"},
{"ops", new[] {addDict, removeDict}}
};
}
return addDict ?? removeDict;
}
if (obj.ClassName != targetClassName) {
throw new ArgumentException(string.Format(
"Tried to create a AVRelation with 2 different types: {0} and {1}",
targetClassName,
obj.ClassName));
public IAVFieldOperation MergeWithPrevious(IAVFieldOperation previous) {
if (previous == null) {
return this;
}
if (previous is AVDeleteOperation) {
throw new InvalidOperationException("You can't modify a relation after deleting it.");
}
var other = previous as AVRelationOperation;
if (other != null) {
if (other.TargetClassName != TargetClassName) {
throw new InvalidOperationException(
string.Format("Related object must be of class {0}, but {1} was passed in.",
other.TargetClassName,
TargetClassName));
}
var newAdd = adds.Union(other.adds.Except(removes)).ToList();
var newRemove = removes.Union(other.removes.Except(adds)).ToList();
return new AVRelationOperation(newAdd, newRemove, TargetClassName);
}
throw new InvalidOperationException("Operation is invalid after previous operation.");
}
public object Apply(object oldValue, string key) {
if (adds.Count == 0 && removes.Count == 0) {
return null;
}
if (oldValue == null) {
return AVRelationBase.CreateRelation(null, key, targetClassName);
}
if (oldValue is AVRelationBase) {
var oldRelation = (AVRelationBase)oldValue;
var oldClassName = oldRelation.TargetClassName;
if (oldClassName != null && oldClassName != targetClassName) {
throw new InvalidOperationException("Related object must be a " + oldClassName
+ ", but a " + targetClassName + " was passed in.");
}
oldRelation.TargetClassName = targetClassName;
return oldRelation;
}
throw new InvalidOperationException("Operation is invalid after previous operation.");
}
public string TargetClassName { get { return targetClassName; } }
private IEnumerable<string> IdsFromObjects(IEnumerable<AVObject> objects) {
foreach (var obj in objects) {
if (obj.ObjectId == null) {
throw new ArgumentException(
"You can't add an unsaved AVObject to a relation.");
}
if (obj.ClassName != targetClassName) {
throw new ArgumentException(string.Format(
"Tried to create a AVRelation with 2 different types: {0} and {1}",
targetClassName,
obj.ClassName));
}
}
return objects.Select(o => o.ObjectId).Distinct();
}
}
return objects.Select(o => o.ObjectId).Distinct();
}
}
}

View File

@ -5,62 +5,49 @@ using System.Linq;
using LeanCloud.Utilities;
namespace LeanCloud.Storage.Internal
{
public class AVRemoveOperation : IAVFieldOperation
{
namespace LeanCloud.Storage.Internal {
public class AVRemoveOperation : IAVFieldOperation {
private ReadOnlyCollection<object> objects;
public AVRemoveOperation(IEnumerable<object> objects)
{
public AVRemoveOperation(IEnumerable<object> objects) {
this.objects = new ReadOnlyCollection<object>(objects.Distinct().ToList());
}
public object Encode()
{
public object Encode() {
return new Dictionary<string, object> {
{ "__op", "Remove" },
{ "objects", PointerOrLocalIdEncoder.Instance.Encode(objects) }
};
}
public IAVFieldOperation MergeWithPrevious(IAVFieldOperation previous)
{
if (previous == null)
{
public IAVFieldOperation MergeWithPrevious(IAVFieldOperation previous) {
if (previous == null) {
return this;
}
if (previous is AVDeleteOperation)
{
if (previous is AVDeleteOperation) {
return previous;
}
if (previous is AVSetOperation)
{
if (previous is AVSetOperation) {
var setOp = (AVSetOperation)previous;
var oldList = Conversion.As<IList<object>>(setOp.Value);
return new AVSetOperation(this.Apply(oldList, null));
}
if (previous is AVRemoveOperation)
{
if (previous is AVRemoveOperation) {
var oldOp = (AVRemoveOperation)previous;
return new AVRemoveOperation(oldOp.Objects.Concat(objects));
}
throw new InvalidOperationException("Operation is invalid after previous operation.");
}
public object Apply(object oldValue, string key)
{
if (oldValue == null)
{
public object Apply(object oldValue, string key) {
if (oldValue == null) {
return new List<object>();
}
var oldList = Conversion.As<IList<object>>(oldValue);
return oldList.Except(objects, AVFieldOperations.AVObjectComparer).ToList();
}
public IEnumerable<object> Objects
{
get
{
public IEnumerable<object> Objects {
get {
return objects;
}
}

View File

@ -1,21 +1,21 @@
namespace LeanCloud.Storage.Internal {
public class AVSetOperation : IAVFieldOperation {
public AVSetOperation(object value) {
Value = value;
}
public class AVSetOperation : IAVFieldOperation {
public AVSetOperation(object value) {
Value = value;
}
public object Encode() {
return PointerOrLocalIdEncoder.Instance.Encode(Value);
}
public object Encode() {
return PointerOrLocalIdEncoder.Instance.Encode(Value);
}
public IAVFieldOperation MergeWithPrevious(IAVFieldOperation previous) {
return this;
}
public IAVFieldOperation MergeWithPrevious(IAVFieldOperation previous) {
return this;
}
public object Apply(object oldValue, string key) {
return Value;
}
public object Apply(object oldValue, string key) {
return Value;
}
public object Value { get; private set; }
}
public object Value { get; private set; }
}
}

View File

@ -1,42 +1,42 @@
namespace LeanCloud.Storage.Internal {
/// <summary>
/// A AVFieldOperation represents a modification to a value in a AVObject.
/// For example, setting, deleting, or incrementing a value are all different kinds of
/// AVFieldOperations. AVFieldOperations themselves can be considered to be
/// immutable.
/// </summary>
public interface IAVFieldOperation {
/// <summary>
/// Converts the AVFieldOperation to a data structure that can be converted to JSON and sent to
/// LeanCloud as part of a save operation.
/// A AVFieldOperation represents a modification to a value in a AVObject.
/// For example, setting, deleting, or incrementing a value are all different kinds of
/// AVFieldOperations. AVFieldOperations themselves can be considered to be
/// immutable.
/// </summary>
/// <returns>An object to be JSONified.</returns>
object Encode();
public interface IAVFieldOperation {
/// <summary>
/// Converts the AVFieldOperation to a data structure that can be converted to JSON and sent to
/// LeanCloud as part of a save operation.
/// </summary>
/// <returns>An object to be JSONified.</returns>
object Encode();
/// <summary>
/// Returns a field operation that is composed of a previous operation followed by
/// this operation. This will not mutate either operation. However, it may return
/// <code>this</code> if the current operation is not affected by previous changes.
/// For example:
/// {increment by 2}.MergeWithPrevious({set to 5}) -> {set to 7}
/// {set to 5}.MergeWithPrevious({increment by 2}) -> {set to 5}
/// {add "foo"}.MergeWithPrevious({delete}) -> {set to ["foo"]}
/// {delete}.MergeWithPrevious({add "foo"}) -> {delete} /// </summary>
/// <param name="previous">The most recent operation on the field, or null if none.</param>
/// <returns>A new AVFieldOperation or this.</returns>
IAVFieldOperation MergeWithPrevious(IAVFieldOperation previous);
/// <summary>
/// Returns a field operation that is composed of a previous operation followed by
/// this operation. This will not mutate either operation. However, it may return
/// <code>this</code> if the current operation is not affected by previous changes.
/// For example:
/// {increment by 2}.MergeWithPrevious({set to 5}) -> {set to 7}
/// {set to 5}.MergeWithPrevious({increment by 2}) -> {set to 5}
/// {add "foo"}.MergeWithPrevious({delete}) -> {set to ["foo"]}
/// {delete}.MergeWithPrevious({add "foo"}) -> {delete} /// </summary>
/// <param name="previous">The most recent operation on the field, or null if none.</param>
/// <returns>A new AVFieldOperation or this.</returns>
IAVFieldOperation MergeWithPrevious(IAVFieldOperation previous);
/// <summary>
/// Returns a new estimated value based on a previous value and this operation. This
/// value is not intended to be sent to LeanCloud, but it is used locally on the client to
/// inspect the most likely current value for a field.
///
/// The key and object are used solely for AVRelation to be able to construct objects
/// that refer back to their parents.
/// </summary>
/// <param name="oldValue">The previous value for the field.</param>
/// <param name="key">The key that this value is for.</param>
/// <returns>The new value for the field.</returns>
object Apply(object oldValue, string key);
}
/// <summary>
/// Returns a new estimated value based on a previous value and this operation. This
/// value is not intended to be sent to LeanCloud, but it is used locally on the client to
/// inspect the most likely current value for a field.
///
/// The key and object are used solely for AVRelation to be able to construct objects
/// that refer back to their parents.
/// </summary>
/// <param name="oldValue">The previous value for the field.</param>
/// <param name="key">The key that this value is for.</param>
/// <returns>The new value for the field.</returns>
object Apply(object oldValue, string key);
}
}

View File

@ -8,8 +8,6 @@ using System.Runtime.CompilerServices;
using System.Threading;
using System.Threading.Tasks;
using System.Collections;
using System.Linq;
using System.Collections.Concurrent;
namespace LeanCloud {
/// <summary>
@ -18,13 +16,6 @@ namespace LeanCloud {
public class AVObject : IEnumerable<KeyValuePair<string, object>>, INotifyPropertyChanged, INotifyPropertyUpdated, INotifyCollectionPropertyUpdated {
private static readonly string AutoClassName = "_Automatic";
#if UNITY
private static readonly bool isCompiledByIL2CPP = AppDomain.CurrentDomain.FriendlyName.Equals("IL2CPP Root Domain");
#else
private static readonly bool isCompiledByIL2CPP = false;
#endif
internal readonly object mutex = new object();
private readonly LinkedList<IDictionary<string, IAVFieldOperation>> operationSetQueue =
@ -75,7 +66,7 @@ namespace LeanCloud {
/// Constructor for use in AVObject subclasses. Subclasses must specify a AVClassName attribute.
/// </summary>
protected AVObject()
: this(AutoClassName) {
: this(AutoClassName) {
}
/// <summary>
@ -1008,7 +999,7 @@ string propertyName
// We've just applied a bunch of operations to estimatedData which
// may have changed all of its keys. Notify of all keys and properties
// mapped to keys being changed.
OnFieldsChanged(changedKeys);
OnFieldsChanged(null);
}
}