1 //-----------------------------------------------------------------------------
2 // Copyright (c) Microsoft Corporation.  All rights reserved.
3 //-----------------------------------------------------------------------------
4 
5 namespace System.Activities.Validation
6 {
7     using System;
8     using System.Collections.Generic;
9     using System.Collections.ObjectModel;
10     using System.Runtime;
11     using System.Text;
12     using System.Threading;
13     using System.Linq;
14 
15     public static class ActivityValidationServices
16     {
17         internal static readonly ReadOnlyCollection<Activity> EmptyChildren = new ReadOnlyCollection<Activity>(new Activity[0]);
18         static ValidationSettings defaultSettings = new ValidationSettings();
19         internal static ReadOnlyCollection<ValidationError> EmptyValidationErrors = new ReadOnlyCollection<ValidationError>(new List<ValidationError>(0));
20 
Validate(Activity toValidate)21         public static ValidationResults Validate(Activity toValidate)
22         {
23             return Validate(toValidate, defaultSettings);
24         }
25 
Validate(Activity toValidate, ValidationSettings settings)26         public static ValidationResults Validate(Activity toValidate, ValidationSettings settings)
27         {
28             if (toValidate == null)
29             {
30                 throw FxTrace.Exception.ArgumentNull("toValidate");
31             }
32 
33             if (settings == null)
34             {
35                 throw FxTrace.Exception.ArgumentNull("settings");
36             }
37 
38             if (toValidate.HasBeenAssociatedWithAnInstance)
39             {
40                 throw FxTrace.Exception.AsError(new InvalidOperationException(SR.RootActivityAlreadyAssociatedWithInstance(toValidate.DisplayName)));
41             }
42 
43             if (settings.PrepareForRuntime && (settings.SingleLevel || settings.SkipValidatingRootConfiguration || settings.OnlyUseAdditionalConstraints))
44             {
45                 throw FxTrace.Exception.Argument("settings", SR.InvalidPrepareForRuntimeValidationSettings);
46             }
47 
48             InternalActivityValidationServices validator = new InternalActivityValidationServices(settings, toValidate);
49             return validator.InternalValidate();
50         }
51 
Resolve(Activity root, string id)52         public static Activity Resolve(Activity root, string id)
53         {
54             return WorkflowInspectionServices.Resolve(root, id);
55         }
56 
ThrowIfViolationsExist(IList<ValidationError> validationErrors, ExceptionReason reason = ExceptionReason.InvalidTree)57         internal static void ThrowIfViolationsExist(IList<ValidationError> validationErrors, ExceptionReason reason = ExceptionReason.InvalidTree)
58         {
59             Exception exception = CreateExceptionFromValidationErrors(validationErrors, reason);
60 
61             if (exception != null)
62             {
63                 throw FxTrace.Exception.AsError(exception);
64             }
65         }
66 
CreateExceptionFromValidationErrors(IList<ValidationError> validationErrors, ExceptionReason reason)67         static Exception CreateExceptionFromValidationErrors(IList<ValidationError> validationErrors, ExceptionReason reason)
68         {
69             if (validationErrors != null && validationErrors.Count > 0)
70             {
71                 string exceptionString = GenerateExceptionString(validationErrors, reason);
72 
73                 if (exceptionString != null)
74                 {
75                     return new InvalidWorkflowException(exceptionString);
76                 }
77                 else
78                 {
79                     return null;
80                 }
81             }
82             else
83             {
84                 return null;
85             }
86         }
87 
GetChildren(ActivityUtilities.ChildActivity root, ActivityUtilities.ActivityCallStack parentChain, ProcessActivityTreeOptions options)88         internal static List<Activity> GetChildren(ActivityUtilities.ChildActivity root, ActivityUtilities.ActivityCallStack parentChain, ProcessActivityTreeOptions options)
89         {
90             ActivityUtilities.FinishCachingSubtree(root, parentChain, options);
91 
92             List<Activity> listOfChildren = new List<Activity>();
93 
94             foreach (Activity activity in WorkflowInspectionServices.GetActivities(root.Activity))
95             {
96                 listOfChildren.Add(activity);
97             }
98 
99             int toProcessIndex = 0;
100 
101             while (toProcessIndex < listOfChildren.Count)
102             {
103                 foreach (Activity activity in WorkflowInspectionServices.GetActivities(listOfChildren[toProcessIndex]))
104                 {
105                     listOfChildren.Add(activity);
106                 }
107 
108                 toProcessIndex++;
109             }
110 
111             return listOfChildren;
112         }
113 
ValidateRootInputs(Activity rootActivity, IDictionary<string, object> inputs)114         internal static void ValidateRootInputs(Activity rootActivity, IDictionary<string, object> inputs)
115         {
116             IList<ValidationError> validationErrors = null;
117             ValidationHelper.ValidateArguments(rootActivity, rootActivity.EquivalenceInfo, rootActivity.OverloadGroups, rootActivity.RequiredArgumentsNotInOverloadGroups, inputs, ref validationErrors);
118 
119             // Validate if there are any extra arguments passed in the input dictionary
120             if (inputs != null)
121             {
122                 List<string> unusedArguments = null;
123                 IEnumerable<RuntimeArgument> arguments = rootActivity.RuntimeArguments.Where((a) => ArgumentDirectionHelper.IsIn(a.Direction));
124 
125                 foreach (string key in inputs.Keys)
126                 {
127                     bool found = false;
128                     foreach (RuntimeArgument argument in arguments)
129                     {
130                         if (argument.Name == key)
131                         {
132                             found = true;
133 
134                             // Validate if the input argument type matches the expected argument type.
135                             object inputArgumentValue = null;
136                             if (inputs.TryGetValue(key, out inputArgumentValue))
137                             {
138                                 if (!TypeHelper.AreTypesCompatible(inputArgumentValue, argument.Type))
139                                 {
140                                     ActivityUtilities.Add(ref validationErrors, new ValidationError(SR.InputParametersTypeMismatch(argument.Type, argument.Name), rootActivity));
141                                 }
142                             }
143                             // The ValidateArguments will validate Required in-args and hence not duplicating that validation if the key is not found.
144 
145                             break;
146                         }
147                     }
148 
149                     if (!found)
150                     {
151                         if (unusedArguments == null)
152                         {
153                             unusedArguments = new List<string>();
154                         }
155                         unusedArguments.Add(key);
156                     }
157                 }
158                 if (unusedArguments != null)
159                 {
160                     ActivityUtilities.Add(ref validationErrors, new ValidationError(SR.UnusedInputArguments(unusedArguments.AsCommaSeparatedValues()), rootActivity));
161                 }
162             }
163 
164             if (validationErrors != null && validationErrors.Count > 0)
165             {
166                 string parameterName = "rootArgumentValues";
167                 ExceptionReason reason = ExceptionReason.InvalidNonNullInputs;
168 
169                 if (inputs == null)
170                 {
171                     parameterName = "program";
172                     reason = ExceptionReason.InvalidNullInputs;
173                 }
174 
175                 string exceptionString = GenerateExceptionString(validationErrors, reason);
176 
177                 if (exceptionString != null)
178                 {
179                     throw FxTrace.Exception.Argument(parameterName, exceptionString);
180                 }
181             }
182         }
183 
ValidateArguments(Activity activity, bool isRoot, ref IList<ValidationError> validationErrors)184         internal static void ValidateArguments(Activity activity, bool isRoot, ref IList<ValidationError> validationErrors)
185         {
186             Fx.Assert(activity != null, "Activity to validate should not be null.");
187 
188             Dictionary<string, List<RuntimeArgument>> overloadGroups;
189             List<RuntimeArgument> requiredArgumentsNotInOverloadGroups;
190             ValidationHelper.OverloadGroupEquivalenceInfo equivalenceInfo;
191             if (ValidationHelper.GatherAndValidateOverloads(activity, out overloadGroups, out requiredArgumentsNotInOverloadGroups, out equivalenceInfo, ref validationErrors))
192             {
193                 // If we're not the root and the overload groups are valid
194                 // then we validate the arguments
195                 if (!isRoot)
196                 {
197                     ValidationHelper.ValidateArguments(activity, equivalenceInfo, overloadGroups, requiredArgumentsNotInOverloadGroups, null, ref validationErrors);
198                 }
199             }
200 
201             // If we are the root, regardless of whether the groups are
202             // valid or not, we cache the group information
203             if (isRoot)
204             {
205                 activity.OverloadGroups = overloadGroups;
206                 activity.RequiredArgumentsNotInOverloadGroups = requiredArgumentsNotInOverloadGroups;
207                 activity.EquivalenceInfo = equivalenceInfo;
208             }
209         }
210 
GenerateExceptionString(IList<ValidationError> validationErrors, ExceptionReason reason)211         static string GenerateExceptionString(IList<ValidationError> validationErrors, ExceptionReason reason)
212         {
213             // 4096 is an arbitrary constant.  Currently clipped by character count (not bytes).
214             const int maxExceptionStringSize = 4096;
215 
216             StringBuilder exceptionMessageBuilder = null;
217 
218             for (int i = 0; i < validationErrors.Count; i++)
219             {
220                 ValidationError validationError = validationErrors[i];
221 
222                 if (!validationError.IsWarning)
223                 {
224                     // create the common exception string
225                     if (exceptionMessageBuilder == null)
226                     {
227                         exceptionMessageBuilder = new StringBuilder();
228 
229                         switch (reason)
230                         {
231                             case ExceptionReason.InvalidTree:
232                                 exceptionMessageBuilder.Append(SR.ErrorsEncounteredWhileProcessingTree);
233                                 break;
234                             case ExceptionReason.InvalidNonNullInputs:
235                                 exceptionMessageBuilder.Append(SR.RootArgumentViolationsFound);
236                                 break;
237                             case ExceptionReason.InvalidNullInputs:
238                                 exceptionMessageBuilder.Append(SR.RootArgumentViolationsFoundNoInputs);
239                                 break;
240                         }
241                     }
242 
243                     string activityName = null;
244 
245                     if (validationError.Source != null)
246                     {
247                         activityName = validationError.Source.DisplayName;
248                     }
249                     else
250                     {
251                         activityName = "<UnknownActivity>";
252                     }
253 
254                     exceptionMessageBuilder.AppendLine();
255                     exceptionMessageBuilder.Append(string.Format(SR.Culture, "'{0}': {1}", activityName, validationError.Message));
256 
257                     if (exceptionMessageBuilder.Length > maxExceptionStringSize)
258                     {
259                         break;
260                     }
261                 }
262             }
263 
264             string exceptionString = null;
265 
266             if (exceptionMessageBuilder != null)
267             {
268                 exceptionString = exceptionMessageBuilder.ToString();
269 
270                 if (exceptionString.Length > maxExceptionStringSize)
271                 {
272                     string snipNotification = SR.TooManyViolationsForExceptionMessage;
273 
274                     exceptionString = exceptionString.Substring(0, maxExceptionStringSize - snipNotification.Length);
275                     exceptionString += snipNotification;
276                 }
277             }
278 
279             return exceptionString;
280         }
281 
GenerateValidationErrorPrefix(Activity toValidate, ActivityUtilities.ActivityCallStack parentChain, ProcessActivityTreeOptions options, out Activity source)282         static internal string GenerateValidationErrorPrefix(Activity toValidate, ActivityUtilities.ActivityCallStack parentChain, ProcessActivityTreeOptions options, out Activity source)
283         {
284             bool parentVisible = true;
285             string prefix = "";
286             source = toValidate;
287 
288             // Processing for implementation of activity
289             // during build time
290             if (options.SkipRootConfigurationValidation)
291             {
292                 // Check if the activity is a implementation child
293                 if (toValidate.MemberOf.Parent != null)
294                 {
295                     // Check if activity is an immediate implementation child
296                     // of x:class activity. This means that the activity is
297                     // being designed and hence we do not want to add the
298                     // prefix at build time
299                     if (toValidate.MemberOf.Parent.Parent == null)
300                     {
301                         prefix = "";
302                         source = toValidate;
303                     }
304                     else
305                     {
306                         // This means the activity is a child of immediate implementation child
307                         // of x:class activity which means the activity is not visible.
308                         // The source points to the first visible parent activity in the
309                         // parent chain.
310                         while (source.MemberOf.Parent.Parent != null)
311                         {
312                             source = source.Parent;
313                         }
314                         prefix = SR.ValidationErrorPrefixForHiddenActivity(source);
315                     }
316                     return prefix;
317                 }
318             }
319 
320             // Find out if any of the parents of the activity are not publicly visible
321             for (int i = 0; i < parentChain.Count; i++)
322             {
323                 if (parentChain[i].Activity.MemberOf.Parent != null)
324                 {
325                     parentVisible = false;
326                     break;
327                 }
328             }
329 
330             // Figure out the source of validation error:
331             //    - For hidden activity - source will be closest visible public parent
332             //    - For visible activity - source will be the activity itself
333             // In current design an activity is visible only if it is in the root id space.
334             // In future, if we provide a knob for the user to specify the
335             // id spaces that are visible, then this check needs to be changed
336             // to iterate over the parentChain and find the closest parent activity that
337             // is in the visible id spaces.
338             while (source.MemberOf.Parent != null)
339             {
340                 source = source.Parent;
341             }
342 
343             if (toValidate.MemberOf.Parent != null)
344             {
345                 // Activity itself is hidden
346                 prefix = SR.ValidationErrorPrefixForHiddenActivity(source);
347             }
348             else
349             {
350                 if (!parentVisible)
351                 {
352                     // Activity itself is public but has a private parent
353                     prefix = SR.ValidationErrorPrefixForPublicActivityWithHiddenParent(source.Parent, source);
354                 }
355             }
356             return prefix;
357         }
358 
RunConstraints(ActivityUtilities.ChildActivity childActivity, ActivityUtilities.ActivityCallStack parentChain, IList<Constraint> constraints, ProcessActivityTreeOptions options, bool suppressGetChildrenViolations, ref IList<ValidationError> validationErrors)359         internal static void RunConstraints(ActivityUtilities.ChildActivity childActivity, ActivityUtilities.ActivityCallStack parentChain, IList<Constraint> constraints, ProcessActivityTreeOptions options, bool suppressGetChildrenViolations, ref IList<ValidationError> validationErrors)
360         {
361             if (constraints != null)
362             {
363                 Activity toValidate = childActivity.Activity;
364 
365                 LocationReferenceEnvironment environment = toValidate.GetParentEnvironment();
366 
367                 Dictionary<string, object> inputDictionary = new Dictionary<string, object>(2);
368 
369                 for (int constraintIndex = 0; constraintIndex < constraints.Count; constraintIndex++)
370                 {
371                     Constraint constraint = constraints[constraintIndex];
372 
373                     // there may be null entries here
374                     if (constraint == null)
375                     {
376                         continue;
377                     }
378 
379                     inputDictionary[Constraint.ToValidateArgumentName] = toValidate;
380                     ValidationContext validationContext = new ValidationContext(childActivity, parentChain, options, environment);
381                     inputDictionary[Constraint.ToValidateContextArgumentName] = validationContext;
382                     IDictionary<string, object> results = null;
383 
384                     try
385                     {
386                         results = WorkflowInvoker.Invoke(constraint, inputDictionary);
387                     }
388                     catch (Exception e)
389                     {
390                         if (Fx.IsFatal(e))
391                         {
392                             throw;
393                         }
394 
395                         ValidationError constraintExceptionValidationError = new ValidationError(SR.InternalConstraintException(constraint.DisplayName, toValidate.GetType().FullName, toValidate.DisplayName, e.ToString()), false)
396                         {
397                             Source = toValidate,
398                             Id = toValidate.Id
399                         };
400 
401                         ActivityUtilities.Add(ref validationErrors, constraintExceptionValidationError);
402                     }
403 
404                     if (results != null)
405                     {
406                         object resultValidationErrors;
407                         if (results.TryGetValue(Constraint.ValidationErrorListArgumentName, out resultValidationErrors))
408                         {
409                             IList<ValidationError> validationErrorList = (IList<ValidationError>)resultValidationErrors;
410 
411                             if (validationErrorList.Count > 0)
412                             {
413                                 if (validationErrors == null)
414                                 {
415                                     validationErrors = new List<ValidationError>();
416                                 }
417 
418                                 Activity source;
419                                 string prefix = ActivityValidationServices.GenerateValidationErrorPrefix(childActivity.Activity, parentChain, options, out source);
420 
421                                 for (int validationErrorIndex = 0; validationErrorIndex < validationErrorList.Count; validationErrorIndex++)
422                                 {
423                                     ValidationError validationError = validationErrorList[validationErrorIndex];
424 
425                                     validationError.Source = source;
426                                     validationError.Id = source.Id;
427                                     if (!string.IsNullOrEmpty(prefix))
428                                     {
429                                         validationError.Message = prefix + validationError.Message;
430                                     }
431                                     validationErrors.Add(validationError);
432                                 }
433                             }
434                         }
435                     }
436 
437                     if (!suppressGetChildrenViolations)
438                     {
439                         validationContext.AddGetChildrenErrors(ref validationErrors);
440                     }
441                 }
442             }
443         }
444 
HasErrors(IList<ValidationError> validationErrors)445         internal static bool HasErrors(IList<ValidationError> validationErrors)
446         {
447             if (validationErrors != null && validationErrors.Count > 0)
448             {
449                 for (int i = 0; i < validationErrors.Count; i++)
450                 {
451                     if (!validationErrors[i].IsWarning)
452                     {
453                         return true;
454                     }
455                 }
456             }
457 
458             return false;
459         }
460 
461         class InternalActivityValidationServices
462         {
463             ValidationSettings settings;
464             Activity rootToValidate;
465             IList<ValidationError> errors;
466             ProcessActivityTreeOptions options;
467             Activity expressionRoot;
468             LocationReferenceEnvironment environment;
469 
InternalActivityValidationServices(ValidationSettings settings, Activity toValidate)470             internal InternalActivityValidationServices(ValidationSettings settings, Activity toValidate)
471             {
472                 this.settings = settings;
473                 this.rootToValidate = toValidate;
474                 this.environment = settings.Environment;
475             }
476 
InternalValidate()477             internal ValidationResults InternalValidate()
478             {
479                 this.options = ProcessActivityTreeOptions.GetValidationOptions(this.settings);
480 
481                 if (this.settings.OnlyUseAdditionalConstraints)
482                 {
483                     // We don't want the errors from CacheMetadata so we send those to a "dummy" list.
484                     IList<ValidationError> suppressedErrors = null;
485                     ActivityUtilities.CacheRootMetadata(this.rootToValidate, this.environment, this.options, new ActivityUtilities.ProcessActivityCallback(ValidateElement), ref suppressedErrors);
486                 }
487                 else
488                 {
489                     // We want to add the CacheMetadata errors to our errors collection
490                     ActivityUtilities.CacheRootMetadata(this.rootToValidate, this.environment, this.options, new ActivityUtilities.ProcessActivityCallback(ValidateElement), ref this.errors);
491                 }
492 
493                 return new ValidationResults(this.errors);
494             }
495 
ValidateElement(ActivityUtilities.ChildActivity childActivity, ActivityUtilities.ActivityCallStack parentChain)496             void ValidateElement(ActivityUtilities.ChildActivity childActivity, ActivityUtilities.ActivityCallStack parentChain)
497             {
498                 Activity toValidate = childActivity.Activity;
499 
500                 if (!this.settings.SingleLevel || object.ReferenceEquals(toValidate, this.rootToValidate))
501                 {
502                     // 0. Open time violations are captured by the CacheMetadata walk.
503 
504                     // 1. Argument validations are done by the CacheMetadata walk.
505 
506                     // 2. Build constraints are done by the CacheMetadata walk.
507 
508                     // 3. Then do policy constraints
509                     if (this.settings.HasAdditionalConstraints && childActivity.CanBeExecuted && parentChain.WillExecute)
510                     {
511                         bool suppressGetChildrenViolations = this.settings.OnlyUseAdditionalConstraints || this.settings.SingleLevel;
512 
513                         Type currentType = toValidate.GetType();
514 
515                         while (currentType != null)
516                         {
517                             IList<Constraint> policyConstraints;
518                             if (this.settings.AdditionalConstraints.TryGetValue(currentType, out policyConstraints))
519                             {
520                                 RunConstraints(childActivity, parentChain, policyConstraints, this.options, suppressGetChildrenViolations, ref this.errors);
521                             }
522 
523                             if (currentType.IsGenericType)
524                             {
525                                 Type genericDefinitionType = currentType.GetGenericTypeDefinition();
526                                 if (genericDefinitionType != null)
527                                 {
528                                     IList<Constraint> genericTypePolicyConstraints;
529                                     if (this.settings.AdditionalConstraints.TryGetValue(genericDefinitionType, out genericTypePolicyConstraints))
530                                     {
531                                         RunConstraints(childActivity, parentChain, genericTypePolicyConstraints, this.options, suppressGetChildrenViolations, ref this.errors);
532                                     }
533                                 }
534                             }
535                             currentType = currentType.BaseType;
536                         }
537                     }
538 
539                     //4. Validate if the argument expression subtree contains an activity that can induce idle.
540                     if (childActivity.Activity.IsExpressionRoot)
541                     {
542                         if (childActivity.Activity.HasNonEmptySubtree)
543                         {
544                             this.expressionRoot = childActivity.Activity;
545                             // Back-compat: In Dev10 we always used ProcessActivityTreeOptions.FullCachingOptions here, and ignored this.options.
546                             // So we need to continue to do that, unless the new Dev11 flag SkipRootConfigurationValidation is passed.
547                             ProcessActivityTreeOptions options = this.options.SkipRootConfigurationValidation ? this.options : ProcessActivityTreeOptions.FullCachingOptions;
548                             ActivityUtilities.FinishCachingSubtree(childActivity, parentChain, options, ValidateExpressionSubtree);
549                             this.expressionRoot = null;
550                         }
551                         else if (childActivity.Activity.InternalCanInduceIdle)
552                         {
553                             Activity activity = childActivity.Activity;
554                             RuntimeArgument runtimeArgument = GetBoundRuntimeArgument(activity);
555                             ValidationError error = new ValidationError(SR.CanInduceIdleActivityInArgumentExpression(runtimeArgument.Name, activity.Parent.DisplayName, activity.DisplayName), true, runtimeArgument.Name, activity.Parent);
556                             ActivityUtilities.Add(ref this.errors, error);
557                         }
558 
559                     }
560                 }
561             }
562 
ValidateExpressionSubtree(ActivityUtilities.ChildActivity childActivity, ActivityUtilities.ActivityCallStack parentChain)563             void ValidateExpressionSubtree(ActivityUtilities.ChildActivity childActivity, ActivityUtilities.ActivityCallStack parentChain)
564             {
565                 Fx.Assert(this.expressionRoot != null, "This callback should be called activities in the expression subtree only.");
566 
567                 if (childActivity.Activity.InternalCanInduceIdle)
568                 {
569                     Activity activity = childActivity.Activity;
570                     Activity expressionRoot = this.expressionRoot;
571 
572                     RuntimeArgument runtimeArgument = GetBoundRuntimeArgument(expressionRoot);
573                     ValidationError error = new ValidationError(SR.CanInduceIdleActivityInArgumentExpression(runtimeArgument.Name, expressionRoot.Parent.DisplayName, activity.DisplayName), true, runtimeArgument.Name, expressionRoot.Parent);
574                     ActivityUtilities.Add(ref this.errors, error);
575                 }
576             }
577         }
578 
579         // Iterate through all runtime arguments on the configured activity
580         // and find the one that binds to expressionActivity.
GetBoundRuntimeArgument(Activity expressionActivity)581         static RuntimeArgument GetBoundRuntimeArgument(Activity expressionActivity)
582         {
583             Activity configuredActivity = expressionActivity.Parent;
584             Fx.Assert(configuredActivity != null, "Configured activity should not be null.");
585 
586             RuntimeArgument boundRuntimeArgument = null;
587             for (int i = 0; i < configuredActivity.RuntimeArguments.Count; i++)
588             {
589                 boundRuntimeArgument = configuredActivity.RuntimeArguments[i];
590                 if (object.ReferenceEquals(boundRuntimeArgument.BoundArgument.Expression, expressionActivity))
591                 {
592                     break;
593                 }
594             }
595             Fx.Assert(boundRuntimeArgument != null, "We should always be able to find the runtime argument!");
596             return boundRuntimeArgument;
597         }
598 
599         // This method checks for duplicate evaluation order entries in a collection that is
600         // sorted in ascendng order of evaluation order values.
ValidateEvaluationOrder(IList<RuntimeArgument> runtimeArguments, Activity referenceActivity, ref IList<ValidationError> validationErrors)601         internal static void ValidateEvaluationOrder(IList<RuntimeArgument> runtimeArguments, Activity referenceActivity, ref IList<ValidationError> validationErrors)
602         {
603             for (int i = 0; i < runtimeArguments.Count - 1; i++)
604             {
605                 RuntimeArgument argument = runtimeArguments[i];
606                 RuntimeArgument nextArgument = runtimeArguments[i + 1];
607                 if (argument.IsEvaluationOrderSpecified && nextArgument.IsEvaluationOrderSpecified)
608                 {
609                     if (argument.BoundArgument.EvaluationOrder == nextArgument.BoundArgument.EvaluationOrder)
610                     {
611                         ActivityUtilities.Add(ref validationErrors, new ValidationError(SR.DuplicateEvaluationOrderValues(referenceActivity.DisplayName, argument.BoundArgument.EvaluationOrder), false, argument.Name, referenceActivity));
612                     }
613                 }
614             }
615         }
616 
617         internal enum ExceptionReason
618         {
619             InvalidTree,
620             InvalidNullInputs,
621             InvalidNonNullInputs,
622         }
623 
624     }
625 }
626