Slight change of pace with this week’s blog post, which will be a fairly condensed and self-indulgent affair – due to personal circumstances, I have been waylaid somewhat when it comes to producing content for the blog and I have also been unable to make any further progress with my new YouTube video series. Hoping that normal service will resume shortly, meaning additional videos and more content-rich blog posts, so stay tuned.

I’ve been running the CRM Chap blog for just over 2 years now. Over this time, I have been humbled and proud to have received numerous visitors to the site, some of whom have been kind enough to provide feedback or to share some of their Dynamics CRM/365 predicaments with me. Having reached such a landmark now seems to be good a time as any to take a look back on the posts that have received the most attention and to, potentially, give those who missed them the opportunity to read them. In descending order, here is the list of the most viewed posts to date on the crmchap.co.uk website:

  1. Utilising SQL Server Stored Procedures with Power BI
  2. Installing Dynamics CRM 2016 SP1 On-Premise
  3. Power BI Deep Dive: Using the Web API to Query Dynamics CRM/365 for Enterprise
  4. Utilising Pre/Post Entity Images in a Dynamics CRM Plugin
  5. Modifying System/Custom Views FetchXML Query in Dynamics CRM
  6. Grant Send on Behalf Permissions for Shared Mailbox (Exchange Online)
  7. Getting Started with Portal Theming (ADXStudio/CRM Portals)
  8. Microsoft Dynamics 365 Data Export Service Review
  9. What’s New in the Dynamics 365 Developer Toolkit
  10. Implementing Tracing in your CRM Plug-ins

I suppose it is a testament to the blog’s stated purpose that posts covering areas not exclusive to Dynamics CRM/365 rank so highly on the list and, indeed, represents how this application is so deeply intertwined with other technology areas within the Microsoft “stack”.

To all new and long-standing followers of the blog, thank you for your continued support and appreciation for the content 🙂

As part of developing Dynamics CRM/Dynamics 365 Customer Engagement (CRM/D365CE) plug-ins day in, day out, you can often forget about the Execution Mode setting. This can be evidenced by the fact that I make no mention of it in my recent tutorial video on plug-in development. In a nutshell, this setting enables you to customise whether your plug-in executes in Synchronous or Asynchronous mode. Now, you may be asking – just what the hell does that mean?!? The best way of understanding is by rephrasing the terminology; it basically tells the system when you want your code to be executed. Synchronous plug-ins execute all of your business logic whilst the record is being saved by the user, with this action not being considered complete and committed to the backend database until the plug-in completes. By comparison, Asynchronous plug-ins are queued for execution after the record has been saved. A System Job record is created and queued alongside other jobs in the system via the Asynchronous Service. Another way of remembering the difference between each one is to think back to the options available to you as part of a Workflow. They can either be executed in real time (synchronously) or in the background (asynchronously). Plug-ins are no different and give you the flexibility to ensure your business logic is applied immediately or, if especially complex, queued so that the system has sufficient time to process in the background.

I came across a strange issue with an arguably even stranger Synchronous plug-in the other day, which started failing after taking an inordinately long time saving the record:

Unexpected exception from plug-in (Execute): MyPlugin.MyPluginClass: System.AggregateException: One or more errors occurred.

The “strange” plug-in was designed so that, on the Create action of an Entity record, it goes out and creates various related records within the application, based on a set of conditions. We originally had issues with the plug-in a few months back erroring, due to the execution time exceeding the 2 minute limit for sandbox plug-ins. A rather ingenious and much more accomplished developer colleague got around the issue by implementing a degree of asynchronous processing within the plug-in, achieved like so:

await Task.Factory.StartNew(() =>
{
    lock (service)
    {
        Stopwatch stopwatch = Stopwatch.StartNew();
        Guid record = service.Create(newRecord);
        tracing.Trace("Record with ID " + record.ToString() + " created successfully after: {0}ms.", stopwatch.ElapsedMilliseconds);
    }
});

I still don’t fully understand just exactly what this is doing, but I put this down to my novice level C# knowledge 🙂 The important thing was that the code worked…until some additional processing was added to the plug-in, leading to the error message above.

At this juncture, our only choice was to look at forcing the plug-in to execute in Asynchronous mode by modifying the appropriate setting on the plug-in step within the Plugin Registration Tool:

After making this change and attempting to create the record again in the application, everything worked as expected. However, this did create a new problem for us to overcome – end users of the application were previously used to seeing the related records created by the plug-in within sub-grids on the Primary Entity form, which would then be accessed and worked through accordingly. As the very act of creating these records now took place within the background and took some time to complete, we needed to display an informative message to the user to advise them to refresh the form after a few minutes. You do have the ability within plug-ins to display a custom message back to the user, but this is only in situations where you are throwing an error message and it didn’t seem to be a particularly nice solution for this scenario.

In the end, the best way of achieving this requirement was to implement a JScript function on the form. This would trigger whenever the form is saved and displays a message box that the user has to click OK on before the save action is carried out:

function displaySaveMessage(context) {

    var eventArgs = context.getEventArgs();
    var saveMode = eventArgs.getSaveMode();

    if (saveMode == 70 || saveMode == 2 || saveMode == 1 || saveMode == 59) {
        var message = "Records will be populated in the background and you will need to refresh the form after a few minutes to see them on the Sub-Grid. Press OK to save the record."
        Xrm.Utility.alertDialog(message, function () {
            Xrm.Page.data.save().then(function () {
                Xrm.Page.data.refresh();
            })
        });
    }
}

By feeding through the execution context parameter, you are able to determine the type of save action that the alert will trigger on; in this case, SaveSave & CloseSave & New and Autosave. Just make sure you configure your script with the correct properties on the form, which are:

  • Using the OnSave event handler
  • With the Pass execution context as first parameter setting enabled

From the end-users perspective, they will see something similar to the below when the record is saved:

It’s a pity that we don’t have similar kind of functionality exposed via Business Rules that enable us to display OnSave alerts that are more in keeping with the applications look and feel. Nevertheless, the versatility of utilising JScript functions should be evident here and can often achieve these types of bespoke actions with a few lines of code.

When it comes to plug-in development, understanding the impact and processing time that your code has within the application is important for two reasons – first, in ensuring that end users are not frustrated by long loading times and, secondly, in informing the choice of Execution Mode when it comes to deploying out a plug-in. Whilst Asynchronous plug-ins can help to mitigate any user woes and present a natural choice when working with bulk operations within the application, make sure you fully understand the impact that these have on the Asynchronous Service and avoid a scenario where the System Job entity is queued with more jobs then it can handle.

This is an accompanying blog post to my YouTube video Dynamics 365 Customer Engagement Deep Dive: Creating a Basic Plug-in, the second in a series aiming to provide tutorials on how to accomplish developer focused tasks within Dynamics 365 Customer Engagement. You can watch the video in full below:

Below you will find links to access some of the resources discussed as part of the video and to further reading topics:

PowerPoint Presentation (click here to download)

Full Code Sample

using System;
using System.Globalization;

using Microsoft.Xrm.Sdk;

namespace D365.SamplePlugin
{
    public class PreContactCreate_FormatNameValues : IPlugin
    {
        public void Execute(IServiceProvider serviceProvider)
        {
            //Obtain the execution context from the service provider.

            IPluginExecutionContext context = (IPluginExecutionContext)serviceProvider.GetService(typeof(IPluginExecutionContext));

            //Extract the tracing service for use in debugging sandboxed plug-ins

            ITracingService tracingService = (ITracingService)serviceProvider.GetService(typeof(ITracingService));

            tracingService.Trace("Tracing implemented successfully!");

            if (context.InputParameters.Contains("Target") && context.InputParameters["Target"] is Entity)

            {
                Entity contact = (Entity)context.InputParameters["Target"];

                string firstName = contact.GetAttributeValue<string>("firstname");
                string lastName = contact.GetAttributeValue<string>("lastname");

                TextInfo culture = new CultureInfo("en-GB", false).TextInfo;

                if (firstName != null)
                {

                    tracingService.Trace("First Name Before Value = " + firstName);
                    contact["firstname"] = culture.ToTitleCase(firstName.ToLower());
                    tracingService.Trace("First Name After Value = " + contact.GetAttributeValue<string>("firstname"));

                }

                else

                {
                    tracingService.Trace("No value was provided for First Name field, skipping...");
                }

                if (lastName != null)

                {
                    tracingService.Trace("Last Name Before Value = " + lastName);
                    contact["lastname"] = culture.ToTitleCase(lastName.ToLower());
                    tracingService.Trace("Last Name After Value = " + contact.GetAttributeValue<string>("lastname"));
                }

                else

                {
                    tracingService.Trace("No value was provided for Last Name field, skipping...");
                }

                tracingService.Trace("PreContactCreate_FormatNameValues plugin execution complete.");

            }
        }
    }
}

Download/Resource Links

Visual Studio 2017 Community Edition

Setup a free 30 day trial of Dynamics 365 Customer Engagement

C# Guide (Microsoft Docs)

Source Code Management Solutions

Further Reading

MSDN – Plug-in development

MSDN – Supported messages and entities for plug-ins

MSDN – Sample: Create a basic plug-in

MSDN – Debug a plug-in

I’ve written a number of blog posts around plug-ins previously, so here’s the obligatory plug section 🙂 :

Interested in learning more about JScript Form function development in Dynamics 365 Customer Engagement? Then check out my previous post for my video and notes on the subject. I hope you find these videos useful and do let me know if you have any comments or suggestions for future video content.

This is an accompanying blog post to my YouTube video Dynamics 365 Customer Engagement Deep Dive: Creating a Basic Jscript Form Function, the first in a series that aims to provide tutorials on how to accomplish developer focused tasks within Dynamics 365 Customer Engagement. You can watch the video in full below:

Below you will find links to access some of the resources discussed as part of the video and to further reading topics.

PowerPoint Presentation (click here to download)

Full Code Sample

function changeAddressLabels() {

    //Get the control for the composite address field and then set the label to the correct, Anglicised form. Each line requires the current control name for 'getControl' and then the updated label name for 'setLabel'

    Xrm.Page.getControl("address1_composite_compositionLinkControl_address1_line1").setLabel("Address 1");
    Xrm.Page.getControl("address1_composite_compositionLinkControl_address1_line2").setLabel("Address 2");
    Xrm.Page.getControl("address1_composite_compositionLinkControl_address1_line3").setLabel("Address 3");
    Xrm.Page.getControl("address1_composite_compositionLinkControl_address1_city").setLabel("Town");
    Xrm.Page.getControl("address1_composite_compositionLinkControl_address1_stateorprovince").setLabel("County");
    Xrm.Page.getControl("address1_composite_compositionLinkControl_address1_postalcode").setLabel("Postal Code");
    Xrm.Page.getControl("address1_composite_compositionLinkControl_address1_country").setLabel("Country");

    if (Xrm.Page.getControl("address2_composite_compositionLinkControl_address2_line1"))
        Xrm.Page.getControl("address2_composite_compositionLinkControl_address2_line1").setLabel("Address 1");

    if (Xrm.Page.getControl("address2_composite_compositionLinkControl_address2_line2"))
        Xrm.Page.getControl("address2_composite_compositionLinkControl_address2_line2").setLabel("Address 2");

    if (Xrm.Page.getControl("address2_composite_compositionLinkControl_address2_line3"))
        Xrm.Page.getControl("address2_composite_compositionLinkControl_address2_line3").setLabel("Address 3");

    if (Xrm.Page.getControl("address2_composite_compositionLinkControl_address2_city"))
        Xrm.Page.getControl("address2_composite_compositionLinkControl_address2_city").setLabel("Town");

    if (Xrm.Page.getControl("address2_composite_compositionLinkControl_address2_stateorprovince"))
        Xrm.Page.getControl("address2_composite_compositionLinkControl_address2_stateorprovince").setLabel("County");

    if (Xrm.Page.getControl("address2_composite_compositionLinkControl_address2_postalcode"))
        Xrm.Page.getControl("address2_composite_compositionLinkControl_address2_postalcode").setLabel("Postal Code");

    if (Xrm.Page.getControl("address2_composite_compositionLinkControl_address2_country"))
        Xrm.Page.getControl("address2_composite_compositionLinkControl_address2_country").setLabel("Country");
}

Download/Resource Links

Visual Studio 2017 Community Edition

Setup a free 30 day trial of Dynamics 365 Customer Engagement

W3 Schools JavaScript Tutorials

Source Code Management Solutions

Further Reading

MSDN – Use JavaScript with Microsoft Dynamics 365

MSDN – Use the Xrm.Page. object model

MSDN – Xrm.Page.ui control object

MSDN – Overview of Web Resources

Debugging custom JavaScript code in CRM using browser developer tools (steps are for Dynamics CRM 2016, but still apply for Dynamics 365 Customer Engagement)

Have any thoughts or comments on the video? I would love to hear from you! I’m also keen to hear any ideas for future video content as well. Let me know by leaving a comment below or in the video above.

With 2018 now very firmly upon us, it’s time again to see what’s new in the world of Dynamics 365 certification. Nothing much has changed this time around, but there are a few noteworthy updates to be aware if you are keen to keep your certifications as up to date as possible. Here’s my round-up of what’s new – let me know in the comments if you think I have missed anything out.

MCSE Business Applications 2018

The introduction of a dedicated Microsoft Certified Solutions Architect (MCSA) and Microsoft Certified Business Applications (MCSE) certification track for Dynamics 365 was a positive step in highlighting the importance of Dynamics 365 alongside other core Microsoft products. Under the new system, those wishing to maintain a “good standing” MCSE will need to re-certify each year with a brand new exam to keep their certification current for the year ahead. Those who obtained their MCSE last year will now notice on their certificate planner the opportunity to attain the 2018 version of the competency via the passing of a single exam. For Dynamics 365 Customer Engagement focused professionals, assuming you only passed either the Sales or Customer Service exam last year, passing the other exam should be all that is required to recertify – unless you fancy your chances trying some of the new exams described below.

New MCSE Exams

Regardless of what boat you are relating to the Business Applications MCSE, those looking to obtain to 2018 variant of the certification can expect to see two additional exams available that will count towards the necessary award requirements:

The exams above currently only appear on the US Microsoft Learning website but expect them to appear globally within the next few weeks/months.

MB2-877 represents an interesting landmark in Microsoft’s journey towards integrating FieldOne as part of Dynamics CRM/Dynamics 365 Customer Engagement, with it arguably indicating the ultimate fruition of this journey. To pass the exam,  you are going to have to have a good grasp of the various entities involved as part of the field service app, as well as a thorough understanding of the mobile application itself. As is typically the case when it comes to Dynamics 365 certification, the Dynamics Learning Portal (DLP) is going to be your destination for preparatory learning resources for the exam; along with a good play around with the application itself within a testing environment. If you have access to the DLP, it is highly recommended you complete the following courses at your own pace before attempting the exam:

Dynamics 365 for Retail is a fairly new addition to the “family” and one which – admittedly – I have very little experience with. It’s rather speedy promotion to exam level status I, therefore, find somewhat surprising. This is emphasised further by the fact that there are no dedicated exams for other, similar applications to Field Service, such as Portals. Similar to MB2-877, preparation for MB6-897 will need to be directed primarily through DLP, with the following recommended courses for preparation:

Exam Preparation Tips

I’ve done several blog posts in the past where I discuss Dynamics CRM/Dynamics 365 exams, offering help to those who may be looking to achieve a passing grade. Rather than repeat myself (or risk breaking any non-disclosure agreements), I’d invite you to cast your eyes over these and (I hope!) that they prove useful in some way as part of your preparation:

If you have got an exam scheduled in, then good luck and make sure you study! 🙂