locked
Can I use implicit properties (to avoid user having to bind a variable to each activity) RRS feed

  • Question

  • I'm using a workflow designer to let my end-user create workflows to process special objects.  Each workflow will operate on a distinct object.  I'll have custom Activities to operate on these objects.  And I'd like to use multi-threading so multiple objects can be processed (by the same defining workflow) at the same time.

    I found that I can automatically create an input Argument for the workflow, in my workflow designer.  But this will still require the user to set (bind) the variable on each Activity (which will likely be many, and I'm afraid they'll miss one).  Is there a way to have my custom activities automatically access this variable?  It is NOT a requirement to show this variable to the user (i.e., as I currently have as an Argument).

    Using a global (directly accessible by my C# code) would work, except that I want multiple workfows to be processing different objects simultaneously.

    Is there a way I can pass properties to a workflow my code invokes, that I can then access by my custom Activities (that might be in other activities, like a Sequence) without reqiring my users to have to specify this?  These can be either visible or not visible to the end-user (I'd prefer not visible).

    P.S.  Two additional notes.  First, my customer would prefer to use the Flowchart, and my designer is currently defaulting to using this as the containing activity control.  Second, I have additional variables I'd like to do this for as well, so a solution that isn't limited to just one variable is what I'm looking for.

    Thanks for any help.

    Tuesday, July 26, 2011 4:12 PM

Answers

All replies

  • Hi Andy,

    >And I'd like to use multi-threading so multiple objects can be processed (by the same defining workflow) at the same time.

    A single workflow instance does not normally execute multithreaded. I guess you are probably either speaking of getting a similar effect by using AsyncCodeActivity to kick off and await the results of processing launched in another thread (e.g. thread pool worker thread), or running each workflow in a different thread using WorkflowInvoker. These options should both work.

    >I found that I can automatically create an input Argument for the workflow, in my workflow designer. But this will still require the user to set (bind) the variable on each Activity (which will likely be many, and I'm afraid they'll miss one). Is there a way to have my custom activities automatically access this variable?

    Yes, one possible avenue is Execution Properties. Please check out a little post I wrote, "Workflow Scopes and Execution Properties", and also the Entity Activities example here on MSDN which uses an ObjectContextScope for a similar effect. The scope value can be bound to one of your workflow arguments in order to have it vary with the particular workflow instance which is currently executing.

    Another possible avenue that might be suitable when you want the parameter to come in from the environment automatically without the user being aware of the parameter's existance is a workflow instance extension added by the host, see this post from Pat.

    (In terms of what you can achieve the two options are maybe not that different - with execution properties you could also e.g. run a workflow wrapped inside a 'scope' activity that was added after design time and before runtime. However, they might have different semantics in terms of what data gets persisted with the workflow, and with IExecutionProperty you can do things related to thread-local properties.)

    (Neither of these solutions/avenues is limited in how many variables you are interested in.)
    Tim

    Wednesday, July 27, 2011 4:28 AM
  • Thank you for the response.  Yes, I'm planning to use WorkflowInvoker on different threads for my multi-threading.

    I had read the "Workflow Scopes and Execution Properties" article previously.  Perhaps I'm missing something, but I believe it requires the user to add the CorrelationScope activity to the workflow, and place all further activities inside it (similar to putting things in a Sequence activity).  And since my customer wants to use the Flowchart, they would then have to place the Flowchart inside the CorrelationScope activity.  I suppose I could initialize this for them in the designer, but I'd be concerned that the user might change something and break it.

    Is there a way (similar to that article) for the host (that is calling the WorkflowInvoker) to set a property on the context, so the explicit activity isn't needed?  And, if so, can I only access this context specifically from NativeActivity classes (and not also form, say, AsyncCodeActivity or CodeActivity classes)?

    I'm not sure I understood Pat's article on Instance Extensions.  The name sounds like it should help, but it looked like the method that takes an object expects a singleton, which would not work in my multi-threading app.  And while I could create a closure as a delegate to return the proper object, I wasn't sure from the article when the delegate would be called, and if it would also be shared across threads.

    Isn't there a dictionary of parameters/properties that is passed to a workflow to set the workflow's Arguments?  Could I add a property to that dictionary that the workflow didn't define, and access it from (CodeActivity) activities in the workflow?

    Wednesday, July 27, 2011 3:20 PM
  • So, you can actually modify the workflow in between loading the xaml (which your customer wrote), and executing it. The easy way to modify it which I think solves the problem with scopes is to create a new scope and just wrap the workflow you loaded from xaml inside of that.

    Extensions aren't limited to singletons, you can actually create them per workflow instance, I will have to add the details.in another message or post perhaps.

    Thursday, July 28, 2011 2:37 AM
  • Here is a post with an example of registering extensions from the host, and per workflow instance, as a way of passing in data:

    http://blogs.msdn.com/b/tilovell/archive/2011/02/26/wf4-workflow-4-0-hosting-extensions-redux.aspx

    I hope either this or the Execution Property suggestion helps.
    Tim

    • Proposed as answer by Tim Lovell-Smith Tuesday, August 2, 2011 6:08 AM
    • Marked as answer by Andrew_Zhu Wednesday, August 3, 2011 2:53 AM
    • Unmarked as answer by Andy Jacobs Tuesday, August 9, 2011 6:37 PM
    • Marked as answer by Andy Jacobs Wednesday, August 10, 2011 2:19 AM
    Tuesday, August 2, 2011 6:08 AM
  • Tim, thank you for all your help.

    While reading and playing with code based on your articles, I found another solution, which looks like it will allow me to use normal CodeActivity (and AsyncCodeActivity), rather than requiring me to use NativeActivity for all my custom code.  I thought I'd share it here in case it helps others.  Although some caveats are that I haven't extensively played with it, so I don't know all it's limitations (perhaps it is not visible to sub-activities, or something).

    First, as I found in aother thread, an Activity can access the arguments of the workflow, without requiring explicit Activity parameters to be hooked up, like this:

    protected override bool Execute(CodeActivityContext context)
    {
    	var dataContext = context.DataContext;
    	var props = dataContext.GetProperties();
    	var a = props["ArgumentName"].GetValue(dataContext);
    [...]
    

    Then, if the user explicitly adds the argument, that's fine.  But I found I can also add the argument myself to the workflow (and can even do it conditionally, only if the user hasn't):

    	var parameters = new Dictionary<string, object>();
    	parameters["ArgumentName"] = "ArgumentValue";
    	var workflow = (DynamicActivity)ActivityXamlServices.Load(fileName);
    	if (!workflow.Properties.Contains("ArgumentName"))
    		workflow.Properties.Add(new DynamicActivityProperty() {Name = "ArgumentName", Type = typeof(InArgument<string>) });
    	WorkflowInvoker.Invoke(workflow, parameters);
    
    

    I've successfully passed string arguments using this technique to activities in my flowchart.  If I find other problems with this approach (I don't fully understand the DataContext, and haven't found much written about it), I'll probably try to go with the Execution Properties technique.  Thanks again.

    • Marked as answer by Andy Jacobs Tuesday, August 9, 2011 6:37 PM
    • Unmarked as answer by Andy Jacobs Tuesday, August 9, 2011 10:10 PM
    Tuesday, August 9, 2011 6:36 PM
  • >Although some caveats are that I haven't extensively played with it, so I don't know all it's limitations (perhaps it is not visible to sub-activities, or something).

    Yes, that is one of its limitations. I currently think Extensions provided by host are probably the best for what you are doing, you could pass extensions in from the host level, in WorkflowInvoker.Extensions, which is equivalent to how you are passing in the argument - and they can also be accessed from Code Activities.
    Tim

    Tuesday, August 9, 2011 9:37 PM