Welcome to Windows Workflow Foundation (WF)
Top Tasks :

WF Community Bloggers

Browse by Tags

All Tags » VB   (RSS)

  • Unit testing asynchronous workflow activities

    In a previous blog post I demonstrated how TypeMock allowed us to mock out the workflow runtime infrastructure and create true unit tests for a workflow activity. Now this may not seems like a big deal but because most of the classes in the workflow runtime are sealed mocking them with another mocking framework is pretty much impossible. Now you can test custom activities by wrapping them in a test workflow, creating a workflow runtime, running the workflow and inspecting the result I can hardly call this a unit test as we have to create lots of, complex, depended objects. So TypeMock really opens up some new possibilities here.

    But the previous examples where still pretty simple, although mocking the ActivityExecutionContext has always been considered impossible, so lets try something more complicated in the shape of an asynchronous activity. Basically an asynchronous activity is an activity that returns ActivityExecutionStatus.Executing from the Execute method indicating to the workflow runtime that it isn't done yet. The activity uses ActivityExecutionContext at a later point in time to indicate that it is done by calling the CloseActivity function.

    Lets take a quick look at our new activity.

    public partial class WriteLineActivity3 : Activity
    {
        public WriteLineActivity3()
        {
            InitializeComponent();
        }
    
        public string Message { get; set; }
    
        protected override ActivityExecutionStatus Execute(ActivityExecutionContext executionContext)
        {
            ActivityExecutionStatus result = ActivityExecutionStatus.Closed;
    
            if (!string.IsNullOrEmpty(Message))
            {
                IComparable queueName = Guid.NewGuid();
                WorkflowQueuingService wqs = executionContext.GetService<WorkflowQueuingService>();
                WorkflowQueue queue = wqs.CreateWorkflowQueue(queueName, true);
                queue.QueueItemAvailable += new EventHandler<QueueEventArgs>(queue_QueueItemAvailable);
    
    
                WriteLineService3 service = executionContext.GetService<WriteLineService3>();
    
                service.WriteLine(Message, queueName);
                result = ActivityExecutionStatus.Executing;
            }
    
            return result;
        }
    
        void queue_QueueItemAvailable(object sender, QueueEventArgs e)
        {
            ActivityExecutionContext executionContext = sender as ActivityExecutionContext;
            executionContext.CloseActivity();
        }
    }
    

    In this case the return value from the Execute depends on the Message property being filled or not so we need to test this. First a simple test for an activity with an empty message:

     

    /// <summary>
    ///A test for Execute
    ///</summary>
    [TestMethod()]
    [DeploymentItem("WFUnitTest1.exe")]
    [VerifyMocks()]
    public void ExecuteTestEmpty()
    {
        WriteLineActivity3_Accessor target = new WriteLineActivity3_Accessor();
        ActivityExecutionStatus expected = ActivityExecutionStatus.Closed;
        ActivityExecutionStatus actual;
    
        ActivityExecutionContext executionContext = 
    RecorderManager.CreateMockedObject<ActivityExecutionContext>(); WorkflowQueuingService wqs = RecorderManager.CreateMockedObject<WorkflowQueuingService>(); WorkflowQueue queue = RecorderManager.CreateMockedObject<WorkflowQueue>(); WriteLineService3 service = RecorderManager.CreateMockedObject<WriteLineService3>(); using (RecordExpectations recorder = RecorderManager.StartRecording()) { executionContext.GetService<WriteLineService3>(); recorder.FailWhenCalled(); } target.Message = ""; actual = target.Execute(executionContext); Assert.AreEqual(expected, actual); }

    In this test I actually check that the runtime service, called WriteLineService3 in this case, is never retrieved. This test is still pretty simple and no more complicated than the test we did previously.

     

    Lets take a look at something more complicated. The following test has the Message property set so it is expected to call the runtime service. But being an asynchronous activity it is also required to create a WorkflowQueue and bind to its QueueItemAvailable event so we will check that has happened. Finally this time the Execute function should return a status of ActivityExecutionStatus.Executing as it isn't done yet. This test runs like this:

    /// <summary>
    ///A test for Execute
    ///</summary>
    [TestMethod()]
    [DeploymentItem("WFUnitTest1.exe")]
    [VerifyMocks()]
    public void ExecuteTestStart()
    {
        WriteLineActivity3_Accessor target = new WriteLineActivity3_Accessor();
        ActivityExecutionStatus expected = ActivityExecutionStatus.Executing;
        ActivityExecutionStatus actual;
    
    
        ActivityExecutionContext executionContext = 
            RecorderManager.CreateMockedObject<ActivityExecutionContext>();
        WorkflowQueuingService wqs = RecorderManager.CreateMockedObject<WorkflowQueuingService>();
        WorkflowQueue queue = RecorderManager.CreateMockedObject<WorkflowQueue>();
        WriteLineService3 service = RecorderManager.CreateMockedObject<WriteLineService3>();
    
        using (RecordExpectations recorder = RecorderManager.StartRecording())
        {
            // Record methods here
            executionContext.GetService<WorkflowQueuingService>();
            recorder.Return(wqs);
            wqs.CreateWorkflowQueue(null, true);
            recorder.Return(queue);
            queue.QueueItemAvailable += delegate(object sender, QueueEventArgs e) { };
    
            executionContext.GetService<WriteLineService3>();
            recorder.Return(service);
            service.WriteLine(null, null);
        }
    
        target.Message = "A message";
        actual = target.Execute(executionContext);
    
        Assert.AreEqual(expected, actual);
    
    A little more complicated than the previous check but not bad at all Smile

    In this case we are binding to the QueueItemAvailable event so we need to add another test for the event handler and make sure it informs the workflow runtime we are done.

    /// <summary>
    ///A test for queue_QueueItemAvailable
    ///</summary>
    [TestMethod()]
    [DeploymentItem("WFUnitTest1.exe")]
    [VerifyMocks()]
    public void queue_QueueItemAvailableTest()
    {
        WriteLineActivity3_Accessor target = new WriteLineActivity3_Accessor();
        QueueEventArgs e = null; // TODO: Initialize to an appropriate value
    
        ActivityExecutionContext executionContext = 
            RecorderManager.CreateMockedObject<ActivityExecutionContext>();
    
        using (RecordExpectations recorder = RecorderManager.StartRecording())
        {
            executionContext.CloseActivity();
        }
    
        target.queue_QueueItemAvailable(executionContext, e);
    }
    

    Not bad at all Smile

    That leaves us with writing a unit test for the runtime service itself. This runtime service actually looks like this:

    public class WriteLineService3: WorkflowRuntimeService
    {
        public void WriteLine(string message, IComparable queueName)
        {
            Guid instanceId = WorkflowEnvironment.WorkflowInstanceId;
    
            ThreadPool.QueueUserWorkItem(state => WriteLineAsync(message, instanceId, queueName));
        }
    
        private void WriteLineAsync(string message, Guid instanceId, IComparable queueName)
        {
            Thread.Sleep(TimeSpan.FromSeconds(5));
    
            Console.WriteLine(message);
    
            WorkflowInstance instance = GetWorkflow(instanceId);
            instance.EnqueueItem(queueName, null, null, null);
        }
    
        private WorkflowInstance GetWorkflow(Guid instanceId)
        {
            WorkflowInstance result = Runtime.GetWorkflow(instanceId);
            return result;
        }
    }
    

    Not very complicated or realistic for that matter but still lets write a quick unit test for it like this:

    /// <summary>
    ///A test for WriteLine
    ///</summary>
    [TestMethod()]
    [VerifyMocks(10000)]
    public void WriteLineTest()
    {
        WriteLineService3_Accessor target = new WriteLineService3_Accessor(); 
        
        string message = string.Empty; // TODO: Initialize to an appropriate value
        IComparable queueName = null; // TODO: Initialize to an appropriate value
    
        WorkflowInstance instance = RecorderManager.CreateMockedObject<WorkflowInstance>();
    
        using (RecordExpectations recorder = RecorderManager.StartRecording())
        {
            Guid instanceId = WorkflowEnvironment.WorkflowInstanceId;
            recorder.Return(Guid.NewGuid());
    
            target.GetWorkflow(instanceId);
            recorder.Return(instance);
            instance.EnqueueItem(null, null, null, null);
        }
    
        target.WriteLine(message, queueName);
    
    }
    

    The most important thing here is that we can mock the WorkflowInstance object just as easy as the ActivityExecutionContext in the previous tests. I am actually telling TypeMock to wait for 10 seconds with the [VerifyMocks(10000)] attribute. This allows the delay in the service to remain there and still check if the expected response was queued. One thing to note is that I had to create an extra GetWorkflow() function to wrap the real function on the WorkflowRuntime object. I was unable to mock the latter however I am unsure why this is the case and if mocking it is really impossible.

     

    Enjoy!

    www.TheProblemSolver.nl
    http://wiki.WindowsWorkflowFoundation.eu

  • Changing the ActivityDesignerTheme on an activity

    Using an ActivityDesignerTheme is a quick way to customize the appearance of an activity in the workflow designer. But there is a gotcha Sad Because once you applied the theme and have a workflow with the custom activity open in the designer no matter what you do changes will not be applied.

    I am not sure why this is but I suppose the designer caches the color scheme and doesn't see any changes made to it.

    Take the following activity:

    using System;
    using System.ComponentModel;
    using System.Drawing;
    using System.Workflow.ComponentModel;
    using System.Workflow.ComponentModel.Design;
    using System.Drawing.Drawing2D;
    
    namespace WorkflowConsoleApplication4
    {
        [Designer(typeof(MyActivity1ActivityDesigner))]
        public partial class MyActivity: Activity
        {
        }
    
    
        [ActivityDesignerTheme(typeof(MyActivity1ActivityDesignerTheme))]
        public class MyActivity1ActivityDesigner: ActivityDesigner
        {
        }
    
        public class MyActivity1ActivityDesignerTheme : ActivityDesignerTheme
        {
            /// <summary>
            /// Initializes a new instance of the MyActivity1ActivityDesignerTheme class.
            /// </summary>
            public MyActivity1ActivityDesignerTheme(WorkflowTheme theme)
                : base(theme)
            {
                BackColorStart = Color.LightBlue;
                BackColorEnd = Color.Blue;
                BackgroundStyle = LinearGradientMode.ForwardDiagonal;
            }
        }
    }
    

    Which look like this in the designer

    image

    Clearly the background has a blue gradient. Now if I change the theme code to the following:

    public class MyActivity1ActivityDesignerTheme : ActivityDesignerTheme
    {
        /// <summary>
        /// Initializes a new instance of the MyActivity1ActivityDesignerTheme class.
        /// </summary>
        public MyActivity1ActivityDesignerTheme(WorkflowTheme theme)
            : base(theme)
        {
            BackColorStart = Color.LightCyan;
            BackColorEnd = Color.Cyan;
            BackgroundStyle = LinearGradientMode.ForwardDiagonal;
        }
    }
    

    You might expect the activity to change its look to:

    image

    Unfortunately this doesn't happen Sad

    Now you can quit Visual Studio and restart it after which the new style will take affect but there is a quicker way. Right click the activity and select "Select Custom Theme..."

    image

    When the dialog shows just click OK and the designer reads the theme again Smile

     

    image

    Enjoy!

    www.TheProblemSolver.nl
    http://wiki.WindowsWorkflowFoundation.eu

  • Using multiple SetState activities in a StateMachineWorkflow

    When working with state machine workflow's the SetStateActivity is one of the more important activities to use. The main reason is that is the one used to transition from one state to another. You can actually transition state in a different way as well using the StateMachineWorkflowInstance.SetState() function but the SetStateActivity is by far the most often used.

    One of the cool features of the SetStateActivity that may not be immediately obvious is the fact that it doesn't do the state switch immediately. Instead it saved the new target start in an internal property, named NextStateName on the StateMachineExecutionState type to be exact, and only does the state switch when the current EventDrivenActivity or StateInitializationActivity is finished. And just to be clear those are the only two activities where you can use a SetStateActivity.

    image

    So the cool part is that you don't need to have the SetStateActivity right at the bottom of the EventDrivenActivity but it can be anywhere in the execution flow. Something that might make the execution logic quite a bit easier.

    image

    And the second cool behavior is that you can actually execute multiple SetStateActivity objects with different TargetStateName definitions and only the last one will be executed. Again something that might make the logic quite a bit simple as you can set a default TargetStateName at the beginning of your and make changes to the target as exceptional cases arise.

    Both of these behaviors are especially important as the TargetStateName is a meta property, meaning it cannot be changed at runtime but only at design time. So the option of dynamically changing the SetStateActivity doesn't really exist.

    Enjoy!

    www.TheProblemSolver.nl
    http://wiki.WindowsWorkflowFoundation.eu

  • Validating workflow activities at runtime

    As I mentioned in my previous blog post adding a custom ActivityValidator class to an activity validates it at runtime as well as at design time.

    So when is this runtime validation done and what happens when it fails?

    The when part is when the WorkflowInstance object is first created using one of the overloads of the WorkflowRuntime CreateWorkflow function. This means that the activity cannot really validate all properties as they are when it starts executing. After all when it depends on some data provided by another activity it needs to wait until this other activity has executed. And that will not happen at the CreateWorkflow moment but after it is started. So only use it to validate properties that need to be set at compile time. So what is the point of checking at runtime? Well suppose your existing activity changed and has extra requirements. The existing workflow's may not be recompiled so the first opportunity is to detect the error at runtime.

     

    image

     

    So what happens when the workflow is loaded and the runtime validation fails?

    In that case an exception of type WorkflowValidationFailedException is raised. The Errors collection contains all validation errors in the form of ValidationError objects. Check the ErrorText property so see what is wrong.

    Enjoy!

    www.TheProblemSolver.nl
    http://wiki.WindowsWorkflowFoundation.eu

  • How to Download all of Visual Studio 2008 SP1

     VS2008 SP1 Beta is quite a package. By default the installation downloads the packages as needed and when needed. Now that is just fine if you only need to install a single machine. But when you need to install multiple, possibly virtual, machines like I have to it just wastes a lot of bandwidth and time Sad. Fortunately there is a solution and it can be found here in the blog post by Heath Stewart.

    Enjoy! 

  • Microsoft Netherlands Community DevCast

    The Dutch Microsoft office has posted the first of the Community DevCast we recorded. The host is Alex Thissen who actually does most of the work as he also edits the screencasts so don't forget to thank him next week when you run into him at the Dutch DevDays.

    I am happy to say that amongst the first 6 screencasts released are no less that 4 I did with Alex about Workflow Foundation. And there are more to come so stay tuned.

    The other two are about C# and done by Dennis Vroegop again with Alex as the host.

    You can find the Community DevCast here. Keep in mind they are for a Dutch audience so they where recorded in Dutch!

    Enjoy!

    www.TheProblemSolver.nl
    http://wiki.WindowsWorkflowFoundation.eu

  • Changing the default icon of a custom workflow activity

    By default every custom workflow activity shows the same icon. And using an icon that help understand what the activity actually does makes life so much easier for the user of your activity. And remember he is a developer so some day he might just return the favor Smile

     

    The default looks like this.

    image

     

    So changing the icon isn't hard there are just a few steps that might catch you if you aren't careful.

    This is what my demo project looks like:

    image

     

    1. Add the new image to the project. In my case I added a folder named Images and dropped the PushpinHS.png in there. This PushpinHS.png can be found in the standard VS2008ImageLibrary.zip located in C:\Program Files\Microsoft Visual Studio 9.0\Common7\VS2008ImageLibrary\1033.
    2. Change the build action for PushpinHS.png to Embedded Resource.
    3. Next go to the Activity1.cs file and add the ToolboxBitmap attribute to the Activity1 class. We need to specify two parameters, the first is a type in which assembly the PushpinHS.png is located and the second is the name of the resource to use. These two are related because the resource name us relative to the namespace of the type used.
    4. [ToolboxBitmap(typeof(Activity1), "Images.PushpinHS.png")]
      public partial class Activity1: SequenceActivity
      {

      }

    And now we have a nice new icon in the activity like this:

    image

     

    One problem is that the resource name is actually relative to the class specified. Using relfector makes it easy to see all the actual names, including the namespace used.

    image

     

    So what to do if your activity is in another namespace and a direct reference to the resource isn't possible?

    The easiest way is to add another class outside of all namespaces and fully specify the resource name like this:

    using System.Drawing;
    using System.Workflow.Activities;

    internal class ResourceLoader
    { }

    namespace WorkflowConsoleApplication1.MyActivities
    {
        [ToolboxBitmap(typeof(ResourceLoader), "WorkflowConsoleApplication1.Images.PushpinHS.png")]
        public partial class Activity1: SequenceActivity
        {
            public Activity1()
            {
                InitializeComponent();
            }
        }
    }

     

    Note the ResourceLoader used in the ToolboxBitmap attribute is not included in any namespace to we need to specify the full resource name "WorkflowConsoleApplication1.Images.PushpinHS.png".

     

    Enjoy!

  • Using multiple WorkflowRuntime objects in a single AppDomain

    For some reason a lot of people seem to think that only a single WorkflowRuntime object can be created in an AppDomain. And it isn't just the average Joe out there who seems to think so, no even book authors describe this behavior as the Workflow Foundation book I am reading now does so.

    Just to set the record straight I will shout this out once more:

    You can have multiple WorkflowRuntime objects in a single .NET AppDomain

    Now I know where this perception comes from and its the original beta of Workflow Foundation way back before .NET 3.0 was released. Back then the WF team decided that only a single WorkflowRuntime would be enough for an AppDomain and created this restriction. I am not sure if they actually had a requirement or not but when people objected they listened and lifted the restriction.

    The removal of this restriction is a good thing as multiple WorkflowRuntime objects can be useful. The main reason is that different workflow's can have different, and conflicting, requirements of runtime services. And as a single WorkflowRuntime can have only a single runtime service configuration, and that should never be changed while the WorkflowRuntime is active. For example just think of one workflow that is long running and needs a WorkflowPersistenceService while a second is short running and should never be persisted.

    Enjoy!

    www.TheProblemSolver.nl
    http://wiki.WindowsWorkflowFoundation.eu

  • Working with the ReplicatorActivity and an UntilCondition

    The ReplicatorActivity is one of the more interesting Workflow Foundation activities to work with when you have a collection of data and want to iterate over each item. Basically just think of it as a For Each statement in regular code.

    To demonstrate the behavior I have created a very small workflow that looks like this in the designer:

    image

    Or in case you prefer the XOML like this:

    <SequentialWorkflowActivity x:Class="WorkflowConsoleApplication5.Workflow1"
                                x:Name="Workflow1"
                                xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                                xmlns="http://schemas.microsoft.com/winfx/2006/xaml/workflow">
      <ReplicatorActivity x:Name="replicatorActivity1"
                          ChildInitialized="SetupChildData"
                          Initialized="SetupData"
                          ExecutionType="Sequence">
        <ReplicatorActivity.UntilCondition>
          <CodeCondition Condition="RepeatUntill" />
        </ReplicatorActivity.UntilCondition>
        <CodeActivity x:Name="codeActivity1"
                      ExecuteCode="codeActivity1_ExecuteCode" />
      </ReplicatorActivity>
    </SequentialWorkflowActivity>

    The codebehind file I used looks like this:

    Public Class Workflow1
        Inherits SequentialWorkflowActivity
    
        Private Sub codeActivity1_ExecuteCode(ByVal sender As Object, _
                                              ByVal e As EventArgs)
            Dim current As CodeActivity = sender
            Dim data = GetCurrentData(current.Parent)
            Console.WriteLine("The data is '{0}'", data)
        End Sub
    
        Private Sub SetupData(ByVal sender As Object, _
                              ByVal e As EventArgs)
            Dim replicator As ReplicatorActivity = sender
    
            Dim data() As String = _
            {"One", "Two", "Stop here", "Three"}
    
            replicator.InitialChildData = data
        End Sub
    
        Private Sub RepeatUntill(ByVal sender As Object, _
                                 ByVal e As ConditionalEventArgs)
            Dim replicator As ReplicatorActivity = sender
            Dim data = GetCurrentData(sender)
            Console.WriteLine("Testing with data '{0}'", data)
            If data = "Stop here" Then
                e.Result = True
            ElseIf replicator.AllChildrenComplete Then
                e.Result = True
            End If
        End Sub
    
        Private Sub SetupChildData(ByVal sender As Object, _
                                   ByVal e As ReplicatorChildEventArgs)
            Console.WriteLine("Starting child with '{0}'", _
                              e.InstanceData)
        End Sub
    
        Private Function GetCurrentData(ByVal sender As Object)
            Dim replicator As ReplicatorActivity = sender
            Dim result = replicator.CurrentChildData(replicator.CurrentIndex)
            Return result
        End Function
    End Class
    

    Using the without an UntilCondition is actually quite simple. The main thing to be aware of the ExecutionType property. In this case I didn't set it and it defaults to Sequence. This means that each iteration comes after the next iteration and we can use the CurrentIndex and CurrentChildData to retrieve the piece of data we are working with. When we set the ExecutionType to Parallel the ReplicatorActivity behaves very different. In that case each branch is started at the same time so the CurrentIndex, which is scoped to the ReplicatorActivity, doesn't help you. In fact it will return the last item of the collection every time you try.

    In the case of parallel execution the way to get at the data is the ChildInitialized event. This event has a parameter of type ReplicatorChildEventArgs which in turn contains a property InstanceData which is the data this iteration is going to work with. its your responsibility to pass this InstanceData on to the activity inside the ReplicatorActivity. The usual thing to do here is create a custom, possibly sequence, activity with an extra property to hold the data and store it there in the ChildInitialized event.

     

    There are a few interesting, and unexpected, thing to be a aware of when you use an UntilCondition with a ReplicatorActivity. When I run this simple test workflow I receive the following output:

    image

    There are a couple of interesting things to note:

    1. The first time the UntilCondition is called is before the first ChildInitialized event and when we retrieve the current data it actually returns the last item. As far as I can tell there is no standard way to see this is case so if could be a problem you need to take care of it yourself. As far as I am concerned this is a serious shortcoming of the ReplicatorActivity
    2. The UntilCondition fires after and not before each loop is executed. Basically this means it is easy to use the execution result of an iteration and decide to stop further iterations but hard to prevent all remaining iteration, including the current, based on the input data. Again you would need to add some conditional logic inside the ReplicatorActivity. No problem and actually quite a nice behavior but something to be aware of.
    3. The ReplicatorActivity never stops if you don't tell it to. This is the behavior I most dislike with the ReplicatorActivity. Instead of just stopping and continuing with the next activity when all data is processed it just stops Sad. Now this isn't the case when you don't have an UntilCondition, in that case every item is processed and when done it proceeds with the next activity just as you would expect. No with an UntilCondition you are responsible for stopping and to do so the UntilCondition is actually called one more time after the last item has been processed. The thing to check for is the AllChildrenComplete property of the ReplicatorActivity. When this is true all the data has been processed and you are responsible for stopping the ReplicatorActivity. Now this might not be hard but it always is a major point of confusion and, I presume, catches everyone at least the first time. And worse I can see no benefit at all for this behavior as the ReplicatorActivity just stops and the next activity is never scheduled. As far as I am concerned this is a big "bug" in the behavior of the ReplicatorActivity.

    Removing the test for the "Stop here" in the UntilCondition result in the following output:

    image

    As you can see there is one additional UntilCondition at the end at which time the AllChildrenComplete property is set to true.

     

    Enjoy!

    www.TheProblemSolver.nl
    http://wiki.WindowsWorkflowFoundation.eu

  • Workflow Publish as Web Service and Visual Studion 2008

    I am unsure if this is a problem with my PC but when I am building a workflow using the WebServiceInputActivity and choose "Publish as Web Service" from the context menu I get a real helpfull error message Sad

    image

    No other messages appear so that is less that helpfull.

    Fortunatly it isn't all that had to publish the workflow manually. Just add a web service project to the solution and add an ASMX file with the following content:

    <%@ WebService Class="WebServiceInput.Workflow1_WebService" %>
    

    The workflow class is named "WebServiceInput.Workflow1" and just add the "_WebService" suffix to it. Next add a reference from the web service project to the assembly containing the workflow.

    Don't forget to also add refrences to the System.Workflow.Activities, System.Workflow.ComponentModel and System.Workflow.Runtime assemblies used at runtime.

    Next create a WorkflowRuntime config section like so:

            <section name="WorkflowRuntime" 
                 type="System.Workflow.Runtime.Configuration.WorkflowRuntimeSection, 
                       System.Workflow.Runtime, Version=3.0.00000.0, Culture=neutral, 
                       PublicKeyToken=31bf3856ad364e35"/>
    

     

    and

        <WorkflowRuntime Name="WorkflowRuntime">
            <Services>
                <add type="System.Workflow.Runtime.Hosting.ManualWorkflowSchedulerService, 
                     System.Workflow.Runtime, Version=3.0.00000.0, Culture=neutral, 
                     PublicKeyToken=31bf3856ad364e35"/>
                <add type="System.Workflow.Runtime.Hosting.DefaultWorkflowCommitWorkBatchService, 
                     System.Workflow.Runtime, Version=3.0.00000.0, Culture=neutral, 
                     PublicKeyToken=31bf3856ad364e35"/>
            </Services>
        </WorkflowRuntime>
    

     

    Now you should be good to go Smile

     

    The only thing now is adding conversation support. Right now you will het this error

    "System.Web.Services.Protocols.SoapException: Server was unable to process request. ---> System.InvalidOperationException: Current session has no workflow instance associated with it. Send activation message to start new instance in current session."

    even after adding a CookieContainer as I previously described here. The problem is that the cookie is never added to the response as this is done by WorkflowWebHostingModule http module. So to enable conversations just add the following to the web.config.

            <httpModules>
          <add type="System.Workflow.Runtime.Hosting.WorkflowWebHostingModule, 
                     System.Workflow.Runtime, Version=3.0.00000.0, Culture=neutral, 
                     PublicKeyToken=31bf3856ad364e35" 
               name="WorkflowHost"/>
            </httpModules>
    

    Enjoy!

    www.TheProblemSolver.nl
    http://wiki.WindowsWorkflowFoundation.eu

  • Project Mole or visual studio visualizers on steroids

    Visual Studio visualizers are pretty cool and can help you quite a bit when debugging but you need to develop them and that just ads to the workload. So how about having someone else do it? Well Microsoft added a few visualizers to VS2008 and VS2008 but they are pretty much limited to text, html and XML data.

     

    Luckily some people liked the idea of using visualizers and actually decided to take it to the next level with project Mole.

    Its a really great viewer to inspect your data and the "Black Ops" feature that allows you to inspect all private members is awesome.

    One quick tip: If you want to use the Mole with any variable use the "new WeakReference(variable)" in the watch window trick!

     

    Enjoy!

    www.TheProblemSolver.nl
    http://wiki.WindowsWorkflowFoundation.eu

  • Type x does not match type x errors

    Sometimes you can get these kinds of errors using the new Workflow Foundation ReceiveActivity when property binding the incoming value. So why does this happen Well it appears the validation is comparing the type of the original interface to to the type of the proxy generated. In this case because the actual type passed isn't shared between the client and the server.

    But as the generated proxy type actually gets exactly the same name as the original, even though it is a different type defined in a different assembly, it appears as though comparing two the same types says they are not equal.

    So is there a problem in the code?

    No there isn't because the client doesn't have a reference to the original type, which is why the proxy was generated in the first place, the proxy is always used. So the error is bogus and should not be reported.

    So this is a but that is going to be fixed? Well apparently not according to this bug report feedback. Apparently we just have to ignore the error Sad which sort of sucks big time. As far as I am concerned an incorrectly reported error is a serious bug and should be fixed as it is likely to waste a lot of time and might result in serious problems being ignored.

     

    So if you agree that problems like this should be fixed please go here and vote for this to be fixed!

    Enjoy!

  • The Power of Custom Workflow Activities (Part 2)

    I just noticed the second part of my article about developing custom Windows Workflow Foundation activities.

    Read part 2 here. Just in case you missed part 1 you can find it here.

    Enjoy!

    www.TheProblemSolver.nl
    http://wiki.WindowsWorkflowFoundation.eu

  • On the WF ReceiveActivity and WCF bindings

    The new ReceiveActivity and SendActivity that marry Windows Workflow Foundation (WF) and Windows Communication Foundation (WCF) are really cool Smile. Getting started is easy because a new Sequential Workflow Service Library, found under WCF instead of Workflow in VS2008, uses nice defaults for everything. But sooner or later you need to change these defaults and you need to know what can be done and what can't.

    When you want to use the new ReceiveActivity in a workflow you need to use a compatible WCF binding. The reason for this requirement is that the conversation context, see this blog post, is part of the message and needs to be retrieved and returned. The following code returns a list of all WCF binding and how they are composed:

    Sub Main()

    Dim assemblies As New List(Of Assembly)()

    ' .NET 3.0

    assemblies.Add(GetType(ServiceHost).Assembly)

    ' .NET 3.5

    assemblies.Add(GetType(WorkflowServiceHost).Assembly)

    assemblies.Add(GetType(WebServiceHost).Assembly)

     

    Dim query = From assembly In assemblies _

    From type In assembly.GetTypes() _

    Where type.IsSubclassOf(GetType(Binding)) _

    AndAlso Not type.IsAbstract _

    AndAlso type.IsPublic _

    Order By type.Name _

    Select type

     

    PrintBinding(query.ToList)

     

    Console.ReadLine()

    End Sub

     

    Private Sub PrintBinding(ByVal types As List(Of Type))

    For Each type In types

    Console.WriteLine(type.FullName)

    Try

    Dim binding As Binding = _

    CType(Activator.CreateInstance(type), Binding)

    Dim elements = binding.CreateBindingElements

    For Each element In elements

    Console.WriteLine(vbTab + element.GetType().FullName)

    Next

    Catch ex As Exception

    Console.WriteLine(ex.Message)

    End Try

    Console.WriteLine()

    Next

    End Sub

     

    The classes responsible for inserting and removing these conversation tokens are ContextRequestChannel and ContextReplyChannel and they are instantiated by the ContextBindingElement. So seems we are restricted to using BasicHttpContextBinding, NetTcpContextBinding or WSHttpContextBinding.

    So it seems we cannot use NetMsmqBinding which is a shame because one way reliable messaging is the perfect fit for workflow as far as I am concerned. Well not quite so fast because we still have the CustomBinding where we can configure the stack just the way we want right?

    Yeah we do but there is a problem Sad. It turns out the ContextBinding requires a channel with an IReplyChannel interface and the NetMsmqBinding actually implement an IInputChannel or an IOutputChannel. Which one actually depends if you are the client or the service.

    And thinking about how WF/WCF conversations works this restriction makes sense. After all a ReceiveActivity is called without a context in order to create a new workflow, assuming the CanCreateInstance property equals true, and returns the workflow instanceId in the context as part of the response. This design kind of rules out one-way messages and thereby NetMsmqBinding.

    Now this sucks big time if you ask me Sad. I would much rather have seen that you could specify the instanceId of the workflow to be created, just as you can with the WorkflowRuntime.CreateWorkflow() where a number of the overloads let you specify the workflows instanceId. I suppose it is possible to create a different context binding but that would be quite some work and, I assume, duplicate a lot of code already written my Microsoft. So let's hope they see the light and add MSMQ/ReceiveActivity intergration.

     

    Enjoy!

    www.TheProblemSolver.nl
    http://wiki.WindowsWorkflowFoundation.eu

  • The Power of Custom Workflow Activities

    The first of a two part article I wrote on developing custom workflow activities just appeared on the MSDN Visual Basic Developer Center. You can find the article named "The Power of Custom Workflow Activities" here. A second article with more details is scheduled to appear here as well.

    For everyone new to Workflow Foundation I would suggest reading some intro level material first. For example my "Workflow Foundation 101" article or "Hello, Workflow" by K. Scott Allen.

    Enjoy!

    www.TheProblemSolver.nl
    http://wiki.WindowsWorkflowFoundation.eu

     

More Posts Next page »