Search code examples
workflow-foundation-4

Accessing OutArgument value of Receive implementation child activity within custom WF4 activity


Using VS2012/.NET 4.5 I am creating a custom activity which implements a Receive child activity (as an implementation child). The parameters are in the example below fixed to just one: OutValue of type Guid.

I really would love to access the value of incoming parameter value in ReceiveDone, because I need to work with it and transform it before returning it from the activity. Please ignore that I am currently using a Guid, it still fails to access the value with and InvalidOperationException:

An Activity can only get the location of arguments which it owns. Activity 'TestActivity' is trying to get the location of argument 'OutValue' which is owned by activity 'Wait for workflow start request [Internal for TestActivity]'

I have tried everything I could think of, but am stupefied. There must be a way to do this very simple thing?

public class TestActivity : NativeActivity<Guid>
{
    protected override void CacheMetadata(NativeActivityMetadata metadata)
    {
        var content = ReceiveParametersContent.Create(new Dictionary<string, OutArgument>()
            {
                // How to access the runtime value of this inside TestActivity?
                {"OutValue", new OutArgument<Guid>()}
            });

        startReceiver = new Receive()
        {
            DisplayName = string.Format("Wait for workflow start request [Internal for {0}]", this.DisplayName),
            CanCreateInstance = true,
            ServiceContractName = XName.Get("IStartService", Namespace),
            OperationName = "Start",
            Content = content
        };

        foreach (KeyValuePair<string, OutArgument> keyValuePair in content.Parameters)
        {
            metadata.AddImportedChild(keyValuePair.Value.Expression);
        }

        metadata.AddImplementationChild(startReceiver);
    }

    protected override void Execute(NativeActivityContext context)
    {
        context.ScheduleActivity(startReceiver, ReceiveDone);
    }

    private void ReceiveDone(NativeActivityContext context, ActivityInstance completedInstance)
    {
        var receive = completedInstance.Activity as Receive;
        ReceiveParametersContent content = receive.Content as ReceiveParametersContent;

        try
        {
            // This causes InvalidOperationException.
            // An Activity can only get the location of arguments which it owns.  
            // Activity 'TestActivity' is trying to get the location of argument 'OutValue' 
            // which is owned by activity 'Wait for workflow start request [Internal for TestActivity]'
            var parmValue = content.Parameters["OutValue"].Get(context);
        }
        catch (Exception)
        { }
    }

    private Receive startReceiver;
    private const string Namespace = "http://company.namespace";
}

Solution

  • Use internal variables to pass values between internal activities.

    Although not directly related to your code, see the example below which should give you the idea:

    public sealed class CustomNativeActivity : NativeActivity<int>
    {
        private Variable<int> internalVar;
        private Assign<int> internalAssign; 
    
        protected override void CacheMetadata(NativeActivityMetadata metadata)
        {
            base.CacheMetadata(metadata);
    
            internalVar = new Variable<int>("intInternalVar", 10);
    
            metadata.AddImplementationVariable(internalVar);
    
            internalAssign = new Assign<int>
                {
                    To = internalVar,
                    Value = 12345
                };
    
            metadata.AddImplementationChild(internalAssign);
        }
    
        protected override void Execute(NativeActivityContext context)
        {
            context.ScheduleActivity(internalAssign, (activityContext, instance) =>
            {
                // Use internalVar value, which was seted by previous activity
                var value = internalVar.Get(activityContext);
    
                Result.Set(activityContext, value);
            });
        }
    }
    

    Calling the above activity:

    WorkflowInvoker.Invoke<int>(new CustomNativeActivity());
    

    Will output:

    12345
    

    Edit:

    In your case your OutArgument will be the internalVar

    new OutArgument<int>(internalVar);