Showing posts with label functions. Show all posts
Showing posts with label functions. Show all posts

Sunday, December 2, 2018

Customizing D365 forms using JavaScript

I know, a lot of people have written about customizing CRM forms with JavaScript, but I am learning some cool stuff and wanted to share what I learned this week.
The big news for me was that Promises are supported in D365 forms now. I am sure this is not news to many people, and to others they have no idea what I am talking about, so this article is for them.
Why are Promises important?
If you have written a JavaScript function that uses a “call back” (passing a function name as a parameter into another function), then you have probably run into the fact that the call back can happen at any time. If you have several of them that need to be run in a sequence, they will sometimes cause your code to fail because it does not execute them synchronously.
I have seen many programmers try to solve this problem by building “Christmas tree” code that nests one function inside the other until you are 15 levels deep in brackets, and lord help you if you if there is a syntax error with one bracket out of place. Even worse is the difficulty to test and maintain all permutations of “Christmas tree” code so that you are sure there are no errors. Promises are an excellent solution to that problem.
A Promise design pattern is way to write a function that will leverage the asynchronous nature of JavaScript with the ability to sequentially perform a series of tasks. 
I learned about Promises from a Pluralsight course authored by Jonathan Mills called JavaScript Best Practices. I did some research and found a few code samples in GitHub for CRM projects that used Promises. Here are some links that I used to learn more, but frankly, some of it was deeper than I needed.
Promises
https://blogs.msdn.microsoft.com/ie/2011/09/11/asynchronous-programming-in-javascript-with-promises/
Promise API framework for CRM
https://code.msdn.microsoft.com/CRM-Web-API-Functions-and-87cd0b45#content
It uses https://github.com/stefanpenner/es6-promise
https://github.com/mariusso/WebAPI.REST.js/releases




Some of the above links say that you need to include the e6promise.js library, and that may have been necessary in earlier versions of CRM, but I am working in V9.1 and it is not necessary to add any other framework to your project, but it did help me understand what is going on.
What got me started on this article was when I needed to build a customization and realized that I needed several sequential calls to fetch data first, then create new records, but they had to be executed in order.
I noticed that Jason Lattimer’s CRM REST Builder used a Promise syntax when I used the Xrm.WebApi option to build a request for fetching data, so I decided to see if I could make that work for me. Besides, I like the succinct format of WebAPI over the XMLHTTP format.
I created a form script called Library.js and in following best practices, created a FormLib namespace so all my methods and properties are prefixed with “FormLib.”.
First I used CRM REST Builder to create an asynchronous call to get some data for me that looks like this:
Xrm.WebApi.online.retrieveMultipleRecords("tn_changeorder", "?$select=tn_changeorderid&$filter=statuscode eq 0 and  _tn_quoteid_value eq <giud>").then(
     function success(results) {
         for (var i = 0; i < results.entities.length; i++) {
             var tn_changeorderid = results.entities[i]["tn_changeorderid"];
         }
     },
     function(error) {
         Xrm.Utility.alertDialog(error.message);
     }
);
Notice the “then” that follows the async call – this was my clue that the Xrm.WebApi was a Promise.
Following Jonathan Mills instructions, I created a wrapper function :
FormLib.GetChangeOrder = function () {
     return new Promise(function (fulfill, reject) {
         // begin REST code

        // end REST code
     });
};
and then I inserted the “fulfill” and “reject” into the REST code, and put it all together so that now my code looks like this:
FormLib.GetChangeOrder = function () {
     return new Promise(function (fulfill, reject) {
         // begin REST code
         Xrm.WebApi.online.retrieveMultipleRecords("tn_changeorder", "?$select=tn_changeorderid&$filter=statuscode eq 0 and  _tn_quoteid_value eq  <guid>" ).then(
             function success(results) {
                 for (var i = 0; i < results.entities.length; i++) {
                     var tn_changeorderid = results.entities[i]["tn_changeorderid"];
                 }
                fulfill();
             },
             function (error) {
                 Xrm.Utility.alertDialog(error.message);
                 reject();
             }
         );
         // end REST code
     });
};

Now every time I make an async call, I create one of these functions (above) and can fire them sequentially using a syntax that looks like this:
FormLib.GetChangeOrder()
            .then(FormLib.CreateRelatedCase)
            .then(FormLib.SetupDetails)
The magic is the fulfill() that calls the next item in the “then-chain”. The reject() allows you to call an error handler and gracefully exit the chain. In my example above, the “.then(FormLib.CreateRelatedCase)” is passed into GetChangeOrder() and executed after it has completed its task of looking for an existing record.
You could argue that it is easier to just call the next function in the chain by using the specific function name instead of calling fulfill(), but then you would have tight coupling and it is difficult to maintain when you have to decipher the code to figure out what the chain is.
Promises solves the problem of deep nesting of functions by splitting them all out to discrete, re-useable, loosely coupled, and testable functions that can be called sequentially. The Promise Design Pattern also makes handling errors and parallel tasks easier to manage. I recommend you learn this design pattern if you are going to do any amount of JavaScript coding.

Friday, September 29, 2017

Why use Azure to extend Dynamics 365?

What are some good use cases for building logic for your Dynamics 365 using Azure?

1. Inconsistent workload demands
Have you started using the Dynamics 365 portal? It is pretty cool, but if you have waves of people hitting the web site and making updates, and you have numerous plugins and workflows running, you may find that your system can be sluggish if not outright slow. By moving some of your plugins and workflows to an Azure service, you can mitigate some of the effects of the portal. Consider using Azure Functions.

2. Offloading batch processes
Are you an association that needs to update 100k membership records at the same time, or generate 20k invoices on the last day of the month? You shouldn't even try to do that with workflows or your users coming after you with torches and pitchforks. Just like #1 above, you could have an on-demand service running in Azure to do the dirty work without seriously impacting your users. Consider using a Cloud Service that you can deploy, run, then shut down.

3. Scheduled jobs are not easily managed within D365
You want a system update to run at 5PM every Friday? I dont think you can do it in D365 without writing some plugin code and a workflow, but Azure has an OOTB solution for that. Take a look at Azure Logic Apps. You can schedule them to run at a specific time.

4. You are moving to D365 from an on-premises environment that uses SQL Stored Procedures
You have some SQL queries and views you use to support reporting systems. CRM has a feature to replicate an entire entity to an Azure SQL database. You want to push updates back? That is a bit more tricky, but perhaps you can manage it with Azure Logic Apps or Azure Functions.

5. Plugins and workflows in D365 have a timeout limit of 2 minutes
Yep, trying to update more than a few records in a single plugin is detrimental to your employment. In one case, I found that a client had created a bunch of small plugins, but what the developer(s) didn't realize is that they had created a chain of plugins that was over 1500 lines of code and periodically failed from time to time, depending on the current server load. The worst part was that there was no way to re-trigger the plugins because they were designed to work only with a Create event message, and the code did not support idempotent transactions, so you could only run it once and hope it worked the first time. Consider using an Azure Cloud Service, Web Job, Service Fabric, or Function, depending on what you are trying to accomplish.

(update)
6. Consolidate Business Logic 
Perhaps you have several places in your system that do the same thing, like processing a credit card. If you put the logic in a plugin, it is not easily accessible to your web site (yes you can do it, but it is not a good user experience). You could use a WebJob that works with both CRM and your web site.