Search

Search:

Namespace:

Search Result
.
  • Object
.
    • DependencyObject
.

OnActivityExecutionContextLoad also has a component of being paired with Dispose to represent the activity's memory lifetime as it applies to the CLR. Activities which run almost never have their constructors called directly but instead these activities are deserialized from a stream. That means that OnActivityContextLoad is really the only safe place to do object creation time resource allocation. Dispose is called before an object is released for garbage collection by the runtime and could be used for NonSerialized activity fields which need to be closed, disposed, or cleaned up in any other way.

.

Initialize and Uninitialize are the markers for an activity's execution lifetime. For the rest of this paragraph any reference to an "activity instance" will have NOTHING to do with its CLR lifetime. Since workflows are unloaded and loaded numerous times during execution a single "activity instance" could actually have been executed through the use of dozens of CLR object instances of the activity. That said, before an "activity instance" will take part in any of the protocol signals it will be initialized; this includes the initialization of cloned activities which execute in new contexts. Once an "activity instance" is done taking part in the protocol it will have Uninitialize called.

.

OnActivityExecutionContextUnload is called before an activity is saved to the database. This means that if the workflow is unloading then this method will be called … additionally if a context is being saved for future compensation then this method will be called on all contained activities. Note that PersistOnClose does not cause OnActivityExecutionContextUnload to be called for the workflow as a whole because the workflow object is not being jettisoned from memory.

.

Dispose is called when we are done with the .NET object representing the activity. So, in the workflow unload case, we will call OnActivityExecutionContextUnload immediately followed by Dispose. In the case of PersistOnClose we will NOT call Dispose on any activities. Only when the activity is being removed from memory will Dispose be called.

.

Finally, the reason for the caveat that Uninitialize is an “approximation” of .NET’s Dispose is because you can execute disposed activities again. It is possible to “rerun” an activity which has executed and closed in another context. Using a Page Flow example, imagine that you have an activity which represents Page3. The user enters data into Page3 which you save as fields on your activity. They then proceed to Page4 causing WF to close Page3 and then call Uninitialize. It is possible to model the user pressing the Back button by simply creating a new context and passing the closed Page3 object from the old context as the template activity. This will cause the Page3 object to have Initialize called again, but all of the data will be intact just as it was left at the return of the previous Uninitialize call.

.

A serialization surrogate gives you an object that can handle the serialization requirements of a different object and, if necessary, can transform the serialized data.

.

An ActivityTrackingLocation contains match conditions for a location or locations in the potential execution path of a workflow instance. You can add ActivityTrackingLocation objects to MatchingLocations to define locations that should be included in matching by the track point. Similarly, you can add ActivityTrackingLocation objects to ExcludedLocations to define locations that should be excluded from matching by the track point.

.

You can add ActivityDataTrackingExtract objects and WorkflowDataTrackingExtract objects to Extracts to specify data that should be extracted from the workflow instance when the track point is matched. The extracted data will be included in the ActivityTrackingRecord.

.
    public override ValidationErrorCollection Validate(ValidationManager manager, object obj)
.

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

.

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

.
Summary
.

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.

.
    Event ReceiveSomeData(ByVal sender As Object, ByVal e As ReceiveSomeDataEventArgs)
.
    Public Event ReceiveSomeData(ByVal sender As Object, ByVal e As ReceiveSomeDataEventArgs) Implements IDemoDataExchange.ReceiveSomeData
.
    Private Sub InternalSendSomeData(ByVal stateObj As Object)
.

OK so far so good. We have an IronPython class with the code we need. Now we still need to create an object from it. This isn't hard it just takes a bit more doing than just using the New operator.

.

Instantiating the IronPython object

.

We need to load the file with the Python source code using an IronPython ScriptRuntime. Once done we can get a reference to the IronPython type object using the GetVariable() function by passing in the class name. Once we have that we call the ScriptRuntime again to actually create the object. See the code below:

.
    Private Sub WebServiceInvoking(ByVal sender As System.Object, ByVal e As System.Workflow.Activities.InvokeWebServiceEventArgs)
.
    Public Sub OnEvent(ByVal sender As Object, ByVal e As QueueEventArgs) Implements IActivityEventListener(Of QueueEventArgs).OnEvent
.
        Dim item As Object = queue.Dequeue()
.

private void codeActivity1_ExecuteCode(object sender, EventArgs e)

.

private void checkSalesAmount(object sender, ConditionalEventArgs e)

.

private void delayActivity1_InitializeTimeoutDuration(object sender, EventArgs e)

.

The SynchrnoizationHandles property contains the handles that the workflow will acquire before it executes, and release upon completion. A synchronization handle is a simple string object, and the property maintains a collection of strings. Internally, the WF runtime will use each string as the key to a Dictionary of locks. If the activity cannot acquire all the locks specified by the handles, it will wait until it can acquire the locks.

.

The InitialChildData property will hold the list of data objects for the Replicator to process. The Replicator will create a clone of its child activity to process each item. The Replicator will not finish execution until all the children have finished, however, there is an UntilCondition property that the Replicator will evaluate before starting, and after completion of each child. If the UntilCondition returns true, the Replicator will finish execution, even if it leaves children unprocessed. Like other conditions in WF, the UntilCondition can be a rule condition or a code condition.

.
  object sender, EventArgs e)
.

The Roles property of this activity can bind to a WorkflowRoleCollection object and allow the runtime to perform role-based authorization checks. The runtime compares the role memberships of the incoming identity against the allowable roles defined in the collection. The collection holds objects derived from the abstract WorkflowRole class. WF provides two concrete implementations of WorkflowRole with the ActiveDirectoryRole and WebWorkflowRole classes. These classes work with Active Directory and ASP.NET 2.0 Role Providers, respectively. If authorization fails, the runtime will throw a WorkflowAuthorizationException exception.

.

Notice each rule has a priority assigned. Rules with a higher priority will execute before rules with a lower priority. Each rule has an If-Then-Else format, and we can assign actions to both the Then and Else results. A rule's actions can modify the fields and properties of a workflow, or the fields and properties of an object inside the workflow. Actions can also invoke methods.

.
Summary
.

C# is an object-oriented programming language developed by Microsoft as part of their .NET initiative, and later approved as a standard by ECMA and ISO. C# has a procedural, object-oriented syntax based on C+ that includes aspects of several other programming languages (most notably Delphi, Visual Basic, and Java) with a particular emphasis on simplification (fewer symbolic requirements than C+, fewer decorative requirements than Java citation needed).

.
Summary
.

DesignerSerializerAttribute provides a way to indicate to the designer serialization manager that a specific type of serializer should be used when serializing the values of an object. This allows you to specify a serializer that is capable of serializing a custom type, for instance. Place this attribute on a class to indicate the serialization object to use when serializing the class.

.
        protected override bool CanSerializeToString(WorkflowMarkupSerializationManager serializationManager, object value)
.
        protected override void OnBeforeSerialize(WorkflowMarkupSerializationManager serializationManager, object obj)
.
        protected override void OnAfterSerialize(WorkflowMarkupSerializationManager serializationManager, object obj)
.
        protected override void OnBeforeDeserialize(WorkflowMarkupSerializationManager serializationManager, object obj)
.
        protected override void OnAfterDeserialize(WorkflowMarkupSerializationManager serializationManager, object obj)
.
        protected override string SerializeToString(WorkflowMarkupSerializationManager serializationManager, object value)
.
        protected override object DeserializeFromString(WorkflowMarkupSerializationManager serializationManager, Type propertyType, string value)
.
        public object GetSerializer(IDesignerSerializationManager manager, object currentSerializer, Type objectType, Type serializerType)
.
            if (objectType == typeof(List<PageParameter>))
.
    Event ReceiveSomeData(ByVal sender As Object, ByVal e As ReceiveSomeDataEventArgs)
.
    Public Event ReceiveSomeData(ByVal sender As Object, ByVal e As ReceiveSomeDataEventArgs) Implements IDemoDataExchange.ReceiveSomeData
.
    Private Sub InternalSendSomeData(ByVal stateObj As Object)
.
Summary
An event from a ExternalDataExchange object for the workflow is not getting raised.
.

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.".

.

private void codeActivity1_ExecuteCode(object sender, EventArgs e)

.

private void checkSalesAmount(object sender, ConditionalEventArgs e)

.

private void delayActivity1_InitializeTimeoutDuration(object sender, EventArgs e)

.

The SynchrnoizationHandles property contains the handles that the workflow will acquire before it executes, and release upon completion. A synchronization handle is a simple string object, and the property maintains a collection of strings. Internally, the WF runtime will use each string as the key to a Dictionary of locks. If the activity cannot acquire all the locks specified by the handles, it will wait until it can acquire the locks.

.

The InitialChildData property will hold the list of data objects for the Replicator to process. The Replicator will create a clone of its child activity to process each item. The Replicator will not finish execution until all the children have finished, however, there is an UntilCondition property that the Replicator will evaluate before starting, and after completion of each child. If the UntilCondition returns true, the Replicator will finish execution, even if it leaves children unprocessed. Like other conditions in WF, the UntilCondition can be a rule condition or a code condition.

.
  object sender, EventArgs e)
.

The Roles property of this activity can bind to a WorkflowRoleCollection object and allow the runtime to perform role-based authorization checks. The runtime compares the role memberships of the incoming identity against the allowable roles defined in the collection. The collection holds objects derived from the abstract WorkflowRole class. WF provides two concrete implementations of WorkflowRole with the ActiveDirectoryRole and WebWorkflowRole classes. These classes work with Active Directory and ASP.NET 2.0 Role Providers, respectively. If authorization fails, the runtime will throw a WorkflowAuthorizationException exception.

.

Notice each rule has a priority assigned. Rules with a higher priority will execute before rules with a lower priority. Each rule has an If-Then-Else format, and we can assign actions to both the Then and Else results. A rule's actions can modify the fields and properties of a workflow, or the fields and properties of an object inside the workflow. Actions can also invoke methods.

.

The wca.exe tool provides the command-line parameters /collapseArgs and /includeSender to determine how the HandleExternalEventActivity subclass properties appear. The /collapseArgs parameter causes a single E property of the EventArgs type to be generated instead of the default, which generates one property on the activity for each public property and field of the EventArgs. The /includeSender parameter includes a Sender property on the activity of type object, which is populated by the sender parameter of the event handler.

.

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.

.

Part of the WorkflowInstance object.

.
Summary
Gets a collection of WorkflowQueueInfo objects that contains the pending items and subscribed activities for the workflow queues associated with this workflow instance.
.
Summary
.
    Public Sub OnEvent(ByVal sender As Object, ByVal e As QueueEventArgs) Implements IActivityEventListener(Of QueueEventArgs).OnEvent
.

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.

.

static void TimerCallback(object state)

.
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.

.

This event is only used for processing that must occur synchronously with event delivery to the workflow instance. The event handler may execute on a non-workflow thread so access to any shared workflow object, for example, any DependencyObject, must be protected.

.

This code works just fine Smile until we decide to split it up and recreate the WCF proxy object for part two like this Sad:

.

In this case the second proxy object doesn't work at all and we get a FaultException with the message "There is no context attached to incoming message for the service and the current operation is not marked with "CanCreateInstance = true". In order to communicate with this service check whether the incoming binding supports the context protocol and has a valid context initialized."

.

So what gives? Well the WCF service needs to know which workflow the request needs to be routed to and uses the WorkflowInstanceId to do so. When you create a proxu and do the first call this WorkflowInstanceId is automatically added and resent with the next request. So we need to retrieve this WorkflowInstanceId and, when we create the second proxy object, add it again. Doing so turns out to be pretty simple and only takes a few extra lines of code:

.

Basically what we need to do is retrieve the workflow instanceId from the context that is returned with the first call and save that. Next time we create a new proxy object we need to store the same instanceId in the request context before we actually use it and we are good to go Smile Setting the instaceId is actually done through a ClientContextProtocol object which is returned as an IContextManager inside of the channel properties. Check the bold lines in the previous code sample.

.

The way to keep them apart is by specifying the ContextToken for the two ReceiveActivity objects. The picture below shows the ReceiveActivity to the right, the one to the left has a ContextToken named LeftBranch.

.

private void codeActivity1_ExecuteCode(object sender, EventArgs e)

.

A RuleSet is stored in an XML file with the same name as the workflow and the extension .rules. Basically they are stored as an XML representation of a CodeDom object. The same rules file also contains all the Declarative Rule Condition used in the Workflow.

.
Summary
.

When the .NET runtime needs to create an object it first needs to load the assembly the type is stored in. The basics of that process is not all that complex to understand and is a must know for every .NET developer. If the type is stored in the same assembly everything is nice and cozy as no searching for assemblies has to be done. Now the process gets interesting when the runtime has to go and search for another assembly.

.

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

.

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

.

In this post I demonstrated how to create IronPython objects and call them from strongly typed .NET code. So suppose we want to do so with Windows Workflow Foundation where could we use this?

.

I included two implementations, lets first look at the ConsoleDisplayMessageService as this is the simpler of the two. All this does is use the Python print function to display the message on the console. The first parameter, self, is the current object, so this in C# or me in VB. We make sure this class derives from IDisplayMessageService by specifying this after the class name. BTW the lines starting with # are comments in Python.

.
    object service = runtime.Operations.Call(pythonType);
.

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.

.

And the second cool behavior is that you can actually execute multiple SetStateActivity objects with different TargetStateName definitions and only the last one will be executed. Again something that might make the logic quite a bit simple as you can set a default TargetStateName at the beginning of your and make changes to the target as exceptional cases arise.

.

First, let's fully understand the ActivityExecutionContext. This object is passed to all scheduled calls either as a specific parameter (Execute, Cancel, HandleFault) or as the sender (QueueItemAvailable handler, StatusChanged handler). 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.

.

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. You'll notice, however, that the Guid associated with the context does not change.

.

Therefore, the sender above will always be the right one even if the delay is not in a context spawning activity. 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. 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.

.

If you want to see the tracking data itself you might start running SQL queries against the database. An easier way is to use the SqlTrackingQuery.TryGetWorkflow() to load an SqlTrackingWorkflowInstance instance with the tracking data. The SqlTrackingWorkflowInstance object as properties to inspect the workflow status, the workflow events, the activity events and more.

.

SqlTrackingQuery provides methods and properties that you can use in order to access certain kinds of tracking data stored in a SQL database by the SqlTrackingService. Access to this data is provided through SqlTrackingWorkflowInstance objects. You can call TryGetWorkflow to try to get a SqlTrackingWorkflowInstance for a specific workflow instance. You can call GetWorkflows to get a collection of SqlTrackingWorkflowInstance objects that correspond workflow instances that have tracking data that matches a set of query parameters contained in a SqlTrackingQueryOptions that is passed as a parameter to the method. See the SqlTrackingWorkflowInstance class for more information about the kind of tracking data available and see the SqlTrackingQueryOptions class for more information about the kind of queries that can be performed.

.

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.

.

The SqlWorkflowPersistenceService participates in the workflow transaction batching functionality provided by the WorkflowCommitWorkBatchService. That is, it adds objects that represent pending writes to the SQL database to the WorkBatch and implements the IPendingWork interface.

.

Is a member of the StateMachineWorkflowInstance object.

.

static void workflowRuntime_WorkflowIdled(object sender, WorkflowEventArgs e)

.

In UnitTestingWorkflowActivities I demonstrated how TypeMock allowed us to mock out the workflow runtime infrastructure and create true unit tests for a workflow activity. Now this may not seems like a big deal but because most of the classes in the workflow runtime are sealed mocking them with another mocking framework is pretty much impossible. Now you can test custom activities by wrapping them in a test workflow, creating a workflow runtime, running the workflow and inspecting the result I can hardly call this a unit test as we have to create lots of, complex, depended objects. So TypeMock really opens up some new possibilities here.

.
    void queue_QueueItemAvailable(object sender, QueueEventArgs e)
.
        RecorderManager.CreateMockedObject<ActivityExecutionContext>();
.
    WorkflowQueuingService wqs = RecorderManager.CreateMockedObject<WorkflowQueuingService>();
.
    WorkflowQueue queue = RecorderManager.CreateMockedObject<WorkflowQueue>();
.
    WriteLineService3 service = RecorderManager.CreateMockedObject<WriteLineService3>();
.
        RecorderManager.CreateMockedObject<ActivityExecutionContext>();
.
    WorkflowQueuingService wqs = RecorderManager.CreateMockedObject<WorkflowQueuingService>();
.
    WorkflowQueue queue = RecorderManager.CreateMockedObject<WorkflowQueue>();
.
    WriteLineService3 service = RecorderManager.CreateMockedObject<WriteLineService3>();
.
        queue.QueueItemAvailable += delegate(object sender, QueueEventArgs e) { };
.
        RecorderManager.CreateMockedObject<ActivityExecutionContext>();
.
    WorkflowInstance instance = RecorderManager.CreateMockedObject<WorkflowInstance>();
.

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.

.
        RecorderManager.CreateMockedObject<ActivityExecutionContext>();
.
        RecorderManager.CreateMockedObject<WriteLineService2>();
.

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

.
    Private Sub codeActivity1_ExecuteCode(ByVal sender As Object, _
.
    Private Sub SetupData(ByVal sender As Object, _
.
    Private Sub RepeatUntill(ByVal sender As Object, _
.
    Private Sub SetupChildData(ByVal sender As Object, _
.
    Private Function GetCurrentData(ByVal sender As Object)
.

A UserTrackingLocation contains match conditions for a location associated with a user event in the potential execution path of a workflow instance. You can add UserTrackingLocation objects to MatchingLocations to define locations that should be included in matching for the track point. Similarly, you can add UserTrackingLocation objects to ExcludedLocations to define locations that should be excluded from matching for the track point.

.

You can add ActivityDataTrackingExtract objects and WorkflowDataTrackingExtract objects to Extracts to specify data that should be extracted from the workflow instance when the track point is matched. The extracted data will be returned in UserTrackingRecord.Body.

.
            return (string)base.Invoke(typeof(Interface1), "myMethod", true, new object[] { s })[0];
.

When creating a subclass overrule the abstract PerformLoad function to load the workflow. Use the GetService function to retreive a IDesignerHost reference and use its Container object to add all the workflow activities to the designer surface. See the WorkflowUtils GetAllActivities function for an easy way to get a reference to all nested activities.

.

A workflow or activity designer can use WorkflowMarkupSerializationManager to access services useful to managing design-time serialization processes. For example, WorkflowMarkupSerializationManager can be used to create objects, look up types, identify objects, and customize the serialization of particular types.

.

Dim parameters As New Dictionary(Of String, Object)

.
    PublicFunction GetParameters() As Dictionary(Of String, Object)
.
        Dim result As New Dictionary(Of String, Object)
.

The workflowInstance variable doesn’t point to an object of the workflow type itself but to a wrapper containing the workflow definition. So it simply doesn’t have the properties you defined in the workflow.

.

The QueueName property can actually return an object of type EventQueueName when using an HandleExternalEventActivity.

.
Summary
.
Summary
.

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

.

Although a runtime service can be any type of object using this as a base class has some benefits. It provides a handle to the Runtime and notifies the service that the runtime has started through the OnStarted event and when it is stopped through the OnStopped event.

.
    protected override bool CanSerializeToString(WorkflowMarkupSerializationManager serializationManager, object value)
.
    protected override void OnBeforeSerialize(WorkflowMarkupSerializationManager serializationManager, object obj)
.
    protected override void OnAfterSerialize(WorkflowMarkupSerializationManager serializationManager, object obj)
.
    protected override void OnBeforeDeserialize(WorkflowMarkupSerializationManager serializationManager, object obj)
.
    protected override void OnAfterDeserialize(WorkflowMarkupSerializationManager serializationManager, object obj)
.
    protected override string SerializeToString(WorkflowMarkupSerializationManager serializationManager, object value)
.
    protected override object DeserializeFromString(WorkflowMarkupSerializationManager serializationManager, Type propertyType, string value)
.
    public object GetSerializer(IDesignerSerializationManager manager, object currentSerializer, Type objectType, Type serializerType)
.
        if (objectType == typeof(List<Parameter>))