Helpful Hints

The following hints may help when you begin creating and testing workflows.

Use Conditions

Workflows should start with a condition that determines if or when a workflow is executed. It is important to use conditions because all workflows that are stored on a workstation are active. Proper conditions prevent conflicting or unintended changes to the database.

Check for Record Inserts and Changes

When working with workflows, it is important to understand that many of the records that are checked in the workflow will have numerous updates from different sources for different reasons and the workflow will be triggered multiple times. To ensure that the workflow is executed only when a specific value is changed, you can use conditions to check the EntityState property or the HasChanged method on the entity.

Examples

  • entity.HasChanged("Veteran")– checks if the veteran flag on a Person record was modified.

  • entity.Prospects(0).HasChanged(“LeadTypeId”) – checks if the identifier of a Person record was modified indicating that a new record was inserted.

  • entity.HasChanged(StudentCourse.StatusProperty) – checks if the Status property on the Student Course entity has changed.

    In a condition statement for any entity you can select all the available properties that you are looking for to have changed. In this example the entity is StudentCourse and the StatusProperty is selected.

    has changed - student course

    To determine if a Student Course Status changed to "Withdrawal" (= "Drop" in Anthology Student, specify the following condition:

    entity.HasChanged(studentcourse.StatusProperty) and entity.Status = StudentCourseStatus.Withdrawal

    has changed - entity status

As a general rule do not use Save type activities in Saving events, only Saved events.

You can also use the entity.HasChanged condition to prevent infinite loops in the workflow.

The EntityState property applies to the entity to which it belongs. For example, the Person entity did not change, but one of its child entities (Prospects) did. If you check the entity.Prospects(0).EntityState, it should indicate Modified.

The EntityState property and the HasChanged() method are intended for different uses and have specific meanings. The following are examples for a Person entity:

  • entity.HasChanged() – indicates if any direct properties of the Person entity have changed. This does not check any child entities or collections.

  • entity.HasChanged(true) – checks the Person entity plus any child entities and collections. If any property on the Person entity, or any of the entities in the collections (Students, Prospects) have changed, it will return true. Use entity.HasChanged(true) in workflows to determine if anything has changed within the model.

  • entity.Prospects(0).HasChanged() – returns true if the first Prospects child entity of the Person has any changes.

  • entity.Prospects(0).EntityState – returns one of three values Added, Modified, or Removed and only applies to the first Prospects entity in the Prospects collection.

For an activity that adds a record to an entity, every property will be dirty because the values are set from null to something else or to an empty string. Therefore, you should check the EntityState in your workflow to determine if a record is added. Insert a condition similar to the following:

If [ entity.EntityState = Cmc.Core.EntityModel.EntityState.Added ]

  • entity.EntityState – is an enumeration and contains one of three values Added, Modified, or Removed. This gives the workflow developer more information about what has happened to the entity during the process. This is specific to the entity to which the EntityState belongs.

entity state

Prevent Loops

Be careful not to create loops in your workflow statements.

Examples:

  • If a workflow is triggered by a Saving event, don't use a SavePerson activity within the workflow.

  • If a workflow is triggered by the posting of a charge, don't use a CreateCharge activity within the workflow.

Test Workflows for Saved Events

Although Workflow is distributed with logging turned off, you might want to enable logging during the workflow design phase. See NLog for details about the logging configuration.

It is a good practice to insert at least one LogLine activity in workflows for Saved events. The LogLine text will appear in the event log immediately after the event is raised.

logline in notepad

Check the date.errors.log file regularly for any errors in your workflows. For more information, see Event Logs.

Alternatively, you can test workflows for Saved events by including a Contact Manager CreateTask activity. You can confirm that the workflow was executed by checking the Contact Manager UI.

Filter Events Based on Event Source

Every event has arguments. The arguments can be viewed in Intellisense by typing args. in the Workflow Designer.

Event arguments have a connection context that specifies where the transaction came from. The context information can be used to filter events. For example, you can set up a filter to handle only events that came from a specific database trigger.

Switch Expression: "args.Context"

Context Property

The Context property is a string that is set in the code when an event is raised. You can access the Context property in the Workflow Designer, for example, when you specify arg. in the Expression field of a Switch activity.

The Context property is useful when a workflow is associated with a sequence of forms such as the Enrollment Wizard in Anthology Student. When the user clicks Next after completing Step 1 in the Enrollment Wizard, a Person Saving event is raised and the Context is set to a string, in this case, “Enrollment Wizard: Student Selection”. You can use a conditional statement to check the value of Context and validate fields in Step 1. Within the workflow, as you proceed through validating fields in the sequence of steps, check the Context string using each Case of the Switch activity. See the sample workflow Enrolling Students Using the Enrollment Wizard.

Switch activity with Context string

Without the Context property, if the workflow validated a property that was picked in Step 4 of the wizard and the event was triggered for Step 1, unexpected behavior or null reference exceptions may occur.

Note that the Enrollment Wizard uses a Person Saving entity contract, so if you have a validation for the Student Master form (e.g., on Nickname) you should also add a context sensitive If statement in that workflow. Context in that case is “Student Saving Com”. Otherwise some validation you have for the Student Master could show up on every step of the Enrollment Wizard on fields that are not even available there.

if statement

Another use case for the Context property are workflows that deal with PostCharge or AdjustCharge transaction. The Context property can be used to determine the type of event.

switch string

Retrieve an Enum Value

For entities containing enumerations (i.e., a predefined list of values), use the Enum.GetName method to retrieve an enum value.

Example:

The following expression retrieves the value of the TransactionType enumeration in the Cmc.Nexus.Sis.StudentAccounts contract:

[Enum].GetName(GetType(Cmc.Nexus.Sis.StudentAccounts.TransactionType),entity.TransactionType)

GetName method

In the case of the TransactionType enumeration, the Enum.GetName method enables you to capture the Transaction Type value and perform another workflow activity when this value is found.

The log shows the mapping of the TransactionType enum value of "2" to the Transaction Type of "DebitAdjustment".

enum in log

Another commonly used property to retrieve an enumeration is EntityState as shown below:

getname

Type Casting

You can convert data types using the TryCast operator. The example below shows how the Loan ScheduledDisbursement data type can be converted to the more specific DirectLoanScheduledDisbursement.

type casting

Clear a Workflow Instance Id

To clear a Workflow Instance Id value in a workflow, use the following syntax:

clear workflow instance id

Note: The API does not allow you to set the Guid value to all 0s. Therefore, the 1 appears at the end.

Capture Validation Errors

In activities that provide a ValidationMessages field defined as InOutArgument<ValidationMessageCollection>, you can create a variable of type ValidationMessageCollection and use the variable to capture error messages as shown in the example below, where the name of the variable is "validation".

Variable Type ValidationMessageCollection

Check Condition: validationHas.Errors

Note: If you are updating legacy activities to the new object model, be sure to update the variable type for validation messages. Many of the legacy activities use the variable type 'ValidationMessage', while the new object model uses the variable type ' ValidationMessageCollection'. It is not enough to create a variable in the new object model, you also need to instantiate the variable.

Copy/Paste Sequences

If you copy and paste a Sequence from one workflow to another, you may need to recreate any associated variables to ensure all namespaces are properly imported.

Check for StudentCourse.Status Changes

If you are using the Status property in workflows that check for StudentCourse.Status changes, use a logic pattern containing the CTYPE function with multiple combinations of possible status changes.

In our example, the FlowDecision activity contains a condition that checks whether the StudentCourse.StatusProperty entity has changed and whether the original Status value was NotTaken (case a), Registered (case b), or CurrentlyAttending (case c). The CTYPE function changes the original Status values to a new Status values for each case.

entity.HasChanged(studentcourse.StatusProperty)

AND (CTYPE(entity.OriginalValues("Status"), StudentCourseStatus) = StudentCourseStatus.NotTaken

AND entity.Status = StudentCourseStatus.Registered)

OR (CTYPE(entity.OriginalValues("Status"), StudentCourseStatus) = StudentCourseStatus.Registered

AND entity.Status = StudentCourseStatus.NotTaken)

OR (CTYPE(entity.OriginalValues("Status"), StudentCourseStatus) = StudentCourseStatus.CurrentlyAttending

AND entity.Status = StudentCourseStatus.Withdrawal)

For different Status changes, replace the Status values as shown in the following pattern:

Where:

  • status1a = original status (case a)
  • status2a = new status (case a)
  • status1b = original status (case b)
  • status2b = new status (case b)
  • status1c = original status (case c)
  • status2c = new status (case c)

entity.HasChanged(studentcourse.StatusProperty)

AND (CTYPE(entity.OriginalValues("Status"), StudentCourseStatus) = StudentCourseStatus.status1a

AND entity.Status = StudentCourseStatus.status2a)

OR (CTYPE(entity.OriginalValues("Status"), StudentCourseStatus) = StudentCourseStatus.status1b

AND entity.Status = StudentCourseStatus.status2b)

OR (CTYPE(entity.OriginalValues("Status"), StudentCourseStatus) = StudentCourseStatus.status1c

AND entity.Status = StudentCourseStatus.status2c)

Improve Search Performance on "Browse for Types..."

When you need to select the Browse for Types... option in Workflow Composer, the search performance is improved if you copy and paste the entirety of the type to be searched into the "Browse and Select a .Net Type" window.

Example

You need to browse for a variable type named "ValidationMessageCollection". The quickest way to locate the variable type is:

  1. Open Notepad.
  2. Type ValidationMessageCollection.
  3. Copy/paste ValidationMessageCollection into Type Name field of the "Browse and Select a .Net Type" window.

Browse for Types

How to Initialize an Array

You can initialize an array in an Assign activity.

Examples

  • Boolean array:

    New Boolean() {false, false}

    — OR —

    {false,false}

  • Integer array

    New Integer() {1, 2, 4, 8}

  • Nested array

    {{1,2},{3,4}}

You don’t have to worry about the size of the array. The number of values it will have defines the size.

To access these array elements, note that the index always starts at 0.

AndAlso Operator

You can combine expressions using operators. The And operator evaluates expressions on both sides. The AndAlso operator evaluates the right side if and only if the left side is true. The right way of exiting the evaluation (and preventing "Object reference not set to instance" errors) is to use AndAlso.

Example

studentEntity IsNot Nothing AndAlso studentEntity.CountryId.HasValue AndAlso studentEntity.CountryId.Value > 0

Condition with AndAlso operator