示例#1
0
        public void Update(TContext context, BasePrimitive <TItem> current, BasePrimitive <TItem> next)
        {
            var currentList = current?.Attributes.GetAttributeOrDefault(_attribute);
            var nextList    = next?.Attributes.GetAttributeOrDefault(_attribute);

            // get an id to allow for easy viewing
            long loggingId = 0;


            if (IsLoggingEnabled.Value)
            {
                loggingId = Interlocked.Increment(ref LoggingId);
                Logging.Instance.LogInformation("CollectionUpdate:Begin {id} current Length:{currentLength} next Length:{nextLength}", loggingId, currentList?.Length ?? 0, nextList?.Length ?? 0);
            }

            IEnumerable <Operation <TTarget> > operations;

            // get a list of operations that transform currentList to nextList

            IReadOnlyList <Operation <TTarget> > comparisonOperations;

            if (IsLoggingEnabled.Value)
            {
                using var _ = Benchmark.Create((d, __) =>
                {
                    Logging.Instance.LogInformation("CollectionUpdate:Timings {id} duration:{duration}ms, algorithm:{algorithm}", loggingId, d, _collectionComparer.GetType().FriendlyName());
                });

                comparisonOperations = _collectionComparer.Compare(currentList, nextList);
            }
            else
            {
                comparisonOperations = _collectionComparer.Compare(currentList, nextList);
            }

            if (IsLoggingEnabled.Value)
            {
                Logging.Instance.LogInformation("CollectionUpdate:Raw {id} change count:{count}", loggingId, comparisonOperations.Count);
            }


            // only perform an attempt at a compaction if there is an insert and a delete both in existence
            if (comparisonOperations.Count >= 2 && comparisonOperations.Any(f => f.GetType() == typeof(DeleteOp <IPrimitive>)) && comparisonOperations.Any(f => f.GetType() == typeof(InsertOp <IPrimitive>)))
            {
                if (IsLoggingEnabled.Value)
                {
                    Logging.Instance.LogInformation("CollectionUpdate:Compacting Changes");
                }

                // create our collection change compactor which will look to reduce the number of updates made

                var collectionChangeCompactor = new CollectionChangeCompactor <TTarget>(currentList ?? nextList);

                foreach (var operation in comparisonOperations)
                {
                    switch (operation)
                    {
                    case InsertOp <TTarget> op:
                        collectionChangeCompactor.Insert(op.Index, op.Item);
                        break;

                    case DeleteOp <TTarget> op:
                        collectionChangeCompactor.Delete(op.Index);
                        break;

                    case MoveOp <TTarget> op:
                        collectionChangeCompactor.Delete(op.From);
                        collectionChangeCompactor.Insert(op.To, op.Item);
                        break;

                    case UpdateOp <TTarget> op:
                        collectionChangeCompactor.Update(op.Index, op.ToItem);
                        break;
                    }
                }

                operations = collectionChangeCompactor.GetChangOps();
            }
            else
            {
                operations = comparisonOperations;
            }

            // now apply changes

            foreach (var operation in operations)
            {
                switch (operation)
                {
                case UpdateOp <TTarget> op:

                    if (IsLoggingEnabled.Value)
                    {
                        Logging.Instance.LogInformation("CollectionUpdate:Update {id} {index}:{item}", loggingId, op.Index, op.FromItem, op.ToItem);
                    }

                    _connector.Update(op.Index, op.FromItem, op.ToItem);
                    break;

                case DeleteOp <TTarget> op:

                    if (IsLoggingEnabled.Value)
                    {
                        Logging.Instance.LogInformation("CollectionUpdate:Delete {id} {index}:{item}", loggingId, op.Index, op.Item);
                    }

                    _connector.Remove(op.Index, op.Item);
                    break;

                case InsertOp <TTarget> op:

                    if (IsLoggingEnabled.Value)
                    {
                        Logging.Instance.LogInformation("CollectionUpdate:Insert {id} {index}:{item}", loggingId, op.Index, op.Item);
                    }

                    _connector.Insert(op.Index, op.Item);
                    break;

                default:
                    throw new ArgumentOutOfRangeException();
                }
            }

            if (IsLoggingEnabled.Value)
            {
                Logging.Instance.LogInformation("CollectionUpdate:End {id}", loggingId);
            }
        }