DEV Community

Cover image for Simplifying Workflow Execution with Action Mapping in C#
Sean Drew
Sean Drew

Posted on

Simplifying Workflow Execution with Action Mapping in C#

Efficiently managing workflows in code is essential for maintainability and readability, particularly in complex systems. This shows how to use a "Dictionary" to map keys to corresponding methods for streamlined execution, avoiding repetitive conditional logic. Here is a very brief overview of involving Twilio-based pharmacy communications.

Code Overview
Step 1: Prepare Input Data
The "msg_info" list (a List>), is populated by a helper function named "TwilioFunctions.GetMsgTypeByConv". It retrieves message-specific details from SQL that are then passed into the workflows for further processing. I store the helper function in a common shared class file named "TwilioFunctions".

List<KeyValuePair<string, string>> msg_info = TwilioFunctions.GetMsgTypeByConv(pat_id.ToString(), accountSid, ConversationSid, "Email");
Enter fullscreen mode Exit fullscreen mode

Step 2: Define the Action Map
Using a Dictionary, I map specific keys to actions that invoke the "ProcessEmailByStoreParam" method with the appropriate parameters. This methodology simplifies the workflow by eliminating verbose if-else or switch statements. Additionally, adding a new action is as simple as adding a new key-value pair to the "actionMap" dictionary.

var actionMap = new Dictionary<string, Action>
{
  { "SC_TXT_NEWRX_MSG_Y", () => ProcessEmailByStoreParam("Y", msg_info, ConversationSid, patientResponse, accountSid, false) },

  { "SC_TXT_NEWRX_MSG_N", () => ProcessEmailByStoreParam("N", msg_info, ConversationSid, patientResponse, accountSid, false) },

  { "SC_TXT_PREREFILL_MSG_Y", () => ProcessEmailByStoreParam("Y", msg_info, ConversationSid, patientResponse, accountSid, false) },

  { "SC_TXT_PREREFILL_MSG_N", () => ProcessEmailByStoreParam("N", msg_info, ConversationSid, patientResponse, accountSid, false) },

  // additional actions can be added as required
};
Enter fullscreen mode Exit fullscreen mode

Step 3: Execute Actions Based on Keys
The "perfaction" parameter (contains the action to perform) is checked against the keys in the "actionMap" dictionary. If a match is found then the associated action is executed; otherwise, an error view is returned.

// check if the key exists and invoke the associated action
if (actionMap.TryGetValue(perfaction, out var action))
{
  action(); // execute the action
}
else
{
  // the error to pass to the "ContactPharmacy" view
  var model = new ErrorViewModel { ErrorMessage = "general error" };

  ViewName = "ContactPharmacy"; // the cshtml view to load
  return View(ViewName, model); // basically show the view
}
Enter fullscreen mode Exit fullscreen mode

Step 4: Handle Email Processing Logic
The "ProcessEmailByStoreParam" method performs the heavy lifting by validating inputs and processing the workflow logic based on the "YN" parameter. This method ensures all critical fields are validated before proceeding and handles specific operations based on the "YN" value. It also returns an error view when required fields are missing or invalid.

public IActionResult ProcessEmailByStoreParam(string YN, List<KeyValuePair<string, string>> msg_keyValuePairs, string ConversationSid, PatientEmailResponse patientResponse, string accountSid, bool IsMulti)
{
  string? ChatServiceSid = patientResponse.person?[0].ChatServiceSid;
  string? ParticipantSid = patientResponse.person?[0].ParticipantSid;

  // Validate essential parameters
  if (string.IsNullOrEmpty(ConversationSid) || string.IsNullOrEmpty(ParticipantSid) || string.IsNullOrEmpty(accountSid))
  {
    var errorModel = new ErrorViewModel { ErrorMessage = "Required fields cannot be null" };
    ViewName = "ContactPharmacy";
    return View(ViewName, errorModel);
  }

  // Process based on YN value
  if (YN == "N")
  {
    // logic for "N"
  }

  if (YN == "Y")
  {
    // logic for "Y"
  }

  return View();
}
Enter fullscreen mode Exit fullscreen mode

The "ContactPharmacy" View

@model CustomAPI.Models.ErrorViewModel

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Pharmacy Management System</title>
  <link href="@Url.Content("~/css/main.css")" rel="stylesheet" />
</head>
<body>


<div class="image-container">
  <table border="0" class="wfull">
    <tr>
      <td>
        <img src="~/images/cppms.png" />
      </td>
    </tr>

    <tr><td class="v60"></td></tr>

    <tr>
      <td class="patientmessagetext">
        @Html.Raw($"An error occurred while processing your request.<br/><br/>Please contact the pharmacy for assistance.")
      </td>
    </tr>

    <tr>
      <td class="errortext">
        @Html.Raw($"{Model.ErrorMessage}")
      </td>
    </tr>

    <tr><td class="v60"></td></tr>
    <tr><td class="v60"></td></tr>
  </table>
</div>

</body>
</html>
Enter fullscreen mode Exit fullscreen mode

The Model Definition

namespace CustomAPI.Models
{
  public class ErrorViewModel
  {
    public string ErrorMessage { get; set; } = string.Empty; // avoid non-nullable property message

    public static readonly ErrorViewModel Empty = new ErrorViewModel(); // static field to hold a global empty instance

    public bool IsEmpty => string.IsNullOrEmpty(ErrorMessage); // property to check if the ErrorViewModel is "empty"
  }
}
Enter fullscreen mode Exit fullscreen mode

Advantages of Using Action Mapping

  1. Maintainability: Encapsulates logic for each action, making it easier to manage and extend.
  2. Readability: Simplifies workflows by eliminating verbose if-else or switch statements.
  3. Scalability: Adding new actions is as simple as extending the dictionary.
  4. Error Handling: Ensures proper validation and error reporting, making the system more robust.

Conclusion
By combining action mapping with methods like "ProcessEmailByStoreParam", I can achieve cleaner, more maintainable code for complex workflows. This simplifies decision-based logic, enhances scalability, and improves error handling, which makes it particularly useful in systems like pharmacy communications, where clarity and reliability are important.

Top comments (0)