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

with no comments
Filed under: , , ,

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

with 1 comment(s)
Filed under: , , , ,

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

with no comments
Filed under: , , , ,

I just heard about a pretty nasty "bug" with ClickOnce. Well it isn't an actual bug but still a nasty problem to run into.

The problem is actually with the certificate used to sign a ClickOnce installer. When you create a new ClickOnce installer Visual Studio will automatically generate the required certificate for you. And the intention was that this was only a temporary certificate and that a developer would replace it with a real one. Now it turns out that most people, including me, think there is only a single advantage to using a real and that is the claim that the certificate is from an untrusted publisher. Well that is only a warning most people ignore anyway so why bother with the real certificate.

Well it turns out there is a second, much larger, disadvantage to using the generated certificate. And that is the fact that it is only valid for a single year. And after that year the ClickOnce installation will stop downloading new updates.

More information, including a workaround, can be found here.

Thanks to Eric Knox and Cory Smith for pointing this out.

Enjoy!

with 3 comment(s)
Filed under: , , ,

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

with 4 comment(s)
Filed under: , , ,

One of the nice new features in Visual Basic 9 are lambda expressions. They where actually added as one of the underlying pieces for LINQ. But besides enabling LINQ you can use them in all sorts of places. One of the places they really help me is in places where I previously needed to declare a delegate and use that.

Take for example the following code where I use the ThreadPool to do some work:

Private Sub btnStart_Click() Handles btnStart.Click
    ThreadPool.QueueUserWorkItem(AddressOf DoWork)
End Sub

Private Sub DoWork(ByVal state As Object)

    For index As Integer = 1 To 100
        SetProgressLabel(index)
        Thread.Sleep(100)
    Next
End Sub

The SetProgressLabel function reports the progress to the user by setting the text on a label. As the work is done in a background thread I need to use Invoke to set the text property.

Before VB9 I would have to implement the SetProgressLabel like this:

Delegate Sub SetProgressLabelDelegate(ByVal percentage As Integer)
Private Sub SetProgressLabel(ByVal percentage As Integer)
    If lblStatus.InvokeRequired Then
        lblStatus.Invoke( _
            New SetProgressLabelDelegate( _
                AddressOf SetProgressLabel), _
                percentage)
    Else
        lblStatus.Text = percentage.ToString() + " %"
    End If
End Sub

First I would need to declare a delegate with the right signature, create an object passing in the address of the function to call and the parameters. Even though I declared the delegate with its parameters the Invoke is not aware of these and I can add make errors with the parameters which don't show until runtime.

So here is a better way to do this in VB9:

Private Function SetProgressLabel(ByVal percentage As Integer) As Object
    If lblStatus.InvokeRequired Then
        lblStatus.Invoke(Function() SetProgressLabel(percentage))
    Else
        lblStatus.Text = percentage.ToString() + " %"
    End If
    Return Nothing
End Function

This time I use an lambda expression to call the SetProgressLabel function. No need for the extra delegate and I get full compile time checking Smile. In fact the only drawback is that I need to declare SetProgressLabel as a function instead of a sub as VB forces a lambda expression to return a value. This makes working with lambda expressions somewhat less powerful than the C# implementation Sad Lets just hope that problem is rectified soon.

 

image

One neat thing is that you don't actually need to target the 3.5 framework to use lambda expressions. The previous sample works just fine targeting the .NET 2.0 runtime as long as VB9 is used. The reason for this is that the lambda expression syntax is actually just a compiler trick and it creates an delegate for us behind the scenes. Pretty cool!

Enjoy!

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

with 1 comment(s)
Filed under: , , ,

Last week was pretty interesting and busy Smile

First we had the DevDays on Thursday and Friday. I was there to answer questions from the audience. I didn't get too many questions so I could take my time and actually spend around 90 minutes answering questions from one and around 1 hour with another. Add some more smaller questions and it kept me busy but still left time to check a few sessions.

One notable session I watched was by Neno Loje. It was the first time I saw him speak and he did a session about advanced ClickOnce. He did an excellent job. In general I enjoyed the two days and have to congratulate "mister DevDays" Arie Leeuwesteijn on a job well done Smile

Besides the day program there was a new evening program on the Thursday. This was a lighter program with fun session. The best session was IT Boxing with Hassan Fadili as a referee. He is actually a real referee on his spare time and he did an excellent job! Smile

IMAGE_071x

So Friday evening I went to The Hague Jazz. I was kind of tired by then but it was still great fun. I forgot to take my DSLR so I shot a few, lousy quality, pictures with my phone.

IMAGE_085

Enjoy!

with no comments
Filed under: , , ,

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

with no comments
Filed under: , , , ,

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

with no comments
Filed under: , , ,

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

with no comments
Filed under: , , ,

The current version of SQL Server Compact has no support for 64 bits computing. While this may seem a problem if you want to deploy you application on a 64 bits version of Windows it really isn't most of the time. In fact if you change your assembly from the default AnyCPU to x86 compatible it will work just fine using Windows On Windows (WOW).

image

 

But even better there will soon be a native 64bits version of SQL Compact. This isn't in the beta version that is distibrited with Visual Studio 2008 SP1 beta but Steve Lasker promises it will be in the final version when that releases.

Another nice feature that will be added is support for LINQ to Entities. And that includes the designer support we now lack with LINQ to SQL.

More info about the change can be found in this blog post by Steve Lasker.

Enjoy!

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

with no comments
Filed under: , , , ,

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

with 2 comment(s)
Filed under: , , ,

 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! 

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

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!

with no comments
Filed under: , , , ,

 When I tried I received the following very helpful System.ServiceModel.ProtocolException with Message="The remote server returned an unexpected response: (407) Proxy Authentication Required ( The ISA Server requires authorization to fulfill the request. Access to the Web Proxy filter is denied.  )."

And this in turn contained an inner exception of type System.Net.WebException and Message="The remote server returned an error: (407) Proxy Authentication Required.".

Now I cannot find any way to add the required authentication but it turns out that setting the wsHttpBinding useDefaultWebProxy to false works just fine Smile

Enjoy! 

with no comments
Filed under: , , , ,

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

with no comments
Filed under: , , , ,

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

with no comments
Filed under: , , , ,

The recent release of the .NET 3.5 and Visual Studio 2008 beta Service Pack 1 has started an interesting discussion over on Microsoft Watch. Joe Wilcox writes about how some of these posts seem predated and about seeing some blog post before being published, under embargo, just as previously happened with press releases. he raises an interesting question: are blog posts degraded to just just another marketing outlet and, possibly, not even written by the people under whose name they are published?

There may well be some truth in this as blog are an important communication feature. And why would a pr or marketing person ignore such an obvious opportunity. So I am sure that some blog posts are really just PR. But than again I also believe that the vast majority is real and, other that the writers conviction that MS is the best thing since sliced bread, is trying to be objective.

Read the full article by Joe Wilcox here.

Enjoy!

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