Welcome to Windows Workflow Foundation (WF)
Top Tasks :

WF Community Bloggers

Browse by Tags

All Tags » NetFx3   (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

  • Unit testing custom workflow activities

    Most people consider unit testing of custom workflow activities to pretty much impossible. Sure you can create a dummy test workflow containing your new activity, new up a WorkflowRuntime, create a WorkflowInstance and start it. But just think about all the dependencies here with the extra dummy workflow and the complete WorkflowRuntime with all its dependencies. Hardly a unit test for an activity but more like an integration test. Now there is nothing wrong with integration tests, they are very useful and necessary, but they do not give the speedy and dynamic test coverage you expect and need from a unit test.

    So is this really the case?

    Lets look at a few simple examples. This first example is really simple, in fact simple enough to say unrealistic but lets take a look anyway:

    public partial class WriteLineActivity1 : Activity
    {
        public WriteLineActivity1()
        {
            InitializeComponent();
        }
    
        public string Message { get; set; }
    
        protected override ActivityExecutionStatus Execute(
            ActivityExecutionContext executionContext)
        {
            if (!string.IsNullOrEmpty(Message))
                Console.WriteLine(Message);
    
            return ActivityExecutionStatus.Closed;
        }
    }
    

    This can easily be tested with a unit test like this.

    /// <summary>
    ///A test for Execute
    ///</summary>
    [TestMethod()]
    [DeploymentItem("WFUnitTest1.exe")]
    public void ExecuteTest()
    {
        WriteLineActivity1_Accessor target = 
            new WriteLineActivity1_Accessor();
        ActivityExecutionContext executionContext = null; 
        ActivityExecutionStatus expected = ActivityExecutionStatus.Closed;
        ActivityExecutionStatus actual;
        
        actual = target.Execute(executionContext);
    
        Assert.AreEqual(expected, actual);
    }
    

    So that at least proves some activities can be unit tested Smile

     

    But admittedly this is an oversimplification. In a more realistic activity the actual Console.WriteLine(), or whatever the activity needs done, would be implemented in a workflow runtime service so its implementation can be changed independently of the workflow. And that means using the ActivityExecutionContext to retrieve a reference to the service.

    Now this is where most people stop because the ActivityExecutionContext is a sealed type and sealed types cannot be mocked! But that isn't quite true because TypeMock can mock just about anything!

    So lets take look at slightly more realistic sample.

    public partial class WriteLineActivity2: Activity
    {
        public WriteLineActivity2()
        {
            InitializeComponent();
        }
    
        public string Message { get; set; }
    
        protected override ActivityExecutionStatus Execute(
            ActivityExecutionContext executionContext)
        {
            if (!string.IsNullOrEmpty(Message))
            {
                WriteLineService2 service = 
                    executionContext.GetService<WriteLineService2>();
    
                service.WriteLine(Message);
            }
    
            return ActivityExecutionStatus.Closed;
        }
    }
    

    This activity does the same job as the first one but delegates the actual work to this, simple, runtime service:

    public class WriteLineService2
    {
    
        public void WriteLine(string message)
        {
            Console.WriteLine(message);
        }
    }
    

    So lets create a few unit tests for this activity. The first test checks the behavior when the message is empty:

    /// <summary>
    ///A test for Execute
    ///</summary>
    [TestMethod()]
    [DeploymentItem("WFUnitTest1.exe")]
    [VerifyMocks()]
    public void ExecuteTestNoMessage()
    {
        WriteLineActivity2_Accessor target = new WriteLineActivity2_Accessor();
        ActivityExecutionContext executionContext = null; 
        ActivityExecutionStatus expected = ActivityExecutionStatus.Closed;
        ActivityExecutionStatus actual;
    
        actual = target.Execute(executionContext);
    
        Assert.AreEqual(expected, actual);
    }
    

    Still pretty simple because there is no interaction between the activity and the ActivityExecutionContext. But what happens the message is filled?

    /// <summary>
    ///A test for Execute
    ///</summary>
    [TestMethod()]
    [DeploymentItem("WFUnitTest1.exe")]
    [VerifyMocks()]
    public void ExecuteTestWithMessage()
    {
        WriteLineActivity2_Accessor target = new WriteLineActivity2_Accessor();
        ActivityExecutionStatus expected = ActivityExecutionStatus.Closed;
        ActivityExecutionStatus actual;
    
        ActivityExecutionContext executionContext = 
            RecorderManager.CreateMockedObject<ActivityExecutionContext>();
        WriteLineService2 service = 
            RecorderManager.CreateMockedObject<WriteLineService2>();
    
        using (RecordExpectations recorder = RecorderManager.StartRecording())
        {
            // Record methods here
            service.WriteLine(null);
    
            executionContext.GetService<WriteLineService2>();
            recorder.Return(service);
        }
    
        target.Message = "A message";
        actual = target.Execute(executionContext);
    
        Assert.AreEqual(expected, actual);
    }
    

    In this case we are mocking the ActivityExecutionContext using the RecorderManager.CreateMockedObject<ActivityExecutionContext>() statement. And this mock ActivityExecutionContext returns a mock WriteLineService2 when asked, pretty cool right? Big Smile

    Now I won't claim to know every mocking framework out there but as far as I know TypeMock is the only one that can to this and create proper unit test for a custom workflow activity.

    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

  • Runtime modification of workflow's

    Modifying already executing workflow's is a very powerful option. Now, as with most advanced options, not something you are going to need every day but certainly something to be aware of and use when the needed.

     

    First lets take a quick look at why we would want to modify an already running workflow in the first place.

    Consider the following scenario. You have long running, lets assume a year on average, and part of the workflow has to do with shipping products. And lets assume that the shipping is done by any one of a number op shippers and that this involves multiple steps, sort of a mini workflow itself, which are different for each shipper. Now you could add a big IfElseActivity with a branch for each possible shipper and be done with it. But now suppose the company starts doing business with a new shipper. So you go and add an branch to the IfElseActivity and implement the new shipper, no big deal. But all already running workflow's are based upon the original workflow without the new shipper, so they cannot use the new shipper. And if the new shipper is a lot cheaper that means you have to explain why all the additional shipping costs are made :-(

    So how to solve a problem like this?

    Well is we decide to add the shipping activity to the workflow at runtime, at the moment we actually need it, we get the flexibility we need. In fact it even makes the workflow simpler as we don't need to implement the big IfElseActivity. And in software development simple is good :-)

     

    So what do we actually need to change an executing WorkflowInstance? The class to start with is the WorkflowChanges. This WorkflowChanges object has a property named TransientWorkflow that lets us get to the actual workflow definition. Now we can use this to add the activities we want. In fact we can also change or remove activities as well.

    One easy way is to add a SequenceActivity as a placeholder where you want to add your new functionality like this:

    image

    Adding an extra activity is done from codeActivity3 (actually the second in the workflow) with the following code:

    WorkflowChanges changes = new WorkflowChanges(this);
    
    SequenceActivity activity = 
        (SequenceActivity)changes.TransientWorkflow.GetActivityByName("dynamicActivity");
    
    NewActivity newActivity = new NewActivity();
    newActivity.TextToPrint = "This is a dynamically added activity!";
    activity.Activities.Add(newActivity);
    
    ValidationErrorCollection validationErrors = changes.Validate();
    if (!validationErrors.HasErrors)
        try
        {
            ApplyWorkflowChanges(changes);
        }
        catch (WorkflowValidationFailedException ex)
        {
            validationErrors = ex.Errors;
        }
    
    foreach (var error in validationErrors)
        Console.WriteLine(error);
    

     

    The most important thing to notice is that the ApplyWorkflowChanges is the one that actually changes the running WorkflowInstance. And if the update fails for some reason an WorkflowValidationFailedException is thrown with a property Errors that is a collection of ValidationError objects.

    Before using the ApplyWorkflowChanges function we van validate the changes. This will catch some, but not all possible, errors so just validating and removing the try/catch is not recommended.

    Enjoy!

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

  • Validating workflow activity metadata

    There are several options for validating properties on a custom activity.

    Besides the possibility of using an ActivityValidator as described here we can also use the ValidationOptionAttribute. Now the ValidationOptionAttribute isn't useful all the time and only for a limited set of properties.

    First of all the limited set of properties. This will only work for dependency properties defined with DependencyPropertyOptions.Metadata. This makes them metadata and they cannot be changed at runtime, only design time.

    The second is the limitation in that is can only enforce a required field. No specific values or even an error tekst.

    So using this is a little weird to. With the ValidationOptionAttribute being an attribite the first thing that comes to mind is decorating the property with it. Well that doesn't seem to do anything Sad.

    [ValidationOption(ValidationOption.Required)]
    public string MyMetadataProperty
    {
        get
        {
            return ((string)(base.GetValue(ValidatedActivity.MyMetadataPropertyProperty)));
        }
        set
        {
            base.SetValue(ValidatedActivity.MyMetadataPropertyProperty, value);
        }
    }
    

    No in fact what you need to do is add it to the DependencyProperty constructor like this:

    public static DependencyPropertyMyMetadataPropertyProperty =
        DependencyProperty.Register("MyMetadataProperty",
        typeof(string),
        typeof(ValidatedActivity),
        newPropertyMetadata(DependencyPropertyOptions.Metadata,
            newAttribute[] { newValidationOptionAttribute ValidationOption.Required) }));

    Not very inuitive at all.

    I guess the only benefit of this is not having to include a ActivityValidator but as far as I am concerned this is not a big benefit.

    Enjoy!

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

  • Validating custom activities in the Workflow designer

    Create a derived class from ActivityValidator and add it to your custom activity using the ActivityValidatorAttribute. For example:

    [ActivityValidator(typeof(ValidatedActivityValidator))]
    public partial class ValidatedActivity : SequenceActivity
    {
    }

    The validator class now look like this:

    class ValidatedActivityValidator : ActivityValidator
    {
        public override ValidationErrorCollection Validate(ValidationManager manager, object obj)
        {
            ValidationErrorCollection result = base.Validate(manager, obj);
    
            ValidatedActivity activity = obj as ValidatedActivity;
            if (activity != null && activity.Parent != null)
            {
                if (activity.AValueBetween1And250 < 1)
                    result.Add(
                        new ValidationError("The value is to small", 
                            102, false, "AValueBetween1And250"));
    
                if (activity.AValueBetween1And250 > 250)
                    result.Add(
                        new ValidationError("The value is to big", 
                            103, true, "AValueBetween1And250"));
            }
    
            return result;
        }
    }
    

    Note that the object parameter (obj) is the activity to be validated.

    The ActivityValidator type contains two virtual function Validate and ValidateProperties. It doesn't really matter which you use as the Validate actually calls the ValidateProperties function. This means they are both called at the same time unless you change the behavior by not calling the base.Validate() function.

    To indicate errors or warnings you can add a ValidationError object to the ValidationErrorCollection to be returned. Adding the property name makes life easy on the developer using your activity because it allows the designer to automatically select the property by double clicking the error.

    Another thing to keep in mind is that the validator will be called when the activity itself is compiles. To see if this is the case check the activity Parent class which will be null.

    The validator is also called when the workflow is executed allowing for runtime property checking.

    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

  • 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!

More Posts Next page »

<July 2008>
SuMoTuWeThFrSa
293012345
6789101112
13141516171819
202122232425