//--------------------------------------------------------------------- // Public Cadence workflow related operations. /// <summary> /// Registers a workflow implementation with Cadence. /// </summary> /// <typeparam name="TWorkflow">The <see cref="IWorkflowBase"/> derived class implementing the workflow.</typeparam> /// <param name="workflowTypeName"> /// Optionally specifies a custom workflow type name that will be used /// for identifying the workflow implementation in Cadence. This defaults /// to the fully qualified <typeparamref name="TWorkflow"/> type name. /// </param> /// <param name="domain">Optionally overrides the default client domain.</param> /// <returns>The tracking <see cref="Task"/>.</returns> /// <exception cref="InvalidOperationException">Thrown if another workflow class has already been registered for <paramref name="workflowTypeName"/>.</exception> /// <exception cref="CadenceWorkflowWorkerStartedException"> /// Thrown if a workflow worker has already been started for the client. You must /// register activity workflow implementations before starting workers. /// </exception> /// <remarks> /// <note> /// Be sure to register all of your workflow implementations before starting a workflow worker. /// </note> /// </remarks> public async Task RegisterWorkflowAsync <TWorkflow>(string workflowTypeName = null, string domain = null) where TWorkflow : WorkflowBase { CadenceHelper.ValidateWorkflowImplementation(typeof(TWorkflow)); CadenceHelper.ValidateWorkflowTypeName(workflowTypeName); if (string.IsNullOrEmpty(workflowTypeName)) { workflowTypeName = workflowTypeName ?? typeof(TWorkflow).FullName; } if (workflowWorkerStarted) { throw new CadenceWorkflowWorkerStartedException(); } // We need to register a workflow type name for each workflow method, // appending the method type separator and method name for workflow // methods that specify names. if (!WorkflowBase.Register(this, typeof(TWorkflow), workflowTypeName)) { var reply = (WorkflowRegisterReply) await CallProxyAsync( new WorkflowRegisterRequest() { Name = workflowTypeName, Domain = ResolveDomain(domain) }); reply.ThrowOnError(); } }
//--------------------------------------------------------------------- // Public Cadence workflow related operations. /// <summary> /// Registers a workflow implementation with Cadence. /// </summary> /// <typeparam name="TWorkflow">The <see cref="WorkflowBase"/> derived class implementing the workflow.</typeparam> /// <param name="workflowTypeName"> /// Optionally specifies a custom workflow type name that will be used /// for identifying the workflow implementation in Cadence. This defaults /// to the fully qualified <typeparamref name="TWorkflow"/> type name. /// </param> /// <param name="domain">Optionally overrides the default client domain.</param> /// <returns>The tracking <see cref="Task"/>.</returns> /// <exception cref="InvalidOperationException">Thrown if another workflow class has already been registered for <paramref name="workflowTypeName"/>.</exception> /// <exception cref="WorkflowWorkerStartedException"> /// Thrown if a workflow worker has already been started for the client. You must /// register workflow implementations before starting workers. /// </exception> /// <remarks> /// <note> /// Be sure to register all of your workflow implementations before starting workers. /// </note> /// </remarks> public async Task RegisterWorkflowAsync <TWorkflow>(string workflowTypeName = null, string domain = null) where TWorkflow : WorkflowBase { await SyncContext.ClearAsync; CadenceHelper.ValidateWorkflowImplementation(typeof(TWorkflow)); CadenceHelper.ValidateWorkflowTypeName(workflowTypeName); EnsureNotDisposed(); if (workflowWorkerStarted) { throw new WorkflowWorkerStartedException(); } var workflowType = typeof(TWorkflow); if (string.IsNullOrEmpty(workflowTypeName)) { workflowTypeName = CadenceHelper.GetWorkflowTypeName(workflowType, workflowType.GetCustomAttribute <WorkflowAttribute>()); } await WorkflowBase.RegisterAsync(this, workflowType, workflowTypeName, ResolveDomain(domain)); lock (registeredWorkflowTypes) { registeredWorkflowTypes.Add(CadenceHelper.GetWorkflowInterface(typeof(TWorkflow))); } }
/// <summary> /// Constructor. /// </summary> public SignalRequest() { // This class may only be constructed within signal or workflow methods. WorkflowBase.CheckCallContext(allowSignal: true, allowWorkflow: true); SignalId = Workflow.Current.SignalId; }
/// <summary> /// Releases all associated resources. /// </summary> /// <param name="disposing">Pass <c>true</c> if we're disposing, <c>false</c> if we're finalizing.</param> protected virtual void Dispose(bool disposing) { WorkflowBase.CheckCallContext(allowWorkflow: true, allowSignal: true); if (disposing && !isClosed) { CloseAsync().WaitWithoutAggregate(); } isDisposed = true; }
/// <summary> /// Internal constructor. /// </summary> /// <param name="parentWorkflow">The parent workflow.</param> /// <param name="queueId">The queue ID.</param> /// <param name="capacity">The maximum number of items allowed in the queue.</param> /// <exception cref="NotSupportedException">Thrown when this is called outside of a workflow entry point method.</exception> /// <remarks> /// <note> /// <see cref="WorkflowQueue{T}"/> instances may only be created within /// workflow entry point methods. /// </note> /// </remarks> internal WorkflowQueue(Workflow parentWorkflow, long queueId, int capacity) { Covenant.Requires <ArgumentNullException>(parentWorkflow != null, nameof(parentWorkflow)); Covenant.Requires <ArgumentException>(queueId > 0, nameof(queueId)); Covenant.Requires <ArgumentException>(capacity >= 2, nameof(capacity)); WorkflowBase.CheckCallContext(allowWorkflow: true); this.client = parentWorkflow.Client; this.contextId = parentWorkflow.ContextId; this.Capacity = capacity; this.queueId = queueId; this.isClosed = false; }
/// <summary> /// Called by your workflow logic to indicate that processing for the synchronous /// signal is complete as well as specifying the signal result. /// </summary> /// <param name="result">The value to be returned by the signal.</param> /// <returns>The tracking <see cref="Task"/>.</returns> public async Task ReplyAsync(TResult result) { await SyncContext.Clear; // This may only be called within a workflow method. WorkflowBase.CheckCallContext(allowWorkflow: true); // Save the signal completion and result so a subsequent polling query can retrieve it. SignalStatus.Result = Workflow.Current.Client.DataConverter.ToData(result); SignalStatus.Completed = true; await Task.CompletedTask; }
/// <summary> /// Called by your workflow logic to indicate that processing for the synchronous /// signal is complete. This method also waits for a period of time before /// returning to help ensure that the signal result can be delivered back to /// the calling client before the workflow terminates. /// </summary> /// <returns>The tracking <see cref="Task"/>.</returns> public async Task ReplyAsync() { await SyncContext.Clear; // This may only be called within a workflow method. WorkflowBase.CheckCallContext(allowWorkflow: true); // Save the signal completion so a subsequent polling query can retrieve it. SignalStatus.Result = null; // NULL result for void signal methods SignalStatus.Completed = true; await Task.CompletedTask; }
/// <summary> /// Scans the assembly passed looking for workflow implementations derived from /// <see cref="WorkflowBase"/> and tagged by <see cref="WorkflowAttribute"/> with /// <see cref="WorkflowAttribute.AutoRegister"/> set to <c>true</c> and registers /// them with Cadence. /// </summary> /// <param name="assembly">The target assembly.</param> /// <returns>The tracking <see cref="Task"/>.</returns> /// <exception cref="TypeLoadException"> /// Thrown for types tagged by <see cref="WorkflowAttribute"/> that are not /// derived from <see cref="WorkflowBase"/>. /// </exception> /// <exception cref="InvalidOperationException">Thrown if one of the tagged classes conflict with an existing registration.</exception> /// <exception cref="CadenceWorkflowWorkerStartedException"> /// Thrown if a workflow worker has already been started for the client. You must /// register activity workflow implementations before starting workers. /// </exception> /// <remarks> /// <note> /// Be sure to register all of your workflow implementations before starting a workflow worker. /// </note> /// </remarks> public async Task RegisterAssemblyWorkflowsAsync(Assembly assembly) { Covenant.Requires <ArgumentNullException>(assembly != null); if (workflowWorkerStarted) { throw new CadenceWorkflowWorkerStartedException(); } foreach (var type in assembly.GetTypes()) { var workflowAttribute = type.GetCustomAttribute <WorkflowAttribute>(); if (workflowAttribute != null) { if (type.BaseType == typeof(WorkflowBase)) { if (workflowAttribute.AutoRegister) { var workflowTypeName = workflowAttribute.TypeName ?? type.FullName; if (!WorkflowBase.Register(this, type, workflowTypeName)) { var reply = (WorkflowRegisterReply) await CallProxyAsync( new WorkflowRegisterRequest() { Name = workflowTypeName }); reply.ThrowOnError(); } } } else { throw new TypeLoadException($"Type [{type.FullName}] is tagged by [{nameof(WorkflowAttribute)}] but is not derived from [{nameof(WorkflowBase)}]."); } } } }
/// <summary> /// Scans the assembly passed looking for workflow implementations derived from /// <see cref="WorkflowBase"/> and tagged by <see cref="WorkflowAttribute"/> with /// <see cref="WorkflowAttribute.AutoRegister"/> set to <c>true</c> and registers /// them with Cadence. /// </summary> /// <param name="assembly">The target assembly.</param> /// <param name="domain">Optionally overrides the default client domain.</param> /// <returns>The tracking <see cref="Task"/>.</returns> /// <exception cref="TypeLoadException"> /// Thrown for types tagged by <see cref="WorkflowAttribute"/> that are not /// derived from <see cref="WorkflowBase"/>. /// </exception> /// <exception cref="InvalidOperationException">Thrown if one of the tagged classes conflict with an existing registration.</exception> /// <exception cref="WorkflowWorkerStartedException"> /// Thrown if a workflow worker has already been started for the client. You must /// register workflow implementations before starting workers. /// </exception> /// <remarks> /// <note> /// Be sure to register all of your workflow implementations before starting workers. /// </note> /// </remarks> public async Task RegisterAssemblyWorkflowsAsync(Assembly assembly, string domain = null) { await SyncContext.ClearAsync; Covenant.Requires <ArgumentNullException>(assembly != null, nameof(assembly)); EnsureNotDisposed(); foreach (var type in assembly.GetTypes().Where(t => t.IsClass)) { var workflowAttribute = type.GetCustomAttribute <WorkflowAttribute>(); if (workflowAttribute != null && workflowAttribute.AutoRegister) { var workflowTypeName = CadenceHelper.GetWorkflowTypeName(type, workflowAttribute); await WorkflowBase.RegisterAsync(this, type, workflowTypeName, ResolveDomain(domain)); lock (registeredWorkflowTypes) { registeredWorkflowTypes.Add(CadenceHelper.GetWorkflowInterface(type)); } } } }