Microsoft Dynamics 365 Blog

Today we welcome guest blogger Jim Steger, developer, blogger, and writer for Sonoma Partners with this guest post.

The new workflow functionality of Microsoft Dynamics CRM 4.0 opens new and exciting opportunities for developers and end users to easily create sophisticated business logic. However, sending alert-type e-mails is still one of the most common uses I see used for workflow. As you begin to work with CRM’s workflow e-mail capabilities, I hear two common requests frequently:

1. The ability to add a hyperlink URL to the body of a workflow generated e-mail message

2. Text in ntext fields do not display properly in an HTML email.

Unfortunately, neither of these requests works natively with CRM. In the case of #1, CRM does not surface the record id as a dynamic value, so you are unable to construct the URL properly in an e-mail.

With request #2, CRM doesn’t translate the ntext field into HTML. Take for example the following lead record. The text in the Description field is on 3 lines.


When a workflow e-mail is sent, the text in the Description field gets concatenated as seen in this example:


In this post, I demonstrate how you can solve both of the earlier requests to enhance the native Send E-mail workflow action using a very simple workflow assembly.

Creating the Workflow Utility Solution
As many of you know by now, Microsoft based the Dynamics CRM 4.0 workflow on Windows Workflow Foundation (WF). This choice allows developers to easily create additional workflow logic to use within your CRM workflow rules.

Our workflow utilities solution will have two classes UrlBuilder and FormatLineBreaks. The UrlBuilder class simply creates an instance of the IContextService and then retrieves the current context. The context’s PrimaryEntityId property contains the record id that triggered the workflow. The code then simply appends the entity id to the inputted URL and formats it as a hyperlink. The code for FormatLineBreaks is even simpler…I simply replace any ASCII line breaks with an HTML break node.

Start by creating a basic workflow project which will contain your workflow activity classes. The CRM SDK, various books, and numerous blog posts describe this in better detail, so I will just walk you through the process quickly.

  1. Using Visual Studio 2008, create a new Workflow Activity Library project targeting version 3.0 of the .NET Framework, and then do the following
    1. Digitally sign it
    2. Add references to the Microsoft Dynamics CRM SDK assembly files (located as part of the SDK download).
  2. Create a new class, and name it UrlBuilder.
  3. Replace the default code in the UrlBuilder class with the following code:
   1: using System;
   2: using System.Workflow.ComponentModel;
   3: using System.Workflow.Activities;
   4: using Microsoft.Crm.Workflow;
   6: namespace SonomaPartners.Crm.Workflow.Utilities
   7: {
   8:   [CrmWorkflowActivity("Url Builder", "Utilities")]
   9:   public partial class UrlBuilder : SequenceActivity
  10:   {
  11:     // Override this method with our custom logic
  12:     protected override ActivityExecutionStatus Execute(ActivityExecutionContext executionContext)
  13:     {
  14:       //Get context
  15:       IContextService contextService = (IContextService)executionContext.GetService(typeof(IContextService));
  16:       IWorkflowContext ctx = contextService.Context;
  18:       // Get the record id from the context
  19:       Guid id = ctx.PrimaryEntityId;
  21:       // Configure the Url and pass back to the output parameter
  22:       string fullUrl = string.Format(this.Url,id);
  23:       this.RecordUrl = string.Format(@"<a href=""{0}"">{0}</a>", fullUrl);
  25:       return base.Execute(executionContext);
  26:     }
  28:     // Allow the user to set the Url with this input parameter
  29:     public static DependencyProperty UrlProperty = DependencyProperty.Register("Url", typeof(string), typeof(UrlBuilder));
  30:     [CrmInput("Url")]
  31:     public string Url
  32:     {
  33:       get { return (string)base.GetValue(UrlProperty); }
  34:       set { base.SetValue(UrlProperty, value); }
  35:     }
  37:     // Returns the formatted record Url to the workflow rule for use
  38:     public static DependencyProperty RecordUrlProperty = DependencyProperty.Register("RecordUrl", typeof(string), typeof(UrlBuilder));
  39:     [CrmOutput("RecordUrl")]
  40:     public string RecordUrl
  41:     {
  42:       get { return (string)base.GetValue(RecordUrlProperty); }
  43:       set { base.SetValue(RecordUrlProperty, value); }
  44:     }
  45:   }
  46: }

  1. Create another new class, and name it FormatLineBreaks.
  2. Replace the default code in the FormatLineBreaks class with the following code:
   1: using System;
   2: using System.Workflow.ComponentModel;
   3: using System.Workflow.Activities;
   4: using Microsoft.Crm.Workflow;
   6: namespace SonomaPartners.Crm.Workflow.Utilities
   7: {
   8:   [CrmWorkflowActivity("Format Line Breaks", "Utilities")]
   9:   public partial class FormatLineBreaks : SequenceActivity
  10:   {
  11:     protected override ActivityExecutionStatus Execute(ActivityExecutionContext executionContext)
  12:     {
  13:             this.FormattedValue = (this.InputValue != null) ? this.InputValue.Replace("\n", "<br>") : string.Empty;
  14:             return base.Execute(executionContext);
  15:     }
  17:     // Input value
  18:     public static DependencyProperty InputValueProperty = DependencyProperty.Register("InputValue", typeof(string), typeof(FormatLineBreaks));
  19:     [CrmInput("Input Value")]
  20:     public string InputValue
  21:     {
  22:       get { return (string)base.GetValue(InputValueProperty); }
  23:       set { base.SetValue(InputValueProperty, value); }
  24:     }
  26:     // Returns the updated value
  27:     public static DependencyProperty FormattedValueProperty =
  28:  DependencyProperty.Register("FormattedValue", typeof(string), typeof(FormatLineBreaks));
  29:     [CrmOutput("Formatted Value")]
  30:     public string FormattedValue
  31:     {
  32:       get { return (string)base.GetValue(FormattedValueProperty); }
  33:       set { base.SetValue(FormattedValueProperty, value); }
  34:     }
  35:   }
  36: }

  1. Build the solution.

Registering the Utilities Assembly

Your workflow assembly is now ready for deployment. We use Ajith’s free Plug-in Registration Tool to add the workflow assembly to our Dynamics CRM application. Don’t worry that the name says plug-ins…it works for workflow assemblies as well!

  1. Download the tool if you don’t already have it.
  2. Open the tool and create a connection to your CRM Web server and then connect to it.
  3. Select your organization and click Connect.
  4. Click Register, and then select Register New Assembly.
  5. In the Register New Plugin window, choose your compiled workflow utilities assembly.
  6. Leave the database option selected and click Register Selected Plugins.

  7. The tool will then register the assembly and provide a pop-up message if it was successful.

Using the E-mail Utilities within Workflow User Interface

Now that the custom solution is complete and registered with Microsoft Dynamics CRM, the next step is to use it in a workflow rule.

  1. Create a new workflow rule for the Lead entity called New Lead Notification.
  2. Add a step, selecting the new Url Builder option step located in the Utilities group.

  3. Enter Url Builder as the step’s description.
  4. Click Set Properties, and enter the correct URL to the Lead’s edit page as the Value of the Url property. For example:

    http://<crmserver>/<organization name>/sfa/leads/edit.aspx?id={0}


  5. Add a step, selecting the new Format Line Breaks option step located in the Utilities group.
  6. Enter Format Description as the step’s description.
  7. Click Set Properties, and set the lead’s description attribute.

  8. Add a new Send E-mail step and enter Send Alert E-mail for the description.
  9. Leave the Create New Message option selected, and click Set Properties.
  10. In the Send E-mail dialog box, configure the e-mail message, and in the body, add the new Url Builder and Format Line Breaks dynamic value.

  11. Save and publish your workflow rule.


When a new lead is created, I will receive an alert e-mail that looks like this:


Additional Comments

Here are some additional thoughts and tips to consider.

  • I used Visual Studio 2008 to create my workflow assembly. You can also use Visual Studio 2005 to create a custom workflow assembly, but be sure to add the Windows Workflow Foundation framework first.
  • Also check Nirav’s post about compiling the workflow activity with anycpu flag to provide the best compatibility between 32 and 64 bit environments.
  • You can easily add additional utility classes to build out a useful library for your users.
  • If you modify the assembly and register it again with your deployment, you may need to restart IIS and the AsyncService for your changes to take effect.
  • A complete list of record URL’s are listed in the SDK, although you could just open a record in Internet Explorer to determine the correct link.
  • For custom entities, your input URL would be something similar to: http://<crmServer>/<organizationname>/userdefined/edit.aspx?etn=<entity name>&id={0}. Consider using the etn query string parameter instead of the etc (object type code), since the object type code on custom entities can vary between deployments.

  • My example assumes that the URL you enter will be accessible to your users. If you use IFD with your deployment, you can either create proper domain mappings within your DNS to properly open the hyperlink. Or even simpler (although maybe a bit less elegant), you could create two instances of the UrlBuilder with two different URLs and set up two links in your e-mail messages…one for internal users and one for external users.
  • You could consider wrapping the Send e-mail and the workflow assembly steps into a child workflow, which can then be reused within other workflow rules. Just remember to check the As a child workflow option before publishing.
  • Finally, don’t confuse the Create Record step (and selecting E-mail activity) with the Send E-mail step. If you use the Create Record step to create an E-mail activity, Dynamics CRM will create the e-mail activity but not actually send it. This approach is useful if you want a user to manually review or make alterations to the e-mail prior to sending. If you want the workflow rule to automatically send the e-mail, then be sure to choose the Send E-mail step.

Hopefully, this simple example demonstrates the ease by which a developer can enhance the native functionality of workflow.

Happy coding!

Jim Steger

We're always looking for feedback and would like to hear from you. Please head to the Dynamics 365 Community to start a discussion, ask questions, and tell us what you think!