Using a State Machine Workflow from ASP.NET and Winform
Hello,
I am developing an application, where parts are accessed as an ASP.Net Page and some parts are used in an winform application. I am using the state machine paradigm to connect the two. In order to get the reuse I wanted, I have encapsulated all business functionality (including the workflow) into a business layer (dll) which is called from both presentation layers.
Everything works well, except for the SQLWorkflowPersistenceService. It does not store any information into the PersistenceStore.
Has anybody run into similar problems and can give me a hint if this use case is not supported or am I just doing something wrong?
Thanks for the help,
Bjoern
Can you describe, how you configured the SqlWorkflowPersistenceService ?
Maybe the problem is not the architecture of your application, but simply the setup and use of the persistence service.
Hi Iris,
here is my web.xml. The workflow-runtime section is not used by the application. The configuration is coded into a WorkflowSingleton.
<?xml version="1.0"?>
<configuration>
<configSections>
<section name="WorkflowRuntime" type="System.Workflow.Runtime.Configuration.WorkflowRuntimeSection, System.Workflow.Runtime, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
</configSections>
<appSettings/>
<connectionStrings>
<add name="DataModels.DataManager" connectionString="Data Source=BPDEMO04;Initial Catalog=KitchenService;Integrated Security=True" providerName="System.Data.SqlClient"/>
<add name="WorkflowPersistenceConnection" connectionString="Data Source=BPDEMO04;Initial Catalog=PersistenceStore;Integrated Security=True" providerName="System.Data.SqlClient"/>
<add name="WorkflowTrackingConnection" connectionString="Initial Catalog=TrackingStore;Data Source=localhost;Integrated Security=SSPI;" providerName="System.Data.SqlClient"/>
</connectionStrings>
<system.web>
<compilation debug="true">
<assemblies>
<add assembly="System.Data.OracleClient, Version=2.0.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089"/>
<add assembly="System.Workflow.Activities, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
<add assembly="System.Workflow.ComponentModel, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
<add assembly="System.Workflow.Runtime, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
<add assembly="Microsoft.Build.Tasks, Version=2.0.0.0, Culture=neutral, PublicKeyToken=B03F5F7F11D50A3A"/>
<add assembly="System.Messaging, Version=2.0.0.0, Culture=neutral, PublicKeyToken=B03F5F7F11D50A3A"/>
<add assembly="System.Design, Version=2.0.0.0, Culture=neutral, PublicKeyToken=B03F5F7F11D50A3A"/>
<add assembly="System.Runtime.Remoting, Version=2.0.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089"/>
<add assembly="System.DirectoryServices, Version=2.0.0.0, Culture=neutral, PublicKeyToken=B03F5F7F11D50A3A"/>
<add assembly="System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089"/>
<add assembly="Microsoft.Build.Utilities, Version=2.0.0.0, Culture=neutral, PublicKeyToken=B03F5F7F11D50A3A"/>
<add assembly="System.Transactions, Version=2.0.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089"/>
<add assembly="Microsoft.Build.Framework, Version=2.0.0.0, Culture=neutral, PublicKeyToken=B03F5F7F11D50A3A"/></assemblies></compilation>
<authentication mode="Windows"/>
</system.web>
<WorkflowRuntime Name="WorkflowRuntime">
<CommonParameters>
<add name="ConnectionString" value="Data Source=BPDEMO04;Initial Catalog=PersistenceStore;Integrated Security=True" />
</CommonParameters>
<Services>
<add type="System.Workflow.Runtime.Hosting.ManualWorkflowSchedulerService, System.Workflow.Runtime, Version=3.0.00000.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
<add type="System.Workflow.Runtime.Hosting.DefaultWorkflowTransactionService, System.Workflow.Runtime, Version=3.0.00000.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
<add type="System.Workflow.Runtime.Hosting.SqlWorkflowPersistenceService, System.Workflow.Runtime, Version=3.0.00000.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" UnloadOnIdle="true" ConnectionString="Initial Catalog=PersistenceStore;Data Source=localhost;Integrated Security=SSPI;"/>
<add type="System.Workflow.Runtime.Tracking.SqlTrackingService, System.Workflow.Runtime, Version=3.0.00000.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" ConnectionString="Initial Catalog=TrackingStore;Data Source=localhost;Integrated Security=SSPI;"/>
</Services>
</WorkflowRuntime>
</configuration>
Here is the WorkflowSingleton which does the actual configuration:
using System;
using System.Collections.Generic;
using System.Text;
using System.Workflow.Runtime;
using System.Workflow.Runtime.Hosting;
using System.Workflow.Runtime.Tracking;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
namespace Core
{
public sealed class WorkflowRuntimeFactory
{
private static volatile WorkflowRuntimeFactory instance;
private static object SyncRoot = new Object();
private static WorkflowRuntime _WFRuntime = null;
private WorkflowRuntimeFactory()
{
// create a new Workflow Runtime for this application
_WFRuntime = new WorkflowRuntime();
// check, if the SQL persistence service is already active
ReadOnlyCollection<object> wfInstances = _WFRuntime.GetAllServices(typeof(SqlWorkflowPersistenceService));
if (wfInstances.Count == 0)
{
// stop the runtime in order to add the new persistence
if (_WFRuntime.IsStarted == true)
_WFRuntime.StopRuntime();
// get the connection string
string connectionString = System.Configuration.ConfigurationManager.ConnectionStrings["WorkflowPersistenceConnection"].ConnectionString;
string trackingConnectionString = System.Configuration.ConfigurationManager.ConnectionStrings["WorkflowTrackingConnection"].ConnectionString;
// set the SQL Persistence Service parameter
NameValueCollection parameters = new NameValueCollection();
parameters.Add("ConnectionString", connectionString);
parameters.Add("OwnershipTimeoutSeconds", "10");
parameters.Add("UnloadOnIdle", true.ToString());
parameters.Add("LoadIntervalSeconds", "1");
// make the workflow SQL-persistent
SqlWorkflowPersistenceService sqlPersistence = new SqlWorkflowPersistenceService(parameters);
_WFRuntime.AddService(sqlPersistence);
// add the tracking service
SqlTrackingService sqlTracking = new SqlTrackingService(trackingConnectionString);
_WFRuntime.AddService(sqlTracking);
// add the ManualWorkflowSchedulerService
ManualWorkflowSchedulerService mService = new ManualWorkflowSchedulerService();
_WFRuntime.AddService(mService);
}
// start the runtime to activate the changes
if (_WFRuntime.IsStarted == false)
_WFRuntime.StartRuntime();
}
~WorkflowRuntimeFactory()
{
// check, if a runtime has been initialized
if (_WFRuntime != null)
{
// get all active workflow instances
ReadOnlyCollection<WorkflowInstance> wfInstances = _WFRuntime.GetLoadedWorkflows();
// loop through the workflow instances and unload them to the persistence store
foreach (WorkflowInstance workflowInstance in wfInstances)
{
workflowInstance.Unload();
}
}
}
public static WorkflowRuntime Instance
{
get
{
if (instance == null)
{
lock (SyncRoot)
{
if (instance == null)
instance = new WorkflowRuntimeFactory();
}
}
return _WFRuntime;
}
}
}
}
I hope it is only a configuration problem.
Best Regards,
Bjoern
Hi Bjoern,
did you check that the persistence service does work with your settings ?
E.g. in a small console application example ?
Hi Iris,
I have tried the ASPNETWithPersistenceAndTracking Example from the worksite, works fine and persists everything. It must be something in the application.
Best Regards,
Bjoern
To get more information, you could switch on tracing.
My experience was that messages about errors and exceptions in the services sometimes do not appear on the console. But you see them in the .log file.
How do I enable logging for WWF and where can I find the log-files?
Which is the version of WF you have installed ?
There are some differences in the settings in Beta 2.2 and RC2 as I have read.
OK, this is what I used so far in the App.config for Beta 2.2 :
<configuration>
<system.diagnostics>
<switches>
<add name="WorkflowTraceToDefault" value="1" />
<add name="Host" value="All" />
<add name="Runtime" value="All" />
<add name="Tracking" value="All" />
<add name="Activity" value="All" />
<add name="Rules" value="All" />
</
switches><
trace autoflush="true" indentsize="4"><
listeners><
add name="myListener"type="System.Diagnostics.TextWriterTraceListener"initializeData="WFTrace.log" /></
listeners></
trace></
system.diagnostics></
configuration>
As I do not have RC2 installed, I can only quote the changes from the published document :
The trace switches that could be specified in a configuration file have been renamed as follows:
· Runtime -> System.Workflow.Runtime
· Host -> System.Workflow.Runtime.Hosting
· Tracking -> System.Workflow.Runtime.Tracking
· Activity -> System.Workflow.Activities
· Rules -> System.Workflow.Activities.Rules
· LogTraces -> System.Workflow LogToFile
· WorkflowTraceToDefault -> System.Workflow LogToTraceListeners
The .log file will appear in the /bin/debug directory.
I hope that will help you, Iris
Hi Iris,
the trace indicates, that the system does not find the assembly WorkflowLibrary1. The only problem is, that I am not using the name "WorkflowLibrary1" anywhere.
System.Workflow.Runtime.Hosting Information: 0 : WorkflowRuntime: Created WorkflowRuntime 31a2acd9-168b-4c09-b7a4-cdc51b7329fd
System.Workflow.Runtime.Hosting Information: 0 : WorkflowRuntime: Starting WorkflowRuntime 31a2acd9-168b-4c09-b7a4-cdc51b7329fd
System.Workflow.Runtime.Hosting Information: 0 : SqlWorkflowPersistenceService(ba93f6c2-910d-4098-8807-b3c002f6f98b): Starting, LoadInternalSeconds=1
System.Workflow.Runtime.Hosting Information: 0 : DefaultWorkflowCommitWorkBatchService: Starting
System.Workflow.Runtime.Hosting Information: 0 : SqlWorkflowPersistenceService OpenConnection start: 07/12/2006 02:59:15
System.Workflow.Runtime.Hosting Information: 0 : SqlWorkflowPersistenceService. OpenConnection end: 07/12/2006 02:59:15
System.Workflow.Runtime.Hosting Information: 0 : SqlWorkflowPersistenceService.TryRetrieveANonblockingInstanceStateId ExecuteNonQuery start: 07/12/2006 02:59:16
System.Workflow.Runtime.Hosting Information: 0 : SqlWorkflowPersistenceService.TryRetrieveANonblockingInstanceStateId ExecuteNonQuery end: 07/12/2006 02:59:16
System.Workflow.Runtime.Hosting Information: 0 : WorkflowRuntime: Started WorkflowRuntime 31a2acd9-168b-4c09-b7a4-cdc51b7329fd
System.Workflow.Runtime.Hosting Information: 0 : SqlWorkflowPersistenceService OpenConnection start: 07/12/2006 02:59:17
System.Workflow.Runtime.Hosting Information: 0 : SqlWorkflowPersistenceService. OpenConnection end: 07/12/2006 02:59:17
System.Workflow.Runtime.Hosting Information: 0 : SqlWorkflowPersistenceService.RetrieveExpiredTimerIds ExecuteReader start: 07/12/2006 02:59:17
System.Workflow.Runtime.Hosting Information: 0 : SqlWorkflowPersistenceService.RetrieveExpiredTimerIds ExecuteReader end: 07/12/2006 02:59:17
System.Workflow.Runtime.Hosting Information: 0 : SqlWorkflowPersistenceService(ba93f6c2-910d-4098-8807-b3c002f6f98b): Loading instance with expired timers 5ca3c597-a4bc-45e5-98d2-b8129e36b7dd
System.Workflow.Runtime.Hosting Information: 0 : SqlWorkflowPersistenceService OpenConnection start: 07/12/2006 02:59:18
System.Workflow.Runtime.Hosting Information: 0 : SqlWorkflowPersistenceService. OpenConnection end: 07/12/2006 02:59:18
System.Workflow.Runtime.Hosting Information: 0 : SqlWorkflowPersistenceService(ba93f6c2-910d-4098-8807-b3c002f6f98b):Loading instance 5ca3c597-a4bc-45e5-98d2-b8129e36b7dd
System.Workflow.Runtime.Hosting Information: 0 : SqlWorkflowPersistenceService(ba93f6c2-910d-4098-8807-b3c002f6f98b): retreiving instance: 5ca3c597-a4bc-45e5-98d2-b8129e36b7dd, database: PersistenceStore
System.Workflow.Runtime.Hosting Information: 0 : SqlWorkflowPersistenceService.RetrieveStateFromDB 5ca3c597-a4bc-45e5-98d2-b8129e36b7dd ExecuteReader start: 07/12/2006 02:59:19
System.Workflow.Runtime.Hosting Information: 0 : SqlWorkflowPersistenceService.RetrieveStateFromDB 5ca3c597-a4bc-45e5-98d2-b8129e36b7dd ExecuteReader end: 07/12/2006 02:59:19
System.Workflow.Runtime.Hosting Critical: 0 : WorkflowRuntime:ServicesExceptionNotHandled event raised for instance Id 5ca3c597-a4bc-45e5-98d2-b8129e36b7dd System.Runtime.Serialization.SerializationException: Unable to find assembly 'WorkflowLibrary1, Version=3.0.0.0, Culture=neutral, PublicKeyToken=null'.
at System.Runtime.Serialization.Formatters.Binary.BinaryAssemblyInfo.GetAssembly()
at System.Runtime.Serialization.Formatters.Binary.ObjectReader.GetType(BinaryAssemblyInfo assemblyInfo, String name)
at System.Runtime.Serialization.Formatters.Binary.ObjectMap..ctor(String objectName, String[] memberNames, BinaryTypeEnum[] binaryTypeEnumA, Object[] typeInformationA, Int32[] memberAssemIds, ObjectReader objectReader, Int32 objectId, BinaryAssemblyInfo assemblyInfo, SizedArray assemIdToAssemblyTable)
at System.Runtime.Serialization.Formatters.Binary.__BinaryParser.ReadObjectWithMapTyped(BinaryObjectWithMapTyped record)
at System.Runtime.Serialization.Formatters.Binary.__BinaryParser.ReadObjectWithMapTyped(BinaryHeaderEnum binaryHeaderEnum)
at System.Runtime.Serialization.Formatters.Binary.__BinaryParser.Run()
at System.Runtime.Serialization.Formatters.Binary.ObjectReader.Deserialize(HeaderHandler handler, __BinaryParser serParser, Boolean fCheck, Boolean isCrossAppDomain, IMethodCallMessage methodCallMessage)
at System.Runtime.Serialization.Formatters.Binary.BinaryFormatter.Deserialize(Stream serializationStream, HeaderHandler handler, Boolean fCheck, Boolean isCrossAppDomain, IMethodCallMessage methodCallMessage)
at System.Runtime.Serialization.Formatters.Binary.BinaryFormatter.Deserialize(Stream serializationStream)
at System.Workflow.ComponentModel.Activity.Load(Stream stream, Activity outerActivity, IFormatter formatter)
at System.Workflow.ComponentModel.Activity.Load(Stream stream, Activity outerActivity)
at System.Workflow.Runtime.Hosting.WorkflowPersistenceService.RestoreFromDefaultSerializedForm(Byte[] activityBytes, Activity outerActivity)
at System.Workflow.Runtime.Hosting.SqlWorkflowPersistenceService.LoadWorkflowInstanceState(Guid id)
at System.Workflow.Runtime.WorkflowRuntime.InitializeExecutor(Guid instanceId, CreationContext context, WorkflowExecutor executor, WorkflowInstance workflowInstance)
at System.Workflow.Runtime.WorkflowRuntime.Load(Guid key, CreationContext context, WorkflowInstance workflowInstance)
at System.Workflow.Runtime.WorkflowRuntime.GetWorkflow(Guid instanceId)
at System.Workflow.Runtime.Hosting.SqlWorkflowPersistenceService.LoadWorkflowsWithExpiredTimers(Object ignored)
System.Workflow.Runtime.Hosting Information: 0 : SqlWorkflowPersistenceService(ba93f6c2-910d-4098-8807-b3c002f6f98b): Loading instance with expired timers e32f6127-9e4c-4b2c-8c78-f602bb61cbdd
I have changed the Singleton to explicitly register the workflow.
// register the Workflow
System.Workflow.ComponentModel.Compiler.TypeProvider typeProvider = new System.Workflow.ComponentModel.Compiler.TypeProvider(_WFRuntime);
typeProvider.AddAssembly(typeof(Workflow.CustomerContractWorkflow).Assembly);
_WFRuntime.AddService(typeProvider);
It still does not find the assembly. Do I need to use a .xoml file for the workflow (which I am not using right now)?
Best Regards,
Bjoern
Two more ideas,
- whould strongly signing the workflow DLL help (which I have not done)?
- do I need to convert my Workflow to xoml?
Best Regards,
Bjoern
Hi Bjoern,
maybe the exception was a result of loading an "old" workflow from experiments that you made earlier.
WorkflowLibrary1 is contained in the ASPNETWithPersistenceAndTracking example which you have tried out. I assume you have automatically loaded a workflow from that example and it could not find it's own assembly.
So the solution is to clear the databases for the new project.
I hope, everything will work now, Iris
Hello,
you are right, I have sent the wrong section of the log file. For it looks like I ran into the bug that WWF is not writing into the IntanceState table. But I don't know why the ASPNetWithPersistenceAndTracking is working well and my application is not writing into the table.
Best Regards,
Bjoern
If your workflow completes successfully or terminates, persistence service will delete the instance from the database.While it isexecuting,you will find the instance in the persistence store if your workflow has any persistence points or it is unloading due to an event or a delay activity. This
article has good information about persistence points and lifecycle of the workflow. Let me know if your workflow is not persisting inspite of having the persistence points.