Search

Search:

Namespace:

Search Result
.

For a book this size, about 700 pages, I was amazed at how little I could find that I didn't like. In fact one of few errors I could find is the statement that only a single instance of the WorkflowRuntime can be created per AppDomain. This is a myth that stems back to the first beta's where it was briefly the case but the restriction has been lifted long ago and has never been part of the released product. But given the size of the book and how few people ever need to create multiple WorkflowRuntime objects it is hardly a big objection.

.

Most often used when creating asynchronously executing, ie long running activities. In the Execute function you return ActivityExecutionStatus.Executing indicating that the Activity is still executing. When the work in the activity is finished the ActivityExecutionContext.CloseActivity() function is called informing the WorkflowRuntime that the Activity is done.

.

To ensure consistency between the WorkflowInstance and the database being updated the TransactionScopeActivity is adorned with the PersistOnCloseAttribute. When the TransactionScopeActivity is used the WorkflowRuntime must be configured with a WorkflowPersistenceService. This could be the build in SqlWorkflowPersistenceService or another custom WorkflowPersistenceService.

.

workflowRuntime.AddService(dataService);

.

DefaultWorkflowSchedulerService creates and manages the threads that run workflow instances in an asynchronous manner on the workflow runtime engine and includes default support for having multiple workflow instances queued in the runtime thread pool. If no other workflow scheduler service instance is added to WorkflowRuntime, it uses the DefaultWorkflowSchedulerService by default.

.
        workflowInstance = workflowRuntime.CreateWorkflow(reader)
.

Another possible reason is that the workflow you are sending a message to is already terminated. This might happen if your code is responding to an event that has already timed out allowing the workflow to terminate. The error is misleading in this case because the WorkflowRuntime doesn't know that the workflow has finished and tries to load it from the, in this case, non existent WorkflowPersistenceServices. In this case you will find an InnerException with the text "The workflow hosting environment does not have a persistence service as required by an operation on the workflow instance "SomeId"."

.

Used to let an Activity communicate with services provided through the WorkflowRuntime.

.

If you are trying to raise an event from an ExternalDataExchange service the most likely cause of the problem is adding the service object to the WorkflowRuntime instead of the ExternalDataExchangeService. Calling the ExternalDataExchange service from the workflow will work just fine but raising an event will not work. If you are using C# and not testing for null you will get an NullReferenceException "Object reference not set to an instance of an object.".

.

workflowRuntime.AddService(externalDataExchangeService);

.

workflowRuntime.AddService(new MyService());

.

workflowRuntime.AddService(dataService);

.

The GetService function can be used inside of an activity to return a reference to one of the BaseRuntimeServices derived classes that have been added to the WorkflowRuntime.

.

The GetService function is defined on a number of classes and interfaces like WorkflowRuntime, ActivityExecutionContext, ExternalDataExchangeService and IServiceProvider.

.

Depending on the object used to call GetService a number of RuntimeServices might not be returned, a null/Nothing is returned instead. This is the case with a ActivityExecutionContext which will not return any of the following: WorkflowSchedulerService, WorkflowPersistenceService, TrackingService, WorkflowCommitWorkBatchService, WorkflowLoaderService or the WorkflowRuntime itself.

.

http://msdn2.microsoft.com/en-us/library/system.workflow.runtime.workflowruntime.getworkflow.aspx

.

The base HandleExternalEventActivity class blocks the workflow until the event specified by the InterfaceType and EventName properties is raised by the corresponding local service registered with the WorkflowRuntime. After the event is raised, or if it was raised before the activity started executing, the inbound data is assigned to bound locations as defined in the ParameterBindings collection.

.

If you are tracking the progress of events in a workflow using a SqlTrackingService object you might have noticed you aren't seeing any events in the database until the workflow is either finished or unloaded. In fact you won't get to see any event until the WorkflowPersisted event has fired. The reason for this is that the default for the SqlTrackingService is to be transactional. Even though the docs claim the default for the SqlTrackingService.IsTransactional is false in fact it is true. This results in the records being written to the database but you won't see them when querying because they are still locked in a transaction. So if you need or want to see the up-to-date events while the workflow is still executing you need to set the SqlTrackingService.IsTransactional to false before adding the service to the WorkflowRuntime.

.
Summary
.

The WorkflowRuntime CreateWorkflow function has a number of different overloads. Basically it offers the choice between specifying the Workflow as a .NET Type or as an XmlReader.

.

Next create a WorkflowRuntime config section like so:

.
        <section name="WorkflowRuntime" 
.
             type="System.Workflow.Runtime.Configuration.WorkflowRuntimeSection, 
.
    <WorkflowRuntime Name="WorkflowRuntime">
.
    </WorkflowRuntime>
.
    using (WorkflowRuntime workflowRuntime = new WorkflowRuntime())
.
        workflowRuntime.AddService(scheduler);
.
        _timer = new Timer(TimerCallback, workflowRuntime, TimeSpan.FromSeconds(1), TimeSpan.FromMilliseconds(250));
.
        WorkflowInstance instance = workflowRuntime.CreateWorkflow(typeof(WorkflowConsoleApplication1.Workflow1));
.
    WorkflowRuntime workflowRuntime = state as WorkflowRuntime;
.
    if (workflowRuntime.IsStarted)
.
        ManualWorkflowSchedulerService scheduler = workflowRuntime.GetService<ManualWorkflowSchedulerService>();
.
        var workflows = workflowRuntime.GetLoadedWorkflows();
.
Summary
A single AppDomain can contain mutiple WorkflowRuntime objects
.

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.

.

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.

.

When one of these PersistencePoints is reached the WorkflowRuntime will call the WorkflowPersistenceService SaveWorkflowInstanceState function to persist the workflow.

.

If you are developing your own WorkflowActivities make sure you take a good look at the PersistOnClose attribute! If your activity does any work like changing a database its more than likely that you don’t want to have the action executed twice. If the WorkflowRuntime is stopped after your activity has completed without this attribute and is restarted later it will be restarted at the last persisted point, which is before your activity. The result being that your activity is executed again, not quite what you had in mind . The PersistOnClose attribute makes sure that the workflow state is persisted as soon as the activity has completed resulting in a consistent state even after a restart.

.

When an Activity adorned with this attribute is used the WorkflowRuntime must be configured with a WorkflowPersistenceService.

.
Summary
Use to notify the WorkflowRuntime of an unhandled exception in a WorkflowRuntimeService derived type.
.

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

.
    using (WorkflowRuntime workflowRuntime = new WorkflowRuntime())
.
        AddMessageService(workflowRuntime, DisplayMode.Windows);
.
        WorkflowInstance instance = workflowRuntime.CreateWorkflow(
.

static void AddMessageService(WorkflowRuntime workflowRuntime, DisplayMode mode)

.
    workflowRuntime.AddService(service);
.

The basics are the same as in my previous IronPython post. Depending on the display mode I make a choice of class to load and I use the same code as previous time to create the object. Finally I add it to the WorkflowRuntime as a runtime service. This last bit is easy as this accepts every object type as a valid service and only when we call the GetService do we check the actual type.

.

Collection of objects that actually do most of the work in WF. There are some required services that are always present, if the developer add one that is used otherwise a default is used. For example the DefaultWorkflowSchedulerService is the default implementation of the rquired WorkflowSchedulerService. Some other RuntimeServices are optional but provided out of the box like the SqlWorkflowPersistenceService. If not provided there is no default and this functionality is unavailable. The third kind of RuntimeServices is the custom RuntimeServices usually developed in combination with a custom workflow Activity. These services can use the WorkflowRuntimeService as a base class although this is not required and they can derive from another class is so desired. Use the WorkflowRuntime AddService function to configure the WorkflowRuntime.

.

Hosting the workflow runtime in a WorkflowServiceHost is nice but in all likelihood you will also need to configure the workflow runtime itself and add some WorkflowRuntimeService to it. So how to do this when you never actually create the workflow runtime yourself?

.

There are two possible ways to go about this depending if you prefer to use code or a configuration file. Lets first look at the code option as it is easier to see all the working parts. In the case of a WCF hosted workflow runtime the WorkflowRuntimeBehavior is the WCF ServiceBehavior that is actually responsible for creating the WorkflowRuntime. Fortunately getting a reference to it, and thereby the WorkflowRuntime, isn't hard.

.

WorkflowRuntimeBehavior workflowRuntimeBehaviour =

.

host.Description.Behaviors.Find<WorkflowRuntimeBehavior>();

.

WorkflowRuntime workflowRuntime = workflowRuntimeBehaviour.WorkflowRuntime;

.

workflowRuntime.AddService(persistenceService);

.
          <workflowRuntime name="WorkflowServiceHostRuntime"
.
          </workflowRuntime>
.

This configuration file is basically the same as in the previous post except that contains a workflowRuntime element inside of the service behavior. This workflowRuntime contains the services collection where you can add any services you need.

.

All services deriving from WorkflowRuntimeService can use the RaiseServicesExceptionNotHandledEvent function that some kind of exception occurred that it was unable to handle.

.
            Using workflowRuntime As New WorkflowRuntime()
.
                workflowRuntime.AddService(persistence)
.
                workflowInstance = workflowRuntime.CreateWorkflow(GetType(Workflow1))
.
            using (WorkflowRuntime workflowRuntime = new WorkflowRuntime())
.
                workflowRuntime.AddService(persistence);
.
                WorkflowInstance instance = workflowRuntime.CreateWorkflow(typeof(Workflow1));
.

If you are tracking the progress of events in a workflow using a SqlTrackingService object you might have noticed you aren't seeing any events in the database until the workflow is either finished or unloaded. In fact you won't get to see any event until the WorkflowPersisted event has fired. The reason for this is that the default for the SqlTrackingService is to be transactional. Even though the docs claim the default for the SqlTrackingService IsTransactional is false in fact it is true. This results in the records being written to the database but you won't see them when querying because they are still locked in a transaction. So if you need or want to see the up-to-date events while the workflow is still executing you need to set the SqlTrackingService.IsTransactional to false before adding the service to the WorkflowRuntime.

.
    using (WorkflowRuntime workflowRuntime = new WorkflowRuntime())
.
        workflowRuntime.AddService(tracking);
.
        workflowRuntime.WorkflowIdled += new EventHandler<WorkflowEventArgs>(workflowRuntime_WorkflowIdled);
.
        WorkflowInstance instance = workflowRuntime.CreateWorkflow(typeof(WorkflowConsoleApplication2.Workflow1));
.

static void workflowRuntime_WorkflowIdled(object sender, WorkflowEventArgs e)

.
    WorkflowRuntime runtime = sender as WorkflowRuntime;
.

To ensure consistency between the WorkflowInstance and the database being updated the TransactionScopeActivity is adorned with the PersistOnCloseAttribute. When the TransactionScopeActivity is used the WorkflowRuntime must be configured with a WorkflowPersistenceService. This could be the build in SqlWorkflowPersistenceService or another custom WorkflowPersistenceService.

.

workflowRuntime.AddService(tp)

.

public class WriteLineService3: WorkflowRuntimeService

.

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.

.

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.

.

A workflow instance is considered Aborted when the WorkflowRuntime engine throws away the in-memory instance. This is done by calling WorkflowInstance.Abort. Aborted workflow instances can be resumed from their last persistence point by calling WorkflowInstance.Resume. Aborting workflows is only valid when there is a persistence service. Aborting workflows is used in extreme situations where applications decide to discard all the work that is done from the last persistence point till abort is called.

.

A workflow is created when the instance is completely constructed but before activities are processed. That is, before the workflow start executing. The workflow instance is created by calling WorkflowRuntime.CreateWorkflow().

.

Use the WorkflowRuntime CreateWorkflow function to create the WorkflowInstance or the GetWorkflow function to retreive an existing.

.

Raises the WorkflowRuntime WorkflowAborted event

.

workflowInstance = workflowRuntime.CreateWorkflow(GetType(Workflow1), parameters)

.

workflowInstance = workflowRuntime.CreateWorkflow(GetType(Workflow1), parameters.GetParameters())

.

workflowInstance = workflowRuntime.CreateWorkflow(GetType(Workflow1), parameters.GetParameters())

.

During normal operations a WorkflowRuntimeService will enqueue some data and the activity listening for it will use the QueueItemAvailable event handler to receive the messages.

.

One special thing to note about the WorkflowQueuingService is that it is not replacable. While most services used by the WorkflowRuntime can be replaced with alternatives the WorkflowQueuingService is hard-coded in WorkflowExecutor Initialize method.

.

Each WorkflowRuntime is hosted in a WorkflowRuntimeHost. This WorkflowRuntimeHost adds a number of WorkflowServices to the WorkflowRuntime enabling Activities to communicate with the outside world.

.

The WorkflowRuntime provides a number of services, all derived from WorkflowRuntimeService, for the executing Workflows:

.

Important events raised by the WorkflowRuntime:

.

You can also have MultipleWorkflowRuntimeObjects per AppDomain if you so desire.

.
Summary
.

http://msdn2.microsoft.com/en-us/library/system.servicemodel.description.workflowruntimebehavior.aspx

.

Because the WorkflowRuntime is a set of classes, not a standalone application, each WorkflowRuntime must be hosted in another application. This can be a WinFormsApplication, a ConsoleApplication, a WindowsManagedService or a AspDotNET aplication/WebService hosted in IIS.

.
Summary
.

Use the RaiseServicesExceptionNotHandledEvent function to notify the WorkflowRuntime that an exception for a specific WorkflowInstance was unhandled.

.

Using reflection to dynamically load an assembly and its workflows and execute them. If the assembly is not in the application path there is a risk that the WorkflowRuntime cannot load and execute all activities in the Workflow. So far the exact circumstances are unknown but activities contained in the WhileActivity, and probably other activities with SpawnedContexts, can cause problems.

.
        workflowInstance = workflowRuntime.CreateWorkflow(reader)
.

XAML base workflows can either be loaded directly into the WorkflowRuntime CreateWorkflow function or they can be compiled using wfc.exe or the WorkflowCompiler class. If the root node has a x:Class attribute the XAML must be compiled before it can be used.