Wednesday, February 25, 2009

MS CRM 4.0 Plug-in Stages, Pipelines and Execution Modes

I've found a lot of contradictory information and misinformation about what you can and can't do inside of a plugin depending on how you are calling it, and when you should call each one, so I hope this will help.

Stage of Execution:

Pre-Stage: (synchronous) called before the write to the database.

The main reasons to use a Pre-Stage of Execution are synchronous in nature:

  • Modify data before it is written to the database.
  • Abort the action being taken
  • Server Side Validity checking

Post-Stage: (synchronous or asynchronous)

Use the Post-Stage of Execution whenever you don't need to use Pre-Stage.

If you don't want the user to wait for the process to finish and there is no race condition use asynchronous.

Triggering Pipeline:

The triggering pipeline is a little less obvious in nature and has an impact on what functionality that you can use in your plugin.

Parent Pipeline: This is the pipeline that is the most common and there are no restrictions imposed in a parent pipeline.

Child Pipeline: This is the pipeline that causes the most confusion and there are limitations that vary with Stage and Execution Mode.

Sometimes a child pipeline is required to trap the event you need, and sometimes you need to trap both the parent and child pipeline for the same entity, message and stage.

Example: Change the quotenumber in the Pre-Stage for the Create of a Quote.

The quotenumber is a field that the CRM web service will not allow you to change after it is written to the database so you need to use the Pre-Stage and modify the context Entity before the end of the Execute method as shown below.

var entity = (DynamicEntity)context.InputParameters.Properties[ParameterName.Target];

// . . . retrieve data to populate number . . .

// Set value before it is written to the database

if( entity.Properties.Contains(numberField))
{
    entity.Properties.Remove(numberField);
}

var prop = new StringProperty(numberField, newNumber);
entity.Properties.Add(prop);

So far so good. This will work in any Pre-Create pipeline!

You can create a Quote in a few ways.

  • Sales -> Quotes -> New
  • (open an Account) -> Quotes -> New Quote
  • Sales -> Opportunities (open an opportunity) -> Quotes -> New Quote

Do you think that Creating a Quote always happens in the same pipeline?

Since you are an efficient plugin developer and are using the ICrmService and dynamic entities to access CRM data because it is pre-authenticated and fast. You retrieve some information to help create the number.

ICrmService service = context.CreateCrmService(true);
retrieved = (RetrieveMultipleResponse)service.Execute(retrieve);

You register your plugin using the handy plugin registration tool for the Quote entity with the following settings:

  • Create
  • Pre Stage
  • Synchronous
  • Parent Pipeline

 CreateQuoteParent

You create your Quote from Sales -> Quotes -> New and it works!

However, in many business processes the user will create a Quote from an opportunity so it will inherit opportunityproduct information.

Now you create your quote from Sales -> Opportunities (open an opportunity) -> Quotes -> New Quote and for some reason your plugin did not fire. That is because this quote was created in a child pipeline.

The entire QOI chain also occurs in a child pipeline. If you create an order from a quote or invoice from an order it is happening in a child pipeline. There are other places that this occurs as well like creation of an account or contact from a lead.

You are in a child pipeline, now what ?

The first thing that you might try is to take your existing plugin and register it against the child pipeline, but it won't work. The ICrmService is not available to you in a child pipeline.

If you downloaded the plug-in template for CRM, you have probably seen the following method, which is what you need to use in a child pipeline.

/// <summary>
/// Creates a CrmService proxy for plug-ins that execute in the child pipeline.
/// </summary>
/// <param name="context">The execution context that was passed to the plug-ins Execute method.</param>
/// <param name="flag">Set to True to use impersontation.</param>
/// <returns>A CrmServce instance.</returns>
private static CrmService CreateCrmService(IPluginExecutionContext context, Boolean flag)
{
    var authToken = new CrmAuthenticationToken { AuthenticationType = 0, OrganizationName = context.OrganizationName, CallerId = (flag ? context.UserId : context.InitiatingUserId) };

    var corToken = new CorrelationToken { CorrelationId = context.CorrelationId, CorrelationUpdatedTime = context.CorrelationUpdatedTime, Depth = context.Depth };

    var regkey = Registry.LocalMachine.OpenSubKey("SOFTWARE\\Microsoft\\MSCRM", false);

    var service = new CrmService
    {
        CrmAuthenticationTokenValue = authToken,
        UseDefaultCredentials = true,
        Url = String.Concat(regkey.GetValue("ServerUrl").ToString(), "/2007/crmservice.asmx"),
        CorrelationTokenValue = corToken
    };

    return service;
}

You may find another method called GetCrmProxyUsingEndpointUrlInChildPipeline. It is essentially the same.

There is a comment in the plugin template that doesn't tell the whole story.

// For a plug-in running in the child pipeline, use this statement.
// CrmService crmService = CreateCrmService(context, true);

What it doesn't mention is that what you are allowed to do with that service depends on what your execution mode is.

Hard Coded Restrictions:

A CrmService running Synchronously in a child pipeline is limited to the following:

  • Create
  • Delete
  • Update
  • RetrieveExchangeRate

However a child plugin running in Asynchronous Execution Mode is not limited this way.

Those 4 allowed actions are hard coded by MS whenever the plugin is executed inside a transaction, and it appears that all synchronous child pipeline events occur inside a transaction.

However you can do a query in child pipeline if it is registered as asynchronous.

In review:

  • Use ICrmService for all parent pipelines when at all possible.
  • Use CrmService for child pipelines in asynchronous execution mode.
  • There is very limited functionality allowed with CrmService for any plugin registered in a synchronous child pipeline
  • Querying CRM data in a plug-in registered as a synchronous Pre Create in a child process is currently very unsupported. (ie. direct SQL access)

6 comments:

Bert-Jan Diedering said...

Great post! Thanks to you I was able to build a plugin for my company (see my site). But now I want to do the same (send an email and change the regardingobject) when clicking multiple contacts and click "Send Direct Email" button on the contact-grid. Do you know if that is possible? And how?
Thank you in advance!

Nishant Rana said...

Thank You very much for sharing your knowledge !!!

Unknown said...

Thank you very much it great post,

now I tried this way on my plugin, I need to add image when create new email with following options:

Message: Create
Entity: Email
Eventing Pipeline: Post Stage
Execution Mode: Async.
Triggering Execution: Child

but it caused SOAP exception [System.Web.Services.Protocols.SoapException: Server was unable to process request.]

any advice about this issue!!!!!

Chris Cohen said...

In answer to the question about email post async child pipeline - bear in mind that perhaps the email has already been sent!

Anonymous said...

great post, It's interesting to understand how child processes work

Shweta-Amol said...

Thank you for detailed post!!