This content is archived. See latest version here

Last updated: Oct 21 2014

Introduction

This document provides an introduction to the customization of workflows. In order to replace or customize an existing workflow, the basic steps you need to perform are:

  1. Create and build a new sequential workflow in a Visual Studio project and add existing or new activities to a workflow.
  2. Update the ecf.workflow.config file to use the new workflow assembly.

Classes referred to here are available in the following namespaces:

Configuration

Workflow configuration is found in \Configs\ecf.workflow.config. This is the default content of ecf.workflow.config:

XML
<?xml version="1.0"?>
<Workflow>
  <Workflows>
     <add name="CartValidate" displayname="Validate Cart" description="Checks inventory, populates extra fields" type="Mediachase.Commerce.Workflow.CartValidateWorkflow, Mediachase.Commerce.Workflow" xomlpath="" rulespath=""/>
     <add name="CartPrepare" displayname="Cart Prepare" description="Creates shipments" type="Mediachase.Commerce.Workflow.CartPrepareWorkflow, Mediachase.Commerce.Workflow" xomlpath="" rulespath=""/>
     <add name="CartCheckout" displayname="Cart Checkout" description="Processes credit card information" type="Mediachase.Commerce.Workflow.CartCheckoutWorkflow, Mediachase.Commerce.Workflow" xomlpath="" rulespath=""/>
     <\!-\- Administration Workflowsmapped totoolbar actionsadd name="cmdPurchaseOrderCalculateTotals" displayname="Calculate Totals" description="Calculates totals" type="Mediachase.Commerce.Workflow.Admin.POCalculateTotalsWorkflow, Mediachase.Commerce.Workflow" xomlpath="" rulespath=""/>
     <add name="cmdPurchaseOrderProcessPayment" displayname="Process Payment" description="Process Payment" type="Mediachase.Commerce.Workflow.Admin.POProcessPaymentWorkflow, Mediachase.Commerce.Workflow" xomlpath="" rulespath=""/>
  </Workflows>
</Workflow>

Create new activity to send notifications

In the following example, we create a new workflow activity and then utilize it in a new workflow. We then use this workflow to replace the shipped CartCheckout workflow. The new activity sends an automated email to store customers after an order completion. The main aim of this example is to illustrate how a workflow can be updated and customized.

Create a new activity

  1. Create a Workflow Activity Library project in Visual Studio, in this example we have named the project CustomActivityLibrary1.
  2. Add a new activity to the activities project for the email. Name the Activity SendNotification.
  3. Set the inheritence for the activity to System.Workflow.ComponentModel.Activity.
  4. Add references to the CommerceLib project and Mediachase.MetaDataPlus dll in the Components folder.
  5. Add the code into the new activity's code-behind file (SendNotification.cs) from the Code Sample section in this document. Ensure the naming is correct and that you create the stub of the method to send the email and that your method is called in the activity Execute method. A stub for the method is provided in the SendEmailNotification() method. See the SendEmails method code in the PublicLayer/SDKs/B2CSampleSite/Templates/Everything/BusinessControls/CheckoutControls/CheckoutWizardModule.cs file.
    Note that you should use the activity OrderGroup object reference to retrieve the information about the order that you want to put in the email. To generate the formatted copy of the email body text with the order information, use the Mediachase.Commerce.Engine.Template.TemplateService. Refer to Sending Order Notifications for information on how to setup and use the template service.
  6. Build the project.
  7. Ensure that the activity has been added to the Visual Studio Toolbox. For information refer to the MSDN article "How to: Add Activities to the Toolbox".

Adding the activity to a workflow

Here we shall add the activity (created above) along with other activities contained in the Mediachase.Commerce.Workflow.Activities.dll in a new workflow. We will then use this workflow to replace the CartCheckout workflow (Mediachase.Commerce.Workflow.CartCheckoutWorkflow).

  1. Create a Workflow project in Visual Studio, in this example we name the project MyCustomCartCheckout.
  2. Add a sequential workflow to the project customCartCheckout - in this example we name the workflow customCartCheckout.
  3. Here we re-create the original CartCheckout Workflow and add the SendNotification activity created above. From the Toolbox, drag and drop activities onto the workflow design area in the following sequence:
    - ProcessPaymentActivity
    - AdjustInventoryActivity
    - RecordPromotionUsageActivity
    - SendNotification
  4. In the workflow code-behind file (in this example named customCartCheckout.cs), add the properties OrderGroup and Warnings. See the relevant code in the code sample section in this document.
  5. For each activity in the design view of the workflow, set the OrderGroup and Warning properties to the workflow OrderGroup and Warning properties. When you click on each activity in the workflow design view, you see the option to set this in the properties window.
  6. Add a reference to the CommerceLib project. Also add a reference to the Common.Logging and Mediachase.MetaDataPlus dlls in the Components folder.
  7. Build the project.
  8. Locate the newly built assembly and copy it to the bin folder of the project.
  9. Change the Configs\ecf.workflow.config file to point to the new assembly (workflow) which will handle the CartCheckout process. Update the CartCheckout element. Configuration for this example:
    <add name="CartCheckout" displayname="Cart Checkout" description="Processes credit card information" type="MyCustomCartCheckout.customCartCheckout, MyCustomCartCheckout" xomlpath="" rulespath="" />
  10. Finally - recompile, reset IIS, and access the website where this activity is used during checkout.

Examples

Example: the starting code for the SendNotification activity

C#
using System;
            using System.ComponentModel;
            using System.ComponentModel.Design;
            using System.Collections;
            using System.Collections.Generic;
            using System.Data;
            using System.Drawing;
            using System.Linq;
            using System.Threading;
            using System.Workflow.ComponentModel.Compiler;
            using System.Workflow.ComponentModel.Serialization;
            using System.Workflow.ComponentModel;
            using System.Workflow.ComponentModel.Design;
            using System.Workflow.Runtime;
            using System.Workflow.Activities;
            using System.Workflow.Activities.Rules;

            using Mediachase.Commerce.Orders;
            using Mediachase.Commerce.Catalog;
            using Mediachase.Commerce.Catalog.Dto;
            using Mediachase.Commerce.Orders.Managers;
            using Mediachase.Commerce.Catalog.Managers;

            namespace CustomActivity
            {
                public partial class SendNotification: Activity
                {
                    public static DependencyProperty OrderGroupProperty = DependencyProperty.Register("OrderGroup", typeof(OrderGroup), typeof(SendNotification));

                    /// <summary>
                    /// Gets or sets the order group.
                    /// </summary>
                    /// <value>The order group.</value>
                    [ValidationOption(ValidationOption.Required)]
                    [BrowsableAttribute(true)]
                    public OrderGroup OrderGroup
                    {
                        get
                        {
                            return (OrderGroup)(base.GetValue(SendNotification.OrderGroupProperty));
                        }
                        set
                        {
                            base.SetValue(SendNotification.OrderGroupProperty, value);
                        }
                    }

                    /// <summary>
                    /// Initializes a new instance of the <see cref="CalculateTaxActivity"/> class.
                    /// </summary>
                    public SendNotification()
                    {
                        InitializeComponent();
                    }

                    /// <summary>
                    /// Called by the workflow runtime to execute an activity.
                    /// </summary>
                    /// <param name="executionContext">The <see cref="T:System.Workflow.ComponentModel.ActivityExecutionContext"/> to associate with this <see cref="T:System.Workflow.ComponentModel.Activity"/> and execution.</param>
                    /// <returns>
                    /// The <see cref="T:System.Workflow.ComponentModel.ActivityExecutionStatus"/> of the run task, which determines whether the activity remains in the executing state, or transitions to the closed state.
                    /// </returns>
                    protected override ActivityExecutionStatus Execute(ActivityExecutionContext executionContext)
                    {
                        try
                        {
                            // Validate the properties at runtime
                            this.ValidateRuntime();

                            //Send notification
                            this.SendEmailNotification();

                            // Retun the closed status indicating that this activity is complete.
                            return ActivityExecutionStatus.Closed;
                        }
                        catch
                        {
                            // An unhandled exception occured.  Throw it back to the WorkflowRuntime.
                            throw;
                        }
                    }

                    /// <summary>
                    /// Calculates the sale taxes.
                    /// </summary>
                    private void SendEmailNotification()
                    {
                        //provide implementation here
                    }

                    /// <summary>
                    /// Validates the runtime.
                    /// </summary>
                    /// <returns></returns>
                    private bool ValidateRuntime()
                    {
                        // Create a new collection for storing the validation errors
                        ValidationErrorCollection validationErrors = new ValidationErrorCollection();

                        // Validate the Order Properties
                        this.ValidateOrderProperties(validationErrors);

                        // Raise an exception if we have ValidationErrors
                        if (validationErrors.HasErrors)
                        {
                            string validationErrorsMessage = String.Empty;

                            foreach (ValidationError error in validationErrors)
                            {
                                validationErrorsMessage +=
                                    string.Format("Validation Error:  Number {0} - '{1}' \n",
                                    error.ErrorNumber, error.ErrorText);
                            }

                            // Throw a new exception with the validation errors.
                            throw new WorkflowValidationFailedException(validationErrorsMessage, validationErrors);

                        }


                        // If we made it this far, then the data must be valid.
                        return true;
                    }

                    private void ValidateOrderProperties(ValidationErrorCollection validationErrors)
                    {
                        // Validate the To property
                        if (this.OrderGroup == null)
                        {
                            ValidationError validationError = ValidationError.GetNotSetValidationError(SendNotification.OrderGroupProperty.Name);
                            validationErrors.Add(validationError);
                        }
                    }
                }
            }

Example: the properties to be added to the workflow .cs file

C#
private OrderGroup _OrderGroup;

                /// <summary>
                /// Gets or sets the order group.
                /// </summary>
                /// <value>The order group.</value>
                public OrderGroup OrderGroup
                {
                    get
                    {
                        return _OrderGroup;
                    }
                    set
                    {
                        _OrderGroup = value;
                    }
                }

                private StringDictionary _Warnings = new StringDictionary();
                /// <summary>
                /// Gets the warnings.
                /// </summary>
                /// <value>The warnings.</value>
                public StringDictionary Warnings
                {
                    get
                    {
                        return _Warnings;
                    }
                }

Do you have feedback on this documentation? Send an email to documentation@episerver.com. For development-related questions and discussions, refer to our Forums on https://world.episerver.com/forum/