diff --git a/Storage/Storage.Test/ObjectControllerTests.cs b/Storage/Storage.Test/ObjectControllerTests.cs index 5f0c338..3dbe70b 100644 --- a/Storage/Storage.Test/ObjectControllerTests.cs +++ b/Storage/Storage.Test/ObjectControllerTests.cs @@ -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); } } diff --git a/Storage/Storage/Internal/Operation/AVAddOperation.cs b/Storage/Storage/Internal/Operation/AVAddOperation.cs index ed0a83e..6f836e4 100644 --- a/Storage/Storage/Internal/Operation/AVAddOperation.cs +++ b/Storage/Storage/Internal/Operation/AVAddOperation.cs @@ -5,49 +5,49 @@ using System.Linq; using LeanCloud.Utilities; namespace LeanCloud.Storage.Internal { - public class AVAddOperation : IAVFieldOperation { - private ReadOnlyCollection objects; - public AVAddOperation(IEnumerable objects) { - this.objects = new ReadOnlyCollection(objects.ToList()); - } + public class AVAddOperation : IAVFieldOperation { + private readonly ReadOnlyCollection objects; - public object Encode() { - return new Dictionary { - {"__op", "Add"}, - {"objects", PointerOrLocalIdEncoder.Instance.Encode(objects)} - }; - } + public AVAddOperation(IEnumerable objects) { + this.objects = new ReadOnlyCollection(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>(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 { + { "__op", "Add" }, + { "objects", PointerOrLocalIdEncoder.Instance.Encode(objects) } + }; + } - public object Apply(object oldValue, string key) { - if (oldValue == null) { - return objects.ToList(); - } - var oldList = Conversion.To>(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>(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 Objects { - get { - return objects; - } + public object Apply(object oldValue, string key) { + if (oldValue == null) { + return objects.ToList(); + } + var oldList = Conversion.To>(oldValue); + return oldList.Concat(objects).ToList(); + } + + public IEnumerable Objects { + get { + return objects; + } + } } - } } diff --git a/Storage/Storage/Internal/Operation/AVAddUniqueOperation.cs b/Storage/Storage/Internal/Operation/AVAddUniqueOperation.cs index 8e0a957..6a820e9 100644 --- a/Storage/Storage/Internal/Operation/AVAddUniqueOperation.cs +++ b/Storage/Storage/Internal/Operation/AVAddUniqueOperation.cs @@ -5,65 +5,64 @@ using System.Linq; using LeanCloud.Utilities; namespace LeanCloud.Storage.Internal { - public class AVAddUniqueOperation : IAVFieldOperation { - private ReadOnlyCollection objects; - public AVAddUniqueOperation(IEnumerable objects) { - this.objects = new ReadOnlyCollection(objects.Distinct().ToList()); - } - - public object Encode() { - return new Dictionary { - {"__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>(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)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>(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(objToAdd, comparer)) { - newList.Add(objToAdd); + public class AVAddUniqueOperation : IAVFieldOperation { + private ReadOnlyCollection objects; + public AVAddUniqueOperation(IEnumerable objects) { + this.objects = new ReadOnlyCollection(objects.Distinct().ToList()); } - } - return newList; - } - public IEnumerable Objects { - get { - return objects; - } + public object Encode() { + return new Dictionary { + { "__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>(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)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>(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(objToAdd, comparer)) { + newList.Add(objToAdd); + } + } + return newList; + } + + public IEnumerable Objects { + get { + return objects; + } + } } - } } diff --git a/Storage/Storage/Internal/Operation/AVDeleteOperation.cs b/Storage/Storage/Internal/Operation/AVDeleteOperation.cs index 7b77b94..6b548ae 100644 --- a/Storage/Storage/Internal/Operation/AVDeleteOperation.cs +++ b/Storage/Storage/Internal/Operation/AVDeleteOperation.cs @@ -1,37 +1,32 @@ using System.Collections.Generic; -namespace LeanCloud.Storage.Internal -{ +namespace LeanCloud.Storage.Internal { /// /// An operation where a field is deleted from the object. /// - 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 { {"__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; } } diff --git a/Storage/Storage/Internal/Operation/AVFieldOperations.cs b/Storage/Storage/Internal/Operation/AVFieldOperations.cs index 9c07ffa..39a2d6a 100644 --- a/Storage/Storage/Internal/Operation/AVFieldOperations.cs +++ b/Storage/Storage/Internal/Operation/AVFieldOperations.cs @@ -2,39 +2,36 @@ using System.Collections.Generic; namespace LeanCloud.Storage.Internal { - public class AVObjectIdComparer : IEqualityComparer { - bool IEqualityComparer.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 json) { - throw new NotImplementedException(); - } - - public static IEqualityComparer AVObjectComparer { - get { - if (comparer == null) { - comparer = new AVObjectIdComparer(); + public class AVObjectIdComparer : IEqualityComparer { + bool IEqualityComparer.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 json) { + throw new NotImplementedException(); + } + + public static IEqualityComparer AVObjectComparer { + get { + if (comparer == null) { + comparer = new AVObjectIdComparer(); + } + return comparer; + } } - return comparer; - } } - } } diff --git a/Storage/Storage/Internal/Operation/AVIncrementOperation.cs b/Storage/Storage/Internal/Operation/AVIncrementOperation.cs index 1606e5f..b0922f1 100644 --- a/Storage/Storage/Internal/Operation/AVIncrementOperation.cs +++ b/Storage/Storage/Internal/Operation/AVIncrementOperation.cs @@ -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, Func> 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, Func> { - {new Tuple(typeof(sbyte), typeof(sbyte)), (left, right) => (sbyte)left + (sbyte)right}, - {new Tuple(typeof(sbyte), typeof(short)), (left, right) => (sbyte)left + (short)right}, - {new Tuple(typeof(sbyte), typeof(int)), (left, right) => (sbyte)left + (int)right}, - {new Tuple(typeof(sbyte), typeof(long)), (left, right) => (sbyte)left + (long)right}, - {new Tuple(typeof(sbyte), typeof(float)), (left, right) => (sbyte)left + (float)right}, - {new Tuple(typeof(sbyte), typeof(double)), (left, right) => (sbyte)left + (double)right}, - {new Tuple(typeof(sbyte), typeof(decimal)), (left, right) => (sbyte)left + (decimal)right}, - {new Tuple(typeof(byte), typeof(byte)), (left, right) => (byte)left + (byte)right}, - {new Tuple(typeof(byte), typeof(short)), (left, right) => (byte)left + (short)right}, - {new Tuple(typeof(byte), typeof(ushort)), (left, right) => (byte)left + (ushort)right}, - {new Tuple(typeof(byte), typeof(int)), (left, right) => (byte)left + (int)right}, - {new Tuple(typeof(byte), typeof(uint)), (left, right) => (byte)left + (uint)right}, - {new Tuple(typeof(byte), typeof(long)), (left, right) => (byte)left + (long)right}, - {new Tuple(typeof(byte), typeof(ulong)), (left, right) => (byte)left + (ulong)right}, - {new Tuple(typeof(byte), typeof(float)), (left, right) => (byte)left + (float)right}, - {new Tuple(typeof(byte), typeof(double)), (left, right) => (byte)left + (double)right}, - {new Tuple(typeof(byte), typeof(decimal)), (left, right) => (byte)left + (decimal)right}, - {new Tuple(typeof(short), typeof(short)), (left, right) => (short)left + (short)right}, - {new Tuple(typeof(short), typeof(int)), (left, right) => (short)left + (int)right}, - {new Tuple(typeof(short), typeof(long)), (left, right) => (short)left + (long)right}, - {new Tuple(typeof(short), typeof(float)), (left, right) => (short)left + (float)right}, - {new Tuple(typeof(short), typeof(double)), (left, right) => (short)left + (double)right}, - {new Tuple(typeof(short), typeof(decimal)), (left, right) => (short)left + (decimal)right}, - {new Tuple(typeof(ushort), typeof(ushort)), (left, right) => (ushort)left + (ushort)right}, - {new Tuple(typeof(ushort), typeof(int)), (left, right) => (ushort)left + (int)right}, - {new Tuple(typeof(ushort), typeof(uint)), (left, right) => (ushort)left + (uint)right}, - {new Tuple(typeof(ushort), typeof(long)), (left, right) => (ushort)left + (long)right}, - {new Tuple(typeof(ushort), typeof(ulong)), (left, right) => (ushort)left + (ulong)right}, - {new Tuple(typeof(ushort), typeof(float)), (left, right) => (ushort)left + (float)right}, - {new Tuple(typeof(ushort), typeof(double)), (left, right) => (ushort)left + (double)right}, - {new Tuple(typeof(ushort), typeof(decimal)), (left, right) => (ushort)left + (decimal)right}, - {new Tuple(typeof(int), typeof(int)), (left, right) => (int)left + (int)right}, - {new Tuple(typeof(int), typeof(long)), (left, right) => (int)left + (long)right}, - {new Tuple(typeof(int), typeof(float)), (left, right) => (int)left + (float)right}, - {new Tuple(typeof(int), typeof(double)), (left, right) => (int)left + (double)right}, - {new Tuple(typeof(int), typeof(decimal)), (left, right) => (int)left + (decimal)right}, - {new Tuple(typeof(uint), typeof(uint)), (left, right) => (uint)left + (uint)right}, - {new Tuple(typeof(uint), typeof(long)), (left, right) => (uint)left + (long)right}, - {new Tuple(typeof(uint), typeof(ulong)), (left, right) => (uint)left + (ulong)right}, - {new Tuple(typeof(uint), typeof(float)), (left, right) => (uint)left + (float)right}, - {new Tuple(typeof(uint), typeof(double)), (left, right) => (uint)left + (double)right}, - {new Tuple(typeof(uint), typeof(decimal)), (left, right) => (uint)left + (decimal)right}, - {new Tuple(typeof(long), typeof(long)), (left, right) => (long)left + (long)right}, - {new Tuple(typeof(long), typeof(float)), (left, right) => (long)left + (float)right}, - {new Tuple(typeof(long), typeof(double)), (left, right) => (long)left + (double)right}, - {new Tuple(typeof(long), typeof(decimal)), (left, right) => (long)left + (decimal)right}, - {new Tuple(typeof(char), typeof(char)), (left, right) => (char)left + (char)right}, - {new Tuple(typeof(char), typeof(ushort)), (left, right) => (char)left + (ushort)right}, - {new Tuple(typeof(char), typeof(int)), (left, right) => (char)left + (int)right}, - {new Tuple(typeof(char), typeof(uint)), (left, right) => (char)left + (uint)right}, - {new Tuple(typeof(char), typeof(long)), (left, right) => (char)left + (long)right}, - {new Tuple(typeof(char), typeof(ulong)), (left, right) => (char)left + (ulong)right}, - {new Tuple(typeof(char), typeof(float)), (left, right) => (char)left + (float)right}, - {new Tuple(typeof(char), typeof(double)), (left, right) => (char)left + (double)right}, - {new Tuple(typeof(char), typeof(decimal)), (left, right) => (char)left + (decimal)right}, - {new Tuple(typeof(float), typeof(float)), (left, right) => (float)left + (float)right}, - {new Tuple(typeof(float), typeof(double)), (left, right) => (float)left + (double)right}, - {new Tuple(typeof(ulong), typeof(ulong)), (left, right) => (ulong)left + (ulong)right}, - {new Tuple(typeof(ulong), typeof(float)), (left, right) => (ulong)left + (float)right}, - {new Tuple(typeof(ulong), typeof(double)), (left, right) => (ulong)left + (double)right}, - {new Tuple(typeof(ulong), typeof(decimal)), (left, right) => (ulong)left + (decimal)right}, - {new Tuple(typeof(double), typeof(double)), (left, right) => (double)left + (double)right}, - {new Tuple(typeof(decimal), typeof(decimal)), (left, right) => (decimal)left + (decimal)right} - }; + {new Tuple(typeof(sbyte), typeof(sbyte)), (left, right) => (sbyte)left + (sbyte)right}, + {new Tuple(typeof(sbyte), typeof(short)), (left, right) => (sbyte)left + (short)right}, + {new Tuple(typeof(sbyte), typeof(int)), (left, right) => (sbyte)left + (int)right}, + {new Tuple(typeof(sbyte), typeof(long)), (left, right) => (sbyte)left + (long)right}, + {new Tuple(typeof(sbyte), typeof(float)), (left, right) => (sbyte)left + (float)right}, + {new Tuple(typeof(sbyte), typeof(double)), (left, right) => (sbyte)left + (double)right}, + {new Tuple(typeof(sbyte), typeof(decimal)), (left, right) => (sbyte)left + (decimal)right}, + {new Tuple(typeof(byte), typeof(byte)), (left, right) => (byte)left + (byte)right}, + {new Tuple(typeof(byte), typeof(short)), (left, right) => (byte)left + (short)right}, + {new Tuple(typeof(byte), typeof(ushort)), (left, right) => (byte)left + (ushort)right}, + {new Tuple(typeof(byte), typeof(int)), (left, right) => (byte)left + (int)right}, + {new Tuple(typeof(byte), typeof(uint)), (left, right) => (byte)left + (uint)right}, + {new Tuple(typeof(byte), typeof(long)), (left, right) => (byte)left + (long)right}, + {new Tuple(typeof(byte), typeof(ulong)), (left, right) => (byte)left + (ulong)right}, + {new Tuple(typeof(byte), typeof(float)), (left, right) => (byte)left + (float)right}, + {new Tuple(typeof(byte), typeof(double)), (left, right) => (byte)left + (double)right}, + {new Tuple(typeof(byte), typeof(decimal)), (left, right) => (byte)left + (decimal)right}, + {new Tuple(typeof(short), typeof(short)), (left, right) => (short)left + (short)right}, + {new Tuple(typeof(short), typeof(int)), (left, right) => (short)left + (int)right}, + {new Tuple(typeof(short), typeof(long)), (left, right) => (short)left + (long)right}, + {new Tuple(typeof(short), typeof(float)), (left, right) => (short)left + (float)right}, + {new Tuple(typeof(short), typeof(double)), (left, right) => (short)left + (double)right}, + {new Tuple(typeof(short), typeof(decimal)), (left, right) => (short)left + (decimal)right}, + {new Tuple(typeof(ushort), typeof(ushort)), (left, right) => (ushort)left + (ushort)right}, + {new Tuple(typeof(ushort), typeof(int)), (left, right) => (ushort)left + (int)right}, + {new Tuple(typeof(ushort), typeof(uint)), (left, right) => (ushort)left + (uint)right}, + {new Tuple(typeof(ushort), typeof(long)), (left, right) => (ushort)left + (long)right}, + {new Tuple(typeof(ushort), typeof(ulong)), (left, right) => (ushort)left + (ulong)right}, + {new Tuple(typeof(ushort), typeof(float)), (left, right) => (ushort)left + (float)right}, + {new Tuple(typeof(ushort), typeof(double)), (left, right) => (ushort)left + (double)right}, + {new Tuple(typeof(ushort), typeof(decimal)), (left, right) => (ushort)left + (decimal)right}, + {new Tuple(typeof(int), typeof(int)), (left, right) => (int)left + (int)right}, + {new Tuple(typeof(int), typeof(long)), (left, right) => (int)left + (long)right}, + {new Tuple(typeof(int), typeof(float)), (left, right) => (int)left + (float)right}, + {new Tuple(typeof(int), typeof(double)), (left, right) => (int)left + (double)right}, + {new Tuple(typeof(int), typeof(decimal)), (left, right) => (int)left + (decimal)right}, + {new Tuple(typeof(uint), typeof(uint)), (left, right) => (uint)left + (uint)right}, + {new Tuple(typeof(uint), typeof(long)), (left, right) => (uint)left + (long)right}, + {new Tuple(typeof(uint), typeof(ulong)), (left, right) => (uint)left + (ulong)right}, + {new Tuple(typeof(uint), typeof(float)), (left, right) => (uint)left + (float)right}, + {new Tuple(typeof(uint), typeof(double)), (left, right) => (uint)left + (double)right}, + {new Tuple(typeof(uint), typeof(decimal)), (left, right) => (uint)left + (decimal)right}, + {new Tuple(typeof(long), typeof(long)), (left, right) => (long)left + (long)right}, + {new Tuple(typeof(long), typeof(float)), (left, right) => (long)left + (float)right}, + {new Tuple(typeof(long), typeof(double)), (left, right) => (long)left + (double)right}, + {new Tuple(typeof(long), typeof(decimal)), (left, right) => (long)left + (decimal)right}, + {new Tuple(typeof(char), typeof(char)), (left, right) => (char)left + (char)right}, + {new Tuple(typeof(char), typeof(ushort)), (left, right) => (char)left + (ushort)right}, + {new Tuple(typeof(char), typeof(int)), (left, right) => (char)left + (int)right}, + {new Tuple(typeof(char), typeof(uint)), (left, right) => (char)left + (uint)right}, + {new Tuple(typeof(char), typeof(long)), (left, right) => (char)left + (long)right}, + {new Tuple(typeof(char), typeof(ulong)), (left, right) => (char)left + (ulong)right}, + {new Tuple(typeof(char), typeof(float)), (left, right) => (char)left + (float)right}, + {new Tuple(typeof(char), typeof(double)), (left, right) => (char)left + (double)right}, + {new Tuple(typeof(char), typeof(decimal)), (left, right) => (char)left + (decimal)right}, + {new Tuple(typeof(float), typeof(float)), (left, right) => (float)left + (float)right}, + {new Tuple(typeof(float), typeof(double)), (left, right) => (float)left + (double)right}, + {new Tuple(typeof(ulong), typeof(ulong)), (left, right) => (ulong)left + (ulong)right}, + {new Tuple(typeof(ulong), typeof(float)), (left, right) => (ulong)left + (float)right}, + {new Tuple(typeof(ulong), typeof(double)), (left, right) => (ulong)left + (double)right}, + {new Tuple(typeof(ulong), typeof(decimal)), (left, right) => (ulong)left + (decimal)right}, + {new Tuple(typeof(double), typeof(double)), (left, right) => (double)left + (double)right}, + {new Tuple(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(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 + public object Encode() { + return new Dictionary { {"__op", "Increment"}, {"amount", amount} }; } - private static object Add(object obj1, object obj2) - { + private static object Add(object obj1, object obj2) { Func adder; - if (adders.TryGetValue(new Tuple(obj1.GetType(), obj2.GetType()), out adder)) - { + if (adders.TryGetValue(new Tuple(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; } } diff --git a/Storage/Storage/Internal/Operation/AVRelationOperation.cs b/Storage/Storage/Internal/Operation/AVRelationOperation.cs index 512b5f5..dab4a5f 100644 --- a/Storage/Storage/Internal/Operation/AVRelationOperation.cs +++ b/Storage/Storage/Internal/Operation/AVRelationOperation.cs @@ -6,113 +6,113 @@ using System.Text; using System.Threading.Tasks; namespace LeanCloud.Storage.Internal { - public class AVRelationOperation : IAVFieldOperation { - private readonly IList adds; - private readonly IList removes; - private readonly string targetClassName; + public class AVRelationOperation : IAVFieldOperation { + private readonly IList adds; + private readonly IList removes; + private readonly string targetClassName; - private AVRelationOperation(IEnumerable adds, - IEnumerable removes, - string targetClassName) { - this.targetClassName = targetClassName; - this.adds = new ReadOnlyCollection(adds.ToList()); - this.removes = new ReadOnlyCollection(removes.ToList()); - } - - public AVRelationOperation(IEnumerable adds, - IEnumerable 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(IdsFromObjects(adds).ToList()); - this.removes = new ReadOnlyCollection(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 { - {"__op", "AddRelation"}, - {"objects", adds} - }; - var removeDict = removes.Count == 0 ? null : new Dictionary { - {"__op", "RemoveRelation"}, - {"objects", removes} - }; - - if (addDict != null && removeDict != null) { - return new Dictionary { - {"__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 adds, + IEnumerable removes, + string targetClassName) { + this.targetClassName = targetClassName; + this.adds = new ReadOnlyCollection(adds.ToList()); + this.removes = new ReadOnlyCollection(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 adds, + IEnumerable 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(IdsFromObjects(adds).ToList()); + this.removes = new ReadOnlyCollection(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 { + {"__op", "AddRelation"}, + {"objects", adds} + }; + var removeDict = removes.Count == 0 ? null : new Dictionary { + {"__op", "RemoveRelation"}, + {"objects", removes} + }; - private IEnumerable IdsFromObjects(IEnumerable 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 { + {"__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 IdsFromObjects(IEnumerable 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(); } - } } diff --git a/Storage/Storage/Internal/Operation/AVRemoveOperation.cs b/Storage/Storage/Internal/Operation/AVRemoveOperation.cs index 23b9f24..65c35e7 100644 --- a/Storage/Storage/Internal/Operation/AVRemoveOperation.cs +++ b/Storage/Storage/Internal/Operation/AVRemoveOperation.cs @@ -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 objects; - public AVRemoveOperation(IEnumerable objects) - { + public AVRemoveOperation(IEnumerable objects) { this.objects = new ReadOnlyCollection(objects.Distinct().ToList()); } - public object Encode() - { + public object Encode() { return new Dictionary { { "__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>(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(); } var oldList = Conversion.As>(oldValue); return oldList.Except(objects, AVFieldOperations.AVObjectComparer).ToList(); } - public IEnumerable Objects - { - get - { + public IEnumerable Objects { + get { return objects; } } diff --git a/Storage/Storage/Internal/Operation/AVSetOperation.cs b/Storage/Storage/Internal/Operation/AVSetOperation.cs index 1c3a412..360b3fd 100644 --- a/Storage/Storage/Internal/Operation/AVSetOperation.cs +++ b/Storage/Storage/Internal/Operation/AVSetOperation.cs @@ -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; } + } } diff --git a/Storage/Storage/Internal/Operation/IAVFieldOperation.cs b/Storage/Storage/Internal/Operation/IAVFieldOperation.cs index 70b2930..9e5bd97 100644 --- a/Storage/Storage/Internal/Operation/IAVFieldOperation.cs +++ b/Storage/Storage/Internal/Operation/IAVFieldOperation.cs @@ -1,42 +1,42 @@ namespace LeanCloud.Storage.Internal { - /// - /// 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. - /// - public interface IAVFieldOperation { /// - /// 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. /// - /// An object to be JSONified. - object Encode(); + public interface IAVFieldOperation { + /// + /// Converts the AVFieldOperation to a data structure that can be converted to JSON and sent to + /// LeanCloud as part of a save operation. + /// + /// An object to be JSONified. + object Encode(); - /// - /// 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 - /// this 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} /// - /// The most recent operation on the field, or null if none. - /// A new AVFieldOperation or this. - IAVFieldOperation MergeWithPrevious(IAVFieldOperation previous); + /// + /// 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 + /// this 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} /// + /// The most recent operation on the field, or null if none. + /// A new AVFieldOperation or this. + IAVFieldOperation MergeWithPrevious(IAVFieldOperation previous); - /// - /// 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. - /// - /// The previous value for the field. - /// The key that this value is for. - /// The new value for the field. - object Apply(object oldValue, string key); - } + /// + /// 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. + /// + /// The previous value for the field. + /// The key that this value is for. + /// The new value for the field. + object Apply(object oldValue, string key); + } } diff --git a/Storage/Storage/Public/AVObject.cs b/Storage/Storage/Public/AVObject.cs index 6c688de..ad0cc18 100644 --- a/Storage/Storage/Public/AVObject.cs +++ b/Storage/Storage/Public/AVObject.cs @@ -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 { /// @@ -18,13 +16,6 @@ namespace LeanCloud { public class AVObject : IEnumerable>, 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> operationSetQueue = @@ -75,7 +66,7 @@ namespace LeanCloud { /// Constructor for use in AVObject subclasses. Subclasses must specify a AVClassName attribute. /// protected AVObject() - : this(AutoClassName) { + : this(AutoClassName) { } /// @@ -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); } }