Microsoft Dynamics 365 Blog

The CRM 4.0 e-mail router provides an extensible framework based on the concept of e-mail providers and XML files. This article explains how you can extend the model to build your own highly specialized e-mail provider.

Out-of-the-box, the CRM 4.0 e-mail router includes different e-mail providers, each implementing a particular e-mail protocol. For example, the POP3 e-mail provider examines e-mails received by a POP3 mailbox, and calls into CRM to create e-mail activities. For example, the SMTP e-mail provider connects to CRM to download e-mail activities waiting to be sent, and composes then submits e-mails to an SMTP server.

For each type of e-mail provider, several instances may be activated. For example, at a given time, 5 instances of the POP3 e-mail provider may be running, each processing e-mails from one mailbox, and each delivering e-mail to a different CRM organization.

Any type of e-mail provider should accept configuration parameters (ex: name of the mailbox to connect to, name of the e-mail server to connect to, etc.). Some e-mail providers may also need to persist runtime state information as well (ex: time stamp of the last processed message, number of e-mails processed so far, etc.). Such state information should not be lost if, for example, the service is restarted.

Understanding E-mail Providers:

The following diagram describes the class hierarchy used to implement e-mail providers. The base-most class is called EmailProvider, and all e-mail providers derive from it. Intermediate classes provide additional behavior (ex: the PollingMailboxProvider implements a design pattern to connect to a mailbox using a configurable polling schedule). Derived-most classes correspond to actual e-mail providers (ex: the Pop3PollingMailboxProvider class is the POP3 e-mail router).

extendingEmail

Extending the Model:

There are two ways you can extend the model. You can:

  • Derive from existing e-mail provider classes to alter their behavior. For example, you may want to create a specialized POP3 e-mail provider which identifies certain keywords in the subject of processed e-mails, and creates alert records in CRM. In this case, starting an implementation from scratch could be expensive, because you will have to implement the entire POP3 client protocol. Using class derivation to provide partial specialization makes more sense.
  • Create new e-mail providers to provide totally new functionality. For example, you may want to create an e-mail provider which downloads RSS feeds, and creates e-mail activities in CRM. In this case, deriving from existing e-mail providers makes little sense, because none of them has anything to do with RSS technology or protocols.

In this post, we will focus on the second approach (the first approach will be discussed in a later post).

Analyzing Requirements:

You should start the design process by analyzing specific requirements:

  • What type of configuration parameters will each provider instance need?
  • What state information should each provider instance persist?
  • When should each provider instance run?
  • When should persisted state information be deleted?

Defining a Usage Scenario:

For illustrative purposes, we will discuss a hypothetical e-mail provider whose responsibility is to download RSS feed items and to create corresponding records in CRM. Each provider instance should know the name of the RSS feed to connect to, and whether a secure protocol should be used to connect.

Our provider state information should include the timestamp of the last processed RSS feed item. This will be used as a threshold date/time to incrementally download more recent RSS feed items. Note that this post will not provide an implementation of the code required to download RSS feed items. This exercise is left to the reader, because the key point is that you can decide to do about anything.

Designing Configuration XML:

We expect our configuration XML (which should be copied to Microsoft.Crm.Tools.EmailAgent.xml) to look as follows. For every <ProviderConfiguration> instance, an instance of the specified e-mail provider class will be instantiated. Each instance will be bound to one single XML fragment. Below, we assume that the class implementing our custom e-mail provider is called MyEmailNamespace.MyEmailProviderClass, and that it resides in assembly MyEmailAssembly.dll.

<ProviderConfiguration>

   <ProviderAssembly>MyEmailAssembly.dll</ProviderAssembly>

   <ProviderClass>MyEmailNamespace.MyEmailProviderClass</ProviderClass>

   <CrmServerUrl>http://MyCrmServer/MyOrg</CrmServerUrl>

   <RssFeedName>cnn.newsalerts.com</RssFeedName>

   <RssUseSecureMode>true</RssUseSecureMode>

   <PollingPeriod>60000</PollingPeriod>

</ProviderConfiguration>

 

<ProviderConfiguration>

   <ProviderAssembly>MyEmailAssembly.dll</ProviderAssembly>

   <ProviderClass>MyEmailNamespace.MyEmailProviderClass</ProviderClass>

   <CrmServerUrl>http://MyCrmServer/MyOrg</CrmServerUrl>

   <RssFeedName>msnbc.dailynews.com</RssFeedName>

   <RssUseSecureMode>true</RssUseSecureMode>

   <PollingPeriod>60000</PollingPeriod>

</ProviderConfiguration>

Starting a Project:

We recommend you follow these steps as a starting point:

a. Create a Visual Studio 2005 C# project

b. Add a reference to assembly Microsoft.Crm.Tools.EmailProviders.dll

c. Include a using statement for namespace Microsoft.Crm.Tools.Email.Providers

d. Create a new class called MyEmailProvider derived from EmailProvider

e. Implement a constructor

f. Override abstract methods and provide an implementation

e. Compile the assembly and place it under %program files%\ Microsoft CRM Email\Service

Here is what your implementation stub should look like at this point. The Check() method is not applicable to custom providers and can safely be ignored.

public class MyEmailProvider : EmailProvider

{

/// <summary>

/// Constructor.

/// </summary>

/// <param name=”providerConfiguration”>Provider configuration.</param>

/// <param name=”shutdownEvent”>Shutdown event.</param>

public MyEmailProvider(ProviderConfiguration providerConfiguration, ManualResetEvent shutdownEvent)

: base(providerConfiguration, shutdownEvent)

{

}

/// <summary>

/// Indicates if queuing should be accepted because the provider wants to perform some work.

/// </summary>

/// <returns>True if a work request should be queued, false otherwise.</returns>

public override bool AcceptQueuing()

{

}

/// <summary>

/// Executes an operation.

/// </summary>

public override void Run()

{

}

/// <summary>

/// Verifies that the provider works as expected.

/// </summary>

public override void Check()

{

}

}

Specifying Configuration Parameters:

The next step is to create class members which map to expected configuration XML parameters. For XML binding to work properly, we must use the supplied ProviderConfiguration.ConfigNodeReader member to access configuration parameters. Special functions are used to retrieve configuration parameters. The third argument indicates if an exception should be thrown when the XML node is missing. The second argument specifies a default value. In the example below, specifying parameter RssFeedName name is mandatory, while specifying parameter RssUseSecureMode is optional (the default being true).

/// <summary>

/// RSS feed name (read from the bound XML configuration node).

/// </summary>

public string RssFeedName

{

get

{

return this.ProviderConfiguration.ConfigNodeReader.GetCheckedStringValue( “RssFeedName”, null, true);

}

}

/// <summary>

/// RSS secure mode (read from the bound XML configuration node).

/// </summary>

public bool RssUseSecureMode

{

get

{

return this.ProviderConfiguration.ConfigNodeReader.GetCheckedBoolValue( “RssUseSecureMode”, true, false);

}

}

Specifying State Information:

The next step is to create members to manage persisted state information. For state persistence to work properly, we must use the supplied ProviderConfiguration.StateNodeReader member. Again, special functions must be used to retrieve and set state information.

/// <summary>

/// Time stamp of the last processed RSS item.

/// </summary>

public string RssThresholdDateTime

{

get

{

return this.ProviderConfiguration.StateNodeReader.GetCheckedStringValue( “RssThresholdDateTime”, DateTime.MinValue.ToString(), false);

}

set

{

this.ProviderConfiguration.StateNodeReader.SetStringValue( “RssThresholdDateTime”, value);

}

}

Ensuring Proper Scheduling:

The next step allows provider instances to get scheduled for execution. Providers cannot indicate when they should execute. Instead, they indicate whether they are ready to execute. Unless the system is busy, execution will happen shortly after scheduling. To indicate it is ready, a provider instance should return true when method AcceptQueuing() is called. This method is called quite frequently, so minimize the amount of effort spent making a decision. In the example below, scheduling is requested every 15 minutes (ideally, this value should become a new configuration parameter).

/// <summary>

/// Remembers the last time we ran.

/// </summary>

private DateTime lastTimeRan = DateTime.MinValue;

/// <summary>

/// Executes an operation.

/// </summary>

public override void Run()

{

this.lastTimeRan = DateTime.Now; // remember the last time we ran

}

/// <summary>

/// Indicates if queuing should be accepted because the provider wants to perform some work.

/// </summary>

/// <returns>True if a work request should be queued, false otherwise.</returns>

public override bool AcceptQueuing()

{

return (DateTime.Now > this.lastTimeRan.AddMinutes(15));

}

Implementing the Execution Routine:

The next step consists in implementing the Run() method. In our case, we will connect to the specified RSS feed and download the first item past the specified threshold. This will allow our implementation to keep downloading new messages. To ensure a graceful shutdown behavior, we will often check the result of method ShutdownRequested(). Also, after doing a sufficient amount of work, we should exit and wait for the next scheduling cycle. E-mail providers which do not comply with these rules may see their Run() method forcefully aborted by the scheduling engine.

/// <summary>

/// Executes an operation.

/// </summary>

public override void Run()

{

// Keep reading RSS feed items

int i = 0;

while (true)

{

// Stop on shutdown

// Stop when enough work has been done for the current cycle

if (this.ShutdownRequested() || (10 < ++i))

break;

// Process the next item from the specified RSS feed

// This function should return the first item whose time stamp is

// past the specified threshold

RssFeed feed = DownloadFeedItemAndTrackInCrm(this.RssFeedName,

this.RssUseSecureMode, this.RssThresholdDateTime);

if (null == feed)

break; // empty feed

// Update the threshold (set it to the current item’s timestamp)

this.RssThresholdDateTime = feed.TimeStamp;

}

// Remember the last time we ran

this.lastTimeRan = DateTime.Now;

}

Managing State Information:

After completing the previous steps, your e-mail provider should be fully functional. But one important question remains: what if someone modifies the configuration XML file? The e-mail router implements a scheduling engine which automatically detects and accounts for such changes (ex: you do not need to restart the e-mail router service for them to take effect). If you add a new XML fragment, a new instance will automatically be created and scheduled. If you delete an existing fragment, the instance will be deleted from memory and stop executing (all associated persisted state information will be deleted as well).

Things become more complicated when an existing XML fragment is modified. Some changes may be significant enough that the change should be treated as a delete followed by an add (all associated persisted state information should be deleted). But some changes may be small enough to justify keeping persisted state information around.

For example, if you change the RssFeedName parameter of a <ProviderConfiguration> XML node, then persisted state information should be deleted, because it contains a threshold date which is now meaningless. But if you only change the RssUseSecureMode parameter, then the threshold date should be preserved, because otherwise processing for the same RSS feed will start from scratch.

In other terms, you need to define which parameters define a provider instance’s identity. As long as the identity remains unchanged, persisted state information will be preserved. By default, a cryptographic hash is applied to all configuration parameters to define the identity. But you can override this behavior. In the example below, the RssUseSecureMode parameter does not participate in the provider instance’s identity any more. So even if it changes, state information will be preserved.

/// <summary>

/// Identity hash.

/// </summary>

public override string IdentityHash

{

get

{

return String.Format(CultureInfo.InvariantCulture, “{0}/{1}”,

this.GetType(), this.RssFeedName);

}

}

Conclusion:

This article describes how you can create new e-mail providers. The framework allows developers to delegate configuration, instantiation, persistence, and scheduling responsibilities to the e-mail router. In the future, we will discuss other approaches to extending e-mail integration.

Dominic Pouzin

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!