internal static void UpdateInvalidArgumentsIfNecessary(object sender, ValidationService.ErrorsMarkedEventArgs args)
        {
            if (args.Reason != ValidationReason.ModelChange)
            {
                return;
            }

            if (args.Errors.Count == 0)
            {
                return;
            }

            using (EditingScope editingScope = args.ModelTreeManager.CreateEditingScope(string.Empty))
            {
                // Prevent the validation -> fix arguments -> validation loop.
                editingScope.SuppressUndo = true;

                // Suppress validation. We will do it ourselves (see below)
                editingScope.SuppressValidationOnComplete = true;

                // Re-compile erroreous expressions to see if update is necessary
                ValidationService validationService = args.Context.Services.GetRequiredService<ValidationService>();
                ArgumentAccessorWrapperCache argumentAccessorWrapperCache = new ArgumentAccessorWrapperCache();
                List<ExpressionReplacement> expressionReplacements = ComputeExpressionReplacements(args.Errors.Select(error => error.Source).OfType<ActivityWithResult>(), args.Context, argumentAccessorWrapperCache);
                bool argumentReplacementOccurred = false;
                if (expressionReplacements.Count > 0)
                {
                    try
                    {
                        foreach (ExpressionReplacement expressionReplacement in expressionReplacements)
                        {
                            if (expressionReplacement.TryReplaceArgument(args.ModelTreeManager, validationService))
                            {
                                argumentReplacementOccurred = true;
                            }
                        }

                        if (argumentReplacementOccurred)
                        {
                            args.Handled = true;
                            editingScope.Complete();
                        }
                    }
                    catch (Exception e)
                    {
                        if (Fx.IsFatal(e))
                        {
                            throw;
                        }

                        // We handle exception here instead of letting WF Designer handle it, so that the validation below will run even
                        // if any of the ArgumentAccessor.Setter methods throw exceptions.
                        ErrorReporting.ShowErrorMessage(e);
                    }

                    // Since any pending validation will be canceled if argument replacement occured (=has model change), we need to re-validate the workflow.
                    // We suppressed validation upon EditingScope completion and do it ourselves, because
                    // the argument replacement could have been done directly to the underlying activity instance, rather than thru ModelItem.
                    if (argumentReplacementOccurred)
                    {
                        validationService.ValidateWorkflow();
                    }
                }
            }
        }
        internal static ExpressionReplacement ComputeExpressionReplacement(ActivityWithResult expression, Activity parentActivity, EditingContext context, ArgumentAccessorWrapperCache argumentAccessorWrapperCache, Type preferredReturnType = null)
        {
            Fx.Assert(expression != null, "expressions cannot be null.");
            Fx.Assert(parentActivity != null, "parentActivity cannot be null.");
            Fx.Assert(context != null, "context cannot be null.");
            Fx.Assert(argumentAccessorWrapperCache != null, "argumentAccessorWrapperCache cannot be null.");

            IEnumerable<ArgumentAccessorWrapper> argumentAccessorWrappers = argumentAccessorWrapperCache.GetArgumentAccessorWrappers(parentActivity);
            if (argumentAccessorWrappers != null)
            {
                ArgumentAccessorWrapper argumentAccessorWrapper = argumentAccessorWrappers.FirstOrDefault(wrapper => object.ReferenceEquals(wrapper.Argument.Expression, expression));
                if (argumentAccessorWrapper != null)
                {
                    bool isLocationExpression = ExpressionHelper.IsGenericLocationExpressionType(expression);
                    bool canInferType = true;
                    Type expectedReturnType;
                    ActivityWithResult morphedExpression;
                    if (preferredReturnType != null)
                    {
                        expectedReturnType = preferredReturnType;
                    }
                    else
                    {
                        canInferType = ExpressionHelper.TryInferReturnType(expression, context, out expectedReturnType);
                    }

                    if (canInferType && expectedReturnType != null && ExpressionHelper.TryMorphExpression(expression, isLocationExpression, expectedReturnType, context, out morphedExpression))
                    {
                        Type expressionResultType = isLocationExpression ? expression.ResultType.GetGenericArguments()[0] : expression.ResultType;
                        if (expressionResultType != expectedReturnType)
                        {
                            Argument newArgument = Argument.Create(expectedReturnType, argumentAccessorWrapper.Argument.Direction);
                            newArgument.Expression = morphedExpression;
                            return new ExpressionReplacement(expression, argumentAccessorWrapper.Argument, newArgument, argumentAccessorWrapper.ArgumentAccessor);
                        }
                    }
                }
            }

            return null;
        }
        internal static List<ExpressionReplacement> ComputeExpressionReplacements(IEnumerable<ActivityWithResult> expressions, EditingContext context, ArgumentAccessorWrapperCache argumentAccessorWrapperCache)
        {
            Fx.Assert(expressions != null, "expressions cannot be null.");
            Fx.Assert(context != null, "context cannot be null.");
            Fx.Assert(argumentAccessorWrapperCache != null, "argumentAccessorWrapperCache cannot be null.");

            HashSet<Assign> assignsWithRValueToFix = new HashSet<Assign>();
            Dictionary<Assign, Type> assignLValueTypes = new Dictionary<Assign, Type>();
            List<ExpressionReplacement> expressionReplacements = new List<ExpressionReplacement>();
            foreach (ActivityWithResult expression in expressions)
            {
                Activity parentActivity = ValidationService.GetParent(expression);
                if (parentActivity == null)
                {
                    continue;
                }

                Assign assignActivity = parentActivity as Assign;
                if (assignActivity != null && !ExpressionHelper.IsGenericLocationExpressionType(expression))
                {
                    assignsWithRValueToFix.Add(assignActivity);
                }
                else
                {
                    ExpressionReplacement expressionReplacement = ComputeExpressionReplacement(expression, parentActivity, context, argumentAccessorWrapperCache);
                    if (expressionReplacement != null)
                    {
                        expressionReplacements.Add(expressionReplacement);
                        if (assignActivity != null)
                        {
                            Type expectedReturnType = expressionReplacement.NewArgument.ArgumentType;
                            assignLValueTypes[assignActivity] = expectedReturnType;
                        }
                    }
                }
            }

            // Special handle Assign R-Values: Assign.To.ArgumentType must be the same as Assign.Value.ArgumentType.
            foreach (Assign assignWithRValueToFix in assignsWithRValueToFix)
            {
                Type expectedReturnType;
                if (assignLValueTypes.TryGetValue(assignWithRValueToFix, out expectedReturnType))
                {
                    assignLValueTypes.Remove(assignWithRValueToFix);
                }
                else if (assignWithRValueToFix.To != null)
                {
                    expectedReturnType = assignWithRValueToFix.To.ArgumentType;
                }

                ExpressionReplacement expressionReplacement = ComputeExpressionReplacement(assignWithRValueToFix.Value.Expression, assignWithRValueToFix, context, argumentAccessorWrapperCache, expectedReturnType);
                if (expressionReplacement != null)
                {
                    expressionReplacements.Add(expressionReplacement);
                }
            }

            // These Assign activities have their L-value argument (To) changed but not the R-value argument (Value).
            // Now make sure that the R-value arguments are compatible with the L-value argument.
            foreach (KeyValuePair<Assign, Type> kvp in assignLValueTypes)
            {
                Assign remainingAssign = kvp.Key;
                Type expectedReturnType = kvp.Value;
                if (remainingAssign.Value != null && remainingAssign.Value.Expression != null)
                {
                    ActivityWithResult expression = remainingAssign.Value.Expression;
                    if (expression.ResultType != expectedReturnType)
                    {
                        ExpressionReplacement expressionReplacement = ComputeExpressionReplacement(expression, remainingAssign, context, argumentAccessorWrapperCache, expectedReturnType);
                        if (expressionReplacement != null)
                        {
                            expressionReplacements.Add(expressionReplacement);
                        }
                    }
                }
            }

            return expressionReplacements;
        }
        internal static void UpdateInvalidArgumentsIfNecessary(object sender, ValidationService.ErrorsMarkedEventArgs args)
        {
            if (args.Reason != ValidationReason.ModelChange)
            {
                return;
            }

            if (args.Errors.Count == 0)
            {
                return;
            }

            using (EditingScope editingScope = args.ModelTreeManager.CreateEditingScope(string.Empty))
            {
                // Prevent the validation -> fix arguments -> validation loop.
                editingScope.SuppressUndo = true;

                // Suppress validation. We will do it ourselves (see below)
                editingScope.SuppressValidationOnComplete = true;

                // Re-compile erroreous expressions to see if update is necessary
                ValidationService            validationService            = args.Context.Services.GetRequiredService <ValidationService>();
                ArgumentAccessorWrapperCache argumentAccessorWrapperCache = new ArgumentAccessorWrapperCache();
                List <ExpressionReplacement> expressionReplacements       = ComputeExpressionReplacements(args.Errors.Select(error => error.Source).OfType <ActivityWithResult>(), args.Context, argumentAccessorWrapperCache);
                bool argumentReplacementOccurred = false;
                if (expressionReplacements.Count > 0)
                {
                    try
                    {
                        foreach (ExpressionReplacement expressionReplacement in expressionReplacements)
                        {
                            if (expressionReplacement.TryReplaceArgument(args.ModelTreeManager, validationService))
                            {
                                argumentReplacementOccurred = true;
                            }
                        }

                        if (argumentReplacementOccurred)
                        {
                            args.Handled = true;
                            editingScope.Complete();
                        }
                    }
                    catch (Exception e)
                    {
                        if (Fx.IsFatal(e))
                        {
                            throw;
                        }

                        // We handle exception here instead of letting WF Designer handle it, so that the validation below will run even
                        // if any of the ArgumentAccessor.Setter methods throw exceptions.
                        ErrorReporting.ShowErrorMessage(e);
                    }

                    // Since any pending validation will be canceled if argument replacement occured (=has model change), we need to re-validate the workflow.
                    // We suppressed validation upon EditingScope completion and do it ourselves, because
                    // the argument replacement could have been done directly to the underlying activity instance, rather than thru ModelItem.
                    if (argumentReplacementOccurred)
                    {
                        validationService.ValidateWorkflow();
                    }
                }
            }
        }
        internal static ExpressionReplacement ComputeExpressionReplacement(ActivityWithResult expression, Activity parentActivity, EditingContext context, ArgumentAccessorWrapperCache argumentAccessorWrapperCache, Type preferredReturnType = null)
        {
            Fx.Assert(expression != null, "expressions cannot be null.");
            Fx.Assert(parentActivity != null, "parentActivity cannot be null.");
            Fx.Assert(context != null, "context cannot be null.");
            Fx.Assert(argumentAccessorWrapperCache != null, "argumentAccessorWrapperCache cannot be null.");

            IEnumerable <ArgumentAccessorWrapper> argumentAccessorWrappers = argumentAccessorWrapperCache.GetArgumentAccessorWrappers(parentActivity);

            if (argumentAccessorWrappers != null)
            {
                ArgumentAccessorWrapper argumentAccessorWrapper = argumentAccessorWrappers.FirstOrDefault(wrapper => object.ReferenceEquals(wrapper.Argument.Expression, expression));
                if (argumentAccessorWrapper != null)
                {
                    bool isLocationExpression = ExpressionHelper.IsGenericLocationExpressionType(expression);
                    bool canInferType         = true;
                    Type expectedReturnType;
                    ActivityWithResult morphedExpression;
                    if (preferredReturnType != null)
                    {
                        expectedReturnType = preferredReturnType;
                    }
                    else
                    {
                        canInferType = ExpressionHelper.TryInferReturnType(expression, context, out expectedReturnType);
                    }

                    if (canInferType && expectedReturnType != null && ExpressionHelper.TryMorphExpression(expression, isLocationExpression, expectedReturnType, context, out morphedExpression))
                    {
                        Type expressionResultType = isLocationExpression ? expression.ResultType.GetGenericArguments()[0] : expression.ResultType;
                        if (expressionResultType != expectedReturnType)
                        {
                            Argument newArgument = Argument.Create(expectedReturnType, argumentAccessorWrapper.Argument.Direction);
                            newArgument.Expression = morphedExpression;
                            return(new ExpressionReplacement(expression, argumentAccessorWrapper.Argument, newArgument, argumentAccessorWrapper.ArgumentAccessor));
                        }
                    }
                }
            }

            return(null);
        }
        internal static List <ExpressionReplacement> ComputeExpressionReplacements(IEnumerable <ActivityWithResult> expressions, EditingContext context, ArgumentAccessorWrapperCache argumentAccessorWrapperCache)
        {
            Fx.Assert(expressions != null, "expressions cannot be null.");
            Fx.Assert(context != null, "context cannot be null.");
            Fx.Assert(argumentAccessorWrapperCache != null, "argumentAccessorWrapperCache cannot be null.");

            HashSet <Assign>             assignsWithRValueToFix = new HashSet <Assign>();
            Dictionary <Assign, Type>    assignLValueTypes      = new Dictionary <Assign, Type>();
            List <ExpressionReplacement> expressionReplacements = new List <ExpressionReplacement>();

            foreach (ActivityWithResult expression in expressions)
            {
                Activity parentActivity = ValidationService.GetParent(expression);
                if (parentActivity == null)
                {
                    continue;
                }

                Assign assignActivity = parentActivity as Assign;
                if (assignActivity != null && !ExpressionHelper.IsGenericLocationExpressionType(expression))
                {
                    assignsWithRValueToFix.Add(assignActivity);
                }
                else
                {
                    ExpressionReplacement expressionReplacement = ComputeExpressionReplacement(expression, parentActivity, context, argumentAccessorWrapperCache);
                    if (expressionReplacement != null)
                    {
                        expressionReplacements.Add(expressionReplacement);
                        if (assignActivity != null)
                        {
                            Type expectedReturnType = expressionReplacement.NewArgument.ArgumentType;
                            assignLValueTypes[assignActivity] = expectedReturnType;
                        }
                    }
                }
            }

            // Special handle Assign R-Values: Assign.To.ArgumentType must be the same as Assign.Value.ArgumentType.
            foreach (Assign assignWithRValueToFix in assignsWithRValueToFix)
            {
                Type expectedReturnType;
                if (assignLValueTypes.TryGetValue(assignWithRValueToFix, out expectedReturnType))
                {
                    assignLValueTypes.Remove(assignWithRValueToFix);
                }
                else if (assignWithRValueToFix.To != null)
                {
                    expectedReturnType = assignWithRValueToFix.To.ArgumentType;
                }

                ExpressionReplacement expressionReplacement = ComputeExpressionReplacement(assignWithRValueToFix.Value.Expression, assignWithRValueToFix, context, argumentAccessorWrapperCache, expectedReturnType);
                if (expressionReplacement != null)
                {
                    expressionReplacements.Add(expressionReplacement);
                }
            }

            // These Assign activities have their L-value argument (To) changed but not the R-value argument (Value).
            // Now make sure that the R-value arguments are compatible with the L-value argument.
            foreach (KeyValuePair <Assign, Type> kvp in assignLValueTypes)
            {
                Assign remainingAssign    = kvp.Key;
                Type   expectedReturnType = kvp.Value;
                if (remainingAssign.Value != null && remainingAssign.Value.Expression != null)
                {
                    ActivityWithResult expression = remainingAssign.Value.Expression;
                    if (expression.ResultType != expectedReturnType)
                    {
                        ExpressionReplacement expressionReplacement = ComputeExpressionReplacement(expression, remainingAssign, context, argumentAccessorWrapperCache, expectedReturnType);
                        if (expressionReplacement != null)
                        {
                            expressionReplacements.Add(expressionReplacement);
                        }
                    }
                }
            }

            return(expressionReplacements);
        }