locked
How to find activity's children with beta 2 before running the workflow. RRS feed

  • Question

  • In beta 1, I was using the WorkflowElement.GetChildren() method to navigate the workflow. We were finding our customs activities and do some validations (security and restriction checks for exemples). But now the only way we seem to be able to find the children is using the NativeActivityContext, but it's only available at runtime and by overriding the Execute method (which I can't use with dot.net standard activities).

    I could implement a visitor pattern, but I would need to implement the pattern for every composite activities of dot.net activities. So is there any way I navigate in activities before running the workflows in an easy way?

    Tuesday, October 20, 2009 2:59 PM

All replies

  • You can accomplish your scenario by using constraint validation. How to implement the validation depends of your scenario. For example, do you have a custom activity that contains the custom activities that you want to validate? If not, have you tried adding the validation logic ((security and restriction checks) directly to the custom activity in the cache metadata method?

    If your scenario is the first one you will need to create a constraint that will walk all the children of the container activity and run your specific validation logic like this:

    static Constraint CheckParent()

            {

                DelegateInArgument<CreateState> element = new DelegateInArgument<CreateState>();

                DelegateInArgument<ValidationContext> context = new DelegateInArgument<ValidationContext>();                       

                Variable<bool> result = new Variable<bool>();

                DelegateInArgument<Activity> parent = new DelegateInArgument<Activity>();

              

                return new Constraint<CreateState>

                {                                  

                    Body = new ActivityAction<CreateState,ValidationContext>

                    {                   

                        Argument1 = element,

                        Argument2 = context,

                        Handler = new Sequence

                        {

                            Variables =

                            {

                                result

                            },

                            Activities =

                            {

                                new ForEach<Activity>

                                {                               

                                    Values = new GetParentChain

                                    {

                                        ValidationContext = context                                   

                                    },

                                    Body = new ActivityAction<Activity>

                                    {  

                                        Argument = parent,

                                        Handler = new If()

                                        {                                         

                                            Condition = new InArgument<bool>((env) => object.Equals(parent.Get(env).GetType(),typeof(CreateCountry))),                                       

                                            Then = new Assign<bool>

                                            {

                                                Value = true,

                                                To = result

                                            }

                                        }

                                    }                               

                                },

                                new AssertValidation

                                {

                                    Assertion = new InArgument<bool>(result),

                                    Message = new InArgument<string> ("CreateState has to be inside a CreateCountry activity"),                                                               

                                }

                            }

                        }

                    }

                };

            }

     

     Then you need to add this constraint to the constraint collection of the container activity like this:

    public sealed class CreateState : CodeActivity

        {

            public CreateState()

            {

                base.Constraints.Add(CheckParent());

                this.Cities = new List<Activity>();           

            }

     

    Does this so

    You can accomplish your scenario by using constraint validation. How to implement the validation depends of your scenario. For example, do you have a custom activity that contains the custom activities that you want to validate? If not, have you tried adding the validation logic ((security and restriction checks) directly to the custom activity in the cache metadata method?

    If your scenario is the first one you will need to create a constraint that will walk all the children of the container activity and run your specific validation logic like this:

    static Constraint CheckParent()

            {

                DelegateInArgument<CreateState> element = new DelegateInArgument<CreateState>();

                DelegateInArgument<ValidationContext> context = new DelegateInArgument<ValidationContext>();                       

                Variable<bool> result = new Variable<bool>();

                DelegateInArgument<Activity> parent = new DelegateInArgument<Activity>();

              

                return new Constraint<CreateState>

                {                                  

                    Body = new ActivityAction<CreateState,ValidationContext>

                    {                   

                        Argument1 = element,

                        Argument2 = context,

                        Handler = new Sequence

                        {

                            Variables =

                            {

                                result

                            },

                            Activities =

                            {

                                new ForEach<Activity>

                                {                               

                                    Values = new GetParentChain

                                    {

                                        ValidationContext = context                                   

                                    },

                                    Body = new ActivityAction<Activity>

                                    {  

                                        Argument = parent,

                                        Handler = new If()

                                        {                                         

                                            Condition = new InArgument<bool>((env) => object.Equals(parent.Get(env).GetType(),typeof(CreateCountry))),                                       

                                            Then = new Assign<bool>

                                            {

                                                Value = true,

                                                To = result

                                            }

                                        }

                                    }                               

                                },

                                new AssertValidation

                                {

                                    Assertion = new InArgument<bool>(result),

                                    Message = new InArgument<string> ("CreateState has to be inside a CreateCountry activity"),                                                               

                                }

                            }

                        }

                    }

                };

            }

     

     Then you need to add this constraint to the constraint collection of the container activity like this:

    public sealed class CreateState : CodeActivity

        {

            public CreateState()

            {

                base.Constraints.Add(CheckParent());

                this.Cities = new List<Activity>();           

            }

     

    Tell me if it solves your scenario.

    Sergio

     

    Wednesday, October 21, 2009 4:33 AM
  • Hi,

    I have the same question as Instriker. I need to traverse the workflow before it runs (to identify some of my custom activities, and collect some metadata about them that is used outside the workflow). How do i solve this in beta 2? Sergio, perhaps your answer applied to Instrikers question, but it does not apply in my case.

    This is a central part of our application, and is currently prohibiting us froming moving from beta 1 to beta 2.

    Wednesday, October 21, 2009 8:47 AM
  • I did some digging in the the framwork and found:

    WorkflowInspectionServices.GetActivities(Activity)

    which returns a IEnumerable<Activity> and can be used in a similar way to WorkflowElement.GetChildren().
    Wednesday, October 21, 2009 1:15 PM
  • This could work for most of my validations, but like zpirou, I also need to do some checks before it run to get some metadata from the workflow. So is there any way to do it outside the workflow?
    Wednesday, October 21, 2009 3:01 PM
  • Instriker, did not WorkflowInspectionServices.GetActivities(Activity) work out for you? It solved my problem at least.
    Wednesday, October 21, 2009 6:32 PM
  • Hi zpirou, sorry, I didn't see your post when I posted mine, I guess my web browser wasn't refreshed...

    Well this seem to be exactly the functionnality I need. But when trying to use it, I came with a problem : StackOverflow.

    The problem is depending on my root activity, I restrict some of the other children activities types we can use. So I tryied to make my self validating activity, this an exemple of what I tryied :

       public class TestElementForInspection : System.Activities.CodeActivity
       {
          public Collection<Activity> Activities
          {
             get;
             set;
          }
    
          public TestElementForInspection ()
          {
             this.Activities = new Collection<Activity> ();
          }
    
          protected override void Execute (CodeActivityContext context)
          {
             // some execution code
    } protected override void CacheMetadata (CodeActivityMetadata metadata) { base.CacheMetadata (metadata); // Exemple of validation
    ValidateChildren (metadata, this, typeof (TestElementForInspection)); } private static void ValidateChildren (CodeActivityMetadata metadata, Activity toValidate, Type requiredType) { IEnumerable<Activity> myChildren = WorkflowInspectionServices.GetActivities (toValidate); foreach (Activity myChild in myChildren) { if (!requiredType.IsAssignableFrom (myChild.GetType ())) metadata.AddValidationError ("Bad child activity..."); ValidateChildren (metadata, myChild, requiredType); } } }
    And I'm trying to do this :

          private static void Test4 ()
          {
             TestElementForInspection workflowDefinition = new TestElementForInspection ();
             workflowDefinition.Activities.Add (new TestElementForInspection ());
    
             // Both next two lines will make a StackOverflow.
             WorkflowInvoker.Invoke (workflowDefinition);
             List<Activity> children = WorkflowInspectionServices.GetActivities (workflowDefinition).ToList ();
          }
    But when trying to either run the workflow ou GetActivities by the WorkflowInspectionServices, I get a StackOverflow.

    I'll try to use the trick from Users MedalsSergio Zamora for validation and use the WorkflowInspectionServices for external checks. I'll give some more news when my tests are done with the combined technique.
    Thursday, October 22, 2009 12:21 PM
  • I've tryied to combien both methods without success. I tried to use the GetChildSubtree class (insteadof GetParentChain in the sample) to be able to add validation by the parent instead of the children (the children doesn't know they have restrictions). So I tryied something this next exemple, but before getting to end, I've already got a recursion (or infinite loop) problem with the CacheMetadata method... Not to mention the complexity vs what I did with the GetChildren method...

       public class TestElementForInspection : System.Activities.CodeActivity
       {
          public Collection<Activity> Activities
          {
             get;
             set;
          }
    
          public TestElementForInspection ()
          {
             Activities = new Collection<Activity> ();
             ValidateChildren ();
          }
    
          protected override void Execute (CodeActivityContext context)
          {
             // something...
    } private void ValidateChildren () { Constraint<TestElementForInspection> requiredTypeConstraint = new Constraint<TestElementForInspection> (); requiredTypeConstraint.Body = new ActivityAction<TestElementForInspection, ValidationContext> () { Argument1 = new DelegateInArgument<TestElementForInspection> (), Argument2 = new DelegateInArgument<ValidationContext> (), Handler = new AssertChildrenValid () }; base.Constraints.Add (requiredTypeConstraint); } } public class AssertChildrenValid : NativeActivity { private Activity _toRun; private Variable<bool> _result = new Variable<bool> (); protected override void Execute (NativeActivityContext context) { _result.Set (context, true); context.ScheduleActivity (_toRun); } protected override void CacheMetadata (NativeActivityMetadata metadata) { base.CacheMetadata (metadata); metadata.AddImplementationVariable (_result); _toRun = new Sequence () { Activities = { new ValidateChildren (_result), new AssertValidation { Assertion = _result, Message = new InArgument<string> ("At least one invalid child type"), } } }; metadata.AddImplementationChild (_toRun); } } public class ValidateChildren : NativeActivity { private Activity _toRun; private Variable<bool> _localResult = new Variable<bool> (); public InOutArgument<bool> Result { get; set; } public ValidateChildren (InOutArgument<bool> result) { Result = result; } protected override void Execute (NativeActivityContext context) { // Useless if already in error. if (Result.Get (context)) { context.ScheduleActivity (_toRun); } } protected override void CacheMetadata (NativeActivityMetadata metadata) { base.CacheMetadata (metadata); DelegateInArgument<Activity> parent = new DelegateInArgument<Activity> (); DelegateInArgument<ValidationContext> contextArgument = new DelegateInArgument<ValidationContext> (); _toRun = new ForEach<Activity> () { Values = new GetChildSubtree () { ValidationContext = contextArgument }, Body = new ActivityAction<Activity> () { Argument = parent, Handler = new Sequence () { Variables = { _localResult }, Activities = { new Assign<bool> () { Value = new InArgument<bool> (child => !typeof (TestElementForInspection).IsAssignableFrom (child.GetType ())), To = new OutArgument<bool> (_localResult), }, new System.Activities.Statements.If () { Condition = _localResult, Then = new ValidateChildren (_localResult), }, new System.Activities.Statements.If () { Condition = new InArgument<bool> (new System.Activities.Expressions.Not<bool, bool> () { Operand = _localResult }), Then = new Assign<bool> { Value = false, To = new OutArgument<bool> (Result.Expression), }, }, } } } }; metadata.AddImplementationChild (_toRun); } }
    So, any other idea to do recursive validation in children activities?
    Thursday, October 22, 2009 3:52 PM