@@ -83,11 +83,14 @@ public static IQueryable<CaseTagEntity> Tags(this CaseEntity e) =>
8383 public static IQueryable < CaseNotificationEntity > Notifications ( this CaseActivityEntity e ) =>
8484 As . Expression ( ( ) => Database . Query < CaseNotificationEntity > ( ) . Where ( a => a . CaseActivity . Is ( e ) ) ) ;
8585
86-
8786 [ AutoExpressionField ]
8887 public static IQueryable < CaseActivityExecutedTimerEntity > ExecutedTimers ( this CaseActivityEntity e ) =>
8988 As . Expression ( ( ) => Database . Query < CaseActivityExecutedTimerEntity > ( ) . Where ( a => a . CaseActivity . Is ( e ) ) ) ;
9089
90+ [ AutoExpressionField ]
91+ public static CaseActivityExecutedTimerEntity ? LastExecutedTimer ( this CaseActivityEntity ca , Lite < WorkflowEventEntity > we ) =>
92+ As . Expression ( ( ) => ca . ExecutedTimers ( ) . Where ( a => a . BoundaryEvent . Is ( we ) ) . OrderByDescending ( a => a . CreationDate ) . FirstOrDefault ( ) ) ;
93+
9194 public static void Start ( SchemaBuilder sb )
9295 {
9396 if ( sb . NotDefined ( MethodInfo . GetCurrentMethod ( ) ) )
@@ -291,11 +294,10 @@ IQueryable<CaseActivityEntity> CancelledCases(CaseEntity c)
291294 where ca . State == CaseActivityState . Pending
292295 from we in ( ( WorkflowActivityEntity ) ca . WorkflowActivity ) . BoundaryTimers
293296 where we . Type == WorkflowEventType . BoundaryInterruptingTimer ? true :
294- we . Type == WorkflowEventType . BoundaryForkTimer ? ! ca . ExecutedTimers ( ) . Any ( t => t . BoundaryEvent . Is ( we ) ) :
297+ we . Type == WorkflowEventType . BoundaryForkTimer ? ( we . RunRepeatedly || ! ca . ExecutedTimers ( ) . Any ( t => t . BoundaryEvent . Is ( we ) ) ) :
295298 false
296299 select new ActivityEvent ( ca , we ) ) . ToList ( ) ;
297300
298-
299301 var intermediateCandidates =
300302 ( from ca in Database . Query < CaseActivityEntity > ( )
301303 where ! ca . Workflow ( ) . HasExpired ( )
@@ -306,12 +308,20 @@ from we in ((WorkflowActivityEntity)ca.WorkflowActivity).BoundaryTimers
306308
307309 var candidates = boundaryCandidates . Concat ( intermediateCandidates ) . Distinct ( ) . ToList ( ) ;
308310 var conditions = candidates . Select ( a => a . Event . Timer ! . Condition ) . Distinct ( ) . ToList ( ) ;
311+ var lastExecutions = boundaryCandidates
312+ . Where ( a => a . Event . Type == WorkflowEventType . BoundaryForkTimer && a . Event . RunRepeatedly )
313+ . Select ( a => new { ActivityEvent = a , LastExecution = a . Activity . LastExecutedTimer ( a . Event . ToLite ( ) ) } )
314+ . ToList ( ) ;
309315
310316 var now = Clock . Now ;
311317 var activities = conditions . SelectMany ( cond =>
312318 {
313319 if ( cond == null )
314- return candidates . Where ( a => a . Event . Timer ! . Duration != null && a . Event . Timer ! . Duration ! . Add ( a . Activity . StartDate ) < now ) . Select ( a => a . Activity . ToLite ( ) ) . ToList ( ) ;
320+ return candidates . Where ( a =>
321+ {
322+ var startDate = lastExecutions . SingleOrDefaultEx ( le => le . ActivityEvent . Is ( a ) ) ? . LastExecution ? . CreationDate ?? a . Activity . StartDate ;
323+ return a . Event . Timer ! . Duration != null && a . Event . Timer ! . Duration ! . Add ( startDate ) < now ;
324+ } ) . Select ( a => a . Activity . ToLite ( ) ) . ToList ( ) ;
315325
316326 return candidates . Where ( a => a . Event . Timer ! . Condition . Is ( cond ) && cond . Evaluate ( a . Activity , now ) ) . Select ( a => a . Activity . ToLite ( ) ) . ToList ( ) ;
317327 } ) . Distinct ( ) . ToList ( ) ;
@@ -426,6 +436,11 @@ public ActivityEvent(CaseActivityEntity activity, WorkflowEventEntity @event)
426436 Event = @event ;
427437 }
428438
439+ public bool Is ( ActivityEvent other )
440+ {
441+ return this . Activity . Is ( other . Activity ) && this . Event . Is ( other . Event ) ;
442+ }
443+
429444 public CaseActivityEntity Activity { get ; set ; }
430445 public WorkflowEventEntity Event { get ; set ; }
431446 }
@@ -835,15 +850,22 @@ public static void Register()
835850 {
836851 var now = Clock . Now ;
837852
838- var alreadyExecuted = ca . ExecutedTimers ( ) . Select ( a => a . BoundaryEvent ) . ToHashSet ( ) ;
839-
840853 var candidateEvents = ca . WorkflowActivity is WorkflowEventEntity @event ? new WorkflowEventEntity [ ] { @event } :
841854 ( ( WorkflowActivityEntity ) ca . WorkflowActivity ) . BoundaryTimers . ToArray ( ) ;
842855
843- var timer = candidateEvents . Where ( e => e . Type == WorkflowEventType . BoundaryInterruptingTimer || ! alreadyExecuted . Contains ( e . ToLite ( ) ) ) . FirstOrDefault ( t =>
856+ var lastExecutions = (
857+ from et in ca . ExecutedTimers ( )
858+ group et by et . BoundaryEvent into g
859+ select new { Event = g . Key , LastExecution = g . OrderByDescending ( a => a . CreationDate ) . FirstOrDefault ( ) }
860+ ) . ToDictionary ( a => a . Event , a => a . LastExecution ) ;
861+
862+ var timer = candidateEvents . Where ( e => e . Type == WorkflowEventType . BoundaryInterruptingTimer || e . RunRepeatedly || ! lastExecutions . ContainsKey ( e . ToLite ( ) ) ) . FirstOrDefault ( t =>
844863 {
845864 if ( t . Timer ! . Duration != null )
846- return t . Timer ! . Duration ! . Add ( ca . StartDate ) < now ;
865+ {
866+ var startDate = lastExecutions . GetValueOrDefault ( t . ToLite ( ) ) ? . CreationDate ?? ca . StartDate ;
867+ return t . Timer ! . Duration ! . Add ( startDate ) < now ;
868+ }
847869
848870 return t . Timer ! . Condition ! . Evaluate ( ca , now ) ;
849871 } ) ;
@@ -1098,7 +1120,7 @@ private static void ExecuteBoundaryTimer(CaseActivityEntity ca, WorkflowEventEnt
10981120 } . Save ( ) ;
10991121 break ;
11001122 case WorkflowEventType . BoundaryInterruptingTimer :
1101- ca . MakeDone ( DoneType . Timeout , null ) ;
1123+ ca . MakeDone ( DoneType . Timeout , boundaryEvent . DecisionOptionName ) ;
11021124 break ;
11031125 default :
11041126 throw new InvalidOperationException ( "Unexpected Boundary Timer Type " + boundaryEvent . Type ) ;
0 commit comments