Reputation: 43
I have a Windows Service wrapping a WCF Service which wraps a WorkflowApplication and this invokes activities. The WCF client is wrapped in a PowerShell command that invokes against a given server. The end-to-end Workflow executes flawlessly, as this demonstrates:
Get-PingResponse -Server 192.168.0.252 -Target 8.8.8.8
Workflow 33d9bd45-663a-4f95-b487-cf2f63081881 completed. Result: True
However, I believe that because the Activity/WorkflowApplication completes before the polling interval hits (the minimum running interval cannot be less than 1 second), the WorkflowApplication orphans updating the SQL entry.
33D9BD45-663A-4F95-B487-CF2F63081881 NULL 2019-02-07 10:57:43.880 2019-02-07 10:57:43.867 NULL NULL NULL NULL KALLIX Executing 1 0 0 1 NULL NULL 0x1F8B0800000000000400B5565D6FDB36149DF2D1D84ED2A50186ED69308ABD6CB32539F26780CCCCB26E3096A4811DA4018A7AA5242AE52C912E49D955860143036CF909C9D37EE4FEC02859921B0746822AB55F04823CF7DE73CEBD24281C313A444C60C4412EFE0EC0D35F5170025D1F1D41CC9E3BA787D0439004C7C11091C383F1F8E410B6173AF6FBF652B874F55F2FE00279EA1E755D64094C09577F4104316CA91F22BDDE7AF932DE7AEAB9EA3E266FD508BB549C592E154F10E31268A7AAEAE1BF54DCF35DE133B443902F18744BC523DF74B125F18FE900911DB3D18035AB56AFB48C2AD29BAD57A524D673F3779955A9E8718B32179B0F01FEAA9DDBE51C79A61BFC95FBF18D10C36D4DE3D61BE441AEDA50408B1299A62554CACEB42D5DAF6A7A439BCF535EC1DFC628E3F1581D1BC9B98A767AB0DF8B80CB9870018985F2CAB93113D2C316A39C3A42B5A817C633B48AAEF5A404D0C5E730D444038B031484BA5D2EB4577F96B95116497AB501B7438593DD88C5BA7E3DA38A3AB32BA5E0AA3BB3F30128CE2BB03153E45DBC26A6024B44961355BA1897B21E27D8130C93B30FC5FBECFAA73F7C46B663F1CA299365C9E4767C6C57BA7A84C32ED1A421B561DA347FBEA06CE0B874AC80E551D83151D4A538EA77B78EAB5D9F08EC2135460C9EBD43962F9598A674757CEBD447D069544CC768D6EAD036EA5564D4C0469269C74632053171C272AE7537C9D249FA5662DE291552A17B1CBEE1FCE9E1FE54A347D76B4752948491FECA68D2FB11952BD72BBA5A0947800236D312E246E8D819D2BFFEC630EC9669576BE57ADD80E5AAD3AA95CD6AB351B69C2DA76EE8CD4AB3590105986A1F2594CB10127C99F698DD896BD8C75CF00839DF5EEAE173F45EF2BA7BB72873784D0CD62F267C26810EE0504DBEC3A051CC022870C9BD8B3A7280472BABB1779FDE7261EAD904E5537BF63E4CCFA1017C3165FA1919614689276D1F55B8761FDC399EEFE75D6A45E374A2D9FA44B3CBE5BC627E7C33FC96DB8F51D54582DDBF958758501490377D1EEC51397232947C011670963EBB00CB742C1F0311F78F332472734C801CF74D791F0A940572B38B387547E1F861677E6811AEF40B69EBD851D29FFFAB843FB06A523AF0201B1C9C4DAAD9C8101A3C22E89DE8D8170A78EC422E92A6CA34D32EC01AA334C56A2F769173B93A3155865CA593C2EBD1F6DD58C62719C0C0BA831917E12C4F87CE663C747EB83D74E65D98DF4F6E4ED495F5A658D317C927B9441F66326FC4374A90487E4326A5FF15436F7DCC10EF059E49DDC8A17EF882FB27B2A1F23F5068CE26B50B0000 NULL PingActivity NULL 0 0 1 0
In principal/theory, since WorkflowApplication.Completed is called, it should signal an update to the SQL record for that specific instance; however, this doesn't seem be occuring - as the state of the record (above) shows.
The end-goal is - ultimately - that the record is updated to reflect the state of the workflow. None of the states are changed in the SQL row for the record, so all executions just show "Executing" - even for Faulted Workflows.
Previously, I had an issue with Persistance writing an entry into the SQL table, and this was resolved by calling Persist() on the WorkflowApplication. See here.
SqlWorkflowInstanceStore newSqlWorkflowInstanceStore = new SqlWorkflowInstanceStore("Server=.\\SQL2008EXPRESS;Initial Catalog=WorkflowInstanceStore;Integrated Security=SSPI")
{
HostLockRenewalPeriod = TimeSpan.FromSeconds(5),
InstanceCompletionAction = InstanceCompletionAction.DeleteNothing,
InstanceLockedExceptionAction = InstanceLockedExceptionAction.AggressiveRetry,
RunnableInstancesDetectionPeriod = TimeSpan.FromSeconds(1) // Minimum allowed value.
};
InstanceHandle workflowInstanceStoreHandle = newSqlWorkflowInstanceStore.CreateInstanceHandle();
CreateWorkflowOwnerCommand createWorkflowOwnerCommand = new CreateWorkflowOwnerCommand();
InstanceView newInstanceView = newSqlWorkflowInstanceStore.Execute(workflowInstanceStoreHandle, createWorkflowOwnerCommand, TimeSpan.FromSeconds(30));
newSqlWorkflowInstanceStore.DefaultInstanceOwner = newInstanceView.InstanceOwner;
// The Dictionary will throw for non-found key before we ever get here, so no need to validate input.
WorkflowIdentity newWorkflowIdentity = new WorkflowIdentity
{
Name = command,
Version = new Version(0, 1, 0, 0)
};
// Now stage the WorkflowApplication, using the SQL instance.
AutoResetEvent syncEvent = new AutoResetEvent(false);
WorkflowApplication newWorkflowApplication = new WorkflowApplication(newSwedishCodeActivity, newWorkflowIdentity)
{
InstanceStore = newSqlWorkflowInstanceStore
};
newWorkflowApplication.Persist();
this.Result = new object();
newWorkflowApplication.Completed += delegate(WorkflowApplicationCompletedEventArgs e)
{
if (e.CompletionState == ActivityInstanceState.Faulted)
{
this.Result = $"Workflow {e.InstanceId} has faulted.\nException: {e.TerminationException.GetType().FullName}\nMessage:{e.TerminationException.Message}";
syncEvent.Set();
}
else if (e.CompletionState == ActivityInstanceState.Canceled)
{
this.Result = $"Workflow {e.InstanceId} has been canceled.";
syncEvent.Set();
}
else
{
this.Result = $"Workflow {e.InstanceId} completed. Result: {e.Outputs["Result"]}";
syncEvent.Set();
}
};
newWorkflowApplication.Aborted = delegate(WorkflowApplicationAbortedEventArgs e)
{
// The workflow aborted, so let's find out why.
this.Result = $"Workflow {e.InstanceId} has been aborted.\nException: {e.Reason.GetType().FullName}\nMessage:{e.Reason.Message}";
syncEvent.Set();
};
newWorkflowApplication.Idle = delegate(WorkflowApplicationIdleEventArgs e)
{
this.Result = $"Workflow {e.InstanceId} has entered the Idle state.";
syncEvent.Set();
};
newWorkflowApplication.PersistableIdle = delegate(WorkflowApplicationIdleEventArgs e)
{
this.Result = $"Workflow {e.InstanceId} has entered PersistableIdle.";
syncEvent.Set();
// Runtime will persist.
return PersistableIdleAction.Persist;
};
newWorkflowApplication.Unloaded = delegate(WorkflowApplicationEventArgs e)
{
syncEvent.Set();
};
newWorkflowApplication.OnUnhandledException = delegate(WorkflowApplicationUnhandledExceptionEventArgs e)
{
// Display the unhandled exception.
this.Result = $"Workflow {e.InstanceId} has reached OnUnhandledException.\nException Source: {e.ExceptionSource.DisplayName}\nException Instance ID: {e.ExceptionSourceInstanceId}\nException: {e.UnhandledException.GetType().FullName}\nMessage: {e.UnhandledException.Message}";
syncEvent.Set();
// Instruct the runtime to terminate the workflow.
// The other viable choices here are 'Abort' or 'Cancel'
return UnhandledExceptionAction.Terminate;
};
newWorkflowApplication.Run();
syncEvent.WaitOne();
DeleteWorkflowOwnerCommand newDeleteWorkflowOwnerCommand = new DeleteWorkflowOwnerCommand();
newSqlWorkflowInstanceStore.Execute(
workflowInstanceStoreHandle,
newDeleteWorkflowOwnerCommand,
TimeSpan.FromSeconds(30));
The expected result is that when the delegate is invoked for WorkflowApplication.Completed, it should signal to the InstanceStore to update the SQL record for the instance.
The actual result is that the record is orphaned and left with the "Executing" state.
Upvotes: 1
Views: 49
Reputation: 43
Found the answer: The Workflow Instance must be unloaded, in order for the state to be updated in the SQL record.
newWorkflowApplication.Unload(TimeSpan.FromSeconds(30));
Leaving this here, in case someone else ever runs into this issue.
Upvotes: 1