Qualify a Lead in C#

Typically, when we want to deactivate a record in a plugin or a workflow, we turn to SetStateRequest. Qualifying a Lead isn't quite as straightforward. If you've qualified a Lead through the CRM interface, you'll know that:
  • An Opportunity is created from the originating Lead.
  • You can select whether to create a Contact or Account from the originating Lead.
QualifyLeadRequest in the Microsoft.Crm.Sdk.Messages namespace provides us with a class which allows us to specify all of these options and more programatically.

The code snippet below creates a new QualifyLeadRequest, populating all of it's required properties.

Replace all of my empty new EntityReference() objects with valid EntityReferences (which are instantiated by passing an entity name and an ID). Refer to the Microsoft documentation for information on other optional parameters which you can use.

The request is executed and its response stored in the qualifyLeadResponse:

The most interesting property of the response object is CreatedEntities. See the Microsoft documentation for information on other properties stored in the QualifyLeadResponse.

CreatedEntities contains a collection of EntityReferences, one for each entity created when qualifying the Lead. To get the Contact that was created, for example, iterate through the collection and query the LogicalName of each EntityReference:

Retrieve Option Set Metadata in C#

Given the name of an entity and the name of an option set field, Microsoft.Xrm.Sdk.Messages can retrieve all the labels and corresponding integer values for an option set.

Simply build a new RetrieveAttributeRequest and iterate over the response like so:

optionList now contains a list of all option set options where each object in the list has a Value property and a Text property.

To retrieve a given option set value for a label, use a Linq query:

Plugin Extensions: Shorten Your Code

There are some lines of code in plugins and workflows that I've written too many times.

1. Getting an attribute

Before we get an entity's attribute, we always check that the entity first contains the attribute. This is because attempting to access the value of an attribute that doesn't exist throws a NullReferenceException. Here's an example that gets the first name of a contact:

2. Initialising plugin variables

The following code gets the basic objects needed to run a decent plugin. Look how long-winded it is!

How to shorten things: write extension methods

Create a separate, static class called Extensions.cs. Note the namespace your class is in. If it's in a different namespace to your plugins, you must add a using statement at the top of your plugin classes.

Here's an example extension method that allows the simplification of 1. from above:

It can be used as follows. Note that we no longer need to check if an entity contains an attribute.

Here's an extension method that shortens the initialisation of plugin variables:

Here's how we can now initialise the same variables as shown in 2. from above:

Much cleaner!

TypeScript Development for CRM: Keep It Clean

More recently, applications built to integrate with CRM, either as external SPAs or HTML pages embedded within CRM, have utilised the TypeScript superset of JavaScript. If you're not already using TypeScript, see this great StackOverflow answer on why you should consider it (though this answer is general and not specific to Dynamics CRM development).

One annoyance many developers face when using TypeScript is file layout within the IDE. TypeScript compiles from .ts files to multiple .js files which can clutter file hierarchy layout on the IDE interface. This can be resolved in one of two ways.

1. Change your output directory

Navigate to your tsconfig.json file and specify an output directory that is different to the location of your .ts files. This could be /dist or /build:

2. Hide your .js files

Changing your output directory and moving your .js files can cause issues. For example an index.html page that references main.js will no longer work if you're now compiling to /dist/main.js. You may therefore consider whether you just want to hide your .js files from your IDE.

In Visual Studio Code, go to File > Preferences > Settings or your .vscode\settings.json file and enter:

To hide .js files one file directory deeper, add further lines to your .json file with an additional /*:

Before
With .js files
After
Without .js files

Finish a Business Process Flow in C#

When you create a business process flow you create a custom entity. For example, if you create a business process flow called Marketing Management in the default solution, you will create an entity called new_marketingmanagement. When you activate a business process flow on a record, an instance of that entity is created. The instance holds information such as which stage the process is at and when the process was started. You can deactivate the instance using Microsoft.Xrm.Sdk.Messages.SetStateRequest:

Where processFlowName is the logical name of your business process flow as a string and processId is the ID of the process flow instance you want to deactivate as a GUID.

To find the ID of your process flow instance, you can query the attribute _bpf_primary_key_value where primary_key should be replaced with the primary key of the entity which your process is on. For example on the Account entity this would read _bpf_accountid_value.

Finish a Business Process Flow in JavaScript

Often it's required that a business process flow is moved to its next stage via JavaScript. Less often it's required that the flow is marked as finished as well. Fortunately, an active business process flow can be set to finished using the Xrm namespace.

An important note is that although the Microsoft documentation says to pass finish as a parameter, you must pass finished. You can also pass active or abandoned.

Get Faults from an ExecuteMultipleRequest

If you have multiple entities to create or update in a plugin, it can be useful to package all entity create messages up into one bundle and send the request off at once. It is possible to see a performance benefit from this, but typically it's used to fulfill a business requirement. However, it can be trickier to establish if any errors have occurred. When only one request is sent, such as executing a create request for one new entity, typically the request is wrapped in a try/catch block.

Here's an example showing how to bundle multiple UpsertRequests into one ExecuteMultipleRequest:

requests can then be executed to return a list of responses, which can be iterated over. For each response in the iteration, we can determine if a fault occurred and if it did, add the error to a list of errors:

Notes:
  • response.RequestIndex is used to aaccess the response's corresponding request by matching their indexes.
  • responses.Responses contains a collection of ExecuteMultipleResponseItems.