简体   繁体   English

Workflow Foundation 4.5 InstanceLockedException

[英]Workflow Foundation 4.5 InstanceLockedException

I am currently experimenting with Workflow Foundation 4.5 in a client, WCF server architecture. 我目前正在在客户端WCF服务器体系结构中试用Workflow Foundation 4.5。

The way i set up the server is by creating a regular WCF Service Application (not a WCF Workflow Service Application) and added a bunch of web methods to start, resume, get states of of entities living in the workflow. 我设置服务器的方式是通过创建常规的WCF服务应用程序(而不是WCF工作流服务应用程序)并添加了一堆Web方法来启动,恢复和获取工作流中实体的状态。

So i have things like : 所以我有这样的事情:

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;
        };
    }
}

Now, what i want is to get the workflow version from the instance id, so in my GetCurrentState i call : 现在,我想要从实例ID获取工作流版本,因此在我的GetCurrentState中,我致电:

var instance = WorkflowApplication.GetInstance(instanceId, Store);

And load the WorkflowApplication with that 然后加载WorkflowApplication

var wfApp = new WorkflowApplication(activity, instance.DefinitionIdentity)

this works fine, but i suspect somehow leaves the host of my workflow loaded, and my instance locked. 这工作正常,但是我怀疑以某种方式使工作流程的主机处于加载状态,并且实例被锁定。

So when right after calling GetCurrentState(), the client calls Resume() i get the following exception (at the wfApp.Load(instanceId) line). 因此,在调用GetCurrentState()之后,客户端立即调用Resume(),我得到以下异常(在wfApp.Load(instanceId)行)。

InstanceLockedException InstanceLockedException

The execution of an InstancePersistenceCommand was interrupted because the instance 'a1bcbd11-50fc-4a72-b5d2-87b71d0c3c45' is locked by a different instance owner. InstancePersistenceCommand的执行被中断,因为实例“ a1bcbd11-50fc-4a72-b5d2-87b71d0c3c45”已由其他实例所有者锁定。 This error usually occurs because a different host has the instance loaded. 通常会发生此错误,因为其他主机已加载了实例。 The instance owner ID of the owner or host with a lock on the instance is '190ea0d9-788f-4278-883e-84226f5788bc'. 实例上具有锁定的所有者或主机的实例所有者ID为“ 190ea0d9-788f-4278-883e-84226f5788bc”。

This exception goes away when i stop using the WorkflowApplication.GetInstance method and specifying the version manually, but i would rather avoid that. 当我停止使用WorkflowApplication.GetInstance方法并手动指定版本时,此异常消失了,但是我宁愿避免这种情况。

So what am i doing wrong here ? 那我在这里做错了什么?

Googling around took me to these pages : 谷歌搜索将我带到这些页面:

InstanceLockedException: How to handle locking issues with WF 4.0? InstanceLockedException:如何处理WF 4.0的锁定问题?

-> Looks like the issue i have, but i am not sure how where i can set this timeToUnload value in my current code as i do not use WorkflowServiceHost. ->看起来像我遇到的问题,但是由于我不使用WorkflowServiceHost,所以我不确定如何在当前代码中设置此timeToUnload值。 Should i just create a new one in each webmethod ? 我应该在每个网络方法中创建一个新方法吗? or make a singleton ? 还是单身?

http://social.msdn.microsoft.com/Forums/en-US/3e38a60e-8a99-4c01-a26b-f82670ca5601/instancelockedexception-when-application-restarts-using-persistableidleactionpersist?forum=wfprerelease http://social.msdn.microsoft.com/Forums/zh-CN/3e38a60e-8a99-4c01-a26b-f82670ca5601/instancelockedexception-when-application-restarts-using-persistableidleactionpersist?forum=wfprerelease

-> Tried this on my store with a TimeSpan of 1 sec, did not help either. ->在我的商店中尝试使用1秒的时间跨度,也没有帮助。

Any suggestions would be greatly appreciated :-) 任何建议将不胜感激 :-)

FOLLOW UP 跟进

After reading this post : Error when attempting to Resume a Windows Workflow 阅读本文后: 尝试恢复Windows工作流时出错

I have created a DisposableStore class designed as follow : 我创建了一个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));
    }
}

And i use it as follow : 我使用它如下:

[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;
}

Things get better with this approach, meaning i no longer get the InstanceLockedException, but now when the Resume method returns to the client, i my wfApp gets aborted : 用这种方法可以使事情变得更好,这意味着我不再获得InstanceLockedException,但是现在当Resume方法返回到客户端时,我的wfApp被中止了:

Exception: System.Runtime.DurableInstancing.InstanceOwnerException The execution of an InstancePersistenceCommand was interrupted because the instance owner registration for owner ID '33ed6492-0685-4f31-8652-4b91acaf50ef' has become invalid. 异常:System.Runtime.DurableInstancing.InstanceOwnerException由于所有者ID为'33ed6492-0685-4f31-8652-4b91acaf50ef'的实例所有者注册已无效,因此InstancePersistenceCommand的执行被中断。 This error indicates that the in-memory copy of all instances locked by this owner have become stale and should be discarded, along with the InstanceHandles. 此错误表明,此所有者锁定的所有实例的内存中副本已过时,应与InstanceHandles一起丢弃。 Typically, this error is best handled by restarting the host 通常,最好通过重新启动主机来解决此错误

Ok, so i was on the right track using a DisposableStore. 好的,所以我使用DisposableStore走上了正确的轨道。 The only issue what that the store owner was deleted too soon because of this line : 唯一的问题是由于此行而导致商店所有者过早删除的内容:

wfApp.Idle = x => sync.Set();

This was causing my synchroniser to be released too soon, causing the dispose method (which kills the store) to be called before the workflow had a chance to persist itself (which requires the store & its owner). 这导致我的同步器发布得太早,导致在工作流有机会自行持久化(需要商店及其所有者)之前调用dispose方法(这会杀死商店)。

So i replaced this line with : 所以我用:

wfApp.Unloaded = x => sync.Set();

This way, i wait for the workflow instance to be unloaded before releasing my synchroniser. 这样,我等待工作流程实例卸载后再释放我的同步器。

So for people facing similar issues, here is the big picture the final code : 因此,对于面临类似问题的人们,最终的代码如下:

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

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM