Friday, 24 March 2017

How to update CRM records correctly in C#?

Today I am going to write about a common mistake that CRM developers do very often. If you don’t correct this right now you will face some serious issues with your CRM system and some unpredictable behaviours even in Production systems which will be very hard to troubleshoot.
What is it?
Most of the time you will need to update a CRM record in your plugins or custom workflow activities or some of your console applications which you might write for various requirements.

I will take a plugin for example; Look at this basic plugin which updates the Do Not Email field of a contact record.

public class UpdateContact:IPlugin
    {
        public void Execute(IServiceProvider serviceProvider)
        {
            IPluginExecutionContext context = (IPluginExecutionContext)serviceProvider.GetService(typeof(IPluginExecutionContext));           
            IOrganizationServiceFactory serviceFactory = (IOrganizationServiceFactory)serviceProvider.GetService(typeof(IOrganizationServiceFactory));
            IOrganizationService service = serviceFactory.CreateOrganizationService(context.InitiatingUserId);

            using (XrmServiceContext serviceContext = new XrmServiceContext(service))
            {
                if (context.InputParameters.Contains("Target") && context.InputParameters["Target"] is Entity)
                {
                    Entity entity = (Entity)context.InputParameters["Target"];
                    if (entity.LogicalName == Contact.EntityLogicalName)
                    {
                        var contactRecord = serviceContext.ContactSet.FirstOrDefault(c => c.Id == entity.Id);

                        //wrong way! This will update all the fields of the record.
                        contactRecord.DoNotEMail = false;
                        service.Update(contactRecord);


                        //Correct way to update. This will update only the specified field(s)
                        Contact contactRecordToUpdate = new Contact();
                        contactRecordToUpdate.Id = contactRecord.Id;
                        contactRecordToUpdate.DoNotEMail = false;
                        service.Update(contactRecordToUpdate);
                    }
                }
            }
        }
    }

Once you have taken the ID of the entity from the input parameters then you retrieve the Contact record. Now you are setting the field values you need to update on that object. This is the big mistake. If you do so and call update CRM will update all the fields of the entity which will cause to run many other plugins, workflows, business rules, JavaScripts etc.

Correct way is get the ID of the entity from the input parameters; then create a new instance of the entity and set the ID and the field values. Then pass that instance when you call the update method. 

Tuesday, 21 March 2017

How to call a Global action from JavaScript?

We have covered how to call a custom action in JavaScript in a previous blog.
But sometimes there are scenarios you need to create Global Actions where you don't specify an entity in particularly. When you create such a global action and if you need to call that action from your JavaScript code how can you do it?

Let's say this is your Global Action. Please note the name and the Unique Name. Unique name is the one you need to use in the code.



















Then use the following code to call your action. Replace the "your action name" with the unique name of your action. 

function CallAction()
{
    var req = new XMLHttpRequest();
    req.open("POST", Xrm.Page.context.getClientUrl() + "/api/data/v8.0/new_TestGlobalAction", false);
    req.setRequestHeader("OData-MaxVersion", "4.0");
    req.setRequestHeader("OData-Version", "4.0");
    req.setRequestHeader("Accept", "application/json");
    req.setRequestHeader("Content-Type", "application/json; charset=utf-8");
    req.onreadystatechange = function () {
        if (this.readyState === 4) {
            req.onreadystatechange = null;
            if (this.status === 204) {
                alert("Action Called");
                //Success - No Return Data - Do Something
            } else {
                Xrm.Utility.alertDialog(this.statusText);
            }
        }
    };
    req.send();
}

Make sure your action is activated!

Monday, 20 March 2017

Tracing in Plugins and Custom Workflow Activities

Sometimes when you develop plugins or custom workflow activities in Dynamics CRM you might not get the expected output and you might wonder what went wrong and where. One way to find out this is debug your code. But there is an easier and light weight way of finding out where the error occurred. That is using tracing.


In system settings under customisation you can enable tracing.

















You can enable it for all or only for exceptions. Normally what I do while developing is enable for all. Once you are done with your code you can set it to Exception.
After you set this settings you need to write to the trace in your code. This is how you do it.
First you need to get hold of tracing service instance. After that you can start writing the values of your variables to the trace.

Example code:

using Microsoft.Xrm.Sdk;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Plugin1
{
    public class Class1 : IPlugin
    {
        private const string className = "Class1";
        IOrganizationService service;
        ITracingService tracingService;

        public void Execute(IServiceProvider serviceProvider)
        {
            IPluginExecutionContext context = (IPluginExecutionContext)serviceProvider.GetService(typeof(IPluginExecutionContext));
            tracingService = (ITracingService)serviceProvider.GetService(typeof(ITracingService));
            IOrganizationServiceFactory serviceFactory = (IOrganizationServiceFactory)serviceProvider.GetService(typeof(IOrganizationServiceFactory));
            service = serviceFactory.CreateOrganizationService(context.InitiatingUserId);
            tracingService.Trace("{0} plugin started", Class1.className);

            var inputs = context.InputParameters;
            foreach (var item in inputs)
            {
                tracingService.Trace("{0} Input paramenter. key: {1}, value: {2}", Class1.className, item.Key, item.Value);
            }
        }
    }
}


Once your plugin or custom workflow activity has executed you can check the plugin trace log in CRM and you will see what values are passed and if an error occurred where it occurred and so on.































Note: There is a small delay in displaying trace logs so be patient J

This feature is handy in production environments too to know what went wrong and where?

Depending on requirement you can enable tracing for all or exceptions only. Normally Exceptions only in Production environments.

To clean up the disc space you can schedule bulk deletion jobs to delete old trace logs (e.g. trace logs which are older than 2 days or so)

How to debug a CRM Online Plugin?


When you develop plugins for Dynamics CRM unless it is a very simple scenario 9 out of 10 situations you will require to debug your plugin. One way of fulfilling this requirement is using the tracing which is bit easier. But sometimes this is not sufficient. So you have no other choice but debug the plugin!

With Dynamics CRM online debugging a plugin is not very straight forward. With On-Premise environments you can run the remote debugger on the CRM server and attach to the process in you visual studio and proceed. But in the cloud version with the limited access this is not possible. So here is how you do it:

Start Plugin Registration Tool in the SDK and connect to your CRM organisation.

If this is the first time someone is going to debug a plugin in this organisation you will see the button “Install Profiler” in the menu bar. (Otherwise profiler will be installed already) Click on it and it will start installing profiler. 





Then register you plugin and the steps. Select the step which you want to debug and click "Start Profiling".


































Here you select the option “Exception” and click Ok.


Now in CRM perform the operation where your plugin is supposed to trigger. (In this example it triggers when multiple accounts are retrieved. So I open the Accounts view).

You will get a business process error. Click Download Log File and save the log file into your local drive.














Then open your plugin code in visual studio; set the break points; click Attach to process under the debug menu. Select the PluginRegistration.exe from the available processes and click attach.


Then in the plugin registration tool click “Debug” from the menu.
For the Profile browse to the downloaded error log file and in the previous step and select it.
Select your plugin dll for the Assembly location and then the plugin.

Then click Start Execution.




Debugger will hit your breakpoints you had set up in Visual Studio and you can continue debugging your code with pressing F10.

Once you are done with debugging make sure you go back to the plugin registration tool; select the step in your plugin and click “Stop Profiling”; otherwise you will always get business process error when you perform your operation!


Happy debugging J 



How to call a plugin from a JavaScript or a ribbon button?


Sometimes there are situations where you need to call a plugin from a JavaScript or click of a ribbon button. With the help of actions and latest web API this is easily achievable.

Step 1

Under Processes create a new Process with Category selected as Action; give a name and select the entity. In this example I have selected Account as the entity. It’s just a blank action with no arguments or steps.



Step 2

Create your plugin and register your plugin on that action you created. Make sure you have activated your action and published. Otherwise you will not see the newly created action in the plugin registration tool. If you still don’t see your action name in messages in plugin registration tool close the plugin registration tool and open again. It should resolve this.



Step 3

In your JavaScript on account form use following code; replace the action name with the unique name of your action.
In this example replace new_AccountAction with your unique name.
function CallAction() {   
    var currentRecordIdString = Xrm.Page.data.entity.getId();
    var currentRecordId = currentRecordIdString.replace("{", '').replace("}", '');
    var query = "accounts(" + currentRecordId + ")/Microsoft.Dynamics.CRM.new_AccountAction";
    var req = new XMLHttpRequest();
    var url = Xrm.Page.context.getClientUrl() + "/api/data/v8.0/" + query;
    req.open("POST", Xrm.Page.context.getClientUrl() + "/api/data/v8.0/" + query, false);
    req.setRequestHeader("OData-MaxVersion", "4.0");
    req.setRequestHeader("OData-Version", "4.0");
    req.setRequestHeader("Accept", "application/json");
    req.setRequestHeader("Content-Type", "application/json; charset=utf-8");
    req.onreadystatechange = function () {
        if (this.readyState == 4) {
            req.onreadystatechange = null;
            if (this.status == 204) {
                //Xrm.Utility.alertDialog("Action Called");
            }
            else {
                var error = JSON.parse(this.response).error;
                Xrm.Utility.alertDialog(error.message);
            }
        }
    };
    req.send();
}


That’s all. Then you can call this function from your ribbon button if you need to.



Connect to CRM via a console application


Connect to CRM via a console application.

If you are writing some complicated business logic inside your custom workflow activity or a plugin there is a high chance of your code throwing exception once you have registered them and started testing.

What I do is I create a simple Visual Studio C# console application and connect to CRM. Then debug the business logic in the console application first and make sure everything is working well before I insert the code into the plugin or workflow activity skeleton. This saves lot of time as debugging plugins and custom workflow activities is not very straight forward.

Creating a Console application is very easy and once created you can use it for multiple projects as you need to change only few variables.

Step 1:
Open visual studio and create a new project of type Console Application as below:

 


Step 2:
Under solution explorer right click on the project created and click properties. Then under the Application tab change the Target Framework to .Net Framework 4.5.2
If you don’t have this version of .net installed in your computer download it from Microsoft site and install.


















Step 3:
Refer the following assemblies. CRM dlls can be found under the bin folder in CRM SDK. You can download the SDK from Microsoft site.

























Update your Program.cs class like below.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.Xrm.Sdk;
using System.ServiceModel.Description;
using Microsoft.Xrm.Sdk.Client;
using Microsoft.Xrm.Sdk.Messages;
using Microsoft.Xrm.Sdk.Metadata;
using Microsoft.Xrm.Sdk.Query;

namespace CRM2016ConsoleApplication
{
    class Program
    {
        static void Main(string[] args)
        { 
            #region credentials
            string SoapOrgServiceUri = "https://CRMUrl/OrganizationName/XRMServices/2011/Organization.svc";
            ClientCredentials credentials = new ClientCredentials();
            credentials.UserName.UserName = "username";
            credentials.UserName.Password = "password";
            #endregion                     

            Uri serviceUri = new Uri(SoapOrgServiceUri);
            OrganizationServiceProxy proxy = new OrganizationServiceProxy(serviceUri, null, credentials, null);
            proxy.EnableProxyTypes();
            IOrganizationService orgService = (IOrganizationService)proxy;
            XrmServiceContext serviceContext = new XrmServiceContext(orgService);

        }
    }
}

Update username, password and crm url.




Now you have OrganizationService instance and if you have built your early bound classes then you have access to the ServiceContext too. In this example they are orgService and serviceContext respectively.

Now you can build your CRM queries with hard coded GUIDs to test your business logic. Once all tested and good to go move them to your plugins. J

How to tackle Concurrent Business Process flows

Dynamics 365 has introduced the new feature of Concurrent Business Process Flows. Here is a couple of good articles on that: http://dev...