[英]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.