Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Custom activity setting parameters for child activity

I am trying to create a custom activity for WF4 that host a child activity and pass some arguments to its child activity. I attach below a simplified version of my activities (Parent and Child)

public class Child : CodeActivity
{
    public InArgument<Dictionary<string, object>> Data;

    protected override void Execute(CodeActivityContext context)
    {
        Dictionary<string, object> data = Data.Get(context);

        //Some operations on the input data
    }
}


 public class Parent : NativeActivity
{
    public InArgument<int> Value1 { get; set; }

    public InArgument<string> Value2 { get; set; }

    public Child Body { get; set; }


    protected override void Execute(NativeActivityContext context)
    {
        int value1 = Value1.Get(context);
        string value2 = Value2.Get(context);

        Dictionary<string, object> data = new Dictionary<string, object>();
        data.Add("value1", value1);
        data.Add("value2", value2);

        context.SetValue(Body.Data, data);

        context.ScheduleActivity(this.Body);
    }


    protected override void CacheMetadata(NativeActivityMetadata metadata)
    {
        Body = new Child();

        base.CacheMetadata(metadata);
    }
}

The Data argument from the Child activity is null when the workflow execution reaches the Execute method for the activity.

Can someone please, give me some direction how could the arguments be passed between this two activities?

like image 344
axl g Avatar asked Apr 04 '11 10:04

axl g


3 Answers

While the approach with the extra variable works the "official" way of scheduling a child with some inputs is using in ActivityAcytion or if you also want a result with an ActivityFunc.

I am afraid the code doesn't really get to be any simpler or easier to understand but for completeness sake I just decided to add this.

public class Child : CodeActivity<object>
{
    public InArgument<Dictionary<string, object>> Data { get; set; }

    protected override object Execute(CodeActivityContext context)
    {
        Dictionary<string, object> data = Data.Get(context);

        foreach (var item in data)
        {
            Console.WriteLine(item);
        }

        return "Some result";
    }
}



public class Parent : NativeActivity<object>
{
    public InArgument<int> Value1 { get; set; }
    public InArgument<string> Value2 { get; set; }

    public ActivityFunc<Dictionary<string, object>, object> Body { get; set; }

    protected override void Execute(NativeActivityContext context)
    {
        int value1 = Value1.Get(context);
        string value2 = Value2.Get(context);

        Dictionary<string, object> data = new Dictionary<string, object>();
        data.Add("value1", value1);
        data.Add("value2", value2);

        context.ScheduleFunc<Dictionary<string, object>, object>(Body, data, ChildCompletionCallback);
    }


    protected override void CacheMetadata(NativeActivityMetadata metadata)
    {
        var arg = new DelegateInArgument<Dictionary<string, object>>();

        Body = new ActivityFunc<Dictionary<string, object>, object>
        {
            Argument = arg,
            Handler = new Child() { Data = arg }
        };

        base.CacheMetadata(metadata);
    }

    private void ChildCompletionCallback(NativeActivityContext context, ActivityInstance completedInstance, object result)
    {
        //Set the output for the parent activity at the completion of the child activity
        this.Result.Set(context, result);
    }
}
like image 66
Maurice Avatar answered Oct 14 '22 16:10

Maurice


I also needed to return the result from the child activity via the parent activity. For this I used the ScheduleActivity method with the CompletionCallback parameter. I attached below the Parent / Child example activities containing the out argument set also, maybe it will be needed by someone at some time :)

Parent:

public class Parent : NativeActivity<object>
{
    public InArgument<int> Value1 { get; set; }
    public InArgument<string> Value2 { get; set; }
    private Variable<Dictionary<string, object>> SomeVariable { get; set; } // intermediate variable

    private Child Body { get; set; }

    public Parent()
    {
        this.SomeVariable = new Variable<Dictionary<string, object>>("SomeVariable");

        this.Body = new Child();
        this.Body.Data = new InArgument<Dictionary<string, object>>(SomeVariable);
    }

    protected override void Execute(NativeActivityContext context)
    {
        int value1 = Value1.Get(context);
        string value2 = Value2.Get(context);

        Dictionary<string, object> data = new Dictionary<string, object>();
        data.Add("value1", value1);
        data.Add("value2", value2);

        this.SomeVariable.Set(context, data);

        context.ScheduleActivity(this.Body, ChildCompletionCallback);
    }

    protected override void CacheMetadata(NativeActivityMetadata metadata)
    {
        base.CacheMetadata(metadata);

        // needs to be cached as implementation child and variable (Body and SomeVariable must be declared as private then)
        metadata.AddImplementationChild(this.Body);
        metadata.AddImplementationVariable(SomeVariable);
    }

    private void ChildCompletionCallback<TResult>(NativeActivityContext context, ActivityInstance completedInstance, TResult result)
    {
        //Set the output for the parent activity at the completion of the child activity
        this.Result.Set(context, result);
    }
}

Child:

public class Child : CodeActivity<object>
{
    public InArgument<Dictionary<string, object>> Data { get; set; }

    protected override object Execute(CodeActivityContext context)
    {
        Dictionary<string, object> data = Data.Get(context);

        return "Some result";
    }
}
like image 2
axl g Avatar answered Oct 14 '22 15:10

axl g


it is probably not possible to set value of input argument directly in Execute method. An intermediate variable must be introduced. Child.Data argument is connected to this variable and in Parent.Execute method.

Parent:

public class Parent : NativeActivity
{
    public InArgument<int> Value1 { get; set; }
    public InArgument<string> Value2 { get; set; }
    private Variable<Dictionary<string, object>> SomeVariable { get; set; } // intermediate variable

    private Child Body { get; set; }

    public Parent()
    {
        this.SomeVariable = new Variable<Dictionary<string, object>>("SomeVariable");

        this.Body = new Child();
        this.Body.Data = new InArgument<Dictionary<string, object>>(SomeVariable);
    }

    protected override void Execute(NativeActivityContext context)
    {
        int value1 = Value1.Get(context);
        string value2 = Value2.Get(context);

        Dictionary<string, object> data = new Dictionary<string, object>();
        data.Add("value1", value1);
        data.Add("value2", value2);

        this.SomeVariable.Set(context, data);

        context.ScheduleActivity(this.Body);
    }

    protected override void CacheMetadata(NativeActivityMetadata metadata)
    {
        base.CacheMetadata(metadata);

        // needs to be cached as implementation child and variable (Body and SomeVariable must be declared as private then)
        metadata.AddImplementationChild(this.Body);
        metadata.AddImplementationVariable(SomeVariable);
    }
}

Child:

public class Child : CodeActivity
{
    // Must be declared as property.
    public InArgument<Dictionary<string, object>> Data
    {
        get;
        set;
    }

    protected override void Execute(CodeActivityContext context)
    {
        var data = this.Data.Get(context);
    }
}

This is not a nice solution but I hardly see anything nice in WF4:).

like image 1
Jakub Linhart Avatar answered Oct 14 '22 14:10

Jakub Linhart