<?xml version="1.0" encoding="UTF-8" ?>
<?xml-stylesheet type="text/xsl" href="http://blogs.msdn.com/utility/FeedStylesheets/atom.xsl" media="screen"?><feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en-US"><title type="html">Advanced Workflow: Enabling Tricky Scenarios</title><subtitle type="html" /><id>http://blogs.msdn.com/b/advancedworkflow/atom.aspx</id><link rel="alternate" type="text/html" href="http://blogs.msdn.com/b/advancedworkflow/" /><link rel="self" type="application/atom+xml" href="http://blogs.msdn.com/b/advancedworkflow/atom.aspx" /><generator uri="http://telligent.com" version="5.6.50428.7875">Telligent Evolution Platform Developer Build (Build: 5.6.50428.7875)</generator><updated>2006-02-23T00:23:00Z</updated><entry><title>My New Blog</title><link rel="alternate" type="text/html" href="http://blogs.msdn.com/b/advancedworkflow/archive/2006/10/18/my-new-blog.aspx" /><id>http://blogs.msdn.com/b/advancedworkflow/archive/2006/10/18/my-new-blog.aspx</id><published>2006-10-18T21:11:00Z</published><updated>2006-10-18T21:11:00Z</updated><content type="html">&lt;P&gt;Two things have happened which made me think I needed a second blog:&lt;/P&gt;
&lt;OL&gt;
&lt;LI&gt;My job has changed slightly so I've got a few non-workflow things to say now.&lt;/LI&gt;
&lt;LI&gt;I wanted&amp;nbsp;a place where I didn't feel obligated to stick to work related topics.&lt;/LI&gt;&lt;/OL&gt;
&lt;P&gt;Let me introduce to you &lt;A href="http://sharedmemory.spaces.live.com/"&gt;http://SharedMemory.spaces.live.com&lt;/A&gt;.&amp;nbsp; It's just a place for me to post the interesting non-workflow things that I come across at work as well as any other random technical thoughts that come to mind.&lt;/P&gt;
&lt;P&gt;Enjoy.&lt;/P&gt;
&lt;P&gt;Nate&lt;/P&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=839723" width="1" height="1"&gt;</content><author><name>ntalbert</name><uri>http://blogs.msdn.com/ntalbert/ProfileUrlRedirect.ashx</uri></author></entry><entry><title>Forum post about Persistence, IEventActivity, Transactions, and Correlation</title><link rel="alternate" type="text/html" href="http://blogs.msdn.com/b/advancedworkflow/archive/2006/06/20/638059.aspx" /><id>http://blogs.msdn.com/b/advancedworkflow/archive/2006/06/20/638059.aspx</id><published>2006-06-20T04:34:00Z</published><updated>2006-06-20T04:34:00Z</updated><content type="html">&lt;P&gt;&lt;A href="http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=488698&amp;amp;SiteID=1&amp;amp;mode=1"&gt;http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=488698&amp;amp;SiteID=1&amp;amp;mode=1&lt;/A&gt;&lt;/P&gt;
&lt;P&gt;&lt;FONT face=Tahoma size=2&gt;There was a great set of questions that showed up on the forum the other day and I've finally gotten around to answering them all.&amp;nbsp; I was planning on posting the answers in this blog, but instead I'll just reference back to the forum post.&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;&lt;FONT face=Tahoma size=2&gt;Summary:&lt;BR&gt;* What is persisted with a workflow instance and what state am I in after a computer crash?&lt;BR&gt;&lt;/FONT&gt;&lt;FONT face=Tahoma size=2&gt;&lt;STRONG&gt;Queues, the internal list of scheduled items, and the binary serialization of the workflow tree are all persisted.&amp;nbsp; Reloading an instance after a workflow crashes gives you the exact state you had at the last persistence point.&lt;/STRONG&gt;&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;&lt;FONT face=Tahoma size=2&gt;* I want a single activity which delegates work out to the host and then waits for notification that the work is done.&amp;nbsp; Should I use the ExternalDataExchangeService or a custom IEventActivity?&amp;nbsp; &lt;BR&gt;&lt;STRONG&gt;If it must be a single activity then I would recommend a custom IEventActivity to enable use of the solution in the StateMachine and in EventDrivens in general.&amp;nbsp; This is a prime scenario for ExternalDataExchangeService, but the requirement for a single activity to do the work makes that a less than viable solution.&lt;/STRONG&gt;&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;&lt;FONT face=Tahoma size=2&gt;* How do I get persistence of the workflow to take part in the same transaction as cleanup of a database caused by an inbound event?&amp;nbsp; &lt;BR&gt;&lt;STRONG&gt;Batching is your friend ... see the post for details of batching.&lt;/STRONG&gt;&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;&lt;FONT face=Tahoma size=2&gt;* What are the implications of dropping correlation from the Correlated Service Sample?&amp;nbsp; &lt;BR&gt;&lt;/FONT&gt;&lt;FONT face=Tahoma size=2&gt;&lt;STRONG&gt;In short, if you drop correlation you will risk getting a runtime exception.&amp;nbsp; If two activities are waiting on the same event at the same time then we will throw an exception when the second activity tries to "handle" the already handled message.&lt;/STRONG&gt;&lt;/FONT&gt;&lt;/P&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=638059" width="1" height="1"&gt;</content><author><name>ntalbert</name><uri>http://blogs.msdn.com/ntalbert/ProfileUrlRedirect.ashx</uri></author></entry><entry><title>Passivation (Dehydration, Unloading) Policy</title><link rel="alternate" type="text/html" href="http://blogs.msdn.com/b/advancedworkflow/archive/2006/05/19/602116.aspx" /><id>http://blogs.msdn.com/b/advancedworkflow/archive/2006/05/19/602116.aspx</id><published>2006-05-19T21:38:00Z</published><updated>2006-05-19T21:38:00Z</updated><content type="html">&lt;P&gt;&lt;FONT face=Tahoma size=2&gt;Windows Workflow Foundation (WF) ships with two out of box modes of passivation (also referred to as dehydration and unloading) of a workflow.&amp;nbsp; Passivation is the process by which a workflow's state is saved to the database AND the workflow is removed from memory for the time being.&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;&lt;FONT face=Tahoma size=2&gt;&lt;STRONG&gt;Out of Box Support&lt;/STRONG&gt;&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;&lt;FONT face=Tahoma size=2&gt;Unfortunately our support is very binary and only related to the idle time of a workflow instance.&amp;nbsp; Our base persistence service class, WorkflowPersistenceService, defines a boolean method UnloadOnIdle which will be called when a workflow goes Idle.&amp;nbsp; If this method returns true, then the instance in question will be unloaded (persisted and removed from memory).&amp;nbsp; If, however, the method returns true then the instance will remain in-memory.&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;&lt;FONT face=Tahoma size=2&gt;&lt;STRONG&gt;This is Less Than Ideal&lt;/STRONG&gt;&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;&lt;FONT face=Tahoma size=2&gt;WF has done a wonderful job providing extensibility points.&amp;nbsp; WF has also done a great job not enforcing our semantics on the user.&amp;nbsp; You want to write your own persistence then feel free ... serialize the workflows however you want and store them wherever you please.&amp;nbsp; You don't like either of our out of box threading models ... go ahead and write your own.&amp;nbsp; Our definition of ParallelActivity doesn't suite you ... create a parallel which does what you desire.&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;&lt;FONT face=Tahoma size=2&gt;Here, however, we have locked you in.&amp;nbsp; We have a method which you must implement on the persistence service which returns true or false.&amp;nbsp; Either you want the idle instance to unload right then, or you don't.&amp;nbsp; But what about advanced policy?&amp;nbsp; What about "unload after 20 minutes of idle time", or "unload when the instance hits a specific point", or "unload after 20 minutes in memory regardless of idling"?&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;&lt;FONT face=Tahoma size=2&gt;&lt;STRONG&gt;Custom Passivation Policy&lt;/STRONG&gt;&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;&lt;FONT face=Tahoma size=2&gt;Luckily, just because we locked you into having to think about our UnloadOnIdle concept, we didn't lock you into using it.&amp;nbsp; From here on out assume that we always return false from UnloadOnIdle.&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;&lt;FONT face=Tahoma size=2&gt;&lt;STRONG&gt;Unload After 20 Minutes Idle&lt;/STRONG&gt;&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;&lt;FONT face=Tahoma size=2&gt;Let's create a new service which derives from WorkflowRuntimeService.&amp;nbsp; WorkflowRuntimeService is a base class which, on start up, will get a reference to the WorkflowRuntime so that you can safely access it.&amp;nbsp; In our custom service (UnloadIn20Service) we'll override the Start method, call base, and then subscribe to WorkflowIdled:&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;&lt;FONT face="Courier New" size=2&gt;&lt;EM&gt;override void Start()&lt;BR&gt;&lt;/EM&gt;&lt;/FONT&gt;&lt;FONT face="Courier New" size=2&gt;&lt;EM&gt;{&lt;BR&gt;&amp;nbsp; base.Start();&lt;BR&gt;&amp;nbsp; WorkflowRuntime.WorkflowIdled += OnWorkflowIdled;&lt;BR&gt;}&lt;/EM&gt;&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;&lt;FONT face=Tahoma size=2&gt;Now, let's assume that we've written a collection with the following behavior:&lt;/FONT&gt;&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;&lt;FONT face=Tahoma size=2&gt;Items in the list are pairs of workflow instance IDs and DateTimes&lt;/FONT&gt;&lt;/LI&gt;
&lt;LI&gt;&lt;FONT face=Tahoma size=2&gt;The list is sorted on the DateTime such that the earliest DateTime is at the head of the list&lt;/FONT&gt;&lt;/LI&gt;
&lt;LI&gt;&lt;FONT face=Tahoma size=2&gt;There is always an active timer which will expire at the DateTime specified by the item at the head of the list&lt;/FONT&gt;&lt;/LI&gt;
&lt;LI&gt;&lt;FONT face=Tahoma size=2&gt;There is an event TimeExpired (void(Guid))which is publicly exposed by the collection and raised any time the timer expires.&lt;/FONT&gt;&lt;/LI&gt;
&lt;LI&gt;&lt;FONT face=Tahoma size=2&gt;When the timer expires the item at the head of the list is removed&lt;/FONT&gt;&lt;/LI&gt;&lt;/UL&gt;
&lt;P&gt;&lt;FONT face=Tahoma size=2&gt;In short, a priority queue which notifies us when a timeout has expired for an instance.&amp;nbsp; Our OnWorkflowIdled now looks like (assume we have already subscribed to TimeExpired):&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;&lt;FONT face="Courier New" size=2&gt;&lt;EM&gt;if (idledWorkflows.Contains(e.WorkflowInstance.InstanceId)&lt;BR&gt;&amp;nbsp; idledWorkflows.Remove(e.WorkflowInstance.InstanceId);&lt;BR&gt;idledWorkflows.Add(e.WorkflowInstance.InstanceId, DateTime.Now.AddMinutes(20));&lt;/EM&gt;&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;&lt;FONT face=Tahoma size=2&gt;Our OnTimeExpired handler:&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;&lt;FONT face="Courier New" size=2&gt;&lt;EM&gt;WorkflowRuntime.GetWorkflow(instId).TryUnload();&lt;/EM&gt;&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;&lt;FONT face=Tahoma size=2&gt;Now, with a few lines of code we have created a service which we can add to the runtime which will manage unloading workflows after 20 minutes of idle time.&amp;nbsp; Note that by using TryUnload instead of Unload we are guaranteed that if the workflow is NOT currently idle then we will not actually unload the instance.&amp;nbsp; TryUnload will return false in that case and is a no-op.&amp;nbsp; Unload, in contrast, will block until the instance can be unloaded (not in a TransactionScopeActivity and the scheduler is not currently running an item - note that items are allowed to be on the scheduler queue) and then follow through with the passivation.&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;&lt;FONT face=Tahoma size=2&gt;So, a quick walkthrough ... a workflow goes Idle and it is added to the list.&amp;nbsp; If it is the next one to "expire" then it a timer will be created by our list for that instance.&amp;nbsp; When the timer expires the service will attempt to unload the instance, but only if it is not currently executing.&amp;nbsp; In the case that it executes and then goes idle again before the 20 minutes are up then the old expiration is removed from the list and a new expiration is added.&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;&lt;FONT face=Tahoma size=2&gt;&lt;STRONG&gt;Unload After 20 Minutes In-Memory&lt;/STRONG&gt;&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;&lt;FONT face=Tahoma size=2&gt;This is actually the same implementation as the last one except for two changes.&amp;nbsp; First, subscribe to the WorkflowLoaded event.&amp;nbsp; This will notify you when an instance is loaded into memory.&amp;nbsp; If you want newly created workflows to have the same behavior then you also should hook the handler to the WorkflowCreated event as this is another mechanism by which a workflow can find its way into memory.&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;&lt;FONT face=Tahoma size=2&gt;Second, change the call to TryUnload to a call to Unload instead.&amp;nbsp; This will make sure that the instance is unloaded regardless of whether or not it has more processing which it could do.&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;&lt;FONT face=Tahoma size=2&gt;&lt;STRONG&gt;Unload At&amp;nbsp; a Specific Point in the Workflow&lt;/STRONG&gt;&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;&lt;FONT face=Tahoma size=2&gt;Well, my fingers are getting tired, so I'm going to give this one a superficial overview.&amp;nbsp; In this case it would make sense to write a tracking service.&amp;nbsp; Many people have the incorrect view that writing a custom tracking service is too much work, but this is not the case.&amp;nbsp; In fact, I will put a post up on writing custom tracking services next time I write.&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;&lt;FONT face=Tahoma size=2&gt;The technique here would be to create a tracking channel which is aware of 1) the instance being tracked (this data is passed when the tracking channel is requested) and 2) the workflow runtime itself.&amp;nbsp; The tracking channel could then wait for a specific message (ActivityTrackingRecord, WorkflowTrackingRecord, UserTrackingRecord) and cause the workflow runtime to unload the instance.&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;&lt;FONT face=Tahoma size=2&gt;&lt;STRONG&gt;Conclusion&lt;/STRONG&gt;&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;&lt;FONT face=Tahoma size=2&gt;WF's extensibility model let's you do almost anything you can imagine.&amp;nbsp; Today I walked through a few examples of custom passivation policies which can be implemented using the framework provided by WF.&amp;nbsp; As noted above, next time I'll talk about writing&amp;nbsp;a custom tracking service to dispel the myth that it is "too hard" ... I think the problem stems from the number of similar methods which must be overridden and the two class structure of a tracking service.&amp;nbsp; As always, questions and comments are welcome.&lt;/FONT&gt;&lt;/P&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=602116" width="1" height="1"&gt;</content><author><name>ntalbert</name><uri>http://blogs.msdn.com/ntalbert/ProfileUrlRedirect.ashx</uri></author></entry><entry><title>ASP.NET and DefaultWorkflowSchedulerService</title><link rel="alternate" type="text/html" href="http://blogs.msdn.com/b/advancedworkflow/archive/2006/04/25/583392.aspx" /><id>http://blogs.msdn.com/b/advancedworkflow/archive/2006/04/25/583392.aspx</id><published>2006-04-25T20:48:00Z</published><updated>2006-04-25T20:48:00Z</updated><content type="html">&lt;P&gt;&lt;FONT face=Tahoma size=3&gt;&lt;FONT size=2&gt;&lt;FONT face=Tahoma&gt;&lt;STRONG&gt;QUESTION: &lt;/STRONG&gt;Can I use DefaultWorkflowSchedulerService instead of ManualWorkflowSchedulerService in my IIS hosted WorkflowRuntime?&lt;/FONT&gt;&lt;/FONT&gt;&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;&lt;FONT face=Tahoma size=2&gt;&lt;STRONG&gt;ANSWER: &lt;/STRONG&gt;Do so at your own risk.&amp;nbsp; It's like swimming in an unguarded pool or skydiving&amp;nbsp;...&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;&lt;FONT face=Tahoma size=3&gt;&lt;FONT size=2&gt;&lt;FONT face=Tahoma&gt;Using the DefaultWorkflowSchedulerService in an IIS host (specifically ASP.NET) is one of those good/bad scenarios depending on point of view. &amp;nbsp;Windows Workflow Foundation (WF) has no qualms about using the DefaultWorkflowSchedulerService in an ASP.NET hosted scenario and everything will work just fine from a WF point of view. &amp;nbsp;The problem is that ASP.NET does NOT like people using up all its threads. &lt;BR&gt;&lt;BR&gt;Our ManualWorkflowSchedulerService was written specifically for the ASP.NET scenario to make sure that we were running on the minimal number of threads. &amp;nbsp;The call to RunWorkflow is an explicit "gifting" of the current thread (the request thread in the IIS case) to run as much of a specific workflow instance as possible. &amp;nbsp;When you introduce the DefaultWorkflowSchedulerService you are, by default, allowing as many as 4 threads to run workflow instances. &lt;BR&gt;&lt;BR&gt;Is that a bad thing? &amp;nbsp;In most cases, no. &amp;nbsp;Don't quote me on these numbers, but .NET supports something like 25 threads per processor per Process. &amp;nbsp;If you are doing anything with Transactions, then at least one of those threads needs to be "available" to avoid deadlock in some cases. &amp;nbsp;The number of threads ASP.NET requires depends on the situation - if you are running a single web service in an isolated AppPool then I can't imagine ever running into an issue. &lt;BR&gt;&lt;BR&gt;So, what's the "official word" - the statement that covers everyone's butt and will hopefully steer users to a design that is free from danger?&amp;nbsp;&amp;nbsp;&lt;/FONT&gt;&lt;/FONT&gt;&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;&lt;FONT face=Tahoma size=2&gt;First, examine your scenario.&amp;nbsp; &lt;/FONT&gt;&lt;FONT size=3&gt;&lt;FONT size=2&gt;&lt;FONT face=Tahoma&gt;Ask yourself why you need to be doing the processing in the background (non-request thread)? &amp;nbsp;Is all processing tied to a request? &amp;nbsp;Does the processing take very long? &amp;nbsp;Is new processing ever driven by a "delay" in the workflow or some event not tied to an inbound request?&amp;nbsp;&lt;BR&gt;&lt;BR&gt;If you are doing a lot of processing then it is often better to model this as a windows service. &amp;nbsp;Using Windows Communication Foundation (WCF) you can directly expose your windows service as a consumable web service. &amp;nbsp;Alternately you can use WCF or .NET Remoting (last generation technology at this point) to communicate between your current web service and your windows service. &lt;BR&gt;&lt;BR&gt;If you aren't doing much processing and it is all tied to the request, then consider using the ManualScheduler. &amp;nbsp;While this will add additional latency to your responses, it can be considered a constant value as opposed to the unpredictable response times you'll see when you've got an unknown number of active workflow instances vying for processor time. &lt;BR&gt;&lt;BR&gt;In general, try to avoid using the DefaultWorkflowSchedulerService in ASP.NET/IIS. &amp;nbsp;If you do use it, use it with caution and expect that there may be a few unknown problems which pop up during stress.&amp;nbsp; There is always an alternate design and in general the tradeoffs will be&amp;nbsp;maintainability/complexity versus request/response speed versus stability under stress.&lt;/FONT&gt;&lt;/FONT&gt;&lt;/P&gt;&lt;/FONT&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=583392" width="1" height="1"&gt;</content><author><name>ntalbert</name><uri>http://blogs.msdn.com/ntalbert/ProfileUrlRedirect.ashx</uri></author></entry><entry><title>Spawned Contexts - Replicator, While, State, EventHandlers, and CAG</title><link rel="alternate" type="text/html" href="http://blogs.msdn.com/b/advancedworkflow/archive/2006/03/21/557121.aspx" /><id>http://blogs.msdn.com/b/advancedworkflow/archive/2006/03/21/557121.aspx</id><published>2006-03-21T22:20:00Z</published><updated>2006-03-21T22:20:00Z</updated><content type="html">&lt;P&gt;&lt;FONT face=Tahoma size=2&gt;Ever wonder why this.delayActivity1.TimeoutDuration sometimes doesn't change the timeout duration?&amp;nbsp; How come this.callExternalMethodActivity1.ParameterBindings["(ReturnValue)"] isn't giving you the value&amp;nbsp;you expect in some scenarios?&amp;nbsp; How is it possible that sometimes this.GetActivityByName("foo") does not equal my sender for one of foo's events?&amp;nbsp; &lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;&lt;FONT face=Tahoma size=2&gt;The answer to all of these questions is: spawned contexts.&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;&lt;FONT face=Tahoma size=2&gt;One of the most powerful and most easily misunderstood concepts in Windows Workflow Foundation (WF)&amp;nbsp;is that of new contexts executing cloned activities.&amp;nbsp; Before we even define a spawned context, let's go back to the beginning ...&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;&lt;STRONG&gt;&lt;FONT face=Tahoma size=2&gt;The Beginning&lt;/FONT&gt;&lt;/STRONG&gt;&lt;/P&gt;
&lt;P&gt;&lt;FONT face=Tahoma size=2&gt;One of the binding qualities of the activity state transition diagram is that there are no transitions from Closed back to Executing.&amp;nbsp; You can only get to Executing through an Initialized activity and you can only get to Initialized from a brand new instance.&amp;nbsp; &lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;&lt;FONT face=Tahoma size=2&gt;How do we handle looping activities then?&amp;nbsp; Replicator, While,&amp;nbsp;and CAG all give the impression of executing the same activity (or set of activities) multiple times.&amp;nbsp; The answer is by creating a new context and cloning the template activity (explained later).&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;&lt;STRONG&gt;&lt;FONT face=Tahoma size=2&gt;ActivityExecutionContext Interlude&lt;/FONT&gt;&lt;/STRONG&gt;&lt;/P&gt;
&lt;P&gt;&lt;FONT face=Tahoma size=2&gt;First, let's fully understand the ActivityExecutionContext.&amp;nbsp; This object is passed to all scheduled calls either as a specific parameter (Execute, Cancel, HandleFault) or as the sender (QueueItemAvailable handler, StatusChanged handler).&amp;nbsp; The ActivityExecutionContext provides the activity writer with an interface for controling activity execution (hence the first two words in the name) while giving the runtime enough control to enforce the rules of the WF engine.&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;&lt;FONT face=Tahoma size=2&gt;But what about the "Context" part of the name?&amp;nbsp; A context in WF is a sphere of execution.&amp;nbsp; There is a root activity for each context and only activities which exist in that context can be executed in that context.&amp;nbsp; In short, a context is a mechanism used by the runtime to determine on which set of activities to enforce rules.&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;&lt;FONT face=Tahoma size=2&gt;One important note is that the ActivityExecutionContext is merely a short-lived expression of the underlying context - every scheduled call to an activity method gets a new instance of the ActivityExecutionContext object which has been configured specifically for that activity.&amp;nbsp; You'll &lt;FONT face=Tahoma size=2&gt;notice, however, that the Guid associated with the context does not change.&lt;/FONT&gt;&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;&lt;STRONG&gt;&lt;FONT face=Tahoma size=2&gt;Cloning Activities&lt;/FONT&gt;&lt;/STRONG&gt;&lt;/P&gt;
&lt;P&gt;&lt;FONT face=Tahoma size=2&gt;Whenever a single activity needs to be executed multiple times it must be cloned.&amp;nbsp; Contexts are the mechanism provided to the activity writer for making this happen.&amp;nbsp; The code looks like this:&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;&lt;FONT face=Tahoma size=2&gt;ActivityExecutionContext childContext = currentContext.ExecutionContextManager.CreateExecutionContext(childActivity);&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;&lt;FONT face=Tahoma size=2&gt;This code will cause a new context, childContext, to be created with a root activity which is a clone of childActivity.&amp;nbsp; Note that this is a deep cloning so if childActivity is a composite activity then its entire tree is cloned as well.&amp;nbsp; Consider that we have a custom activity called WorkflowRoot which clones its only child activity using the above code.&amp;nbsp; Visually, we now have the following tree of contexts:&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;&lt;FONT face=Tahoma size=2&gt;RootContext&lt;BR&gt;|&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;WorkflowRoot (1)&lt;BR&gt;|&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;childActivity (1)&lt;BR&gt;|&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;grandChildActivity (1)&lt;BR&gt;- childContext&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;childActivity (2)&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;grandChildActivity (2)&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;&lt;STRONG&gt;&lt;FONT face=Tahoma size=2&gt;What?&lt;/FONT&gt;&lt;/STRONG&gt;&lt;/P&gt;
&lt;P&gt;&lt;FONT face=Tahoma size=2&gt;Looking at the above diagram there are several questions which come up.&amp;nbsp; Let's try to deal with a couple of easy ones first:&lt;/FONT&gt;&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;&lt;FONT face=Tahoma size=2&gt;Are changes to childActivity(1) or childActivity(2) reflected in the other instance?&lt;BR&gt;No.&amp;nbsp; Once cloned these instances have no connection.&amp;nbsp; Changes to the template will affect future clones and changes to the clone will affect its own execution, but changes to one will not affect the other.&lt;/FONT&gt;&lt;/LI&gt;
&lt;LI&gt;&lt;FONT face=Tahoma size=2&gt;What is the return value of childActivity(2).Parent?&lt;BR&gt;WorkflowRoot(1).&amp;nbsp; The activities inside a new context do not know that they are not part of the rest of the tree.&amp;nbsp; The Parent property of the context's root activity still points to the original parent.&amp;nbsp; It is only when walking &lt;EM&gt;down&lt;/EM&gt; the tree that the context's are noticeable.&amp;nbsp; For example, WorkflowRoot(1).Activities[0] will always return childActivity(1) and never childActivity(2).&amp;nbsp; Said another way, childActivity(2).Parent.Activities[0] == childActivity(1).&amp;nbsp; This is strange at first glance, but this soon becomes natural.&lt;/FONT&gt;&lt;/LI&gt;&lt;/UL&gt;
&lt;P&gt;&lt;FONT face=Tahoma size=2&gt;&lt;STRONG&gt;Scenarios&lt;/STRONG&gt;&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;&lt;FONT face=Tahoma size=2&gt;&lt;EM&gt;&lt;STRONG&gt;While Loop with Delay&lt;/STRONG&gt;&lt;/EM&gt;&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;&lt;FONT face=Tahoma size=2&gt;Consider the following workflow:&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;&lt;FONT face=Tahoma size=2&gt;WhileActivity&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;DelayActivity&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;&lt;FONT face=Tahoma size=2&gt;Not very useful, I'll admit, but it is handy for this demonstration.&amp;nbsp; Now, if we have implemented this as a code only workflow, we'll probably have some field defined on our root called delayActivity1.&amp;nbsp; Let's say that we want to change the delay amount each time through the loop, so we subscribe to the InitializeTimeoutDuration event with the following code:&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;&lt;FONT face=Tahoma size=2&gt;// WRONG CODE&lt;BR&gt;this.delayActivity1.TimeoutDuration = TimeSpan.FromSeconds(iterationCount);&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;&lt;FONT face=Tahoma size=2&gt;Assuming iterationCount is a variable that is incremented each time through the loop, we expect to see: delay&amp;nbsp;1 second, delay&amp;nbsp;2 seconds, delay&amp;nbsp;3 seconds, etc.&amp;nbsp; This, however, is not what we see.&amp;nbsp; Instead we get: delay&amp;nbsp;0 seconds, delay&amp;nbsp;1 seconds, delay&amp;nbsp;2 seconds, etc.&amp;nbsp; &lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;&lt;FONT face=Tahoma size=2&gt;The reason is that the WhileActivity is spawning a new context when it executes the child.&amp;nbsp; So, each iteration looks like this:&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;&lt;FONT face=Tahoma size=2&gt;RootContext&lt;BR&gt;|&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;WhileActivity(1)&lt;BR&gt;|&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Delay(1)&lt;BR&gt;- childContext&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Delay(1 + iterationCount)&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;&lt;FONT face=Tahoma size=2&gt;this.delayActivity1 ALWAYS refers to Delay(1) and therefore we are updating the template every time InitializeTimeoutDuration is called.&amp;nbsp; That means we are always one timeout amount behind ... Delay(2) is about to execute with TimeoutDuration set to&amp;nbsp;0 seconds and we update the template to&amp;nbsp;1 seconds.&amp;nbsp; Delay(3) is created with a&amp;nbsp;1 second timeout because it is just a clone of the template at that point in time.&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;&lt;FONT face=Tahoma size=2&gt;Some new code for InitializeTimeoutDuration:&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;&lt;FONT face=Tahoma size=2&gt;// RIGHT CODE&lt;BR&gt;&lt;/FONT&gt;&lt;FONT face=Tahoma size=2&gt;((DelayActivity)sender).TimeoutDuration = TimeSpan.FromSeconds(iterationCount);&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;&lt;FONT face=Tahoma size=2&gt;This time we will see the following: delay&amp;nbsp;1 seconds, delay&amp;nbsp;2 seconds, delay&amp;nbsp;3 seconds, etc.&amp;nbsp; Here we have updated the cloned value instead of the template.&amp;nbsp; Note that for ALL events subscribed to in code beside the sender will be the actual instance of the activity which is currently running.&amp;nbsp; &lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;&lt;FONT face=Tahoma size=2&gt;Therefore, the sender above will always be the right one even if the delay is not in a context spawning activity.&amp;nbsp; &lt;EM&gt;If you want to avoid issues, learn to access activity properties in a context safe way (like using the sender objects) so that you do it right when it counts.&lt;/EM&gt;&amp;nbsp; If the delay weren't in a context spawning activity then the WRONG CODE and the RIGHT CODE would be equivalent, but if the delay is in a context spawning activity then the WRONG CODE will never work.&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;&lt;STRONG&gt;&lt;EM&gt;&lt;FONT face=Tahoma size=2&gt;Replicator and GetActivityByName&lt;/FONT&gt;&lt;/EM&gt;&lt;/STRONG&gt;&lt;/P&gt;
&lt;P&gt;&lt;FONT face=Tahoma size=2&gt;Replicator&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;Sequence&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;CallExternalMethodActivity&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;HandleExternalEventActivity&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;&lt;FONT face=Tahoma size=2&gt;The above workflow is a common pattern for replicated user tasks.&amp;nbsp; The CallExternalMethodActivity notifies the user of the task and the HandleExternalEventActivity gets an event when the task is complete.&amp;nbsp; Let's say that we're going to assign 3 tasks for UserA, UserB, and UserC so our replicator will initailize itself with the collection {"UserA", "UserB", "UserC"}.&amp;nbsp; Assuming that the user name is the correlation parameter, our ChildInitialized handler might look like:&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;&lt;FONT face=Tahoma size=2&gt;// WRONG CODE&lt;BR&gt;CallExternalMethodActiivty act = this.GetActivityByName("createTask1") as CallExternalMethodActivity;&lt;BR&gt;act.ParameterBindings["userName"].Value = e.InstanceData;&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;&lt;FONT face=Tahoma size=2&gt;The code above will not work as expected.&amp;nbsp; Let's look at why by examining the contexts created:&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;&lt;FONT face=Tahoma size=2&gt;RootContext&lt;BR&gt;&lt;FONT face=Tahoma size=2&gt;|&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Replicator (1)&lt;BR&gt;|&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Sequence(1)&lt;BR&gt;|&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;CallExternalMethodActivity(1)&lt;BR&gt;|&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;HandleExternalEventActivity(1)&lt;BR&gt;- childContext1 (e.InstanceData = "UserA")&lt;BR&gt;|&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Sequence(2)&lt;BR&gt;|&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;CallExternalMethodActivity(2)&lt;BR&gt;|&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;HandleExternalEventActivity(2)&lt;BR&gt;- childContext2 (e.InstanceData = "UserB")&lt;BR&gt;|&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Sequence(3)&lt;BR&gt;|&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;CallExternalMethodActivity(3)&lt;BR&gt;|&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;HandleExternalEventActivity(3)&lt;BR&gt;- childContext3 (e.InstanceData = "UserC")&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Sequence(4)&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;CallExternalMethodActivity(4)&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;HandleExternalEventActivity(4)&lt;/FONT&gt;&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;&lt;FONT face=Tahoma&gt;&lt;FONT size=2&gt;"this" in our code snippet refers to the root workflow which exists in the RootContext.&amp;nbsp; When we call GetActivityByName and pass the CallExternalMethodActivity's name we will get the instance that is in the root context - CallExternalMethodActivity(1).&amp;nbsp; What we want is the one in the current context so the code should look like:&lt;/FONT&gt;&lt;/FONT&gt;&lt;/P&gt;&lt;FONT face=Tahoma&gt;&lt;FONT size=2&gt;&lt;FONT&gt;
&lt;P&gt;&lt;FONT face=Tahoma size=2&gt;// RIGHT CODE&lt;BR&gt;CallExternalMethodActiivty act = e.Activity.GetActivityByName("createTask1", true) as CallExternalMethodActivity;&lt;BR&gt;act.ParameterBindings["userName"].Value = e.InstanceData;&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;Note the two changes - first we use e.Activity instead of this.&amp;nbsp; e.Activity is the clone of the replicator's template (Sequence(2-4)).&amp;nbsp; Second, we have passed the parameter true to GetActivityByName.&amp;nbsp; This tells the method to look only in the context of the activity on which it was called.&amp;nbsp; This keeps the method from walking into other parts of the tree and returning the RootContext instance.&lt;/P&gt;
&lt;P&gt;&lt;STRONG&gt;Conclusion&lt;/STRONG&gt;&lt;/P&gt;
&lt;P&gt;Hopefully this post eases some confusion around contexts and doesn't make it worse.&amp;nbsp; Please post comments if you want clarifications on anything written above or if you want more information about one topic or another.&amp;nbsp; I will write a separate entry at some point to discuss how to manage contexts you create in custom activities.&lt;/P&gt;&lt;/FONT&gt;&lt;/FONT&gt;&lt;/FONT&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=557121" width="1" height="1"&gt;</content><author><name>ntalbert</name><uri>http://blogs.msdn.com/ntalbert/ProfileUrlRedirect.ashx</uri></author></entry><entry><title>Workflows and Transactions</title><link rel="alternate" type="text/html" href="http://blogs.msdn.com/b/advancedworkflow/archive/2006/03/01/541648.aspx" /><id>http://blogs.msdn.com/b/advancedworkflow/archive/2006/03/01/541648.aspx</id><published>2006-03-02T01:52:00Z</published><updated>2006-03-02T01:52:00Z</updated><content type="html">&lt;P&gt;&lt;FONT face=Tahoma size=2&gt;&lt;STRONG&gt;QUESTION:&lt;/STRONG&gt; How do I get access to a transaction inside my workflow?&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;&lt;FONT face=Tahoma size=2&gt;&lt;STRONG&gt;ANSWER:&lt;/STRONG&gt; First of all, this post has nothing to do with flowing a transaction into a workflow instance.&amp;nbsp; That is a completely different topic which we might cover at some other time.&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;&lt;FONT face=Tahoma size=2&gt;&lt;STRONG&gt;TransactionScopeActivity&lt;/STRONG&gt;&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;&lt;FONT face=Tahoma size=2&gt;With that out of the way, there are two ways to get access to a transaction inside of your workflow.&amp;nbsp; The first is through the &lt;EM&gt;TransactionScopeActivity&lt;/EM&gt; activity.&amp;nbsp; In simplest terms, a &lt;EM&gt;System.Transactions.Transaction&lt;/EM&gt; is created at the start of the &lt;EM&gt;TransactionScopeActivity&lt;/EM&gt; and every activity inside the &lt;EM&gt;TransactionScopeActivity&lt;/EM&gt; will have a valid &lt;EM&gt;Transaction.Current&lt;/EM&gt;.&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;&lt;FONT face=Tahoma size=2&gt;At the close of the &lt;EM&gt;TransactionScopeActivity&lt;/EM&gt; the transaction is either completed or aborted.&amp;nbsp; Consider the following psuedo-code:&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;&lt;FONT style="BACKGROUND-COLOR: #ffffff" face="Courier New" color=#000000 size=2&gt;using (TransactionScope scope = new TransactionScope())&lt;BR&gt;{&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;DoSomething();&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;DoSomethingElse();&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;scope.Complete();&lt;BR&gt;}&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;&lt;FONT face=Tahoma size=2&gt;The equivalent workflow would look like:&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;&lt;FONT face=Tahoma size=2&gt;&amp;lt;TransactionScopeActivity&amp;gt;&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;DoSomething&amp;gt;&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;DoSomethingElse&amp;gt;&lt;BR&gt;&amp;lt;/TransactionScopeActivity&amp;gt;&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;&lt;FONT face=Tahoma size=2&gt;In both cases if an exception is thrown which crosses the scope's boundary, the transaction is aborted.&amp;nbsp; In workflow terms this means that if the exception does not find a matching handler on an activity INSIDE the transaction scope then the transaction is aborted.&amp;nbsp; &lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;&lt;FONT face=Tahoma size=2&gt;Another similarity is that &lt;EM&gt;Transaction.Current&lt;/EM&gt; is set on the thread in both cases.&amp;nbsp; For the workflow it means that the DoSomething and DoSomethingElse activities will both find a transaction when executing any signal methods (&lt;EM&gt;Execute&lt;/EM&gt;, &lt;EM&gt;Close&lt;/EM&gt;, &lt;EM&gt;Cancel&lt;/EM&gt;, &lt;EM&gt;HandleFault&lt;/EM&gt; but NOT &lt;EM&gt;Initialize&lt;/EM&gt;) as well as any handlers for status changes or queue item arrival.&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;&lt;FONT face=Tahoma size=2&gt;&lt;STRONG&gt;Batching&lt;/STRONG&gt;&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;&lt;FONT face=Tahoma size=2&gt;While the rules of batching are enough to be a topic in their own right, we will touch on the beginnings of batching here.&amp;nbsp; Stay tuned for future posts on this ...&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;&lt;FONT face=Tahoma size=2&gt;Without getting into details, the following code will add a work item to the batch:&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;&lt;FONT face="Courier New" size=2&gt;WorkflowEnvironment.WorkBatch.Add(somePendingWork, someWorkItem);&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;&lt;FONT face=Tahoma size=2&gt;The somePendingWork object above must implement the &lt;EM&gt;IPendingWork&lt;/EM&gt; interface and the someWorkItem is just a piece of data on which to do work; think of it the same way as you would the object state which you can optionally pass to &lt;EM&gt;ThreadPool.QueueUserWorkItem&lt;/EM&gt;.&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;&lt;FONT face=Tahoma size=2&gt;At the next commit point in the workflow, the runtime will call somePendingWork.&lt;EM&gt;Commit&lt;/EM&gt; and pass a transaction and someWorkItem as parameters.&amp;nbsp; &lt;EM&gt;Transaction.Current&lt;/EM&gt; will also be set before the &lt;EM&gt;Commit&lt;/EM&gt; method is called so that auto-enlisting calls will work correctly.&amp;nbsp; Throwing an exception from the &lt;EM&gt;Commit&lt;/EM&gt; method will cause the ENTIRE workflow persistence to fail and the exception will be thrown into the workflow for handling.&amp;nbsp; If the &lt;EM&gt;Commit&lt;/EM&gt; method returns without error this will be consider a successful vote for completion of the transaction.&amp;nbsp; As long as everything in the batch succeeds, including persistence of the workflow itself, the transaction will be completed.&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;&lt;FONT face=Tahoma size=2&gt;Once again, this is the simple version of how batching works.&amp;nbsp; There are two other methods on IPendingWork as well as several rules about batch merging, batch compression, and batch clearing which will be covered in another post.&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;&lt;STRONG&gt;&lt;FONT face=Tahoma size=2&gt;Conclusion&lt;/FONT&gt;&lt;/STRONG&gt;&lt;/P&gt;
&lt;P&gt;&lt;FONT face=Tahoma size=2&gt;In conclusion, there are two ways to get a transaction in your workflow.&amp;nbsp; You can model the transaction in your flow using the &lt;EM&gt;TransactionScopeActivity&lt;/EM&gt; which works in a manner analogous to &lt;EM&gt;System.Transactions.TransactionScope&lt;/EM&gt;.&amp;nbsp; The other option is to delay the work until the next commit point and borrow the persistence transaction using batching.&lt;/FONT&gt;&lt;/P&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=541648" width="1" height="1"&gt;</content><author><name>ntalbert</name><uri>http://blogs.msdn.com/ntalbert/ProfileUrlRedirect.ashx</uri></author></entry><entry><title>Clean up your Created Workflows</title><link rel="alternate" type="text/html" href="http://blogs.msdn.com/b/advancedworkflow/archive/2006/03/01/541522.aspx" /><id>http://blogs.msdn.com/b/advancedworkflow/archive/2006/03/01/541522.aspx</id><published>2006-03-01T22:58:00Z</published><updated>2006-03-01T22:58:00Z</updated><content type="html">&lt;P&gt;&lt;FONT face=Tahoma size=2&gt;&lt;STRONG&gt;QUESTION: &lt;/STRONG&gt;What happens to a workflow instance I've created but never started?&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;&lt;FONT face=Tahoma size=2&gt;&lt;STRONG&gt;ANSWER:&lt;/STRONG&gt; Calling &lt;EM&gt;WorkflowRuntime.CreateWorkflow&lt;/EM&gt;() will give you back a &lt;EM&gt;WorkflowInstance&lt;/EM&gt; which, in essence, is a proxy to a runtime instance in the &lt;EM&gt;Created&lt;/EM&gt; state.&amp;nbsp; But what happens to that instance if it is never started?&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;&lt;FONT face=Tahoma size=2&gt;Upon calling &lt;EM&gt;CreateWorkflow&lt;/EM&gt; you have caused the runtime to create an in-memory instance of the workflow.&amp;nbsp; This includes calling &lt;EM&gt;Initialize&lt;/EM&gt; on the entire workflow tree as well as readying runtime resources like tracking channels.&amp;nbsp; This in-memory instance will not "unload on idle" because the &lt;EM&gt;Created&lt;/EM&gt; state is technically not idle ... only something that is running can become idle.&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;&lt;FONT face=Tahoma size=2&gt;That means that the in-memory instance will remain in memory, consuming resources, until one of the following happens:&lt;/FONT&gt;&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;&lt;FONT face=Tahoma size=2&gt;&lt;EM&gt;WorkflowInstance.Start&lt;/EM&gt; is called - If the workflow is started then it will begin to process and, as such, is subject to whatever unloading policy the host has implemented.&lt;/FONT&gt; 
&lt;LI&gt;&lt;FONT face=Tahoma size=2&gt;&lt;EM&gt;WorkflowInstance.Abort&lt;/EM&gt; is called - &lt;EM&gt;Abort&lt;/EM&gt; causes the in-memory instance to be removed without any side effects on persistence.&amp;nbsp; Therefore, if you have not forced your &lt;EM&gt;Created&lt;/EM&gt; workflow to persist by calling &lt;EM&gt;Unload,&lt;/EM&gt; you can safely call &lt;EM&gt;Abort&lt;/EM&gt; to free up the resources and return to the same state as if you had never created the instance in the first place.&lt;/FONT&gt; 
&lt;LI&gt;&lt;FONT face=Tahoma size=2&gt;&lt;EM&gt;WorkflowInstance.Unload&lt;/EM&gt; is called - This will cause the &lt;EM&gt;Created&lt;/EM&gt; instance to unload to the persistence service.&amp;nbsp; The in-memory instance will be destroyed.&amp;nbsp; Note that an unloaded, &lt;EM&gt;Created&lt;/EM&gt; workflow will NOT start running when the runtime is started using our out of box &lt;EM&gt;SqlWorkflowPersistenceService&lt;/EM&gt;.&amp;nbsp; Only unblocked &lt;EM&gt;Started&lt;/EM&gt; workflows will automatically continue processing at startup.&lt;/FONT&gt; 
&lt;LI&gt;&lt;FONT face=Tahoma size=2&gt;&lt;EM&gt;WorkflowInstance.Terminate&lt;/EM&gt; is called - This will cause the in-memory instance to be destroyed and the persistence service to be notified that the instance has been terminated.&amp;nbsp; If the &lt;EM&gt;Created&lt;/EM&gt; instance was never unloaded, then this will have the same net effect as &lt;EM&gt;Abort&lt;/EM&gt;, but if it had been unloaded then &lt;EM&gt;Terminate&lt;/EM&gt; should be called if you really want to get rid of the unloaded instance.&lt;/FONT&gt; 
&lt;LI&gt;&lt;FONT face=Tahoma size=2&gt;&lt;EM&gt;WorkflowRuntime.Stop&lt;/EM&gt; is called - When the &lt;EM&gt;WorkflowRuntime&lt;/EM&gt; is stopped all the workflows are unloaded.&amp;nbsp; That means that the &lt;EM&gt;Created&lt;/EM&gt; workflow instance will be persisted just as if &lt;EM&gt;WorkflowInstance.Unload&lt;/EM&gt; had been called.&lt;/FONT&gt; 
&lt;LI&gt;&lt;FONT face=Tahoma size=2&gt;The process is exited without &lt;EM&gt;WorkflowRuntime.Stop&lt;/EM&gt; being called - This is the same as &lt;EM&gt;Abort&lt;/EM&gt;.&amp;nbsp; If the instance was never persisted then the in-memory version is lost and it was as though the instance were never created.&amp;nbsp; If the instance had been persisted then it can be retrieved and started at a later time.&lt;/FONT&gt;&lt;/LI&gt;&lt;/UL&gt;
&lt;P&gt;&lt;FONT face=Tahoma size=2&gt;&lt;STRONG&gt;In short, &lt;/STRONG&gt;if you've created a workflow instance which you don't want to consume any more resources, call &lt;EM&gt;Terminate&lt;/EM&gt; on it.&amp;nbsp; This will make sure that it is taken out of memory as well as cleaned up from any persistence service.&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=541522" width="1" height="1"&gt;</content><author><name>ntalbert</name><uri>http://blogs.msdn.com/ntalbert/ProfileUrlRedirect.ashx</uri></author></entry><entry><title>Replicator Tips and Tricks</title><link rel="alternate" type="text/html" href="http://blogs.msdn.com/b/advancedworkflow/archive/2006/02/23/538183.aspx" /><id>http://blogs.msdn.com/b/advancedworkflow/archive/2006/02/23/538183.aspx</id><published>2006-02-24T00:44:00Z</published><updated>2006-02-24T00:44:00Z</updated><content type="html">&lt;P&gt;&lt;FONT face=Tahoma size=2&gt;Here are a few tips and tricks for using the &lt;EM&gt;ReplicatorActivity&lt;/EM&gt; successfully.&amp;nbsp; It is a powerful activity which, when approached from the correct point of view, can be relatively easy to use.&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;&lt;STRONG&gt;&lt;FONT face=Tahoma size=2&gt;Definitions&lt;/FONT&gt;&lt;/STRONG&gt;&lt;/P&gt;
&lt;P&gt;&lt;FONT face=Tahoma size=2&gt;&lt;EM&gt;seed value&lt;/EM&gt; - a value added to either of the &lt;EM&gt;ReplicatorActivity&lt;/EM&gt;'s child collections.&amp;nbsp; This value is associated with a single instance of the replicator's child.&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;&lt;FONT face=Tahoma size=2&gt;&lt;EM&gt;template activity&lt;/EM&gt; - the direct child of the replicator is referred to as the template activity.&amp;nbsp; At replicator startup one instance of the template activity will be run for each seed value in the child data collection.&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;&lt;STRONG&gt;&lt;FONT face=Tahoma size=2&gt;Tips and Tricks&lt;/FONT&gt;&lt;/STRONG&gt;&lt;/P&gt;
&lt;P&gt;&lt;FONT face=Tahoma size=2&gt;&lt;EM&gt;&lt;STRONG&gt;Use your activity properties!&amp;nbsp;&lt;/STRONG&gt; &lt;/EM&gt;&lt;/FONT&gt;&lt;FONT face=Tahoma size=2&gt;Oftentimes users are confused as to where best to store the seed value.&amp;nbsp; It is with great dismay that the question, "Do I have to create a custom activity just to store the value?" is asked.&amp;nbsp; The answer is no, you don't have to create a custom activity just for value storage in some of the most common cases.&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;&lt;FONT face=Tahoma size=2&gt;First, if your replicator is executing in &lt;EM&gt;Sequence&lt;/EM&gt; and not &lt;EM&gt;Parallel&lt;/EM&gt; then you can store the variable wherever you like, even outside the scope of the replicator.&amp;nbsp; Despite the fact that the object containing the variable will not be cloned once per instance since you are executing in sequence, you will always only have one copy of the template activity active at any given time.&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;&lt;FONT face=Tahoma size=2&gt;Second, consider what you are doing with the value?&amp;nbsp; Chances are that the value you are using has some significance to one or more of the activities inside the template activity.&amp;nbsp; If this is the case, then store the value directly on that activity's property.&amp;nbsp; If more than one activity needs to access the data, then store it on one of them and use an &lt;EM&gt;ActivityBind&lt;/EM&gt; to reference it on the rest of them.&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;&lt;FONT face=Tahoma size=2&gt;Rarely you will find that the seed value is only used within the handler for a &lt;EM&gt;CodeActivity&lt;/EM&gt; or that it must undergo some serious computation before it is settable to any of the activity properties.&amp;nbsp; If this is the case, then you will need to create a custom activity which will act as your template activity.&amp;nbsp; This should, however, be the rare case - perhaps instead your activities should be written to accept the seed value instead of some transformed version!&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;&lt;FONT face=Tahoma size=2&gt;&lt;EM&gt;&lt;STRONG&gt;When is the UntilCondition evaluated?&lt;/STRONG&gt;&amp;nbsp; &lt;/EM&gt;The &lt;EM&gt;UntilCondition&lt;/EM&gt; is evaluated in two places; it is evaluated once before any activities are executed and once after each instance&amp;nbsp;of the template activity closes.&amp;nbsp; Note that if it returns true when the replicator first starts executing then the replicator will close immediately and will not execute any children.&amp;nbsp; Also, any children still executing when the &lt;EM&gt;UntilCondition&lt;/EM&gt; evaluates to true will be cancelled.&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;&lt;FONT face=Tahoma size=2&gt;&lt;EM&gt;&lt;STRONG&gt;How can I tell which child just closed when the&amp;nbsp;UntilCondition is evaluated?&lt;/STRONG&gt;&amp;nbsp; &lt;/EM&gt;Unfortunately, there is no built in mechanism for this; the &lt;EM&gt;UntilCondition&lt;/EM&gt; does not accept parameters and there is no global nor &lt;EM&gt;ReplicatorActivity&lt;/EM&gt; defined accessor for this kind of data.&amp;nbsp; There is, however, a suitable workaround for determining the newly completed activity.&amp;nbsp; &lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;&lt;FONT face=Tahoma size=2&gt;At a child's completion the replicator will first raise the &lt;EM&gt;ChildCompleted&lt;/EM&gt; event and then evaluate the &lt;EM&gt;UntilCondition&lt;/EM&gt;.&amp;nbsp; Since these two actions occur in the same method and Windows Workflow Foundation guarantees a single thread per workflow instance (see post on Parallelism) you can be sure that no other chlidren will execute between the event handler running and the condition evaluation.&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;&lt;FONT face=Tahoma size=2&gt;That said, some identifying data from the completed child can be stored in a well known location (variable on the root activity, property of some other activity, etc) during the &lt;EM&gt;ChildCompleted&lt;/EM&gt; handler and read from the location by the &lt;EM&gt;UntilCondition&lt;/EM&gt;.&lt;/FONT&gt;&lt;/P&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=538183" width="1" height="1"&gt;</content><author><name>ntalbert</name><uri>http://blogs.msdn.com/ntalbert/ProfileUrlRedirect.ashx</uri></author></entry><entry><title>Parallelism in Windows Workflow Foundation (WF)</title><link rel="alternate" type="text/html" href="http://blogs.msdn.com/b/advancedworkflow/archive/2006/02/23/538160.aspx" /><id>http://blogs.msdn.com/b/advancedworkflow/archive/2006/02/23/538160.aspx</id><published>2006-02-24T00:02:00Z</published><updated>2006-02-24T00:02:00Z</updated><content type="html">&lt;P&gt;&lt;FONT face=Tahoma size=2&gt;&lt;STRONG&gt;QUESTION:&lt;/STRONG&gt; Does a &lt;EM&gt;ParallelActivity&lt;/EM&gt; start one thread for each branch?&amp;nbsp; How many threads will it use for processing?&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;&lt;FONT face=Tahoma size=2&gt;&lt;STRONG&gt;ANSWER: &lt;/STRONG&gt;The short answers are "no" and "1".&amp;nbsp; If you've heard it before, then these answers make sense.&amp;nbsp; If not, then read on for an explanation of parallelism in WF.&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;&lt;FONT face=Tahoma size=2&gt;To promote programming simplicity for both the custom activity and workflow writer, WF has chosen to make the guarantee that only one .NET thread will be executing any portion of a workflow at any given time.&amp;nbsp; This means that the handler for your &lt;EM&gt;CodeActivity&lt;/EM&gt;, the &lt;EM&gt;Execute&lt;/EM&gt; method for your custom activity, and the sequence in your &lt;EM&gt;EventHandlerActivity&lt;/EM&gt;&amp;nbsp;will never have to worry that some other part of the workflow is executing at the same time.&amp;nbsp; If you think about it a bit you will see that this greatly simplifies programming WF applications and is one of the few reasons why writing complex custom activities is a surprisingly simple task.&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;&lt;STRONG&gt;&lt;FONT face=Tahoma size=2&gt;Isn't that bad?&lt;/FONT&gt;&lt;/STRONG&gt;&lt;/P&gt;
&lt;P&gt;&lt;FONT face=Tahoma size=2&gt;The usual knee-jerk reaction is outcry about the loss of multi-threaded parallelism.&amp;nbsp; Isn't that&amp;nbsp;a step backward?&amp;nbsp; The answer is no, this is not a step backward.&amp;nbsp; First, we are only stating that there is a single threaded nature&amp;nbsp;within a &lt;EM&gt;single instance&lt;/EM&gt; of a workflow.&amp;nbsp; This means that if you have two instances of the same workflow executing simultaneously then they will exhibit true multi-threaded parallelism.&amp;nbsp;&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;&lt;FONT face=Tahoma size=2&gt;Second, let's remember the nature of the workflow beast.&amp;nbsp; Workflows are meant to coordinate tasks, both human and computer, in an event driven world over an unknown amount of time.&amp;nbsp; The workflow itself processes in short bursts with long periods of dormancy in between.&amp;nbsp; For example, a workflow might send an e-mail requesting that a task be performed and then persist to the database.&amp;nbsp; Only when the task is now complete will the workflow come back to life and process some more ... and this processing should be limited to deciding what action should be taken next and delegating that to some external source whether that be a human or&amp;nbsp;a service added to the &lt;EM&gt;WorkflowRuntime&lt;/EM&gt;.&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;&lt;FONT face=Tahoma size=2&gt;This behavior of a single workflow instance means that true parallelism is wholy unnecessary.&amp;nbsp; If the workflow assigns 10 tasks in parallel then it is highly unlikely that there will be a processing bottleneck when collating the results which return&amp;nbsp;scattered across time.&amp;nbsp; While I've got no hard evidence to back this up, it is my opinion that a single thread per instance actually improves the performance of WF as opposed to hindering it when considering the reduced complexity in activity execution code, the lack of neccessity for locking constructs, and the burst processing nature of workflow.&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;&lt;STRONG&gt;&lt;FONT face=Tahoma size=2&gt;How it works&lt;/FONT&gt;&lt;/STRONG&gt;&lt;/P&gt;
&lt;P&gt;&lt;FONT face=Tahoma size=2&gt;WF is a scheduled environment.&amp;nbsp; Abstractly, you can consider that every instance of&amp;nbsp;a workflow has its own scheduler which is just a queue of delegates.&amp;nbsp; The scheduler simply loops through a 2 step sequence: dequeue the next delegate and call it.&amp;nbsp; If there are no more items on the queue then the workflow is idle.&amp;nbsp; If we consider that the scheduler just has one thread and invokes the delegates synchronously then we see where the single threaded guarantee comes from.&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;&lt;FONT face=Tahoma size=2&gt;During the execution of&amp;nbsp;a delegate (like &lt;EM&gt;Activity.Execute&lt;/EM&gt;) there are several occurrences which can cause new items to be added to the scheduler queue.&amp;nbsp; &lt;EM&gt;ActivityExecutionContext.ExecuteActivity&lt;/EM&gt;() can be used to schedule a child's execution, throwing an exception will cause the runtime to schedule the &lt;EM&gt;HandleFault&lt;/EM&gt; method for the activity, calling &lt;EM&gt;Activity.Invoke&lt;/EM&gt;&amp;lt;&amp;gt;() will cause the specified delegate to be scheduled, and returning a value of &lt;EM&gt;ActivityExecutionStatus.Closed&lt;/EM&gt; will cause the runtime to schedule the &lt;EM&gt;OnClosed&lt;/EM&gt; method.&amp;nbsp; These are just some of the triggers which cause new items to be added to the queue.&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;&lt;STRONG&gt;&lt;FONT face=Tahoma size=2&gt;Extended ParallelActivity walkthrough&lt;/FONT&gt;&lt;/STRONG&gt;&lt;/P&gt;
&lt;P&gt;&lt;FONT face=Tahoma size=2&gt;The &lt;EM&gt;ParallelActivity&lt;/EM&gt;, when executed, will schedule the &lt;EM&gt;Activity.Execute&lt;/EM&gt; method for each of its direct children and subscribe to the &lt;EM&gt;Closed&lt;/EM&gt; event for each child.&amp;nbsp; The result is that the scheduler queue will look something like this (first item to be dequeued is on left): &lt;BR&gt;{child1.Execute, child2.Execute, child3.Execute}&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;&lt;FONT face=Tahoma size=2&gt;Calling child1.Execute might result in a &lt;EM&gt;DelayActivity&lt;/EM&gt;'s &lt;EM&gt;Execute&lt;/EM&gt; to be added to the queue: {child2.Execute, child3.Execute, delay1.Execute}&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;&lt;FONT face=Tahoma size=2&gt;Now consider if child2 contains a single &lt;EM&gt;CodeActivity&lt;/EM&gt; and child3 is empty: &lt;BR&gt;&lt;/FONT&gt;&lt;FONT face=Tahoma size=2&gt;{delay1.Execute, code1.Execute, child3.OnClose}&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;&lt;FONT face=Tahoma size=2&gt;Up to this point we have had purely interleaved execution.&amp;nbsp; Draw it out and remember that in normal execution an activity will have &lt;EM&gt;Execute&lt;/EM&gt; schedule, then it will schedule any work it needs to do, then it will have &lt;EM&gt;OnClose&lt;/EM&gt; scheduled, and then anyone listening to the&amp;nbsp;&lt;EM&gt;Closed&lt;/EM&gt; event will be scheduled.&amp;nbsp; Knowing this you can walk through almost any chain of activities.&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;&lt;FONT face=Tahoma size=2&gt;Back to our queue, we will next see the delay disappear because it has added a timer to the workflow, we will see the code activity execute, and the third child will process its close:&lt;BR&gt;&lt;/FONT&gt;&lt;FONT face=Tahoma size=2&gt;{code1.OnClose, parallel.OnChildClosed(child3)}&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;&lt;FONT face=Tahoma size=2&gt;Without drawing it out, let's say that the delay was a long enough one to let child2 close as well before the timer is fired.&amp;nbsp; The result will be that the &lt;EM&gt;ParallelActivity&lt;/EM&gt;'s &lt;EM&gt;Closed&lt;/EM&gt; handler will determine that the parallel cannot yet close because it has an outstanding executing child and the scheduler will run out of items in the queue and mark the workflow as idle.&amp;nbsp; The next exciting thing to happen is the timer will schedule a callback for the delay: &lt;BR&gt;{delay1.OnTimer}&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;&lt;FONT face=Tahoma size=2&gt;Again, this will cause child1.OnClose to be scheduled which will cause parallel.OnChildClosed(child1) to be scheduled which, finally, will result in parallel.OnClose being scheduled.&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;&lt;FONT face=Tahoma size=2&gt;The important thing to notice is that with non-blocking activities we get predictable interleaved execution.&amp;nbsp; But, as soon as we add a blocking activity, the delay in our case, we get execution that approaches real world workflow scenarios.&amp;nbsp; Imagine that each branch has an event on which it is waiting ... the execution is no longer just interleaved, but whichever branch's event fires first gets executed first.&amp;nbsp; Unless all of the branches receive their events simultaneously, we get parallel processing with a single thread of execution.&lt;/FONT&gt;&lt;/P&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=538160" width="1" height="1"&gt;</content><author><name>ntalbert</name><uri>http://blogs.msdn.com/ntalbert/ProfileUrlRedirect.ashx</uri></author></entry><entry><title>ActivityExecutionStatus.Faulting</title><link rel="alternate" type="text/html" href="http://blogs.msdn.com/b/advancedworkflow/archive/2006/02/23/537428.aspx" /><id>http://blogs.msdn.com/b/advancedworkflow/archive/2006/02/23/537428.aspx</id><published>2006-02-23T03:23:00Z</published><updated>2006-02-23T03:23:00Z</updated><content type="html">&lt;P&gt;&lt;FONT face=Tahoma size=2&gt;&lt;STRONG&gt;QUESTION:&lt;/STRONG&gt; Can I return &lt;EM&gt;ActivityExecutionStatus.Faulting&lt;/EM&gt; from a signal method to "fault" the activity?&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;&lt;FONT face=Tahoma size=2&gt;&lt;STRONG&gt;ANSWER: &lt;/STRONG&gt;As a general rule only the current status and &lt;EM&gt;Closed&lt;/EM&gt; are valid return values from any of the signal methods.&amp;nbsp; Therefore:&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;&lt;FONT face=Tahoma size=2&gt;&lt;EM&gt;Execute&lt;/EM&gt;() – Return values of &lt;EM&gt;ActivityExecutionStatus.Executing&lt;/EM&gt; and &lt;EM&gt;Closed&lt;/EM&gt; valid. &lt;BR&gt;&lt;EM&gt;Cancel&lt;/EM&gt;() – Return values of &lt;EM&gt;ActivityExecutionStatus.Canceling&lt;/EM&gt; and &lt;EM&gt;Closed&lt;/EM&gt; valid. &lt;BR&gt;&lt;EM&gt;HandleFault&lt;/EM&gt;() – Return values of &lt;EM&gt;ActivityExecutionStatus.Faulting&lt;/EM&gt; and &lt;EM&gt;Closed&lt;/EM&gt; valid. &lt;BR&gt;&lt;EM&gt;Compensate&lt;/EM&gt;() – Return values of &lt;EM&gt;ActivityExecutionStatus.Compensating&lt;/EM&gt; and &lt;EM&gt;Closed&lt;/EM&gt; valid.&amp;nbsp;&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;&lt;FONT face=Tahoma size=2&gt;If an invalid value is returned from a singal method then the runtime will throw an InvalidOperationException which will propagate asynchronously just like any other exception occuring within the workflow.&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;&lt;FONT face=Tahoma size=2&gt;If you are in any signal method and would like to “fault” the activity then simply throw an exception from the method.&amp;nbsp; For example:&lt;/FONT&gt;&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;&lt;FONT face=Tahoma size=2&gt;&lt;EM&gt;Execute&lt;/EM&gt;() is called on the activity and the activity logic determines there is an error.&amp;nbsp; An exception is thrown from execute.&lt;/FONT&gt;&lt;/LI&gt;
&lt;LI&gt;&lt;FONT face=Tahoma size=2&gt;The runtime will then capture the exception and pass it to the &lt;EM&gt;HandleFault&lt;/EM&gt;() method of that activity.&amp;nbsp; If &lt;EM&gt;HandleFault&lt;/EM&gt; returns a value of &lt;EM&gt;Faulting&lt;/EM&gt; then the runtime will simply continue processing the scheduler queue expecting that at some later point the activity will signal closed (see below).&amp;nbsp; If &lt;EM&gt;HandleFault&lt;/EM&gt; returns &lt;EM&gt;Closed&lt;/EM&gt; then the runtime knows that the activity has done what it needs to handle the fault.&lt;/FONT&gt;&lt;/LI&gt;
&lt;LI&gt;&lt;FONT face=Tahoma size=2&gt;If the activity in question is composite and has a FaultHandlersActivity with a matching FaultHandler then the exception will be considered handled and will be passed to the appropriate FaultHandler.&amp;nbsp; Otherwise, the exception will be passed to the parent activity’s &lt;EM&gt;HandleFault&lt;/EM&gt; method and the cycle will repeat until either the fault has found a FaultHandlerActivity which matches or has left the scope of the workflow causing it to Terminate.&lt;/FONT&gt;&lt;/LI&gt;&lt;/UL&gt;
&lt;P&gt;&lt;FONT face=Tahoma size=2&gt;When would an activity want to return &lt;EM&gt;Faulting&lt;/EM&gt;?&amp;nbsp; Consider the case where ParallelActivity has its &lt;EM&gt;HandleFault&lt;/EM&gt; method called.&amp;nbsp; The parallel may have one&amp;nbsp;or more currently executing branches which do not yet know about the exception.&amp;nbsp; At this time, the parallel will attempt to cancel the children by subscribing to the closed status change and calling &lt;EM&gt;ActivityExecutionContext.CancelActivity&lt;/EM&gt; for each child.&amp;nbsp; The return value from &lt;EM&gt;HandleFault&lt;/EM&gt; will be &lt;EM&gt;ActivityExecutionStatus.Faulting&lt;/EM&gt; because the parallel should not closed until all of its children have cancelled successfully.&amp;nbsp; Once the parallel has received close notification from each child it can then call &lt;EM&gt;ActivityExecutionContext.CloseActivity&lt;/EM&gt;() to signal its own completion and cause the asynchronous exception handling to resume.&lt;/FONT&gt;&lt;/P&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=537428" width="1" height="1"&gt;</content><author><name>ntalbert</name><uri>http://blogs.msdn.com/ntalbert/ProfileUrlRedirect.ashx</uri></author></entry></feed>