/// <summary> /// Restart the workflows associated with an entity /// </summary> /// <param name="userContext"></param> /// <param name="suggestionsContext"></param> /// <param name="entity"></param> /// <param name="workflowType"></param> public static void RestartWorkflow(UserStorageContext userContext, SuggestionsStorageContext suggestionsContext, ServerEntity entity, string workflowType) { if (entity == null || workflowType == null) { return; } try { // kill all existing workflows associated with this Item // TODO: also need to mark the suggestions associated with this workflow as stale so that they don't // show up for the item again. var runningWFs = suggestionsContext.WorkflowInstances.Where(wi => wi.EntityID == entity.ID).ToList(); if (runningWFs.Count > 0) { foreach (var wf in runningWFs) { suggestionsContext.WorkflowInstances.Remove(wf); } suggestionsContext.SaveChanges(); } StartWorkflow(userContext, suggestionsContext, workflowType, entity, null); } catch (Exception) { StartWorkflow(userContext, suggestionsContext, workflowType, entity, null); } }
/// <summary> /// Delete all workflow instances associated with this entity ID (the entity is already deleted) /// </summary> /// <param name="userContext"></param> /// <param name="suggestionsContext"></param> /// <param name="entity"></param> public static void DeleteWorkflows(UserStorageContext userContext, SuggestionsStorageContext suggestionsContext, Guid entityID) { try { // get all the workflow instances for this Item var wis = suggestionsContext.WorkflowInstances.Where(w => w.EntityID == entityID).ToList(); if (wis.Count > 0) { // loop over the workflow instances and dispatch the new message foreach (var instance in wis) { suggestionsContext.WorkflowInstances.Remove(instance); } suggestionsContext.SaveChanges(); } } catch (Exception ex) { TraceLog.TraceException("DeleteWorkflows failed", ex); } }
/// <summary> /// Delete all workflow instances associated with this entity ID (the entity is already deleted) /// </summary> /// <param name="userContext"></param> /// <param name="suggestionsContext"></param> /// <param name="entity"></param> public static void DeleteWorkflows(UserStorageContext userContext, SuggestionsStorageContext suggestionsContext, Guid entityID) { try { // get all the workflow instances for this Item var wis = suggestionsContext.WorkflowInstances.Where(w => w.EntityID == entityID).ToList(); if (wis.Count > 0) { // loop over the workflow instances and dispatch the new message foreach (var instance in wis) { suggestionsContext.WorkflowInstances.Remove(instance); } suggestionsContext.SaveChanges(); } } catch (Exception ex) { TraceLog.TraceException("DeleteWorkflows failed", ex); } }
/// <summary> /// Execute the workflow instances associated with this entity /// </summary> /// <param name="entity"></param> /// <param name="userContext"></param> /// <param name="suggestionsContext"></param> /// <returns>true if processing happened (and the operation doesn't need to be processed again), /// false if one the workflow instances was locked (causes the message to be reprocessed)</returns> public static bool ExecuteWorkflows(UserStorageContext userContext, SuggestionsStorageContext suggestionsContext, ServerEntity entity) { if (entity == null) { return(true); } List <WorkflowInstance> wis = null; try { // get all the workflow instances for this Item wis = suggestionsContext.WorkflowInstances.Where(w => w.EntityID == entity.ID).ToList(); if (wis.Count > 0) { // if the instance is locked by someone else, stop processing // otherwise lock each of the workflow instances foreach (var instance in wis) { if (instance.LockedBy != null && instance.LockedBy != Me) { return(false); } instance.LockedBy = Me; } suggestionsContext.SaveChanges(); // reacquire the lock list and verify they were all locked by Me (if not, stop processing) // projecting locks and not workflow instances to ensure that the database's lock values are returned (not from EF's cache) var locks = suggestionsContext.WorkflowInstances.Where(w => w.EntityID == entity.ID).Select(w => w.LockedBy).ToList(); foreach (var lockedby in locks) { if (lockedby != Me) { return(false); } } // loop over the workflow instances and dispatch the new message foreach (var instance in wis) { Workflow workflow = null; try { var wt = suggestionsContext.WorkflowTypes.Single(t => t.Type == instance.WorkflowType); workflow = JsonSerializer.Deserialize <Workflow>(wt.Definition); } catch (Exception ex) { TraceLog.TraceException("Could not find or deserialize workflow definition", ex); continue; } // set the database contexts workflow.UserContext = userContext; workflow.SuggestionsContext = suggestionsContext; // invoke the workflow and process steps until workflow is blocked for user input or is done workflow.Run(instance, entity); } } return(true); } catch (Exception ex) { TraceLog.TraceException("ExecuteWorkflows failed", ex); return(true); } finally { // find and unlock all remaining workflow instances that relate to this entity // note that a new context is used for this - to avoid caching problems where the current thread // believes it is the owner but the database says otherwise. var context = Storage.NewSuggestionsContext; wis = context.WorkflowInstances.Where(w => w.EntityID == entity.ID).ToList(); if (wis.Count > 0) { // unlock each of the workflow instances foreach (var instance in wis) { if (instance.LockedBy == Me) { instance.LockedBy = null; } } context.SaveChanges(); } } }
/// <summary> /// Start a workflow of a certain type, passing it an entity and some instance data to start /// </summary> /// <param name="userContext"></param> /// <param name="suggestionsContext"></param> /// <param name="type">String representing the workflow type</param> /// <param name="entity">Entity to associate with the workflow</param> /// <param name="instanceData">Instance data to pass into the workflow</param> public static void StartWorkflow(UserStorageContext userContext, SuggestionsStorageContext suggestionsContext, string type, ServerEntity entity, string instanceData) { WorkflowInstance instance = null; try { Workflow workflow = null; // get the workflow definition out of the database try { var wt = suggestionsContext.WorkflowTypes.Single(t => t.Type == type); workflow = JsonSerializer.Deserialize <Workflow>(wt.Definition); } catch (Exception ex) { TraceLog.TraceException("Could not find or deserialize workflow definition", ex); return; } // don't start a workflow with no states if (workflow.States.Count == 0) { return; } // store the database contexts workflow.UserContext = userContext; workflow.SuggestionsContext = suggestionsContext; // create the new workflow instance and store in the workflow DB DateTime now = DateTime.Now; instance = new WorkflowInstance() { ID = Guid.NewGuid(), EntityID = entity.ID, EntityName = entity.Name, WorkflowType = type, State = workflow.States[0].Name, InstanceData = instanceData ?? "{}", Created = now, LastModified = now, LockedBy = WorkflowHost.Me, }; suggestionsContext.WorkflowInstances.Add(instance); suggestionsContext.SaveChanges(); TraceLog.TraceInfo("Starting workflow " + type); // invoke the workflow and process steps until workflow is blocked for user input or is done workflow.Run(instance, entity); // unlock the workflowinstance instance.LockedBy = null; suggestionsContext.SaveChanges(); } catch (Exception ex) { TraceLog.TraceException("StartWorkflow failed", ex); if (instance != null && instance.LockedBy == WorkflowHost.Me) { // unlock the workflowinstance instance.LockedBy = null; suggestionsContext.SaveChanges(); } } }
/// <summary> /// Execute the workflow instances associated with this entity /// </summary> /// <param name="entity"></param> /// <param name="userContext"></param> /// <param name="suggestionsContext"></param> /// <returns>true if processing happened (and the operation doesn't need to be processed again), /// false if one the workflow instances was locked (causes the message to be reprocessed)</returns> public static bool ExecuteWorkflows(UserStorageContext userContext, SuggestionsStorageContext suggestionsContext, ServerEntity entity) { if (entity == null) return true; List<WorkflowInstance> wis = null; try { // get all the workflow instances for this Item wis = suggestionsContext.WorkflowInstances.Where(w => w.EntityID == entity.ID).ToList(); if (wis.Count > 0) { // if the instance is locked by someone else, stop processing // otherwise lock each of the workflow instances foreach (var instance in wis) { if (instance.LockedBy != null && instance.LockedBy != Me) return false; instance.LockedBy = Me; } suggestionsContext.SaveChanges(); // reacquire the lock list and verify they were all locked by Me (if not, stop processing) // projecting locks and not workflow instances to ensure that the database's lock values are returned (not from EF's cache) var locks = suggestionsContext.WorkflowInstances.Where(w => w.EntityID == entity.ID).Select(w => w.LockedBy).ToList(); foreach (var lockedby in locks) if (lockedby != Me) return false; // loop over the workflow instances and dispatch the new message foreach (var instance in wis) { Workflow workflow = null; try { var wt = suggestionsContext.WorkflowTypes.Single(t => t.Type == instance.WorkflowType); workflow = JsonSerializer.Deserialize<Workflow>(wt.Definition); } catch (Exception ex) { TraceLog.TraceException("Could not find or deserialize workflow definition", ex); continue; } // set the database contexts workflow.UserContext = userContext; workflow.SuggestionsContext = suggestionsContext; // invoke the workflow and process steps until workflow is blocked for user input or is done workflow.Run(instance, entity); } } return true; } catch (Exception ex) { TraceLog.TraceException("ExecuteWorkflows failed", ex); return true; } finally { // find and unlock all remaining workflow instances that relate to this entity // note that a new context is used for this - to avoid caching problems where the current thread // believes it is the owner but the database says otherwise. var context = Storage.NewSuggestionsContext; wis = context.WorkflowInstances.Where(w => w.EntityID == entity.ID).ToList(); if (wis.Count > 0) { // unlock each of the workflow instances foreach (var instance in wis) if (instance.LockedBy == Me) instance.LockedBy = null; context.SaveChanges(); } } }
/// <summary> /// Start a workflow of a certain type, passing it an entity and some instance data to start /// </summary> /// <param name="userContext"></param> /// <param name="suggestionsContext"></param> /// <param name="type">String representing the workflow type</param> /// <param name="entity">Entity to associate with the workflow</param> /// <param name="instanceData">Instance data to pass into the workflow</param> public static void StartWorkflow(UserStorageContext userContext, SuggestionsStorageContext suggestionsContext, string type, ServerEntity entity, string instanceData) { WorkflowInstance instance = null; try { Workflow workflow = null; // get the workflow definition out of the database try { var wt = suggestionsContext.WorkflowTypes.Single(t => t.Type == type); workflow = JsonSerializer.Deserialize<Workflow>(wt.Definition); } catch (Exception ex) { TraceLog.TraceException("Could not find or deserialize workflow definition", ex); return; } // don't start a workflow with no states if (workflow.States.Count == 0) return; // store the database contexts workflow.UserContext = userContext; workflow.SuggestionsContext = suggestionsContext; // create the new workflow instance and store in the workflow DB DateTime now = DateTime.Now; instance = new WorkflowInstance() { ID = Guid.NewGuid(), EntityID = entity.ID, EntityName = entity.Name, WorkflowType = type, State = workflow.States[0].Name, InstanceData = instanceData ?? "{}", Created = now, LastModified = now, LockedBy = WorkflowHost.Me, }; suggestionsContext.WorkflowInstances.Add(instance); suggestionsContext.SaveChanges(); TraceLog.TraceInfo("Starting workflow " + type); // invoke the workflow and process steps until workflow is blocked for user input or is done workflow.Run(instance, entity); // unlock the workflowinstance instance.LockedBy = null; suggestionsContext.SaveChanges(); } catch (Exception ex) { TraceLog.TraceException("StartWorkflow failed", ex); if (instance != null && instance.LockedBy == WorkflowHost.Me) { // unlock the workflowinstance instance.LockedBy = null; suggestionsContext.SaveChanges(); } } }
/// <summary> /// Restart the workflows associated with an entity /// </summary> /// <param name="userContext"></param> /// <param name="suggestionsContext"></param> /// <param name="entity"></param> /// <param name="workflowType"></param> public static void RestartWorkflow(UserStorageContext userContext, SuggestionsStorageContext suggestionsContext, ServerEntity entity, string workflowType) { if (entity == null || workflowType == null) return; try { // kill all existing workflows associated with this Item // TODO: also need to mark the suggestions associated with this workflow as stale so that they don't // show up for the item again. var runningWFs = suggestionsContext.WorkflowInstances.Where(wi => wi.EntityID == entity.ID).ToList(); if (runningWFs.Count > 0) { foreach (var wf in runningWFs) suggestionsContext.WorkflowInstances.Remove(wf); suggestionsContext.SaveChanges(); } StartWorkflow(userContext, suggestionsContext, workflowType, entity, null); } catch (Exception) { StartWorkflow(userContext, suggestionsContext, workflowType, entity, null); } }