Example #1
0
        /// <summary>
        /// Visits the node difference.
        /// </summary>
        /// <param name="difference">The difference.</param>
        /// <returns>
        /// A sequence of actions that must be performed to upgrade
        /// from <see cref="IDifference.Source"/> of the specified
        /// difference to its <see cref="IDifference.Target"/>.
        /// </returns>
        protected virtual void VisitNodeDifference(NodeDifference difference)
        {
            var any = difference.Target ?? difference.Source;
            var isInverseOrderUpgrade =
                Stage == UpgradeStage.Prepare ||
                Stage == UpgradeStage.Cleanup ||
                Stage == UpgradeStage.TemporaryRename;

            using (OpenActionGroup(string.Format(NodeGroupComment, any.Name))) {
                if (isInverseOrderUpgrade)
                {
                    ProcessProperties(difference);
                    if (IsAllowedForCurrentStage(difference))
                    {
                        ProcessMovement(difference);
                    }
                }
                else
                {
                    if (IsAllowedForCurrentStage(difference))
                    {
                        ProcessMovement(difference);
                    }

                    ProcessProperties(difference);
                }
            }
        }
Example #2
0
        /// <summary>
        /// Determines whether the specified difference allowed for current <see cref="Stage"/>.
        /// </summary>
        /// <param name="difference">The difference.</param>
        /// <returns>
        /// <see langword="true"/> if the specified difference allowed
        /// for current <see cref="Stage"/>; otherwise, <see langword="false"/>.
        /// </returns>
        /// <exception cref="ArgumentOutOfRangeException"><c>Stage</c> is out of range.</exception>
        protected virtual bool IsAllowedForCurrentStage(NodeDifference difference)
        {
            var isRemoved         = (difference.MovementInfo & MovementInfo.Removed) != 0;
            var isCreated         = (difference.MovementInfo & MovementInfo.Created) != 0;
            var isNameChanged     = (difference.MovementInfo & MovementInfo.NameChanged) != 0;
            var isImmutable       = Context.IsImmutable;
            var isRemoveOnPrepare =
                isRemoved && !difference.IsRemoveOnCleanup || (isImmutable && !isCreated);

            switch (Stage)
            {
            case UpgradeStage.CleanupData:
                return(difference.IsDataChanged);

            case UpgradeStage.Prepare:
                return(isRemoveOnPrepare);

            case UpgradeStage.TemporaryRename:
                return(isNameChanged && IsCyclicRename(difference));

            case UpgradeStage.Upgrade:
                return(!isRemoveOnPrepare && (isCreated || isNameChanged));

            case UpgradeStage.CopyData:
            case UpgradeStage.PostCopyData:
                return(difference.IsDataChanged);

            case UpgradeStage.Cleanup:
                return(isRemoved);

            default:
                throw new ArgumentOutOfRangeException("Stage");
            }
        }
Example #3
0
 /// <summary>
 /// Gets the property differences for each property of type <see cref="Node"/> or <see cref="NodeCollection"/>.
 /// </summary>
 /// <param name="difference">The difference.</param>
 /// <returns>Property differences set.</returns>
 protected IEnumerable <NodeDifference> GetPropertyDifferences(NodeDifference difference)
 {
     return
         (difference.PropertyChanges.Values.OfType <NodeDifference>().Union(
              difference.PropertyChanges.Values.OfType <NodeCollectionDifference>()
              .SelectMany(collectionDifference => collectionDifference.ItemChanges)));
 }
Example #4
0
        /// <summary>
        /// Process <see cref="NodeDifference.PropertyChanges"/> for specific <see cref="NodeDifference"/>.
        /// </summary>
        /// <param name="difference">The difference.</param>
        protected virtual void ProcessProperties(NodeDifference difference)
        {
            var isImmutable = Context.IsImmutable;
            var isCleanup   = Stage == UpgradeStage.Prepare || Stage == UpgradeStage.Cleanup;
            var any         = difference.Target ?? difference.Source;

            IEnumerable <KeyValuePair <string, Difference> > propertyChanges = difference.PropertyChanges;

            if (isCleanup)
            {
                propertyChanges = propertyChanges.Reverse();
            }
            foreach (var pair in propertyChanges)
            {
                var accessor = any.PropertyAccessors[pair.Key];
                if (!isCleanup || !isImmutable || IsVolatile(difference, accessor))
                {
                    using (CreateContext().Activate()) {
                        Context.Property    = pair.Key;
                        Context.IsImmutable = IsImmutable(difference, accessor);
                        if ((difference.MovementInfo & MovementInfo.Removed) != 0)
                        {
                            Context.IsRemoved = true;
                        }
                        var dependencyRootType = GetDependencyRootType(difference, accessor);
                        if (dependencyRootType != null)
                        {
                            Context.DependencyRootType = dependencyRootType;
                        }
                        Visit(pair.Value);
                    }
                }
            }
        }
Example #5
0
        /// <summary>
        /// Determines whether is cycle rename detected.
        /// </summary>
        /// <param name="difference">The difference.</param>
        /// <returns>
        /// <see langword="true"/> if is cycle rename exists; otherwise, <see langword="false"/>.
        /// </returns>
        protected virtual bool IsCyclicRename(NodeDifference difference)
        {
            if (!difference.IsNameChanged || difference.Source == null || difference.Target == null)
            {
                return(false);
            }
            var source = difference.Source;
            var target = difference.Target;

            if (!source.Nesting.IsNestedToCollection)
            {
                return(false);
            }
            var collection = source.Parent.GetProperty(source.Nesting.PropertyName) as NodeCollection;

            return(collection.Contains(target.Name) ||
                   Hints.OfType <RenameHint>()
                   .Any(renameHint => renameHint.TargetPath == source.Path));
        }
Example #6
0
        /// <summary>
        /// Generates <see cref="NodeAction"/> according to <see cref="NodeDifference.MovementInfo"/>.
        /// </summary>
        /// <param name="difference">The difference.</param>
        protected virtual void ProcessMovement(NodeDifference difference)
        {
            var source      = difference.Source;
            var target      = difference.Target;
            var isImmutable = Context.IsImmutable;
            var isChanged   = (difference.MovementInfo & MovementInfo.Changed) != 0;
            var isCreated   = (difference.MovementInfo & MovementInfo.Created) != 0;
            var isRemoved   = (difference.MovementInfo & MovementInfo.Removed) != 0 ||
                              (isImmutable && difference.HasChanges && !isCreated);

            var sc = StringComparer.OrdinalIgnoreCase;

            switch (Stage)
            {
            case UpgradeStage.CleanupData:
                if (difference.IsDataChanged)
                {
                    Hints.GetHints <UpdateDataHint>(difference.Source)
                    .Where(hint => sc.Compare(hint.SourceTablePath, difference.Source.Path) == 0)
                    .ForEach(hint => AddAction(UpgradeActionType.Regular,
                                               new DataAction {
                        DataHint = hint
                    }));
                    Hints.GetHints <DeleteDataHint>(difference.Source)
                    .Where(hint => !hint.PostCopy)
                    .Where(hint => sc.Compare(hint.SourceTablePath, difference.Source.Path) == 0)
                    .ForEach(hint => AddAction(UpgradeActionType.Regular,
                                               new DataAction {
                        DataHint = hint
                    }));
                }

                break;

            case UpgradeStage.Prepare:
                if (isRemoved && !difference.IsRemoveOnCleanup)
                {
                    if (!Context.IsRemoved || difference.IsDependentOnParent)
                    {
                        AddAction(UpgradeActionType.PreCondition,
                                  new RemoveNodeAction {
                            Path = (source ?? target).Path
                        });
                    }
                }

                break;

            case UpgradeStage.Cleanup:
                if (!Context.IsRemoved || difference.IsDependentOnParent)
                {
                    AddAction(UpgradeActionType.PreCondition,
                              new RemoveNodeAction {
                        Path = (source ?? target).Path
                    });
                }

                break;

            case UpgradeStage.TemporaryRename:
                RegisterTemporaryRename(source);
                var temporaryName = GetTemporaryName(source);
                AddAction(UpgradeActionType.Rename,
                          new MoveNodeAction {
                    Path    = source.Path,
                    Parent  = source.Parent == null ? string.Empty : source.Parent.Path,
                    Name    = temporaryName,
                    Index   = null,
                    NewPath = GetPathWithoutName(source) + temporaryName
                });
                break;

            case UpgradeStage.CopyData:
                Hints.GetHints <CopyDataHint>(difference.Source)
                .Where(hint => sc.Compare(hint.SourceTablePath, difference.Source.Path) == 0)
                .ForEach(hint => AddAction(UpgradeActionType.Regular, new DataAction {
                    DataHint = hint
                }));
                break;

            case UpgradeStage.PostCopyData:
                if (difference.IsDataChanged)
                {
                    Hints.GetHints <DeleteDataHint>(difference.Source)
                    .Where(hint => hint.PostCopy)
                    .Where(hint => sc.Compare(hint.SourceTablePath, difference.Source.Path) == 0)
                    .ForEach(hint => AddAction(UpgradeActionType.Regular,
                                               new DataAction {
                        DataHint = hint
                    }));
                }

                break;

            case UpgradeStage.Upgrade:
                if (target == null)
                {
                    break;
                }

                if (isCreated)
                {
                    var action = new CreateNodeAction {
                        Path  = target.Parent == null ? string.Empty : target.Parent.Path,
                        Type  = target.GetType(),
                        Name  = target.Name,
                        Index = target.Nesting.IsNestedToCollection ? (int?)target.Index : null
                    };
                    AddAction(UpgradeActionType.PostCondition, action);
                }
                else if (isChanged)
                {
                    var action = new MoveNodeAction {
                        Path    = source.Path,
                        Parent  = target.Parent == null ? string.Empty : target.Parent.Path,
                        Name    = target.Name,
                        Index   = target.Nesting.IsNestedToCollection ? (int?)source.Index : null,
                        NewPath = target.Path
                    };
                    AddAction(UpgradeActionType.PostCondition, action);
                }

                break;
            }
        }
Example #7
0
        /// <summary>
        /// Compares source and target node properties.
        /// </summary>
        /// <param name="source">The source.</param>
        /// <param name="target">The target.</param>
        /// <param name="difference">The difference.</param>
        protected virtual void CompareProperties(Node source, Node target, NodeDifference difference)
        {
            var any = source ?? target;

            foreach (var pair in any.PropertyAccessors)
            {
                var accessor = pair.Value;
                if (accessor.IgnoreInComparison)
                {
                    continue;
                }

                var property = accessor.PropertyInfo;
                using (CreateContext().Activate()) {
                    Context.PropertyAccessor = accessor;
                    object sourceValue = (source == null || !accessor.HasGetter)
            ? accessor.Default : accessor.Getter.Invoke(source);
                    object targetValue = (target == null || !accessor.HasGetter)
            ? accessor.Default : accessor.Getter.Invoke(target);
                    object anyValue = sourceValue ?? targetValue;
                    if (anyValue == null)
                    {
                        continue; // Both are null
                    }
                    Difference propertyDifference = null;
                    if (!IsReference(sourceValue, targetValue))
                    {
                        propertyDifference = Visit(sourceValue, targetValue);
                    }
                    else if (Stage == ComparisonStage.ReferenceComparison)
                    {
                        if (IsIgnored(sourceValue))
                        {
                            difference.MovementInfo = 0;
                        }
                        else
                        {
                            propertyDifference = GetReferencedPropertyDifference(sourceValue, targetValue, target, property.Name);
                        }
                    }

                    if (propertyDifference == null)
                    {
                        continue;
                    }

                    if (any.Nesting.PropertyInfo != null && accessor.DependencyRootType == any.Nesting.PropertyInfo.PropertyType)
                    {
                        if (propertyDifference is NodeDifference)
                        {
                            ((NodeDifference)propertyDifference).IsDependentOnParent = true;
                        }
                        else if (propertyDifference is NodeCollectionDifference)
                        {
                            ((NodeCollectionDifference)propertyDifference).ItemChanges
                            .ForEach(item => item.IsDependentOnParent = true);
                        }
                    }
                    difference.PropertyChanges.Add(property.Name, propertyDifference);
                }
            }
        }