[英]Workflow Foundation 4.5 InstanceLockedException
我目前正在在客戶端WCF服務器體系結構中試用Workflow Foundation 4.5。
我設置服務器的方式是通過創建常規的WCF服務應用程序(而不是WCF工作流服務應用程序)並添加了一堆Web方法來啟動,恢復和獲取工作流中實體的狀態。
所以我有這樣的事情:
public class WebService1 : WebService
{
private const string ConnectionString = "Server=.\\SQLEXPRESS;Initial Catalog=WF45GettingStartedTutorial;Integrated Security=SSPI";
private SqlWorkflowInstanceStore _store;
public SqlWorkflowInstanceStore Store
{
get
{
if (_store == null)
{
Debug.WriteLine("Building new store");
_store = new SqlWorkflowInstanceStore(ConnectionString);
StateMachineStateTracker.Promote(Store);
}
return _store;
}
}
[WebMethod]
public Guid Start(Guid resourceId)
{
var activity = new Activity1(); // My state machine
var parameters = new Dictionary<string, object> { { "resourceId", resourceId } };
var wfApp = new WorkflowApplication(activity, parameters, new WorkflowIdentity("v1", new Version(1, 0), string.Empty));
ConfigureWorkflowApplication(wfApp);
wfApp.Run();
return wfApp.Id;
}
[WebMethod]
public void Resume(Guid instanceId, string bookmarkName, object bookmarkParameter)
{
var activity = new Activity1();
var wfApp = new WorkflowApplication(activity, new WorkflowIdentity("v1", new Version(1, 0), string.Empty));
ConfigureWorkflowApplication(wfApp);
wfApp.Load(instanceId);
wfApp.ResumeBookmark(bookmarkName, bookmarkParameter);
}
[WebMethod]
public string GetCurrentState(Guid instanceId)
{
var activity = new Activity1();
// Things get messy here :
// var instance = WorkflowApplication.GetInstance(instanceId, Store);
// var wfApp = new WorkflowApplication(activity, instance.DefinitionIdentity);
// so replaced with :
var wfApp = new WorkflowApplication(activity, new WorkflowIdentity("v1", new Version(1, 0), string.Empty));
ConfigureWorkflowApplication(wfApp);
var trackerInstance = StateMachineStateTracker.LoadInstance(instanceId, wfApp.WorkflowDefinition, ConnectionString);
if (trackerInstance != null)
{
return trackerInstance.CurrentState;
}
return string.Empty;
}
private void ConfigureWorkflowApplication(WorkflowApplication wfApp)
{
// Configure the persistence store.
wfApp.InstanceStore = Store;
// State machine tracker taken from http://code.msdn.microsoft.com/windowsdesktop/Windows-Workflow-fee72008
var tracker = new StateMachineStateTracker(wfApp.WorkflowDefinition);
wfApp.Extensions.Add(tracker);
wfApp.Extensions.Add(new StateTrackerPersistenceProvider(tracker));
wfApp.Completed = delegate(WorkflowApplicationCompletedEventArgs e)
{
Debug.WriteLine("Workflow completed.");
};
wfApp.Aborted = delegate(WorkflowApplicationAbortedEventArgs e)
{
Debug.WriteLine(string.Format("Workflow Aborted. Exception: {0}\r\n{1}",
e.Reason.GetType().FullName,
e.Reason.Message));
};
wfApp.OnUnhandledException = delegate(WorkflowApplicationUnhandledExceptionEventArgs e)
{
Debug.WriteLine(string.Format("Unhandled Exception: {0}\r\n{1}",
e.UnhandledException.GetType().FullName,
e.UnhandledException.Message));
return UnhandledExceptionAction.Terminate;
};
wfApp.PersistableIdle = delegate(WorkflowApplicationIdleEventArgs e)
{
return PersistableIdleAction.Unload;
};
}
}
現在,我想要從實例ID獲取工作流版本,因此在我的GetCurrentState中,我致電:
var instance = WorkflowApplication.GetInstance(instanceId, Store);
然后加載WorkflowApplication
var wfApp = new WorkflowApplication(activity, instance.DefinitionIdentity)
這工作正常,但是我懷疑以某種方式使工作流程的主機處於加載狀態,並且實例被鎖定。
因此,在調用GetCurrentState()之后,客戶端立即調用Resume(),我得到以下異常(在wfApp.Load(instanceId)行)。
InstanceLockedException
InstancePersistenceCommand的執行被中斷,因為實例“ a1bcbd11-50fc-4a72-b5d2-87b71d0c3c45”已由其他實例所有者鎖定。 通常會發生此錯誤,因為其他主機已加載了實例。 實例上具有鎖定的所有者或主機的實例所有者ID為“ 190ea0d9-788f-4278-883e-84226f5788bc”。
當我停止使用WorkflowApplication.GetInstance方法並手動指定版本時,此異常消失了,但是我寧願避免這種情況。
那我在這里做錯了什么?
谷歌搜索將我帶到這些頁面:
InstanceLockedException:如何處理WF 4.0的鎖定問題?
->看起來像我遇到的問題,但是由於我不使用WorkflowServiceHost,所以我不確定如何在當前代碼中設置此timeToUnload值。 我應該在每個網絡方法中創建一個新方法嗎? 還是單身?
->在我的商店中嘗試使用1秒的時間跨度,也沒有幫助。
任何建議將不勝感激 :-)
跟進
閱讀本文后: 嘗試恢復Windows工作流時出錯
我創建了一個DisposableStore類,其設計如下:
public class DisposableStore : IDisposable
{
private const string ConnectionString = "Server=.\\SQLEXPRESS;Initial Catalog=WF45GettingStartedTutorial;Integrated Security=SSPI";
private SqlWorkflowInstanceStore _store;
private InstanceHandle _handle;
public SqlWorkflowInstanceStore Store
{
get
{
if (_store == null)
{
Debug.WriteLine("Building new store");
_store = new SqlWorkflowInstanceStore(ConnectionString);
StateMachineStateTracker.Promote(_store);
_handle = _store.CreateInstanceHandle();
InstanceView view = _store.Execute(_handle, new CreateWorkflowOwnerCommand(), TimeSpan.FromSeconds(30));
_store.DefaultInstanceOwner = view.InstanceOwner;
}
return _store;
}
}
public void Dispose()
{
var deleteOwnerCmd = new DeleteWorkflowOwnerCommand();
Store.Execute(_handle, deleteOwnerCmd, TimeSpan.FromSeconds(30));
}
}
我使用它如下:
[WebMethod]
public bool Resume(Guid instanceId, string bookmarkName, object bookmarkParameter)
{
Debug.WriteLine(string.Format("Resume - Service id : {0}", _serviceId));
WorkflowDescriptor descriptor;
using (var store = new DisposableStore())
{
var instance = WorkflowApplication.GetInstance(instanceId, store.Store);
descriptor = WorkflowLocator.GetWorflowFromIdentity(instance.DefinitionIdentity);
}
using (var store = new DisposableStore())
{
var wfApp = new WorkflowApplication(descriptor.Activity, descriptor.Identity);
ConfigureWorkflowApplication(wfApp, store.Store);
wfApp.Load(instanceId);
var sync = new AutoResetEvent(false);
wfApp.Idle = x => sync.Set();
wfApp.Completed = x=> sync.Set();
wfApp.ResumeBookmark(bookmarkName, bookmarkParameter);
sync.WaitOne();
}
return true;
}
用這種方法可以使事情變得更好,這意味着我不再獲得InstanceLockedException,但是現在當Resume方法返回到客戶端時,我的wfApp被中止了:
異常:System.Runtime.DurableInstancing.InstanceOwnerException由於所有者ID為'33ed6492-0685-4f31-8652-4b91acaf50ef'的實例所有者注冊已無效,因此InstancePersistenceCommand的執行被中斷。 此錯誤表明,此所有者鎖定的所有實例的內存中副本已過時,應與InstanceHandles一起丟棄。 通常,最好通過重新啟動主機來解決此錯誤
好的,所以我使用DisposableStore走上了正確的軌道。 唯一的問題是由於此行而導致商店所有者過早刪除的內容:
wfApp.Idle = x => sync.Set();
這導致我的同步器發布得太早,導致在工作流有機會自行持久化(需要商店及其所有者)之前調用dispose方法(這會殺死商店)。
所以我用:
wfApp.Unloaded = x => sync.Set();
這樣,我等待工作流程實例卸載后再釋放我的同步器。
因此,對於面臨類似問題的人們,最終的代碼如下:
[WebMethod]
public bool Resume(Guid instanceId, string bookmarkName, object bookmarkParameter)
{
Debug.WriteLine(string.Format("Resume - Service id : {0}", _serviceId));
WorkflowDescriptor descriptor;
using (var store = new DisposableStore())
{
var instance = WorkflowApplication.GetInstance(instanceId, store.Store);
descriptor = WorkflowLocator.GetWorflowFromIdentity(instance.DefinitionIdentity);
}
// We have to create a new store at this point, can't use the same one !!!
using (var store = new DisposableStore())
{
var wfApp = new WorkflowApplication(descriptor.Activity, descriptor.Identity);
ConfigureWorkflowApplication(wfApp, store.Store);
wfApp.Load(instanceId);
var sync = new AutoResetEvent(false);
wfApp.ResumeBookmark(bookmarkName, bookmarkParameter);
wfApp.Unloaded = x => sync.Set();
sync.WaitOne();
}
return true;
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.