public void Test_ListTracker_Serialize() { var tracker = new TrackableListTracker <int>(); tracker.TrackPushBack(0); AssertTrackerSerialize(tracker); }
public void TestList_ApplyToTracker_Work() { var list = CreateTestListWithTracker(); ModifyListForTest(list); var tracker2 = new TrackableListTracker <string>(); list.Tracker.ApplyTo(tracker2); var list2 = CreateTestList(); tracker2.ApplyTo(list2); Assert.Equal(GetModifiedList(), list2); }
public static IListTracker <T> Deserialize(ref MessagePackReader reader, MessagePackSerializerOptions options) { var tracker = new TrackableListTracker <T>(); var length = reader.ReadArrayHeader(); for (int i = 0; i < length; i++) { var operation = reader.ReadInt32(); var index = reader.ReadInt32(); var value = MessagePackSerializer.Deserialize <T>(ref reader, options); switch ((TrackableListOperation)operation) { case TrackableListOperation.Insert: tracker.TrackInsert(index, value); break; case TrackableListOperation.Remove: tracker.TrackRemove(index, value); break; case TrackableListOperation.Modify: tracker.TrackModify(index, default(T), value); break; case TrackableListOperation.PushFront: tracker.TrackPushFront(value); break; case TrackableListOperation.PushBack: tracker.TrackPushBack(value); break; case TrackableListOperation.PopFront: tracker.TrackPopFront(default(T)); break; case TrackableListOperation.PopBack: tracker.TrackPopBack(default(T)); break; } } return(tracker); }
public static TrackableListTracker <T> Convert(TrackableListTrackerSurrogate <T> surrogate) { if (surrogate == null) { return(null); } var tracker = new TrackableListTracker <T>(); foreach (var change in surrogate.ChangeList) { tracker.ChangeList.Add(new TrackableListTracker <T> .Change { Operation = change.Operation, Index = change.Index, NewValue = change.NewValue, }); } return(tracker); }
public async Task SaveAsync(IDatabase db, TrackableListTracker <T> tracker, RedisKey key) { foreach (var change in tracker.ChangeList) { switch (change.Operation) { case TrackableListOperation.Insert: throw new InvalidOperationException("Redis doesn't support insert value by index."); case TrackableListOperation.Remove: throw new InvalidOperationException("Redis doesn't support remove value by index."); case TrackableListOperation.Modify: await db.ListSetByIndexAsync(key, change.Index, _valueToRedisValue(change.NewValue)); break; case TrackableListOperation.PushFront: await db.ListLeftPushAsync(key, _valueToRedisValue(change.NewValue)); break; case TrackableListOperation.PushBack: await db.ListRightPushAsync(key, _valueToRedisValue(change.NewValue)); break; case TrackableListOperation.PopFront: await db.ListLeftPopAsync(key); break; case TrackableListOperation.PopBack: await db.ListRightPopAsync(key); break; } } }
public async Task SaveAsync(IMongoCollection <BsonDocument> collection, TrackableListTracker <T> tracker, params object[] keyValues) { if (keyValues.Length < 2) { throw new ArgumentException("At least 2 keyValue required."); } if (tracker.HasChange == false) { return; } var filter = Builders <BsonDocument> .Filter.Eq("_id", keyValues[0]); foreach (var update in BuildUpdatesForSave(null, tracker, keyValues.Skip(1).ToArray())) { await collection.UpdateOneAsync( filter, update, new UpdateOptions { IsUpsert = true }); } }
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { if (reader.TokenType != JsonToken.StartArray) { return(null); } var tracker = new TrackableListTracker <T>(); reader.Read(); while (true) { if (reader.TokenType != JsonToken.StartArray) { break; } reader.Read(); var str = (string)reader.Value; reader.Read(); if (str.Length < 2) { throw new Exception("Wrong index token: " + str); } int index; if (str[1] == 'F') { index = -2; } else if (str[1] == 'B') { index = -1; } else if (int.TryParse(str.Substring(1), out index) == false) { throw new Exception("Invalid token: " + str); } T obj; switch (str[0]) { case '+': obj = serializer.Deserialize <T>(reader); reader.Read(); if (index == -2) { tracker.TrackPushFront(obj); } else if (index == -1) { tracker.TrackPushBack(obj); } else { tracker.TrackInsert(index, obj); } break; case '-': if (index == -2) { tracker.TrackPopFront(default(T)); } else if (index == -1) { tracker.TrackPopBack(default(T)); } else { tracker.TrackRemove(index, default(T)); } break; case '=': obj = serializer.Deserialize <T>(reader); reader.Read(); tracker.TrackModify(index, default(T), obj); break; } if (reader.TokenType != JsonToken.EndArray) { throw new Exception("Wrong token type: " + reader.TokenType); } reader.Read(); } return(tracker); }
public List <UpdateDefinition <BsonDocument> > BuildUpdatesForSave( UpdateDefinition <BsonDocument> update, TrackableListTracker <T> tracker, params object[] keyValues) { var keyNamespace = DocumentHelper.ToDotPath(keyValues); // Multiple push-back batching optimization if (tracker.ChangeList.Count > 1 && tracker.ChangeList.All(c => c.Operation == TrackableListOperation.PushBack)) { var newValues = tracker.ChangeList.Select(c => c.NewValue); return(new List <UpdateDefinition <BsonDocument> > { update == null ? Builders <BsonDocument> .Update.PushEach(keyNamespace, newValues) : update.PushEach(keyNamespace, newValues) }); } // Multiple push-front batching optimization if (tracker.ChangeList.Count > 1 && tracker.ChangeList.All(c => c.Operation == TrackableListOperation.PushFront)) { var newValues = tracker.ChangeList.Select(c => c.NewValue).Reverse(); return(new List <UpdateDefinition <BsonDocument> > { update == null ? Builders <BsonDocument> .Update.PushEach(keyNamespace, newValues, position : 0) : update.PushEach(keyNamespace, newValues, position: 0) }); } // List update can process only one change each time var updates = new List <UpdateDefinition <BsonDocument> >(); foreach (var change in tracker.ChangeList) { switch (change.Operation) { case TrackableListOperation.Insert: updates.Add(update == null ? Builders <BsonDocument> .Update.PushEach(keyNamespace, new[] { change.NewValue }, position: change.Index) : update.PushEach(keyNamespace, new[] { change.NewValue }, position: change.Index)); update = null; break; case TrackableListOperation.Remove: throw new Exception("Remove operation is not supported!"); case TrackableListOperation.Modify: updates.Add(update == null ? Builders <BsonDocument> .Update.Set(keyNamespace + "." + change.Index, change.NewValue) : update.Set(keyNamespace + "." + change.Index, change.NewValue)); break; case TrackableListOperation.PushFront: updates.Add(update == null ? Builders <BsonDocument> .Update.PushEach(keyNamespace, new[] { change.NewValue }, position: 0) : update.PushEach(keyNamespace, new[] { change.NewValue }, position: 0)); break; case TrackableListOperation.PushBack: updates.Add(update == null ? Builders <BsonDocument> .Update.Push(keyNamespace, change.NewValue) : update.Push(keyNamespace, change.NewValue)); break; case TrackableListOperation.PopFront: updates.Add(update == null ? Builders <BsonDocument> .Update.PopFirst(keyNamespace) : update.PopFirst(keyNamespace)); break; case TrackableListOperation.PopBack: updates.Add(update == null ? Builders <BsonDocument> .Update.PopLast(keyNamespace) : update.PopLast(keyNamespace)); break; } } if (update != null) { updates.Add(update); } return(updates); }
public void Serialize(ref MessagePackWriter writer, TrackableListTracker <T> value, MessagePackSerializerOptions options) { TrackableListTrackerMessagePackFormatter <T> .Serialize(ref writer, value, options); }
public void TestList_RollbackToTracker_Work() { var list = CreateTestListWithTracker(); ModifyListForTest(list); var tracker2 = new TrackableListTracker<string>(); list.Tracker.ApplyTo(tracker2); list.Tracker.RollbackTo(tracker2); var list2 = CreateTestList(); tracker2.ApplyTo(list2); Assert.Equal(GetInitialList(), list2); }