Tuesday, March 04, 2008

Ever since I first got into workflow foundation, I've taken a fancy to statemachines. Once you wrap your head around them, they are a natural fit for most business processes.
The main problem everybody seems to be having with workflow though, is the versioning story. There is none!
That might be a bit harsh, you can certainly version your workflows, but to tell you the truth, you will be in a world of hurt.

The sample solution can be downloaded at the end of the post. It contains two workflows and a console application that you can play with.

Why is this updating so tough?
The workflow template is serialized to the persistence store. Any change in the workflow (adding or removing an activity) will make it impossible to deserialize the workflow again. It's serialized as a blob, so no easy transformation. I've written extensively about problems surrounding updating workflows here.

Your options pretty much exist of running side by side (which gives you a world of even more hurt, because now you have your data exchange services to version as well, and the activity library you have built) or use dynamic changes to alter the structure.
The latter being your best bet, but so much work that it takes away from the flexibility and speed of development that workflow brings to the table.

In my previous post I concluded that you would be best of just destroying your old workflow and create a new one. I stand by that! Today I was finally able to revisit the problem, and I hacked together a solution that might be interesting to people.

This solution has the following restriction:

It will only work for statemachines, that are waiting inside a state for an eventdriven activity, not inside an eventdriven activity. In other words: it is only able to update workflows that have entered a state and started waiting, not ones that have executed a few activities and is now waiting on some other input within a sequence.

Luckily for me, that is no problem at all, and it should not be a problem for you either. Statemachines should be modeled such that waiting happens when entered in a state, never inside a sequence. You can model waits inside a sequence, but I would suggest you make the delays short (minutes, as opposed to days/months/years).

My goal here is to be able to do a relatively easy update, where I have control over how I update (what to do with state etc.) and get my delays initialized to the correct timeouts again. So, in workflow1 I had a delay of 11 months, with 8 months left. When I start workflow2 and update, I need to have 8 months left again, and not 11.

Getting the delays right is the hard part.

I use some nice reflection to get to the actual type of a workflow instance. I described how to do that here. However, I was being silly. It's much easier:

            Workflow1 oldWF = workflowRuntime.GetRootActivity(instance) as Workflow1;

Made possible by these extensions:

    public static class WFExtensions
    {
        public static object GetExecutor(this WorkflowRuntime workflowRuntime, WorkflowInstance instance)
        {
            return workflowRuntime.GetType().InvokeMember(
                "Load", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.InvokeMethod, null, workflowRuntime,
                new object[] { instance.InstanceId, null, instance });
        }
        public static object GetRootActivity(this WorkflowRuntime workflowRuntime, WorkflowInstance instance)
        {
            object executor = workflowRuntime.GetExecutor(instance);
            return executor.GetType().GetField("rootActivity",
                    BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.GetField).GetValue(executor) as CompositeActivity;
        }
    }

So, here goes:

  1. Get to your old workflow instance. In my sample I use types Workflow1 and Workflow2.
                WorkflowInstance instance = runtime.GetWorkflow(g);
    
                WorkflowRuntime workflowRuntime = runtime;
    
                 
                Workflow1 oldWF = workflowRuntime.GetRootActivity(instance) as Workflow1;
    
                if (oldWF == null)
    
                    return;
    
                object executor = workflowRuntime.GetExecutor(instance);
    
                instance.Suspend("asdf");   // need not to unload, otherwise the database record would be unlocked

    I suspend the workflow, so it does not get into the way, but I can not unload, or worse: terminate. That would kill the record in the database.

  2. Create a new workflow, of your desired type, and copy the workflowInstanceID to it:
                // get a handle to the instanceid property
    
                DependencyProperty instanceidDP = (DependencyProperty)executor.GetType().GetField("WorkflowInstanceIdProperty",
    
                    BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance).GetValue(executor);
    
                // create new wf2, not starting it yet
    
                WorkflowInstance newWFInstance = workflowRuntime.CreateWorkflow(typeof(Workflow2));
    
                Workflow2 newWF = workflowRuntime.GetRootActivity(newWFInstance) as Workflow2;
    
                // copy the guid
    
                newWF.SetValue(instanceidDP, instance.InstanceId);
  3. Build up a list of activities that are on timers and remember their name and when they expire:
                Dictionary<string, DateTime> activitiesExpireList = new Dictionary<string, DateTime>();
    
                TimerEventSubscriptionCollection subscriptions = ((TimerEventSubscriptionCollection)
    
                    oldWF.GetValue(TimerEventSubscriptionCollection.TimerCollectionProperty));
    
                foreach (TimerEventSubscription subscription in subscriptions)
    
                {
    
                    // find out what activity was subscribed
    
                    var x = from queueInfo in instance.GetWorkflowQueueData()
    
                            where subscription.QueueName.GetType().Equals(queueInfo.QueueName.GetType())
    
                            where subscription.QueueName.CompareTo(queueInfo.QueueName) == 0
    
                            select new { ExpiresAt = subscription.ExpiresAt, Activities = queueInfo.SubscribedActivityNames };
    
                    foreach (var combination in x)
    
                    {
    
                        foreach (string activityname in combination.Activities)
    
                        {
    
                            activitiesExpireList.Add(activityname, combination.ExpiresAt);
    
                        }
    
                    }
    
                }

    The weird part being the fact that the queue names are mostly guids (for delays atleast).

  4. Call a method on your new type. See how cool it is we can actually communicate this way with it, instead of having to go through communication services!!
                // allow new workflow to read information from old workflow to init itself.
    
                newWF.Update(oldWF, instance, activitiesExpireList);
  5. Copy the new workflow to the rootactivity of our executor. Ouch.. yeah.. don't worry.
                // copy the new rootactivity to the executor
    
                executor.GetType().GetField("rootActivity",
    
                    BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.GetField).SetValue(executor, newWF);
    
  6. Last bits:
                // start it up
    
                newWFInstance.Start();
    
                newWFInstance.Unload(); // overwrites current record in persistence store
    
                instance.Abort();   // kills of our original
    
                newWFInstance = runtime.GetWorkflow(g);
    
                StateMachineWorkflowInstance statemachine = new StateMachineWorkflowInstance(runtime, g);
    
                statemachine.SetState(newWF.DefineNewStateAfterUpdate(oldWF.CurrentStateName));
    
                // still need to unload or unload the runtime to get all timers correctly!
    
                Console.WriteLine("updated" + newWFInstance.InstanceId);

    You can see me starting and unloading, then killing our old instance. Finally I am trying to be smart by using the statemachineworkflowinstance to do a transition to a new state on the new workflow. The newstate can be determined by the new workflow (who has knowledge of these things) but is usually the same as in your old workflow. (This was build so that you could rename a state).

  7. That's it. In the Workflow2 class, I have an update method, which will set a boolean to true. The initialization activity will look for it in an if/else and not do anything if it is set to true. All the delays in the new workflow have an initTimeout method like so:
            private void initTimeout(object sender, EventArgs e)
    
            {
    
                DelayActivity delay = (DelayActivity)sender;
    
                if (activitiesExpireList.ContainsKey(delay.Name))
    
                {
    
                    delay.TimeoutDuration = activitiesExpireList[delay.Name].Subtract(DateTime.Now.ToUniversalTime());
    
                    activitiesExpireList.Remove(delay.Name);
    
                }
    
            }
     

I have uploaded the complete sample here.

When you run it, you can press 'c' to create a new workflow of type Workflow1. Then you can press 'u' and paste in the guid of the workflow just created. It will update the workflow. Pressing 'b' will break and unload the workflow.
Your created workflow has this state:

image

Where the delay is 40 seconds. Workflow2 has the same state, but has a delay of only 10 seconds.

As a test you can see that after updating, you will have a workflow2 running (there is another activity present that will print out debug information). The delay was set correctly.

Obviously, you might want to deal with the delays your own way. Because you have all the information in your workflow codebehind, you can think of your own rules on how the delay timeouts should be set.

Realize that touching the internals of WF like this is not what Microsoft envisioned and should be done with care.

Have fun, and let me know what you think.

kick it on DotNetKicks.com

Tuesday, March 04, 2008 11:56:35 PM (Romance Standard Time, UTC+01:00)  #    Comments [4]  |  Trackback
 Thursday, February 28, 2008

Finally wrapping up.

This is the seventh of a series about using Workflow Foundation to control your UI Logic in a WPF application. The full table of contents:

Recap

In the first post, the complete solution was presented. I am presenting a solution to use workflow as the controller part in your MVC inspired WPF application. It is inspired on the thought that you do not need complex frameworks, because WPF already gives you great power (routed eventing, resources). So, no IOC is used, no event aggregator etcetera: it's taken care of by WPF and WF, a natural fit.
The solution is very decoupled and I feel it's a great advantage to be able to visual your control logic.
In the previous post we looked at injecting objects and retrieving.

Broadcasting

CAB (and other systems) uses an event aggregator to publish events. Subscribers (other controllers) can subscribe to a specific 'topic' using a string to identify it. This works well, but does mean yet another communication method is introduced.

Since every workflow/controller is added to the workflow runtime, we could easily ask for all the loaded workflows and send these a message. However, since all adapters subscribe to a weakevent manager to manage communication, I thought I'd stick to this pattern.

The BroadcastCommandMessage was created for the adapter to react on and check if it's controller is interested in it. If it is, the message is transformed to a command message and send to the controller.

I have not yet build an activity to do this.
The Bankteller sample has a CustomerQueueController. When it gets focus or loses focus, it wants to tell 'someone' (just someone who will listen) that it has a popular command to (un)register. The BanktellerLogic controller will use this information to put the command in a list and the view decides to make a menu item for it. You see, I do not believe that the CustomerQueueController should be able to decide that a menu is to be created out of it. He just wants to let the world know about a command.

        private void RegisterCommands(object sender, EventArgs e)
        {
            commandSvc.SendBroadcast(
                new BroadcastCommandMessage(this.WorkflowInstanceId, "RegisterPopularCommand",
                   CustomerQueueInteractions.AcceptNextCustomerFromQueue));
        }
        private void UnRegisterCommands(object sender, EventArgs e)
        {
            commandSvc.SendBroadcast(
                new BroadcastCommandMessage(this.WorkflowInstanceId, "UnRegisterPopularCommand",
                   CustomerQueueInteractions.AcceptNextCustomerFromQueue));
         }

 

That concludes this series for now.

I hope you enjoyed it. I hope you take away the feeling that it is pretty easy to build a MVC system using WPF and WF and that the presented solution is about as decoupled as it gets.

Thursday, February 28, 2008 2:16:34 AM (Romance Standard Time, UTC+01:00)  #    Comments [0]  |  Trackback
 Tuesday, February 26, 2008

Hole crap, this is starting to be a long series!!

This is the seventh of a series about using Workflow Foundation to control your UI Logic in a WPF application. The full table of contents:

Recap

In the first post, the complete solution was presented. I am presenting a solution to use workflow as the controller part in your MVC inspired WPF application. It is inspired on the thought that you do not need complex frameworks, because WPF already gives you great power (routed eventing, resources). So, no IOC is used, no event aggregator etcetera: it's taken care of by WPF and WF, a natural fit.
The solution is very decoupled and I feel it's a great advantage to be able to visual your control logic.
In the previous post, we talked about injecting controllers to manage specific parts of your screen, very cab-alike.

IOC - Inversion of Control

Inversion of Control is a pattern that tries to turn everything upside down when it comes to getting to dependencies. Let's say you have a class, and to do its work it needs a helperclass (maybe a communication service). Instead of having your class create that service explicitly, we can have your class just ask for it and have someone else supply it. This is where Dependency Injection comes from: just state what a class needs to work and have a container 'inject' those dependencies.
Doing it this way makes for a more maintainable application and allows you to better manage the lifetime of helperclasses and services. You might want to get back the same service instance, all the time!

Using a MVC approach to construct your application, you might feel the same need. Maybe you are building an application that allows editing of pieces of information of a customer, for instance, her details, her address, etc.
These pieces are implemented in different views. All the views that belong to that one customer, should use the same instance of the 'customer' object.

Inject and retrieve object into resources - activity

In this system, that is easily done, although possibly more explicitly than many great IOC containers (Windsor, Spring.Net, StructureMap) would like it.

Just have one controller create the object and inject it inside of his resources. Because of the way retrieving resources work, all the controllers that live 'below' this controller (are nested within it), will be able to retrieve it.

image

Here I have dragged in the 'InjectObjectAsResource'Activity, and have bound a public field on my workflow to the 'Service' property of the activity. Well, maybe Service is a bad name, but I just expect you to use it with services most of the time. Also, the activity might better have been called InjectInstanceAsResource, but I guess I didn't.
I used a type as resourcekey this time, instead of a string.

I bet you can figure out how the retrieve activity works ;-)

Tip: since the activity does not know what type of object you want to create, if you let the binding introduce the field or property to your code, it will be typed as object. Just change that to your own type.

The retrieve will work for all controllers that can reach the resource dictionary of the controller that did the inserting. So, that is equivalent to the CAB-term: 'child workitem'.
If you have the need to also be able to share on a global level, just make the inserting happen on the application resources, instead of the adapter resources. Can not be too hard.

Conclusion

I think this mechanism illustrates the way you can use WPF to meet most of your CAB needs. I use it here from a workflow, but that has nothing to do with the core-concept.
I find that the explicit visual call to inject or retrieve, without having to write code to do so, could be beneficial when building systems in a team. There is no need to guess where an object comes from, it is all very much in your face.

Tuesday, February 26, 2008 1:00:22 PM (Romance Standard Time, UTC+01:00)  #    Comments [0]  |  Trackback
 Monday, February 25, 2008

This is the sixth of a series about using Workflow Foundation to control your UI Logic in a WPF application. The full table of contents:

Recap

In the first post, the complete solution was presented. I am presenting a solution to use workflow as the controller part in your MVC inspired WPF application. It is inspired on the thought that you do not need complex frameworks, because WPF already gives you great power (routed eventing, resources). So, no IOC is used, no event aggregator etcetera: it's taken care of by WPF and WF, a natural fit.
The solution is very decoupled and I feel it's a great advantage to be able to visual your control logic.
In the previous post, we talked about decoupling through commands.

This time, we will look at how to inject a controller into a subview

The InjectControllerAsDataTemplate activity

It's all very nice and dandy to have one controller manage it's mainview, but what happens if part of that mainview is different, and should be managed by a completely different controller?

Let's look at ModuleView in the BankTeller sample:

<UserControl x:Class="BankTellerViews.ModuleView"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
        <StackPanel Orientation="Horizontal">
        <StackPanel Orientation="Vertical">
            <ContentPresenter ContentTemplate="{DynamicResource userinfo}" />
            <ContentPresenter ContentTemplate="{DynamicResource customerlist}"/>
        </StackPanel>
        <StackPanel Orientation="Vertical">
            <ContentPresenter ContentTemplate="{DynamicResource customerinfo}" />
            <ContentPresenter ContentTemplate="{DynamicResource customersummary}" />
        </StackPanel>
    </StackPanel>
</UserControl>

You can see that ModuleView really only determines the way this screen is build up, but the individual pieces are left empty.

When we open up the ModuleLogic controller, we wish to inject controllers with the same names that we used here:

image

What happens exactly? Well, you selected a controller type, through the convenient typebrowser, and set a specific resource key (in this case, we used a string: userinfo). The adapter is notified by this adapter to do something with it. It will create a datatemplate in code, and just set it as a resource (or replace, if it already exists).

This means that a deeply nested view could easily define a contentpresenter and a higher level controller could inject a controller for it.

Monday, February 25, 2008 4:12:47 PM (Romance Standard Time, UTC+01:00)  #    Comments [0]  |  Trackback

This is the fifth of a series about using Workflow Foundation to control your UI Logic in a WPF application. The full table of contents:

  • Workflow as controller: Introducing <M,V,C> where M: ViewModel, V : WPF, C : WF
  • Part II, starting the application, and the adapter
  • Intermezzo: new sample application
  • Part III, your first view
  • Part IV, decoupling view from controller
  • Part V, marshalling commands from WPF to WF
  • Part VI, Injecting a controller in a subview / workspace
  • Part VII, IOC on the cheap: injecting and retrieving objects
  • Part VIII, Broadcasting for all to see

    Whoops, I guess I was a bit over enthusiastic in the previous post, because I already explained the hooking mechanism in enough detail.

    It boils down to registering the adapter as global commandhandlers and when a command reaches it, create a commandMessage and send that to the workflow.

  • Monday, February 25, 2008 3:58:49 PM (Romance Standard Time, UTC+01:00)  #    Comments [0]  |  Trackback
     Friday, February 22, 2008

    This is the fourth of a series about using Workflow Foundation to control your UI Logic in a WPF application. The full table of contents:

  • Workflow as controller: Introducing <M,V,C> where M: ViewModel, V : WPF, C : WF
  • Part II, starting the application, and the adapter
  • Intermezzo: new sample application
  • Part III, your first view
  • Part IV, decoupling view from controller
  • Part V, marshalling commands from WPF to WF
  • Part VI, Injecting a controller in a subview / workspace
  • Part VII, IOC on the cheap: injecting and retrieving objects
  • Part VIII, Broadcasting for all to see
    Recap

    In the first post, the complete solution was presented. I am presenting a solution to use workflow as the controller part in your MVC inspired WPF application. It is inspired on the thought that you do not need complex frameworks, because WPF already gives you great power (routed eventing, resources). So, no IOC is used, no event aggregator etcetera: it's taken care of by WPF and WF, a natural fit.
    The solution is very decoupled and I feel it's a great advantage to be able to visual your control logic.
    In the previous post, I talked about the various ways to show a view, and actually already talked about the decoupling mechanism: commands.

  • I'm very lucky to have received some good comments from Wekemf about tight coupling. I urge you to read those comments and maybe chime in.

    In this post I will not return on that subject, but will quickly address two important activities that help in configuring your system quickly: the SetMainContent activity and the SetDataContext activity and sending WPF commands to WF's HandleCommand.

    SetMainContent activity

    A controller adapter is a normal WPF contentcontrol. It has the job to participate in the visual tree on behalf of our workflow controller class. To actually attach a view to it, we need to set it's content property.

    As shown in the previous post, you can just set on in xaml yourself, but it's more logical to let the workflow decide on the view. Ofcourse, the best approach is totally up to you.

    I usually use the stateinitalization activity to set up a view for us. I drag in the SetMainContent activity, and choose a type from the references assemblies.
    If it weren't for this step, the controller assembly would not need a reference to the view assembly at all. I found it very cool to be able to select a type with the typebrowser and just have it show up.

    The typebrowser is located in an assembly I have put in the externalAssemblies folder. It is a project, not started by me. The code did not work when I got my hands on it, but I managed to fix it by using a hammer. Check out this post to learn more about this great design-time experience!

    If you have a business need to decouple even further, you would need to adjust the SetMainContent activity, and instead of sending a real Type, send a string key or whatever. Then you would create some mapping functionality to map that key to the actual view.

    When the adapter get's notified by the SetMainContentMessage that it needs to set a content, it will just create the view (using reflection) and place it as it's own content.

    SetDataContext activity

    I do not like MVP at all, where the presenter talks back to the view directly (using an interface or something). I feel it's way too 'pushy' and way too much work. I believe in databinding (especially WPF bindings, I think Microsoft got it right this time). You view should just bind to your domain objects. In many cases, it's better to create a wrapper for the domainobjects, so you have the opportunity to supply some shortcut properties or view specific stuff: you might have a list of products, and you want the view to display the sum of the prices. That is a great opportunity for the viewmodel to expose a 'Sum' property that the view can simply bind to.

    The object that is used as a ViewModel should live with the controller who will be able to communicate with it.
    I usually create an internal public class, simply called ViewModel and have the controller inject that class with domain objects.

    The Set DataContext activity is very similar to the SetMainContent activity, in that it let's the adapter know it has to set a datacontext on itself.
    You configure the SetDataContext activity simply by choosing a field or property of your controller.

    In small sample applications, I have used the 'invoking' event, to hook up some code that actually initializes the ViewModel object.

    Sending WPF commands to the Workflow: HandleCommandActivity

    The HandleCommandActivity is really what makes using the solution so easy. I have blogged about it already extensively, and I will just summarize here:

    Workflow has a difficult communication story. You need to define your incoming and outgoing calls in an ExternalDataExchangeService. Then you have to hook up events in your workflow to listen to incoming calls/events. It is not possible to listen to the same events in two different states, without using the very difficult CorrelationTechnique.

    This is not necessary for our usage. I have created the HandleCommand activity to just listen to a queue with a specific name. That name is defined by the command we are listening to. So, if you want your workflow to react when you send it the string 'workflowRules', you would just drag in the HandleCommand and configure the Command property to read 'workflowRules'. No need to setup a special event for it.

    The commandService class has a PostCommand method, that you can call to put a message on the queue. That's all there is to it.

    So, when we receive a WPF command, we cast it to a RoutedUI command. The commandname is used to form a SimpleCommandMessage which can be used as input to the PostCommand method.

      1         #region command sinks
      2         private void CmdExecuted(object sender, ExecutedRoutedEventArgs e)
      3         {
      4             string commandname = (e.Command as RoutedUICommand).Name;
      5
      6             PostCommand(commandname, e.Parameter);
      7
      8         }
      9
    10         private void PostCommand(string commandname, object Parameter)
    11         {
    12             if (implementedCommands.Contains(commandname))
    13             {
    14                 commandSvc.PostCommand(new SimpleCommandMessage(instance.InstanceId, commandname, Parameter));
    15             }
    16         }
    17
    18         private void CmdCanExecute(object sender, CanExecuteRoutedEventArgs e)
    19         {
    20             string commandName = (e.Command as RoutedUICommand).Name;
    21
    22             if (implementedCommands.Contains(commandName))
    23             {
    24                 e.CanExecute = commandSvc.CanExecute(new SimpleCommandMessage(instance.InstanceId, commandName));
    25             }
    26         }
    27
    28         #endregion

    As you can see, I first check if the workflow even implements such a command. If not, it would be too expensive to send it to the workflow.
    Also, check out the CmdCanExecute method. It actually makes it possible for the workflow to put rules on the HandleCommand activity that are used to figure out if a command can be executed. For instance, if you are not authorized to do something, the command was never in CanExecute, so the button that hooks up to it was always dimmed!

    I hope that clears up some questions. Let me know what you think!

    Friday, February 22, 2008 11:24:20 AM (Romance Standard Time, UTC+01:00)  #    Comments [0]  |  Trackback
     Thursday, February 21, 2008

    I got a mail yesterday from a German student asking about the future of workflow and my thoughts on it. I will share the thread. It was written in a hurry, so take it for what it is. Leave a comment to give him another view point.

    Read from bottom to top.

    -----

    my reply:

    What you are describing does indeed sound like a typical WF application, and it is absolutely suitable for that.

    Custom activities: don't be afraid. Just create one that is a wrapper around your huge ole-api. Creating an activity is little more than deriving from Activity and overriding the execute method.
    Put some properties on there and of you go.
    Or create multiple activities that do different things to the ole object.

    It sounds to me that you want to re-host the workflow designer. That is certainly do-able and there is a project from some1 you can download that actually did that. However, it was in need of more debugging. I don't have the url here. Sorry.

    What WF is not, is a magical system that requires no development. It is really meant to be a foundation, which a developer uses and builds upon to create a system that really suits the client wishes. So that means, configuring it, creating external data exchange services and building custom activities. Only then will you create a system that your client can use in the way you described. You need to mold it to behave like you want.

    In our case, it was definitely the developers that created the workflows. Best we could hope for was that business analysts could understand it (and they did). However, I've always felt it was possible to create a system that they could use directly.

    Success!
    Ruurd

    -----Original Message-----
    From: Sven
    Sent: woensdag 20 februari 2008 20:56
    To: me
    Subject: Re: Some questions about the future of WF

    Hi Ruurd,

    thank you very much for your in-depth statement! I had not expected this detailed level ;-)

    Actually as a part of my project I have to evaluate if WWF fits into an existing CRM Application.
    It should be possible for solution partners (customizing the application for their customers) without in-depth programming knowledge for example to "wire together" some custom activites to visually build for example the processing of an incoming mail, a little workflow for some little approval process (like you press a button inside the application on an address form, the workflow gets some field from the current record, decides based on the field which e-mail model to use, sends the email and finally writes some information to the same record, like "e-mail XY sent") or things like that... (sounds like a classic
    Flow-WF)
    But their could be use for some "state machines", too. Like there is a WF-Service running and dispatching incoming mails to different employees...

    Is WWF suitable for this ? These things could be done today in the application by coding some huge VB-Scripts, there is a huge OLE-API in the application...

    What I missed is a "CustomOLEActivity" to call whatever function in an application with OLE-API (there are a lot on the market) and to simply return some values...
    (the ExternalDataExchange/CommunicationActivity with wca.exe-Tool-way looks like beeing very complicated - at least if you have to build a CustomActivity for a huge OLE-API, or have I missed something out ?)

    On my "first look" the designer looked a bit complicated (even for people with some advanced knowledge, i have to target not the computer dummies, but also not the programmers on the other hand, some level "between", lets say "System Administrators"), but perhaps you can give me from your own experiance some hints in which direction I have to go for this...
    (who is editing the workflows in your big project?)

    Implementing everything "from scratch" looks like an even bigger effort... (would be the other choice...)

    Thanks a lot for your help and guidance !

    Bye
    Sven

    my reply:
    >
    > Basically, I see quite a few problems surrounding WF. It is very
    > shielded, the designer is not very good still and there is no good
    > update strategy (updating long running persisted workflows to new
    > versions). I think that last issue is one of the biggest problems it
    > has, although it hasn't gotten much publicity.
    >
    > However, as a platform, it does what it should do very well. They are
    > going to use it as the biztalk workflow engine and are already using
    > it as the human workflow engine for sharepoint.
    >
    > I feel we are moving toward an industry that needs to mature (the IT
    > development industry I mean). It is looking for DSL's and other ways
    > to make developing software a more manageable and predictable process.
    > Workflow has a definite place in that eco-system, where you can
    > visualize the flow of your program. This means you have an artifact
    > that will actually help a developer communicate with a business analyst or a client.
    >
    > To be concrete:
    >
    > So, why do I think developers have been slow to take it up: a
    > difficult programming model and some serious issues that are not well understood yet.
    > It is a radically different approach to building software, and it
    > takes time for ppl to feel confident with it.
    >
    > Is there a future: I say _yes_. If you understand the problems of
    > todays WF framework, you can already build great things, and I've
    > heard about some of the stuff that Microsoft is doing on the next
    > version, which will alleviate some big problems. Since we need this
    > kind of technology to build better software, there is definitely a future for it.
    >
    > Is it already used in the industry: Well, I have used it, but I have
    > yet to hear of big projects using it. Then again, Biztalk is used
    > extensively and the WF engine is every bit as powerful. (rules engine maybe slightly less).
    > Sorry, no example possible...)
    >
    > I do not think it will disappear.
    >
    > Kind Regards,
    > Ruurd Boeke
    >
    > -----Original Message-----
    > From: Sven
    > Sent: woensdag 20 februari 2008 19:48
    > To: me
    > Subject: Some questions about the future of WF
    >
    > Hello!
    >
    > I'm a computer student from the university of applied sciences of
    > Emden, Germany.
    >
    > Actually I'm working on a project dealing with the Windows Workflow
    > Foundation.
    >
    > As it was introduced one and a half years ago, but I see not so much
    > implementations or books about it, I wonder why it has been adopted so
    > slowly by the developpers.
    >
    > What do you think about this? (just some thought will be helpful for
    > me!) Is there a "future" ? Or will this stay an "Microsoft Internal" - Affair ?
    >
    > Is this already used in the industry ? Where ?
    > (If you could give me some examples from your experience this would be
    > very helpful for my work)
    >
    > Is this really a technology to build on or might it disappear slowly
    > like other "cool" stuff in the past ?
    >
    > Thank you very much in advance for any hint!
    >
    > Sincerely,
    > Sven

    Thursday, February 21, 2008 5:38:13 PM (Romance Standard Time, UTC+01:00)  #    Comments [0]  |  Trackback

    This is the third of a series about using Workflow Foundation to control your UI Logic in a WPF application. The full table of contents:

  • Workflow as controller: Introducing <M,V,C> where M: ViewModel, V : WPF, C : WF
  • Part II, starting the application, and the adapter
  • Intermezzo: new sample application
  • Part III, your first view
  • Part IV, decoupling view from controller
  • Part V, marshalling commands from WPF to WF
  • Part VI, Injecting a controller in a subview / workspace
  • Part VII, IOC on the cheap: injecting and retrieving objects
  • Part VIII, Broadcasting for all to see
  • Recap

    In the first post, the complete solution was presented. I am presenting a solution to use workflow as the controller part in your MVC inspired WPF application. It is inspired on the thought that you do not need complex frameworks, because WPF already gives you great power (routed eventing, resources). So, no IOC is used, no event aggregator etcetera: it's taken care of by WPF and WF, a natural fit.
    The solution is very decoupled and I feel it's a great advantage to be able to visual your control logic.
    In the previous post, I showed a wizard style application.

    This post follows Part II, starting the application and the adapter. In that post we started our shell and explained how the adapter communicates with a workflow instance, how it can react to commands (normal RoutedUI events from wpf controls) and react to events by the command service.

    We will now continue, by looking at a simple view.

    View responsibility

    Let's first look at how we perceive a view in the MVC paradigm.
    A view should be nothing more than the visualization of your data. The only authority it has, is the authority to decide how to represent a piece of data on the screen. That means it should not contain any business logic. Be very strict about this: the responsibility of a view is the visualization of data.

    So, let's take a look at a common scenario where these lines may blur.
    Take a list of products and let's say that if we have a new product-line that has been introduced within the past month, we want to use another background color, to alert our customers to this new hot product.

    We could solve this in our binding perhaps (let's just assume that is easy), but we should not do that. That would mean the view is deciding when a product is new and hot. It should not.
    The only thing the view should do is create the two visual representations of products and use a datatemplate selector to decide which is hot or not. The datatemplate selector could be injected by our controller. Another way to solve this, is for the controller to put this information in the ViewModel itself. Like, add a boolean 'new' which the view uses.

    If you do not do it this way, and you are embedding logic inside of your view, you will quickly end up with scattered logic, never knowing where something is defined. Changing rules becomes hard and your application will break at some point.
    Now, I understand, and have done many times, that sometimes you just do not have the time to do it right. But always remember that in the long run, you will get burned. Try to setup a situation where it is easy to do the right thing, by making it easy to use datatemplate selectors or use the viewmodel.

    View decoupling

    MVC advocates not letting your view have any knowledge whatsoever of the controller. It does this, because tight coupling of the view to the controller will destroy maintainability and flexibility. If you tight couple, you are unable to swap controllers or views. Most importantly, if you couple the view to the controller (by making it call specific methods on the controller), it becomes harder to maintain/refactor.

    There are certainly approaches that do couple view to controller. If you look at the very powerful Caliburn framework, you will see that the framework has 'action messages' that directly call methods on the controller. I have yet to work with that extensively, so I can not be sure, but it feels to me there should be a very explicit layer between view and controller, which defines how the view will communicate with the controller.

    Our goals in this project are to use the tools WPF provides us to communicate with the rest of the system. We do so with Commands.
    A command can be seen as a message that is passed upward (and downward) the visual tree. Since our adapter lives just above the view and is part of the visual tree, it will have the opportunity to react to the command.

    When building a view, you should also explicitly define all the interactions that view expects to have with the outside world. Do that in a static class like so:

        public static class ImportantWizardInteractions
        {
            public static readonly RoutedUICommand Next;
            public static readonly RoutedUICommand Back;

            public static readonly RoutedUICommand GotoClientScreen;
            public static readonly RoutedUICommand GotoAdresScreen;
            public static readonly RoutedUICommand GotoRoleScreen;
            public static readonly RoutedUICommand GotoCarScreen;

            public static readonly RoutedUICommand Save;
            public static readonly RoutedUICommand SaveYes;
            public static readonly RoutedUICommand SaveNo;


            static ImportantWizardInteractions()
            {
                Next = new RoutedUICommand("Next", "Next", typeof(ImportantWizardInteractions));
                Back = new RoutedUICommand("Back", "Back", typeof(ImportantWizardInteractions));

                GotoClientScreen = new RoutedUICommand("GotoClientScreen", "GotoClientScreen", typeof(ImportantWizardInteractions));
                GotoAdresScreen = new RoutedUICommand("GotoAdresScreen", "GotoAdresScreen", typeof(ImportantWizardInteractions));
                GotoRoleScreen = new RoutedUICommand("GotoRoleScreen", "GotoRoleScreen", typeof(ImportantWizardInteractions));
                GotoCarScreen = new RoutedUICommand("GotoCarScreen", "GotoCarScreen", typeof(ImportantWizardInteractions));

                Save = new RoutedUICommand("Save", "Save", typeof(ImportantWizardInteractions));
                SaveYes = new RoutedUICommand("SaveYes", "SaveYes", typeof(ImportantWizardInteractions));
                SaveNo = new RoutedUICommand("SaveNo", "SaveNo", typeof(ImportantWizardInteractions));


            }
        }

    By being explicit about your interactions like this, you will be able to unit test more easily as well.

    Use in your view like this:

    <Button Command="{x:Static local:ImportantWizardInteractions.GotoClientScreen}">Client</Button>

    A command is great for buttons and other stuff, but how do you do for instance communicate that a customer was selected from a listview?

    1. well, you bind to a selectedCustomer property hopefully, and when the customer was selected, that property changed on the viewmodel. The controller might pick that up.
    2. More explicitly though: do use the SelectedItemChanged event and use the codebehind of your view as a translation layer to talk to the outside world:
    3.         private void ListBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
              {
                  // send a command
                  CustomerQueueInteractions.SelectNewCustomer.Execute(e.AddedItems, this);
              }

    You can call me on that. It's not a very elegant solution. I'd rather be able to do away with the codebehind of a view entirely. But using the codebehind is actually fine: it is part of the view, and it should not be allowed to do anything else than to act as a translator for view specific things to commands.

    So, how to show a view

    Well, building a view is nothing else than just deriving from usercontrol and doing your thing. Using commands and going wild on the visuals. (try to animate everything!!! your client loves it).

    It depends now how you want to show it.

    1. Let's say your building a project where you don't care about fancy composition and pluggable modules in your application, and you just want your shell to show your view. The shell might have the following code:
      <c:GenericWorkflowAdapter WorkflowController="{x:Type logic:AControllerForYourView}" >
         
      <v:YourView/>
      </c:GenericWorkflowAdapter>

      I am assuming you do want a controller around your view.

      Here a controller is instantiated and it's content is set to your view. Easy.
    2. Let's say we want our controller to choose what view it uses. That seems to me to be the nicest way to go about it. We will again put a controller in the visual tree, but will not set a view already:

      1. <c:GenericWorkflowAdapter WorkflowController="{x:Type logic:ImportantClientWizard}" />

      Then, in the workflow, we might use some fancy logic to determine which view we will show (perhaps looking at the role of the user). To actually set a view, we will use the SetMainContentActivit