File: System\Activities\Statements\ForEach.cs
Project: ndp\cdf\src\NetFx40\System.Activities\System.Activities.csproj (System.Activities)
//-----------------------------------------------------------------------------
// Copyright (c) Microsoft Corporation.  All rights reserved.
//-----------------------------------------------------------------------------
 
namespace System.Activities.Statements
{
    using System.Collections;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Runtime;
    using System.Runtime.Serialization;
    using System.Windows.Markup;
    using System.Activities;
    using System.Activities.Validation;
    using SA = System.Activities;
 
    [ContentProperty("Body")]
    public sealed class ForEach<T> : NativeActivity
    {
        Variable<IEnumerator<T>> valueEnumerator;
        CompletionCallback onChildComplete;
 
        public ForEach()
            : base()
        {
            this.valueEnumerator = new Variable<IEnumerator<T>>();
        }
 
        [DefaultValue(null)]
        public ActivityAction<T> Body
        {
            get;
            set;
        }
 
        [RequiredArgument]        
        [DefaultValue(null)]
        public InArgument<IEnumerable<T>> Values
        {
            get;
            set;
        }
 
        CompletionCallback OnChildComplete
        {
            get
            {
                if (this.onChildComplete == null)
                {
                    this.onChildComplete = new CompletionCallback(GetStateAndExecute);
                }
 
                return this.onChildComplete;
            }
        }
 
        protected override void OnCreateDynamicUpdateMap(DynamicUpdate.NativeActivityUpdateMapMetadata metadata, Activity originalActivity)
        {
            metadata.AllowUpdateInsideThisActivity();
        }
 
        protected override void CacheMetadata(NativeActivityMetadata metadata)
        {
            RuntimeArgument valuesArgument = new RuntimeArgument("Values", typeof(IEnumerable<T>), ArgumentDirection.In, true);
            metadata.Bind(this.Values, valuesArgument);
 
            metadata.AddArgument(valuesArgument);
            metadata.AddDelegate(this.Body);
            metadata.AddImplementationVariable(this.valueEnumerator);
        }
 
        protected override void Execute(NativeActivityContext context)
        {
            IEnumerable<T> values = this.Values.Get(context);
            if (values == null)
            {
                throw SA.FxTrace.Exception.AsError(new InvalidOperationException(SA.SR.ForEachRequiresNonNullValues(this.DisplayName)));
            }
 
            IEnumerator<T> valueEnumerator = values.GetEnumerator();
            this.valueEnumerator.Set(context, valueEnumerator);
 
            if (this.Body == null || this.Body.Handler == null)
            {
                while (valueEnumerator.MoveNext())
                {
                    // do nothing                
                };
                valueEnumerator.Dispose();
                return;
            }
            InternalExecute(context, null, valueEnumerator);
        }
 
        void GetStateAndExecute(NativeActivityContext context, ActivityInstance completedInstance)
        {
            IEnumerator<T> valueEnumerator = this.valueEnumerator.Get(context);
            Fx.Assert(valueEnumerator != null, "GetStateAndExecute");
            InternalExecute(context, completedInstance, valueEnumerator);
        }
 
        void InternalExecute(NativeActivityContext context, ActivityInstance completedInstance, IEnumerator<T> valueEnumerator)
        {
            Fx.Assert(this.Body != null && this.Body.Handler != null, "Body and Body.Handler should not be null");
 
            if (!valueEnumerator.MoveNext())
            {
                if (completedInstance != null)
                {
                    if (completedInstance.State == ActivityInstanceState.Canceled ||
                        (context.IsCancellationRequested && completedInstance.State == ActivityInstanceState.Faulted))
                    {
                        context.MarkCanceled();
                    }
                }
                valueEnumerator.Dispose();
                return;
            }
 
            // After making sure there is another value, let's check for cancelation
            if (context.IsCancellationRequested)
            {
                context.MarkCanceled();
                valueEnumerator.Dispose();
                return;
            }
 
            context.ScheduleAction(this.Body, valueEnumerator.Current, this.OnChildComplete);
        }
    }
}