2016-02-05 5 views
5

DearsSqlWorkflowInstanceStore WaitForEvents रिटर्न HasRunnableWorkflowEvent लेकिन LoadRunnableInstance

विफल रहता है कृपया मुझे देरी को पुनर्प्राप्त (और कायम) वर्कफ़्लो के साथ मदद।

मैं स्वयं-होस्ट किए गए वर्कफ़्लो स्टोर पर जांच करने की कोशिश कर रहा हूं, क्या कोई ऐसा उदाहरण है जो देरी हो और फिर से शुरू किया जा सके। परीक्षण उद्देश्यों के लिए मैंने डमी गतिविधि बनाई है जो देरी हो रही है और यह देरी पर बनी हुई है।

आम तौर पर फिर से शुरू प्रक्रिया की तरह दिखता है:

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 

यह ठीक काम करता है, तो दुकान created|initialized है, WaitForEvents कहा जाता है, की दुकान बंद है। ऐसे मामले में स्टोर लगातार उपलब्ध डीबी से सभी उपलब्ध वर्कफ़्लो पढ़ता है और फिर से शुरू करने के लिए workflows उपलब्ध होने पर टाइमआउट अपवाद फेंकता है।

समस्या तब होती है जब स्टोर बनाया जाता है और लूप केवल WaitForEvents के लिए शुरू होता है (वही बात BeginWaitForEvents के साथ होती है)। ऐसे मामले में यह सभी उपलब्ध workflowsDB से (उचित आईडी के साथ) पढ़ता है लेकिन फिर timeout exception के बजाय यह एक और उदाहरण पढ़ने जा रहा है (मुझे पता है कि कितने workflows फिर से शुरू होने के लिए तैयार हैं क्योंकि अलग-अलग परीक्षण database का उपयोग करके)। लेकिन पढ़ने में विफल रहता है और throws InstanceNotReadyExceptioncatch में मैं 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 

लेकिन इस एक के रूप में उम्मीद काम करता है:

for (int q = 0; q < 5; q++) 
    using (var storeWrapper = new StoreWrapper(wf, connStr)) 
    { 
     var id = Resume(storeWrapper); // timeout exception here or beginWaitForEvents continues to wait 

ऐसे मामले में सबसे अच्छा समाधान क्या है? InstanceNotReadyException के लिए खाली catch जोड़ें और इसे अनदेखा करें?

यहाँ मेरी परीक्षण

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)) 
    for (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 the same store has read all instances from DB and 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 to 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; 
} 

यहाँ है दुकान आवरण परिभाषा मैं परीक्षण के लिए उपयोग कर रहा हूँ कर रहे हैं:

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

मूल प्रश्न के लिए उपरोक्त। आशा है कि आपको अधिक ध्यान मिलेगा। असल में, मुझे लगता है कि आपके '150' बक्षीस के बावजूद आपके प्रश्न को कम ध्यान क्यों मिलता है। (1) 'वर्कफ़्लो' का उपयोग करने वाले बहुत से लोग नहीं, (2) आप 'सर्वश्रेष्ठ समाधान' मांग रहे हैं जो स्टैक ओवरफ़्लो की बजाय कोड समीक्षा के लिए अधिक उपयुक्त प्रश्न हो सकता है, (3) यह मामूली है, लेकिन आपका टेस्ट कोड सुंदर है सार्थक स्पष्टीकरण के बिना लंबे समय तक (जो ठीक है अगर लोग वास्तव में आपकी समस्या को सीधे डुप्लिकेट कर सकते हैं, लेकिन अन्यथा कोड पढ़ने को हतोत्साहित करेंगे)। आशा है कि आपको अपना जवाब मिल जाएगा हालांकि ... – Ian

+0

असफल और असफल लूप दो अलग-अलग चीजें नहीं कर रहे हैं, प्रत्येक एक पुनरावृत्ति के लिए आप एक नया स्टोरप्रैपर शुरू करते हैं। और दूसरा आप प्रत्येक पुनरावृत्ति के लिए एक स्टोरवापर का पुन: उपयोग करते हैं। यह बहुत समय पहले मैंने वर्कफ़्लो का उपयोग किया था, लेकिन कोड को देखते हुए मुझे लगता है कि आपका स्टोर रैपर एक पुन: प्रयोज्य वस्तु नहीं है। तो आप केवल काम करने वाले पाश के साथ जाने पर विचार कर सकते हैं। या यह किसी अन्य समस्या का कारण बनता है? – Thorarins

+0

@ थोरारिन मैं सादे SqlWorkflowInstanceStore का उपयोग कर नमूने फिर से लिख सकता हूं लेकिन समस्या बनी रहेगी। मैंने वेब पर नमूने देखे हैं जो वर्कफ़्लो चलाने के लिए तैयार WF डेटाबेस से पूछने के लिए एक ही SqlWorkflowInstanceStore उदाहरण का उपयोग करता है। मैं लूप के अंदर नया उदाहरण बना सकता हूं लेकिन मुझे डर है कि इससे अतिरिक्त मेमोरी ओवरहेड और टाइम देरी होगी। – oleksa

उत्तर

0

मैं नहीं कार्यप्रवाह नींव विशेषज्ञ तो मेरा उत्तर Microsoft से आधिकारिक उदाहरण पर आधारित है । पहला वाला WF4 host resumes delayed workflow (CSWF4LongRunningHost) है और दूसरा Microsoft.Samples.AbsoluteDelay है। दोनों नमूनों में आप एक कोड तुम्हारा यानी .:

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

के समान खाते में ले रहा है जवाब है कि आप सही कर रहे हैं और InstanceNotReadyException के लिए खाली पकड़ एक अच्छा समाधान है मिल जाएगा।

संबंधित मुद्दे