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.
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
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 thePerson
entity have changed. This does not check any child entities or collections. -
entity.HasChanged(true)
– checks thePerson
entity plus any child entities and collections. If any property on thePerson
entity, or any of the entities in the collections (Students, Prospects
) have changed, it will returntrue
. Useentity.HasChanged(true)
in workflows to determine if anything has changed within the model. -
entity.Prospects(0).HasChanged()
– returnstrue
if the firstProspects
child entity of thePerson
has any changes. -
entity.Prospects(0).EntityState
– returns one of three valuesAdded
,Modified
, orRemoved
and only applies to the firstProspects
entity in theProspects
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 valuesAdded
,Modified
, orRemoved
. 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 theEntityState
belongs.
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 Save 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.
Note: The LogLine activity requires the Cmc.Core.ServiceModuleHost.exe.config file to be set up to log to file and error as shown below.
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.
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.
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.
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.
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)
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".
Another commonly used property to retrieve an enumeration is EntityState
as shown below:
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.
Clear a Workflow Instance Id
To clear a Workflow Instance Id value in a workflow, use the following syntax:
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".
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:
- Open Notepad.
- Type
ValidationMessageCollection
. - Copy/paste
ValidationMessageCollection
into Type Name field of the "Browse and Select a .Net Type" window.
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