I finished Part 1 with two questions, which aren’t going to make sense if you haven’t read Part 1 already.

  1. What does the constructor look like?
  2. What exactly does the CacheMetadata() implementation look like?

Let’s start with a possible constructor. It hardcodes a lot, maybe too much:

    public sealed class NativeHandleRequest : NativeActivity

    {

        public Activity Body { get; set; }

        private Sequence Sequence { get; set; }

        private Receive Receive { get; set; }

        private SendReply SendReply { get; set; }

        private Variable<CorrelationHandle> CorrelationHandle { get; set; }

        private Variable<string> RequestParameter { get; set; }

        private Variable<string> ResponseParameter { get; set; }

 

        public NativeHandleRequest()

        {

            CorrelationHandle = new Variable<CorrelationHandle>("CH1");

            RequestParameter = new Variable<string>("RequestParam");

            ResponseParameter = new Variable<string>("ResponseParam");

            Receive = new Receive

            {

                ServiceContractName = XName.Get("{http://tempuri.org/}Service1"),

                OperationName = "Request",

                Content = new ReceiveMessageContent

                {

                    DeclaredMessageType = typeof(string),

                    Message = new OutArgument<string>(RequestParameter),

                },

                CanCreateInstance = true, // assumption

                CorrelationInitializers =

                {

                    new RequestReplyCorrelationInitializer

                    {

                        CorrelationHandle = new InArgument<CorrelationHandle>(CorrelationHandle),

                    }

                }

            };

            SendReply = new SendReply

            {

                Request = Receive,

                Content = new SendMessageContent

                {

                    Message = new InArgument<string>(ResponseParameter),

                }

            };

        }

We need several Variables for passing to InArguments and OutArguments. In CacheMetadata we need to register our children and our variables. For each of these we also have a decision we can make: Is this child activity a public child or an implementation child? Is this variable a public variable or an implementation variable?

First Approach: Make everything public

One good effect of this approach – it is possible to access the public variables from the public child Body, and so Body can access the variables named “RequestParameter” and “ResponseParameter”. We do run into a known designer bug on the way, which is that intellisense doesn’t work for variables not in a public collection called “Variables”. When we are editing the expression, it looks like this:

image

 

 

 

 

But we can press <TAB> afterwards and then a different expression parser kicks in, finds the variable, and the validation error goes away.

image

Due to the same known bug, we also can’t see any of those variables in the Variables control. This is quite a bad usability issue for someone who doesn’t know the parameter names. With an assign activity, getting and settings the variables’ values works. And their values can be copied to variables in scopes higher up in the workflow, etc, etc.

Problem: Lack of Encapsulation

Unfortunately, all those variables from scopes higher up in the workflow being available in the current scope means that those variable names can clash with the hardcoded variable names inside of our implementation. Let’s navigate up to a parent activity of the NativeHandleRequest, and add a variable named “CH1”, “ResponseParameter”, or “RequestParameter”. We get errors like this.

image

If our scenario required us to do something like nest one NativeHandleRequest inside of another, then we would be stuffed. We could ‘fix’ this by making those variables generate new, random names. But that wouldn’t be a very good fix, because since our variables are invisible, the only way we can find out what name got generated is ESP. Or us doing some more work to customize the designer.

Second Approach: Encapsulation

Workflow 4 provides a feature which will magically transport all our variables into a naming scope entirely separate from all the naming scopes in all our parent activities, and child activities, so that they can never name clash. Which is using Implementation Variables. However, once we do this, we have to rethink the design a little… (to be continued)


Blog Post by: tilovell09