Search code examples
c#workflow-foundation-4

TrackingProfile Argument by name


I 'm trying to capture trace for argument named "arg1" and i use this tracking profile

    private  MyTrace CreateTrace()
    {
    string all="*"
        MyTrace trace = new MyTrace();
        trace.TrackingProfile = new TrackingProfile()
        {
            ImplementationVisibility = ImplementationVisibility.All,
            Name = "CustomTrackingProfile",
            Queries = 
                {
                    new CustomTrackingQuery() 
                    {
                     Name = all,
                     ActivityName = all
                    },
                    new WorkflowInstanceQuery()
                    {                                                       
                         // Limit workflow instance tracking records for started and completed workflow states
                           States = {WorkflowInstanceStates.Started, WorkflowInstanceStates.Completed }
                    },
                      ActivityStateQuery query = new ActivityStateQuery()
                    new ActivityStateQuery()
                    {
                        ActivityName = "*",
                        States = { ActivityStates.Executing ,ActivityStates.Closed},
                        Arguments={"arg1"}
                    }


                }
        };
        return trace;
    }

Activity to trace : (It's very simple arg1="value")

    <Activity mc:Ignorable="sap" x:Class="WorkflowConsoleApplication5.Workflow1" xmlns="http://schemas.microsoft.com/netfx/2009/xaml/activities" 
              xmlns:av="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
              xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
              xmlns:mv="clr-namespace:Microsoft.VisualBasic;assembly=System" 
              xmlns:mva="clr-namespace:Microsoft.VisualBasic.Activities;assembly=System.Activities" 
              xmlns:s="clr-namespace:System;assembly=mscorlib" 
              xmlns:s1="clr-namespace:System;assembly=System"
              xmlns:s2="clr-namespace:System;assembly=System.Xml" 
              xmlns:s3="clr-namespace:System;assembly=System.Core" 
              xmlns:s4="clr-namespace:System;assembly=System.ServiceModel" 
              xmlns:sa="clr-namespace:System.Activities;assembly=System.Activities" 
              xmlns:sad="clr-namespace:System.Activities.Debugger;assembly=System.Activities" 
              xmlns:sap="http://schemas.microsoft.com/netfx/2009/xaml/activities/presentation" 
              xmlns:scg="clr-namespace:System.Collections.Generic;assembly=System" 
              xmlns:scg1="clr-namespace:System.Collections.Generic;assembly=System.ServiceModel" 
              xmlns:scg2="clr-namespace:System.Collections.Generic;assembly=System.Core" 
              xmlns:scg3="clr-namespace:System.Collections.Generic;assembly=mscorlib" 
              xmlns:sd="clr-namespace:System.Data;assembly=System.Data" 
              xmlns:sl="clr-namespace:System.Linq;assembly=System.Core" 
              xmlns:st="clr-namespace:System.Text;assembly=mscorlib" 
              xmlns:w="clr-namespace:WorkflowConsoleApplication5" 
              xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
      <x:Members>
        <x:Property Name="arg1" Type="InArgument(x:String)" />
      </x:Members>
      <sap:VirtualizedContainerService.HintSize>654,676</sap:VirtualizedContainerService.HintSize>
      <mva:VisualBasic.Settings>Assembly references and imported namespaces for internal implementation</mva:VisualBasic.Settings>
      <Flowchart  sap:VirtualizedContainerService.HintSize="614,636">
        <sap:WorkflowViewStateService.ViewState>
          <scg3:Dictionary x:TypeArguments="x:String, x:Object">
            <x:Boolean x:Key="IsExpanded">False</x:Boolean>
            <av:Point x:Key="ShapeLocation">270,2.5</av:Point>
            <av:Size x:Key="ShapeSize">60,75</av:Size>
            <av:PointCollection x:Key="ConnectorLocation">300,77.5 300,171</av:PointCollection>
          </scg3:Dictionary>
        </sap:WorkflowViewStateService.ViewState>
        <Flowchart.StartNode>
          <FlowStep x:Name="__ReferenceID0">
            <sap:WorkflowViewStateService.ViewState>
              <scg3:Dictionary x:TypeArguments="x:String, x:Object">
                <av:Point x:Key="ShapeLocation">179,171</av:Point>
                <av:Size x:Key="ShapeSize">242,58</av:Size>
                <av:PointCollection x:Key="ConnectorLocation">300,229 300,259 290,259 290,299.5</av:PointCollection>
              </scg3:Dictionary>
            </sap:WorkflowViewStateService.ViewState>
            <Assign sap:VirtualizedContainerService.HintSize="242,58">
              <Assign.To>
                <OutArgument x:TypeArguments="x:String">[arg1]</OutArgument>
              </Assign.To>
              <Assign.Value>
                <InArgument x:TypeArguments="x:String">value</InArgument>
              </Assign.Value>
              <sap:WorkflowViewStateService.ViewState>
                <scg3:Dictionary x:TypeArguments="x:String, x:Object">
                  <x:Boolean x:Key="IsExpanded">True</x:Boolean>
                </scg3:Dictionary>
              </sap:WorkflowViewStateService.ViewState>
            </Assign>
            <FlowStep.Next>
              <FlowStep x:Name="__ReferenceID1">
                <sap:WorkflowViewStateService.ViewState>
                  <scg3:Dictionary x:TypeArguments="x:String, x:Object">
                    <av:Point x:Key="ShapeLocation">184.5,299.5</av:Point>
                    <av:Size x:Key="ShapeSize">211,61</av:Size>
                  </scg3:Dictionary>
                </sap:WorkflowViewStateService.ViewState>
                <WriteLine sap:VirtualizedContainerService.HintSize="211,61" Text="[arg1]">
                  <sap:WorkflowViewStateService.ViewState>
                    <scg3:Dictionary x:TypeArguments="x:String, x:Object">
                      <x:Boolean x:Key="IsExpanded">True</x:Boolean>
                    </scg3:Dictionary>
                  </sap:WorkflowViewStateService.ViewState>
                </WriteLine>
              </FlowStep>
            </FlowStep.Next>
          </FlowStep>
        </Flowchart.StartNode>
        <x:Reference>__ReferenceID0</x:Reference>
        <x:Reference>__ReferenceID1</x:Reference>
      </Flowchart>
    </Activity>


 public class MyTrace : TrackingParticipant
{
    private  String participantName = "Mytrace";


    protected override void Track(TrackingRecord record, TimeSpan timeout)
    {
        Console.WriteLine("*{0}+++++++++++++++++++++++++++++++++++++",record.RecordNumber);
        Console.Write(String.Format(CultureInfo.InvariantCulture,        "{0} emitted trackRecord: {1}  Level: {2}, RecordNumber: {3}",     participantName, record.GetType().FullName, record.Level, record.RecordNumber));
        WorkflowInstanceRecord workflowInstanceRecord = record as WorkflowInstanceRecord;
        if (workflowInstanceRecord != null)
        {
            Console.WriteLine(String.Format(CultureInfo.InvariantCulture,
                " Workflow InstanceID: {0} Workflow instance state: {1}",
                record.InstanceId, workflowInstanceRecord.State));

        }
        ActivityStateRecord activityStateRecord = record as ActivityStateRecord;
        if (activityStateRecord != null)
        {
            IDictionary<String, object> variables = activityStateRecord.Variables;
            IDictionary<string, object> arguments = activityStateRecord.Arguments;
            StringBuilder vars = new StringBuilder();
            if (variables.Count > 0)
            {
                vars.AppendLine("\n\tVariables:");
                foreach (KeyValuePair<string, object> variable in variables)
                {
                    vars.AppendLine(String.Format(
                        "\t\tName: {0} Value: {1}", variable.Key, variable.Value));

                }
            }
            StringBuilder args = new StringBuilder();

            if (arguments.Count > 0)
            {
                args.AppendLine("\n\tArguments:");
                foreach (KeyValuePair<string, object> arg in arguments)
                {
                    args.AppendLine(String.Format(
                        "\t\tName: {0} Value: {1}", arg.Key, arg.Value));

                }
            }

            Console.WriteLine(String.Format(CultureInfo.InvariantCulture,
       " :Activity DisplayName: {0} :ActivityInstanceState: {1} {2} {3}",
          activityStateRecord.Activity.Name, activityStateRecord.State,
       ((variables.Count > 0) ? vars.ToString() : String.Empty),
       ((arguments.Count > 0) ? args.ToString() : String.Empty)));
        }

        CustomTrackingRecord customTrackingRecord = record as CustomTrackingRecord;

        if ((customTrackingRecord != null) && (customTrackingRecord.Data.Count > 0))
        {
            Console.WriteLine(String.Format(CultureInfo.InvariantCulture,
                "\n\tUser Data:"));
            foreach (string data in customTrackingRecord.Data.Keys)
            {
                Console.WriteLine(String.Format(CultureInfo.InvariantCulture,
                    " \t\t {0} : {1}", data, customTrackingRecord.Data[data]));
            }
        }
        Console.WriteLine();
    }
}   

When i execute activity(Workflow1) , i recive this trace ouput:

                     *0+++++++++++++++++++++++++++++++++++++
        TrackingParticipant emitted trackRecord: System.Activities.Tracking.WorkflowInstanceRecord  Level: Info, RecordNumber: 0 Workflow InstanceID: a5b135ce-03d0-40ea-a491-15ff01030e05 Workflow instance state: Starte

        *1+++++++++++++++++++++++++++++++++++++
        TrackingParticipant emitted trackRecord: System.Activities.Tracking.ActivityStateRecord  Level: Info, RecordNumber: 1 :Activity DisplayName: Workflow1 :ActivityInstanceState: Executing
                Arguments:
                        Name: arg1 Value: HOLA


        *2+++++++++++++++++++++++++++++++++++++
        TrackingParticipant emitted trackRecord: System.Activities.Tracking.ActivityStateRecord  Level: Info, RecordNumber: 2 :Activity DisplayName: Flowchart :ActivityInstanceState: Executing

        *3+++++++++++++++++++++++++++++++++++++
        TrackingParticipant emitted trackRecord: System.Activities.Tracking.ActivityStateRecord  Level: Info, RecordNumber: 3 :Activity DisplayName: Assign :ActivityInstanceState: Executing

        *4+++++++++++++++++++++++++++++++++++++
        TrackingParticipant emitted trackRecord: System.Activities.Tracking.ActivityStateRecord  Level: Info, RecordNumber: 4 :Activity DisplayName: Assign :ActivityInstanceState: Closed

        value
        *5+++++++++++++++++++++++++++++++++++++
        TrackingParticipant emitted trackRecord: System.Activities.Tracking.ActivityStateRecord  Level: Info, RecordNumber: 5 :Activity DisplayName: WriteLine :ActivityInstanceState: Executing

        *6+++++++++++++++++++++++++++++++++++++
        TrackingParticipant emitted trackRecord: System.Activities.Tracking.ActivityStateRecord  Level: Info, RecordNumber: 6 :Activity DisplayName: WriteLine :ActivityInstanceState: Closed

        *7+++++++++++++++++++++++++++++++++++++
        TrackingParticipant emitted trackRecord: System.Activities.Tracking.ActivityStateRecord  Level: Info, RecordNumber: 7 :Activity DisplayName: Flowchart :ActivityInstanceState: Closed

        *8+++++++++++++++++++++++++++++++++++++
        TrackingParticipant emitted trackRecord: System.Activities.Tracking.ActivityStateRecord  Level: Info, RecordNumber: 8 :Activity DisplayName: Workflow1 :ActivityInstanceState: Closed
                Arguments:
                        Name: arg1 Value: value


        *9+++++++++++++++++++++++++++++++++++++

Why argument (arg1) only notify changed when Workflow1 is closed? Whats happend with assing arg1="value"? I think it must notify argument was changed


Solution

  • Use the tracking participant created by Ron Jacobs. It's really easy to query after the activity has run because it has structured results and you'll be able to pick up that argument at every stage actually. It actually uses the Microsoft.Activities.UnitTesting framework for the participant. This participant can actually track child workflows at the same time as well!

    Here are some blog articles where Ron explains how to use it.

    http://blogs.msdn.com/b/rjacobs/archive/2011/01/13/wf4-how-tracking-helped-me-write-a-unit-test.aspx

    http://blogs.msdn.com/b/rjacobs/archive/2011/05/26/tracking-child-workflow-with-invokeworkflow.aspx

    http://blogs.msdn.com/b/rjacobs/archive/2012/06/18/what-state-is-my-statemachine-in.aspx

    EDITED

    I am editing this to clarify that if you must get more frequent updates you'll need to track a variable and not the argument. So, set the value of a variable to that of the argument, and then if you need to before leaving the workflow, set the value of the argument to that of the variable but setting the InArgument at the end of the workflow is really of none effect because nobody is going to be using it (at least I'm assuming).