Freigeben über


(WF4)(PU3) Picking apart SendReceiveExtension

Last time I mentioned there is something new in Platform Update 3 Beta called SendReceiveExtension. I still don’t know much about it other than the general reason for adding it to the .Net framework, which is basically to allow non-.Net classes to play the role of .Net’s WorkflowServiceHost class (System.ServiceModel.Activities).

Remember this stuff is all still in Beta. Unfortunately that also means there’s no documentation for it. Today I’m going to try to partially fix that deficiency, by thinking out loud, studying the new class and figuring out how it ticks.

Let’s start with a Reflector dump (which is where I often start with not-yet-documented features, which I meet regularly):

public abstract class SendReceiveExtension

{

    // Methods

    protected SendReceiveExtension()

    {

    }

 

    public abstract void Cancel(Bookmark bookmark);

protected abstract void OnRegisterReceive(ReceiveSettings settings, InstanceKey correlatesWith, Bookmark receiveBookmark);

public abstract void OnUninitializeCorrelation(InstanceKey correlationKey);

public void RegisterReceive(ReceiveSettings settings, InstanceKey correlatesWith, Bookmark receiveBookmark)

    {

        this.OnRegisterReceive(settings, correlatesWith, receiveBookmark);

    }

 

    public abstract void Send(MessageContext message, SendSettings settings, InstanceKey correlatesWith, Bookmark sendCompleteBookmark);

 

    // Properties

    public abstract HostSettings HostSettings { get; }

}

 

There’s not a lot to look other than the method declarations because it’s an abstract class. But we can notice a bunch of things:

-We’re frequently dealing with Bookmarks.
-We’re frequently dealing with InstanceKeys.
-We’re also dealing with a bunch of settings classes: SendSettings, ReceiveSettings, and HostSettings.

Basically every single method is abstract. There’s an instance of the ‘indirectedly overrideable’ pattern where instead of overriding RegisterReceive directly, we only get to override OnRegisterReceive. This may at first appear to be pointless indirection but notice it makes it possible for more logic to be added to the RegisterReceive base class by the .Net framework in the future (without giving you the option of overriding that logic).

Of the above classes, Bookmark and InstanceKey are established .Net framework citizens, dating back to release of WF4. If you’re a WF4 regular, you’ve probably heard about bookmarks before. What about InstanceKey?

MSDN says: “Represents an instance key and contains an identifier and metadata associated with the instance key. An instance key acts as a non-unique alias for an instance.”

My personal interpretation of that: “InstanceKeys are probably lightweight things that are used somewhere as lookup keys to find workflow instances. Sort of like cookies. With metadata.”

OK. What about SendSettings, ReceiveSettings, HostSettings?
MSDN is absolutely no help at this specific point in time, so back to Reflector again.

namespace System.ServiceModel.Activities

{

    public sealed class HostSettings

    {

        // Methods

        public HostSettings();

 

        // Properties

        public bool IncludeExceptionDetailInFaults { get; set; }

        public XName ScopeName { get; set; }

        public bool UseNoPersistHandle { get; set; }

    }

 

    public sealed class ReceiveSettings

    {

        // Methods

        internal ReceiveSettings();

 

        // Properties

        public string Action { get; internal set; }

        public bool CanCreateInstance { get; internal set; }

    }

 

    public sealed class SendSettings

    {

        // Methods

        internal SendSettings();

 

        // Properties

        public Endpoint Endpoint { get; internal set; }

        public Uri EndpointAddress { get; internal set; }

        public string EndpointConfigurationName { get; internal set; }

        public bool IsOneWay { get; internal set; }

     public ProtectionLevel? ProtectionLevel { get; internal set; }

        public bool RequirePersistBeforeSend { get; internal set; }

        public TokenImpersonationLevel TokenImpersonationLevel { get; internal set; }

    }

}

Observations and deductions we can make based on this interface:

  • They are all just ‘property collection’ classes, there’s no behavior here. No surprise given the class names.
  • SendSettings and ReceiveSettings have internal constructors. This means we never have to create them, the .Net framework will always just give us SendSettings/ReceiveSettings it created itself to deal with.
  • HostSettings has a public constructor, so it is likely we will wish to create one of these and stick it on our SendReceiveExtension. Later, something else will grab our SendReceiveExtension, read the HostSettings, and all of the properties we set on it.

What is that something else likely to be? Well, it’s likely to be either a Send or Receive activity, of course, and the way it grabs our SendReceiveExtension is thus:

    protected override void Execute(NativeActivityContext executionContext)

    {

    SendReceiveExtension sendReceiveExtension = executionContext.GetExtension<SendReceiveExtension>();

    //...

    }

In order for that to work, what has to have happened is that we provided the SendReceiveExtension to the ActivityContext by way of a host extension.

Let’s ask ourselves another question – going back to SendReceiveExtension, what other methods do we guess are going to be called by Send Activity or Receive Activity?

 

RegisterReceive:

public void RegisterReceive(ReceiveSettings settings, InstanceKey correlatesWith, Bookmark receiveBookmark);

 

This looks like it might be called by a Receive activity when there is a bookmark and (maybe?) a known instance correlation key. In order for there to be a bookmark, someone has to have called the CreateBookmark(). Actually it’s a bit more specific than that. The Receive activity has to have called CreateBookmark, which means it has to have started executing.

 

Send:

 

public abstract void Send(MessageContext message, SendSettings settings, InstanceKey correlatesWith, Bookmark sendCompleteBookmark);

This looks like it might be called by a Send activity when it wants to send a message…. context? What’s a MessageContext? Whoops I didn’t notice that guy the first time! It would appear that MessageContext is also probably new to .Net framework in Product Update 3.

 

public class MessageContext

{

    // Fields

    private Message message;

    private Guid traceId;

 

    // Methods

    public MessageContext();

    internal MessageContext(Message message);

 

    // Properties

    public virtual Guid EndToEndTracingId { get; set; }

    public virtual Message Message { get; protected set; }

}

We can see that a MessageContext is a simple wrapper around the class Message… which is a plain old WCF SOAP message : System.ServiceModel.Channels.Message. Sure.

 

So let’s examine the difference – what does MessageContext adding that Message doesn’t have? An EndToEndTracingId tracing id (guid). What on earth could that be for?

 

Right now I have no idea. I mean, obviously we will get passed one of these by Send activity as part of Send message, and we are allowed to use it. Are we required to use it for anything? Can we see any handy APIs to pass this to?  Woah! Here’s a less than obvious one! It turns out that MessageContext is being used inside Receive activity’s implementation by way of type casting (notice the ‘documentation by throwing diagnostic exceptions’)

 

private void OnReceiveMessageFromExtension(NativeActivityContext executionContext, Bookmark bookmark, object state)

{

    //...

 

    this.extensionReceiveBookmark.Set(executionContext, null);

    MessageContext context = state as MessageContext;

    if (context == null)

    {

        throw FxTrace.Exception.AsError(new InvalidOperationException(SR.InvalidDataFromReceiveBookmarkState(this.OperationName)));

    }

 

    //...

}

So the implied contract is that we must pass a MessageContext object in as the ‘object’ parameter to ResumeBookmark() every time that we want to do ResumeBookmark on a Receive activity’s bookmark. Which would be when we received a message destined for that particular Receive activity, no doubt. Which fills in the details pretty well of what we do with the receiveBookmark received inside ‘RegisterReceive’.

That leaves 2 little mysteries left, Cancel() , and UninitializeCorrelation(). I think I’ll have to leave it at that for today. Hopefully this is still useful. Happy holidays!