C# SqlWorkflowInstanceStore WaitForEvents返回HasRunnableWorkflowEvent,但LoadRunnableInstance失败

C# SqlWorkflowInstanceStore WaitForEvents返回HasRunnableWorkflowEvent,但LoadRunnableInstance失败,c#,workflow-foundation-4,C#,Workflow Foundation 4,亲爱的 请帮助我恢复延迟(和持续)的工作流 我正在检查自托管工作流商店是否有任何实例被延迟并可以恢复。出于测试目的,我创建了一个延迟的虚拟活动,它会在延迟时持续存在 通常,恢复过程如下所示: get WF definition configure sql instance store call WaitForEvents is there event with HasRunnableWorkflowEvent.Value name and if it is create WorkflowApp

亲爱的

请帮助我恢复延迟(和持续)的工作流

我正在检查自托管工作流商店是否有任何实例被延迟并可以恢复。出于测试目的,我创建了一个延迟的虚拟活动,它会在延迟时持续存在

通常,恢复过程如下所示:

get WF definition
configure sql instance store 
call WaitForEvents
is there event with HasRunnableWorkflowEvent.Value name and if it is
create WorkflowApplication object and execute LoadRunnableInstance  method
如果创建了存储|初始化,调用了WaitForEvents,存储关闭,那么它工作正常。在这种情况下,存储从持久化数据库读取所有可用的工作流,并在没有可恢复的
工作流时抛出超时异常

如果只为
WaitForEvents
创建存储并启动循环,则会出现此问题(在
BeginWaitForEvents
中也会发生同样的情况)。在这种情况下,它将从
DB
读取所有可用的
工作流
(使用正确的ID),但随后它将不再读取
超时异常
,而是再读取一个实例(我确切地知道有多少
工作流
,因为使用单独的测试
数据库
)。但是无法读取并且
抛出InstanceNotradeyException
。在
catch
中,我正在检查
workflowApplication.Id
,但它以前没有与我的测试一起保存

我尝试在新的(空的)持久数据库上运行,结果相同:(

此代码失败:

using (var storeWrapper = new StoreWrapper(wf, connStr))
    for (int q = 0; q < 5; q++)
    {
        var id = Resume(storeWrapper); // InstanceNotReadyException here when all activities is resumed
下面是我用于测试的存储包装定义:

public class StoreWrapper : IDisposable
{
    Activity WfDefinition { get; set; }

    public static readonly XName WorkflowHostTypePropertyName = XNamespace.Get("urn:schemas-microsoft-com:System.Activities/4.0/properties").GetName("WorkflowHostType");
    public StoreWrapper(Activity wfDefinition, string connectionStr)
    {
        _store = new SqlWorkflowInstanceStore(connectionStr);

        HostTypeName = XName.Get(wfDefinition.DisplayName, "ttt.workflow");

        WfDefinition = wfDefinition;

    }

    SqlWorkflowInstanceStore _store;

    public SqlWorkflowInstanceStore GetStore()
    {
        if (Handle == null)
        {

            InitStore(_store, WfDefinition);
            Handle = _store.CreateInstanceHandle();

            var view = _store.Execute(Handle, new CreateWorkflowOwnerCommand
            {
                InstanceOwnerMetadata = { { WorkflowHostTypePropertyName, new InstanceValue(HostTypeName) } }
            }, TimeSpan.FromSeconds(30));

            _store.DefaultInstanceOwner = view.InstanceOwner;

            //Trace.WriteLine(string.Format("{0} owns {1}", view.InstanceOwner.InstanceOwnerId, HostTypeName));
        }

        return _store;
    }

    protected virtual void InitStore(SqlWorkflowInstanceStore store, Activity wfDefinition)
    {
    }

    public InstanceHandle Handle { get; protected set; }

    XName HostTypeName { get; set; }

    public void Dispose()
    {
        if (Handle != null)
        {
            var deleteOwner = new DeleteWorkflowOwnerCommand();

            //Trace.WriteLine(string.Format("{0} frees {1}", Store.DefaultInstanceOwner.InstanceOwnerId, HostTypeName));

            _store.Execute(Handle, deleteOwner, TimeSpan.FromSeconds(30));
            Handle.Free();
            Handle = null;

            _store = null;
        }
    }

    public WorkflowApplication GetApplication()
    {
        var wfApp = new WorkflowApplication(WfDefinition);
        wfApp.InstanceStore = GetStore();
        wfApp.PersistableIdle = e => PersistableIdleAction.Persist;

        Dictionary<XName, object> wfScope = new Dictionary<XName, object> { { WorkflowHostTypePropertyName, HostTypeName } };
        wfApp.AddInitialInstanceValues(wfScope);

        return wfApp;
    }
}
公共类StoreWrapper:IDisposable
{
活动定义{get;set;}
公共静态只读XName WorkflowHostTypePropertyName=XNamespace.Get(“urn:schemas-microsoft-com:System.Activities/4.0/properties”).GetName(“WorkflowHostType”);
public StoreWrapper(活动定义,字符串连接)
{
_store=新的SqlWorkflowInstanceStore(connectionStr);
HostTypeName=XName.Get(wfDefinition.DisplayName,“ttt.workflow”);
WfDefinition=WfDefinition;
}
SqlWorkflowInstanceStore;
公共SqlWorkflowInstanceStore GetStore()
{
if(Handle==null)
{
InitStore(_store,WfDefinition);
句柄=_store.CreateInstanceHandle();
var view=_store.Execute(句柄,新的CreateWorkflowOwnerCommand
{
InstanceOwnerMetadata={{WorkflowHostTypePropertyName,新InstanceValue(HostTypeName)}
}时距秒(30));
_store.DefaultInstanceOwner=view.InstanceOwner;
//WriteLine(string.Format(“{0}拥有{1}”,view.InstanceOwner.InstanceOwnerId,HostTypeName));
}
退货店;
}
受保护的虚拟void InitStore(SqlWorkflowInstanceStore,活动wfDefinition)
{
}
公共InstanceHandle句柄{get;protected set;}
XName HostTypeName{get;set;}
公共空间处置()
{
if(句柄!=null)
{
var deleteOwner=新的DeleteWorkflowOwnerCommand();
//WriteLine(string.Format(“{0}释放{1}”,Store.DefaultInstanceOwner.InstanceOwnerId,HostTypeName));
_store.Execute(Handle,deleteOwner,TimeSpan.FromSeconds(30));
Handle.Free();
Handle=null;
_store=null;
}
}
公共工作流应用程序GetApplication()
{
var wfApp=新的工作流应用程序(WfDefinition);
wfApp.InstanceStore=GetStore();
wfApp.PersistableIdle=e=>PersistableIdleAction.Persist;
Dictionary wfScope=新字典{{WorkflowHostTypePropertyName,HostTypeName};
wfApp.附加初始值(wfScope);
返回wfApp;
}
}

我不是工作流基础专家,所以我的答案是基于微软的官方例子。第一个是第二个。在这两个示例中,你会找到类似于你的代码,即:

try 
{
    wfApp.LoadRunnableInstance();
    ...
} 
catch (InstanceNotReadyException) 
{
    //Some logging
}

考虑到这一点,答案是你是对的,
InstanceNodeException
的空捕获是一个很好的解决方案。

对基本问题投了赞成票。希望你能得到更多的关注。事实上,我认为,尽管你的
+150
慷慨大方,但你的问题受到较少关注可能有两个原因。(1)使用
工作流
的人不多,(2)您要求的是
最佳解决方案
,它可能更适合于代码审查而不是堆栈溢出,(3)这是一个很小的问题,但您的测试代码很长,没有有意义的解释(如果人们真的可以直接从中复制您的问题,这是可以的,但否则会阻碍代码阅读)。希望你能得到答案…失败循环和未失败循环是两件独立的事情,一件是为每次迭代启动一个新的StoreWrapper。另一件是为每次迭代重用一个StoreWrapper。很久以前我使用了工作流,但看到代码让我觉得你的StoreWrapper不是一个reuse因此,您可以考虑使用工作循环。或者它会引起其他问题吗?@ TracARIN可以用普通的Sql WorkFraseStestOrror重写样本,但是问题将持续下去。我在Web中看到了使用相同的Sql WorkFraseStestOrnStor实例来查询WF数据库运行就绪的工作流的示例。循环中有新实例,但我担心这会导致额外的内存开销和时间延迟。
public class StoreWrapper : IDisposable
{
    Activity WfDefinition { get; set; }

    public static readonly XName WorkflowHostTypePropertyName = XNamespace.Get("urn:schemas-microsoft-com:System.Activities/4.0/properties").GetName("WorkflowHostType");
    public StoreWrapper(Activity wfDefinition, string connectionStr)
    {
        _store = new SqlWorkflowInstanceStore(connectionStr);

        HostTypeName = XName.Get(wfDefinition.DisplayName, "ttt.workflow");

        WfDefinition = wfDefinition;

    }

    SqlWorkflowInstanceStore _store;

    public SqlWorkflowInstanceStore GetStore()
    {
        if (Handle == null)
        {

            InitStore(_store, WfDefinition);
            Handle = _store.CreateInstanceHandle();

            var view = _store.Execute(Handle, new CreateWorkflowOwnerCommand
            {
                InstanceOwnerMetadata = { { WorkflowHostTypePropertyName, new InstanceValue(HostTypeName) } }
            }, TimeSpan.FromSeconds(30));

            _store.DefaultInstanceOwner = view.InstanceOwner;

            //Trace.WriteLine(string.Format("{0} owns {1}", view.InstanceOwner.InstanceOwnerId, HostTypeName));
        }

        return _store;
    }

    protected virtual void InitStore(SqlWorkflowInstanceStore store, Activity wfDefinition)
    {
    }

    public InstanceHandle Handle { get; protected set; }

    XName HostTypeName { get; set; }

    public void Dispose()
    {
        if (Handle != null)
        {
            var deleteOwner = new DeleteWorkflowOwnerCommand();

            //Trace.WriteLine(string.Format("{0} frees {1}", Store.DefaultInstanceOwner.InstanceOwnerId, HostTypeName));

            _store.Execute(Handle, deleteOwner, TimeSpan.FromSeconds(30));
            Handle.Free();
            Handle = null;

            _store = null;
        }
    }

    public WorkflowApplication GetApplication()
    {
        var wfApp = new WorkflowApplication(WfDefinition);
        wfApp.InstanceStore = GetStore();
        wfApp.PersistableIdle = e => PersistableIdleAction.Persist;

        Dictionary<XName, object> wfScope = new Dictionary<XName, object> { { WorkflowHostTypePropertyName, HostTypeName } };
        wfApp.AddInitialInstanceValues(wfScope);

        return wfApp;
    }
}
try 
{
    wfApp.LoadRunnableInstance();
    ...
} 
catch (InstanceNotReadyException) 
{
    //Some logging
}