c# - SqlWorkflowInstanceStore WaitForEvents returns HasRunnableWorkflowEvent but LoadRunnableInstance fails -


dears

please me restoring delayed (and persisted) workflows.

i'm trying check on self-hosted workflow store there instance delayed , can resumed. testing purposes i've created dummy activity delayed , persists on delay.

generally resume process looks like:

get wf definition configure sql instance store  call waitforevents there event hasrunnableworkflowevent.value name , if create workflowapplication object , execute loadrunnableinstance  method 

it works fine if store created|initialized, waitforevents called, store closed. in such case store reads available workflows persisted db , throws timeout exception if there no workflows available resume.

the problem happens if store created , loop started waitforevents (the same thing happens beginwaitforevents). in such case reads available workflows db (with proper ids) instead of timeout exception going read 1 more instance (i know how many workflows there ready resumed because using separate test database). fails read , throws instancenotreadyexception. in catch i'm checking workflowapplication.id, not saved test before.

i've tried run on new (empty) persistent database , result same :(

this code fails:

using (var storewrapper = new storewrapper(wf, connstr))     (int q = 0; q < 5; q++)     {         var id = resume(storewrapper); // instancenotreadyexception here when activities resumed 

but 1 works expected:

for (int q = 0; q < 5; q++)     using (var storewrapper = new storewrapper(wf, connstr))     {         var id = resume(storewrapper); // timeout exception here or beginwaitforevents continues wait 

what best solution in such case? add empty catch instancenotreadyexception , ignore it?

here tests

const int delaytime = 15; string connstr = "server=db;database=appfabricdb_test;integrated security=true;";  [testmethod] public void persistoneonidleandresume() {     var wf = getdelayactivity();      using (var storewrapper = new storewrapper(wf, connstr))     {         var id = createandrun(storewrapper);         trace.writeline(string.format("done {0}", id));     }      using (var storewrapper = new storewrapper(wf, connstr))     (int q = 0; q < 5; q++)     {         var id = resume(storewrapper);         trace.writeline(string.format("resumed {0}", id));     }  }  activity getdelayactivity(string addname = "") {     var name = new variable<string>(string.format("incr{0}", addname));     activity wf = new sequence     {         displayname = "testdelayactivity",         variables = { name, new variable<string>("customdatacontext") },         activities =             {             new writeline                 {                     text = string.format("before delay {0}", delaytime)                 },                 new delay                 {                     duration = new inargument<timespan>(new timespan(0, 0, delaytime))                 },                 new writeline                 {                     text = "after delay"                 }             }     };     return wf; }  guid createandrun(storewrapper sw) {     var idleevent = new autoresetevent(false);     var wfapp = sw.getapplication();      wfapp.idle = e => idleevent.set();     wfapp.aborted = e => idleevent.set();     wfapp.completed = e => idleevent.set();      wfapp.run();      idleevent.waitone(40 * 1000);     var res = wfapp.id;     wfapp.unload();     return res; }  guid resume(storewrapper sw) {     var res = guid.empty;      var events = sw.getstore().waitforevents(sw.handle, new timespan(0, 0, delaytime));      if (events.any(e => e.equals(hasrunnableworkflowevent.value)))     {         var idleevent = new autoresetevent(false);          var obj = sw.getapplication();         try         {             obj.loadrunnableinstance(); //instancenotready here if same store has read instances db , no delayed left              obj.idle = e => idleevent.set();             obj.completed = e => idleevent.set();              obj.run();              idleevent.waitone(40 * 1000);              res = obj.id;              obj.unload();         }         catch (instancenotreadyexception)         {             trace.traceerror("failed resume {0} {1} {2}", obj.id                 , obj.definitionidentity == null ? null : obj.definitionidentity.name                 , obj.definitionidentity == null ? null : obj.definitionidentity.version);             foreach (var e in events)             {                 trace.tracewarning("event {0}", e.name);             }             throw;         }     }     return res; } 

here store wrapper definition i'm using test:

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

i'm not workflow foundation expert answer based on official examples microsoft. first 1 wf4 host resumes delayed workflow (cswf4longrunninghost) , second microsoft.samples.absolutedelay. in both samples find code similar yours i.e.:

try  {     wfapp.loadrunnableinstance();     ... }  catch (instancenotreadyexception)  {     //some logging } 

taking account answer right , empty catch instancenotreadyexception solution.


Comments

Popular posts from this blog

sublimetext3 - what keyboard shortcut is to comment/uncomment for this script tag in sublime -

java - No use of nillable="0" in SOAP Webservice -

ubuntu - Laravel 5.2 quickstart guide gives Not Found Error -