1 //-----------------------------------------------------------------------------
2 // Copyright (c) Microsoft Corporation.  All rights reserved.
3 //-----------------------------------------------------------------------------
4 
5 namespace System.Activities.Statements
6 {
7     using System.Collections;
8     using System.Collections.Generic;
9     using System.ComponentModel;
10     using System.Runtime;
11     using System.Runtime.Serialization;
12     using System.Windows.Markup;
13     using System.Activities;
14     using System.Activities.Validation;
15     using SA = System.Activities;
16 
17     [ContentProperty("Body")]
18     public sealed class ForEach<T> : NativeActivity
19     {
20         Variable<IEnumerator<T>> valueEnumerator;
21         CompletionCallback onChildComplete;
22 
ForEach()23         public ForEach()
24             : base()
25         {
26             this.valueEnumerator = new Variable<IEnumerator<T>>();
27         }
28 
29         [DefaultValue(null)]
30         public ActivityAction<T> Body
31         {
32             get;
33             set;
34         }
35 
36         [RequiredArgument]
37         [DefaultValue(null)]
38         public InArgument<IEnumerable<T>> Values
39         {
40             get;
41             set;
42         }
43 
44         CompletionCallback OnChildComplete
45         {
46             get
47             {
48                 if (this.onChildComplete == null)
49                 {
50                     this.onChildComplete = new CompletionCallback(GetStateAndExecute);
51                 }
52 
53                 return this.onChildComplete;
54             }
55         }
56 
OnCreateDynamicUpdateMap(DynamicUpdate.NativeActivityUpdateMapMetadata metadata, Activity originalActivity)57         protected override void OnCreateDynamicUpdateMap(DynamicUpdate.NativeActivityUpdateMapMetadata metadata, Activity originalActivity)
58         {
59             metadata.AllowUpdateInsideThisActivity();
60         }
61 
CacheMetadata(NativeActivityMetadata metadata)62         protected override void CacheMetadata(NativeActivityMetadata metadata)
63         {
64             RuntimeArgument valuesArgument = new RuntimeArgument("Values", typeof(IEnumerable<T>), ArgumentDirection.In, true);
65             metadata.Bind(this.Values, valuesArgument);
66 
67             metadata.AddArgument(valuesArgument);
68             metadata.AddDelegate(this.Body);
69             metadata.AddImplementationVariable(this.valueEnumerator);
70         }
71 
Execute(NativeActivityContext context)72         protected override void Execute(NativeActivityContext context)
73         {
74             IEnumerable<T> values = this.Values.Get(context);
75             if (values == null)
76             {
77                 throw SA.FxTrace.Exception.AsError(new InvalidOperationException(SA.SR.ForEachRequiresNonNullValues(this.DisplayName)));
78             }
79 
80             IEnumerator<T> valueEnumerator = values.GetEnumerator();
81             this.valueEnumerator.Set(context, valueEnumerator);
82 
83             if (this.Body == null || this.Body.Handler == null)
84             {
85                 while (valueEnumerator.MoveNext())
86                 {
87                     // do nothing
88                 };
89                 valueEnumerator.Dispose();
90                 return;
91             }
92             InternalExecute(context, null, valueEnumerator);
93         }
94 
GetStateAndExecute(NativeActivityContext context, ActivityInstance completedInstance)95         void GetStateAndExecute(NativeActivityContext context, ActivityInstance completedInstance)
96         {
97             IEnumerator<T> valueEnumerator = this.valueEnumerator.Get(context);
98             Fx.Assert(valueEnumerator != null, "GetStateAndExecute");
99             InternalExecute(context, completedInstance, valueEnumerator);
100         }
101 
InternalExecute(NativeActivityContext context, ActivityInstance completedInstance, IEnumerator<T> valueEnumerator)102         void InternalExecute(NativeActivityContext context, ActivityInstance completedInstance, IEnumerator<T> valueEnumerator)
103         {
104             Fx.Assert(this.Body != null && this.Body.Handler != null, "Body and Body.Handler should not be null");
105 
106             if (!valueEnumerator.MoveNext())
107             {
108                 if (completedInstance != null)
109                 {
110                     if (completedInstance.State == ActivityInstanceState.Canceled ||
111                         (context.IsCancellationRequested && completedInstance.State == ActivityInstanceState.Faulted))
112                     {
113                         context.MarkCanceled();
114                     }
115                 }
116                 valueEnumerator.Dispose();
117                 return;
118             }
119 
120             // After making sure there is another value, let's check for cancelation
121             if (context.IsCancellationRequested)
122             {
123                 context.MarkCanceled();
124                 valueEnumerator.Dispose();
125                 return;
126             }
127 
128             context.ScheduleAction(this.Body, valueEnumerator.Current, this.OnChildComplete);
129         }
130     }
131 }
132