locked
About Exception Handlers and RollBacks RRS feed

  • Question

  • Hi,

    For example I have this activities in the workflow:

    Start
    CodeActivity1
    HandleExternalActivity1
    CodeActivity2
    HandleExternalActivity2
    CodeActivity3
    End

    What I want is to handle possible exceptions that may occur in the code activity's...

    For example, I start the workflow and it will wait for a event. When the event HandleExternalActivity1 arrives, it will execute the CodeActivity2.
    If any error occurs inside CodeActivity2, I want to suspend the execution, and return to the previous state, that is wait for event HandleExternalActivity1 to arrive again...
    Is this possible and how can I do it?

    And what if the error occurs in the CodeActivity1? Can I handle the exception and run the same workflow again with other input values or objects, or it's better to just dump this instance and start a new one?

    Thanks in advance
    Wednesday, July 25, 2007 5:03 PM

Answers

  • If an exception occurs in codeactivity, only way you can handle it is using try-catch block. However you will not be able to influence execution of the workflow from code activity. One option is to add codeactivity to a sequence and handle the exception in the faulthandlersactivty of the sequence

    So your workflow could be like this   

    Declare a variable bool repeat = true in codebeside file
    Start
    CodeActivity1
    While ( repeat == true)
       HandleExternalActivity1
       Sequence1
            CodeActivity2 (Set repeat = false at the end of your code)
            FaultHandlersActivity
                 FaultHandlerActivity (Handle System.Exception or whatever is your exception. Add activities to do any other processing)
                     CodeActivity4 (Set repeat = true,  to continue the while loop in case of exception)
      IfElseActivity( If repeat == false ) //Go here only if codeactivity1 completed successfully
        HandleExternalActivity2
         Sequence2
            CodeActivity3
            FaultHandlersActivity
                 FaultHandlerActivity (Handle System.Exception or whatever is your exception. Add activities to do any other processing)
                     CodeActivity5 (Set repeat = true,  to continue the while loop)

    End


    CodeActivity4 and 5 can  point to the same codehandler.

     

    WF provides activities like TransactionScope, CompensatableTransactionScope and CompensatableSequence to handle the rollbacks and compensation of a completed task. You might find these activities useful for your scenario

    http://msdn2.microsoft.com/en-us/library/ms733615.aspx

     

    Wednesday, July 25, 2007 7:46 PM

All replies

  • If an exception occurs in codeactivity, only way you can handle it is using try-catch block. However you will not be able to influence execution of the workflow from code activity. One option is to add codeactivity to a sequence and handle the exception in the faulthandlersactivty of the sequence

    So your workflow could be like this   

    Declare a variable bool repeat = true in codebeside file
    Start
    CodeActivity1
    While ( repeat == true)
       HandleExternalActivity1
       Sequence1
            CodeActivity2 (Set repeat = false at the end of your code)
            FaultHandlersActivity
                 FaultHandlerActivity (Handle System.Exception or whatever is your exception. Add activities to do any other processing)
                     CodeActivity4 (Set repeat = true,  to continue the while loop in case of exception)
      IfElseActivity( If repeat == false ) //Go here only if codeactivity1 completed successfully
        HandleExternalActivity2
         Sequence2
            CodeActivity3
            FaultHandlersActivity
                 FaultHandlerActivity (Handle System.Exception or whatever is your exception. Add activities to do any other processing)
                     CodeActivity5 (Set repeat = true,  to continue the while loop)

    End


    CodeActivity4 and 5 can  point to the same codehandler.

     

    WF provides activities like TransactionScope, CompensatableTransactionScope and CompensatableSequence to handle the rollbacks and compensation of a completed task. You might find these activities useful for your scenario

    http://msdn2.microsoft.com/en-us/library/ms733615.aspx

     

    Wednesday, July 25, 2007 7:46 PM
  • Hi SonaliC,

    Thank you for helping me out.

    Ok. I managed to set up the workflow as you mentioned above.
    The only difference is that I use a TransactionScopeActivity instead of a SequenceActivity.
    I force a error to occur in the CodeActivity and throw one of our custom exception.
    I catch it in the FaultHandlersActivity and do what I want in a CodeActivity.

    The problem now is that when it finishes executing the CodeActivity of the FaultHandlerActivity it persists the workflow and then COMPLETES the workflow (I subscribed to all events just for debugging).
    I set the UnloadOnIdle to false for convenience and use ManualWorkflowSchedulerService because this is an ASP.NET app.
    I understand why it persists before completion, but why it completes I can't figured it out.

    Am I supposed to do anything more?


    Here are the samples of the code I use:
    where the exception is raised inside a CodeActivity:

    if (StockItemDeliveryFactory.Insert(item, Au.UserName))
    {
           throw new SIADBAccessException("Distribution", "OrderDistribution", "StockItemDelivery", "", "StockItemDeliveryFactory.Insert");
    }

    (StockItemDeliveryFactory.Insert returns true if it succeeds, and I omitted the NOT! just for testing)

    and the code of the FaultHandlers CodeActivity:

    void FaultHandler_ExeCode(object sender, EventArgs e)
    {
            repeat = true;
            ............
    }



    I have a few more questions:

    - For state machine workflows, the schema is the same but I don't need the while activity's, it works if in the FaultHandlers I use a SetState to point the path to where I want it, right?

    - In the schema you mentioned above, if the error occurs in CodeActivity1, the best solution is to handle the exception in the FaultHandlers and terminate the workflow, and start a new one on a new submission. Do you have a better or "cleaner" solution?

    Thanks in advance again, and I appreciate your support.
    Thursday, July 26, 2007 5:41 PM
  • If there are no more activities in the workflow, it will complete. Do you mean there are some activities in the execution path and workflow is completing without executing them?

    About your other question, Statemachine is better suited for your scenario. So your solution with statemachine should work fine.

    Thursday, July 26, 2007 8:47 PM
  • I have other workflows in the solution that are statemachine... But this ones, because of it simplicity, i'd like to keep them sequential...

     SonaliC wrote:

    Do you mean there are some activities in the execution path and workflow is completing without executing them?



    Yes! What I mean is exactly that!
    I even forced an exception in the first CodeActivity and the workflow still completes without executing the rest of the activities!
    Friday, July 27, 2007 9:23 AM
  • Viva ;-)

    I don't know the cause for your specific problem but one advice I can give you is to turn on logging on the app/web config. It can be very helpful (i once lost about 5 hours because of some lost exception in the runtime...)

        <system.diagnostics>
            <switches>
                <add name="System.Workflow LogToTraceListeners" value="1" />

                <add name="System.Workflow.Runtime" value="Error" />
                <add name="System.Workflow.Runtime.Hosting" value="Error" />
                <add name="System.Workflow.Runtime.Tracking" value="Error" />
                <add name="System.Workflow.Activities" value="Error" />
                <add name="System.Workflow.Activities.Rules" value="Error" />

            </switches>
            <trace autoflush="true" indentsize="4">
                <listeners>
                    <add name="customListener" type="System.Diagnostics.TextWriterTraceListener" initializeData="X:\somewhere\WFTrace.log" />
                </listeners>
            </trace>
        </system.diagnostics>


    There are more levels of logging for the sources, you can check them here:

    http://msdn2.microsoft.com/en-us/library/ms732240.aspx (Enabling Workflow Logging)

    Sorry if you already knew about this, but since it helped me a lot thought it would be relevant.

    Cumprimentos,
    Pedro Pita
    Friday, July 27, 2007 9:58 AM
  • Boas! ;P

    I've already read about this, but I never actually used it!

    I used it now, but the only logging that was made is this:

    System.Workflow.Runtime Error: 1 : Execute of Activity OrderDistribution threw SIA.BusinessExceptions.SIADBAccessException: Error in the application.
       at SIA.BusinessWorkflows.Distribution.OrderDistribution_ExeCode(Object sender, EventArgs e) in E:\Projectos\SIA\Solution_12_07_2007_v2\BusinessWorkflows\Distribution.designer.cs:line 313
       at System.Workflow.ComponentModel.Activity.RaiseEvent(DependencyProperty dependencyEvent, Object sender, EventArgs e)
       at System.Workflow.Activities.CodeActivity.Execute(ActivityExecutionContext executionContext)
       at System.Workflow.ComponentModel.ActivityExecutor`1.Execute(T activity, ActivityExecutionContext executionContext)
       at System.Workflow.ComponentModel.ActivityExecutor`1.Execute(Activity activity, ActivityExecutionContext executionContext)
       at System.Workflow.ComponentModel.ActivityExecutorOperation.Run(IWorkflowCoreRuntime workflowCoreRuntime)



    When I set the log to All, I get lots of info, but what I can get from here, is that it handles the exception I raised, it returns the execution, and then the activity Distribution (workflow itself) seems to fault also... Indifferent

    My workflow now looks like this
    Start
    TransactionScope3
    OrderDistribution(CodeActivity)
    ProcessOutgoingActivity(HandleExternalActivity)
    TransactionScope2
    UpdateOutgoingStock(CodeActivity)
    ProcessIncomingActivity(HandleExternalActivity)
    TransactionScope3
    UpdateIncomingStock(CodeActivity)
    End

    DistributionFaultHandlers
    FaultHandlerDBAccess
    FaultHandlerDBAccessCodeActivity(CodeActivity)


    I'm raising an exception in the OrderDistribution(CodeActivity). Indifferent
    Friday, July 27, 2007 10:52 AM
  • Ok... That was I just beeing dumb! Sorry!

    It works fine now. I didn't understand why you used a SequenceActivity, I thought that it was because of the WhileActivity, because I can't put a HandleExternalActivity and a CodeActivity inside a WhileActivity, without grouping them with a SequenceActivity or a TransactionalScopeActivity...
    The problem is that the TransactionalScope doesn't support fault handlers, so I thought that I was supposed to used the global FaultHandler...

    If we treat the exception in the global FaultHandlers it finishes the execution of the workflow without executing any more activities, in a "cleaner" way (Completed).

    One last question now...

    My workflow now is like this:

    Start
    SequenceActivity1
    TransactionScope1
    CodeActivity1
    FaultHandlersActivity1
    FaultHandlerActivity1
    CodeActivity4
    WhileActivity1 (repeat == true)
    TransactionScope2
    HandleExternalActivity1
    CodeActivity2
    FaultHandlersActivity2 (from WhileActivity1)
    FaultHandlerActivity2
    CodeActivity5
    WhileActivity2 (repeat2 == true)
    TransactionScope3
    HandleExternalActivity2
    CodeActivity3
    FaultHandlersActivity3 (from WhileActivity2)
    FaultHandlerActivity3
    CodeActivity6
    End

    Is it correct to have a Activity's inside a TransactionScopeActivity, that is inside a SequenceActivity?

    It sounds a little messy... but I think it's the best thing to do... Do you agree?
    Friday, July 27, 2007 2:17 PM
  • While activity can have only single child, so we typically add a sequence to it and then add other activities to the sequence. It is absolutely fine to nest activities they way you want. If it is not our compiler will give a sematic error.

    Why do you need to add codeactivity to TransactionScope? TransactionScope should be typically used if you want to execute multiple tasks within the same transaction. One thing to keep in mind is transaction associated with the TransactionScope might timeout if your event does not arrive within the timeout of the TransactionScope and throw an exception.

     

    Friday, July 27, 2007 9:00 PM