//---------------------------------------------------------------------
        // 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();
            }
        }
Example #2
0
        //---------------------------------------------------------------------
        // 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)));
            }
        }
Example #3
0
        /// <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;
        }
Example #4
0
        /// <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;
        }
Example #5
0
        /// <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;
        }
Example #6
0
        /// <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;
        }
Example #7
0
        /// <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)}].");
                    }
                }
            }
        }
Example #9
0
        /// <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));
                    }
                }
            }
        }