I finished Part 1 with two questions, which aren’t going to make sense if you haven’t read Part 1 already.
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:
But we can press <TAB> afterwards and then a different expression parser kicks in, finds the variable, and the validation error goes away.
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.
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)