Creating a Custom Interactive button in Dynamics

It’s been awhile since I played with the Ribbon Workbench and I had to re-familiarize myself with it to deploy some custom button functionality to a Dynamics tenant.

If you’re not familiar with the Ribbon Workbench, go download it and bask in it’s glory and time saving capabilities.

Once you install the solution into your Dynamics system, usage is as simple as selecting the solution you want your new button to be deployed to, dragging a button onto the Form toolbar and creating a command object that calls a function in your specified JavaScript file.

Creating the Button

As you can see from the screenshot, my function is called SendMail and called from a library within the provided file.  When I first started coding this button, I added a simple alert() to the initial function call so I could quickly validate the buttons functionality, deploy it and move on to the rest of the implementation.

Capture

There are a host of other options in creating a button related to display rules and Hide actions which can make your implementation that much more dynamic.

What I really like about the Ribbon Workbench is that the customizations are deployed directly to your solution without having to deploy the workbench solution between environments.

No external Dependencies = awesome development!

User Interaction

In my scenario, the button that I created was calling an action where the results were passed back to my calling function – for better or for worse.

Adding to my integration, I sent a notification back to the client when the action had completed.  If there was an error, the error was sent to the client.

For an Informative warning this looked like.

Xrm.Page.ui.setFormNotification("Authorization successfully sent.", "INFORMATION");

And in the case of an error.

 function (e) {
 // Error
 console.log("Workflow Err: " + e);
Xrm.Page.ui.setFormNotification("Could not Authorize, Error: " + e, "ERROR");
 }

When both solutions are thrown together, a great integration story for facilitating calls to a custom service, action or workflow and updating users on the status of those calls when completed.

Improve Query Performance to Oracle

I recently had an issue where we were migrating a large Oracle database into Dynamics which required a significant amount of lookups back to Oracle for synchronization keys between both systems.

When we moved the system between different database environments we started to see the following errors.

“ORA-12801: error signaled in parallel query server P001\nORA-12853: insufficient memory for PX buffers: current 1632K, max needed 80640K\nORA-04031: unable to allocate 65560 bytes of shared memory (\”large pool\”,\”unknown object\”,\”large pool\”,\”PX msg pool\”)”  

As a developer, I get very worried when code changes are required between environments when all other variables stay the same (i.e., database, code, etc).  In this case however, we had been lucky that we had not run into this problem in DEV.

Where I was dynamically constructing the query on the fly, Oracle saw this as a new query being built every time (despite the only thing changing was the value in the WHERE Clause).  On their own these queries were fine, but when running about 50,000+ lead to some issues.

To get around the above error we leveraged the OracleParameter syntax as follows.

OracleCommand oraCommand = new OracleCommand("SELECT user FROM test.USER_LOOKUP WHERE user = :userName", db);
oraCommand.Parameters.Add(new OracleParameter("userName", userId));

Once implemented, we noticed a huge shift in performance and no more parallel query errors.  We had a lot of classes to change but were able to implement the change in a little under a day.

Registering Plugin Processing Steps

I was wracking my brain the trying to figure out why my plugins were not being deployed as part of my solution between different tenants.

A closer look yielded the answer, that the plugin itself was being deployed but not the messages themselves.

To do this, you need to add in the SDK Processing Steps separately and then deploy your plugin.

blog.PNG

Which really begs the question, when would you ever deploy an empty plugin assembly without the steps you already validated in a test tenant only to now deploy to Production?

A better solution to the above problem would be for the solution window to prompt you, asking if you want to add these already preconfigured messages to your solution (a la dependency components when adding entities).

 

Debugging Dynamics Plugins

Plugins have been around a long time in Dynamics and you either love them or strongly dislike them.

There isn’t much in between.

In working through a few issues on a recent project I had to build a plugin to tap into some many-2-many relationship joins.

If you are looking to solve a similar problem here are some links to get your started.

https://community.dynamics.com/crm/b/mscrmshop/archive/2012/05/14/how-to-trigger-a-plugin-for-many-to-many-relationship

https://community.dynamics.com/crm/b/mylifemicrosoftdynamiccrm/archive/2017/04/09/ms-dynamics-crm-associate-disassociate-message-plugin

In working on this problem I had to do a little more debugging in my plugins and was surprised to see the changes that have come in the Plugin Registration Tool.

To walk through your plugin’s execution, follow these steps.

  • Register your plugin.
  • If you have not already done it, Install the Plugin Profiler from the Plugin Registration Tool.
  • Before going back to Dynamics, Start the Plugin Profiler.

plug

  • Select your profile storage method (I left as recommended).
  • Now validate your code in Dynamics.  Upon completion you will be prompted with a file to download, save it.plug4
  • Now click on the “Replay Plug-In Execution” button at the top of the plugin tool.  The following window will appear where you will associate your downloaded plugin file and plugin.  Also note, that the tool tells you what PID (Process Id) the system is running as which will allow you to attach your Visual Studio process to, to walk through your code.

plug44

When you are ready, click “Start Execution”.

  • When executing you will see output akin to the following (where in my case the name of my plugin is called RecipientHandler.

plug444

This is a much easier, straight-forward and simpler way to debug plugins.  The step-through is great as you can then configure breakpoints in your code to see what Input/Output parameters you are working with and eliminate much of the guesswork.

 

Dynamics Quick Search

The easiest things to do are sometimes the hardest to find.

If you are frustrated with searching for your items in Dynamics via the Entity Search box even after enabling your field for search, you need to do one more step.  Go to your entity, navigate to the quick find view and then selecting the field you want to search on.

From there, you will then be able to search against your entity by the field(s) selected.

search

Easy to miss but easy to fix.

Disabling Quick Create

I ran into an issue the other day where I needed to turn off the usage of Quick Create forms and ensure that the standard forms would always be used for form creation.

The option to do this per entity is relatively simple – Navigate to your entity (via the solution) and uncheck the following entry.

ActivityPointerFail

Not a problem to do per entity, but it would be nice if there was a bulk, non-coding writing way to disable this functionality across all of your entities.  This is a good example of where these features can then be implemented across the board ensuring a consistent user experience.

ActivityPointer Redirection

I ran into error this a little while ago while doing some data conversions.

ActivityPointerFail

A quick search on Google will yield results on trying to associate an invalid activity record to your entity.

However this was not the case, as I was not doing any linking of activity records.

In the end what it turned out to be was that the relationship was created against Entity A (which was wrong), while I was passing in an EntityReference to Entity B.

Still wrong, still an error, but took me down the wrong path when the error first popped up.

And now you know.