Monday 9 September 2019

Case Sensitivity on Ribbon Workbench Customisations

If you have noticed the ribbons on the CRM classic UI, you might have seen that labels of the buttons are displayed as Capitals no matter how you configure them on the ribbon workbench.

But with the introduction of the UCI, it displays the labels as how it is configured, i.e if you have entered the label in lowercase, UCI also displays as Lower case and if you have configured the label as Full Caps, UCI also displays as Full Caps.
Recently, I was working on custom buttons and one of the button's name was on Full Caps which stood out a little while the label on other buttons are Initial Caps. So, I decided to edit the label with the Initial Caps via Ribbon Workbench, but even after multiple publish, it just didn't seem to react to my changes. The label was still displayed as Full Caps.

To fix this scenario, I had to do two sequential changes.
The label of the button is "Create Account".
Initially, the label was "CREATE ACCOUNT"  and then changed the label to "Create Account" via Ribbon Workbench but like I mentioned before, it didn't work.
What worked was,
1. Change the label to something that is completely different from the original label. For eg, Contact.
2. Publish the solution
3. Now, edit the label again with "Create Account" (Notice the letters are Initial Caps).
4. Publish the solution now and it should work.

This is basically a scenario where the label of the button is same but you are just trying to change the case of the label.





Friday 6 September 2019

Dynamics 365 CE: Quick Reference on Client Side Events




Below are the list of components and related events that might occur on the client side which can further be used as triggers for the custom logic implementation using javascript.

Component
Related Events
Attribute
OnChange
Form
OnLoad, OnSave
Form Data
OnLoad
Grid & SubGrid
OnChange, OnLoad ,OnRecordSelect, OnSave
Iframe
OnReadyStateComplete
Knowledge Base Search
OnResultOpened, OnSelection, PostSearch
Lookup Control
PreSearch
Process
OnProcessStatusChange, OnStateChange, OnStageSelected
Tab
TabStateChange


Saturday 22 June 2019

How To Retrieve More than 5000 records in D365 Console App

As you might already know that, by default, only 5000 records are retrieved irrespective of whether you are using an Advanced Find or server side code, hence it is very important especially in case if you are developing an enterprise application, you should ensure that the result set that you are acting on should hold the complete results and not the default 5000.

Below class can be used to achieve the same.
The key thing to notice here is, we are basically using the pagingCookie, MoreRecords & pageNumber properties to achieve this.

HelperClass:

class RetrieveAllRecords
    {
        #region Retrieve more than 5000 records
        /// <summary>
        /// Retrieve more than 5000 records based on pagig cookie information
        /// </summary>
        /// <param name="service">Organization service</param>
        /// <param name="fetchXml">Fetchml describing the filter criteria</param>
        /// <returns></returns>
        public static List<Entity> RetreiveAll(IOrganizationService service, string fetchXml)
        {
            // Set the number of records per page to retrieve.
            int fetchCount = 5000;
            // Initialize the page number.
            int pageNumber = 1;

            // Specify the current paging cookie. For retrieving the first page,
            // pagingCookie should be null.
            string pagingCookie = null;
            List<Entity> entityCollection = new List<Entity>();

            while (true)
            {
                // Build fetchXml string with the placeholders.
                string xml = CreateXml(fetchXml, pagingCookie, pageNumber, fetchCount);

                // Excute the fetch query and get the xml result.
                RetrieveMultipleRequest fetchRequest = new RetrieveMultipleRequest
                {
                    Query = new FetchExpression(xml)
                };
                EntityCollection returnCollection = ((RetrieveMultipleResponse)service.Execute(fetchRequest)).EntityCollection;
                foreach (var entity in returnCollection.Entities)
                {
                    entityCollection.Add(entity);
                }

                // Check for morerecords, if it returns 1.
                if (returnCollection.MoreRecords)
                {
                    pageNumber++;
                    pagingCookie = returnCollection.PagingCookie;
                }
                else
                {
                    return entityCollection;
                }
            }
        }
        #endregion

        #region CreateXml
        /// <summary>
        /// Creates and fomrats xml based on the paging cookie
        /// </summary>
        /// <param name="xml"></param>
        /// <param name="cookie"></param>
        /// <param name="page"></param>
        /// <param name="count"></param>
        /// <returns>FetchXML</returns>
        public static string CreateXml(string xml, string cookie, int page, int count)
        {
            StringReader stringReader = new StringReader(xml);
            XmlTextReader reader = new XmlTextReader(stringReader);

            // Load document
            XmlDocument doc = new XmlDocument();
            doc.Load(reader);

            return CreateXml(doc, cookie, page, count);
        }
        #endregion

        #region CreateXml Document
        /// <summary>
        /// Creates a XML Document that can be sent back to CRM along with Paging Information
        /// </summary>
        /// <param name="doc"></param>
        /// <param name="cookie"></param>
        /// <param name="page"></param>
        /// <param name="count"></param>
        /// <returns></returns>
        public static string CreateXml(XmlDocument doc, string cookie, int page, int count)
        {
            XmlAttributeCollection attributes = doc.DocumentElement.Attributes;
            if (cookie != null)
            {
                XmlAttribute pagingAttribute = doc.CreateAttribute("paging-cookie");
                pagingAttribute.Value = cookie;
                attributes.Append(pagingAttribute);
            }
            XmlAttribute pageAttribute = doc.CreateAttribute("page");
            pageAttribute.Value = System.Convert.ToString(page);
            attributes.Append(pageAttribute);
            XmlAttribute countAttribute = doc.CreateAttribute("count");
            countAttribute.Value = System.Convert.ToString(count);
            attributes.Append(countAttribute);
            StringBuilder stringBuilder = new StringBuilder(1024);
            StringWriter stringWriter = new StringWriter(stringBuilder);
            XmlTextWriter writer = new XmlTextWriter(stringWriter);
            doc.WriteTo(writer);
            writer.Close();
            return stringBuilder.ToString();
        }
        #endregion
    }

Connecting to D365 Using Xrm.Tooling Connector via Console App

One of the easiest way to connect to a Dynamics 365 CE instance, is to leverage the value of app.config.
Using the below configuration & just placing your organisation related values, you should be able to connect to D365 CE instance within a matter of minutes.

Ensure that the reference to the Microsoft Tooling Connector is added via nuget.
You can add this via nuget manager by searching for "Microsoft.CrmSdk.XrmTooling.CoreAssembly"  and pick up the one that is authored by either Microsoft or crmsdk.


Paste Below in app.config:

<connectionStrings>
    <add name="MyCDSServer" connectionString="AuthType=Office365;Url=http://some:8080/Test;UserName=someone@some.onmicrosoft.com;
  Password=passcode" />
</connectionStrings>


Below code from Main:

static void Main(string[] args)
        {
            CrmServiceClient serviceClient = new CrmServiceClient(ConfigurationManager.ConnectionStrings["connectionString"].ConnectionString);
            IOrganizationService service = serviceClient.OrganizationServiceProxy;
        }


Monday 3 June 2019

D365 Exception: The plug-in type could not be found in the plug-in assembly

I recently faced an error stating "The plug-in type could not be found in the plug-in assembly" in an On-Premise environment.

The reason for this is, my target D365 instance was still on version 8.0 whereas the SDK assemblies (basically the nuget packages) that I was referring in my plugin were of version 9.0.



After downgrading the sdk dlls to 8.0(any version of 8.0 should work mostly), this issue was fixed and the plugin completed its operation as expected.

So, if you are facing this issue, you might have to ensure that your SDK assemblies are matching with that of your target D365 instance.

Wednesday 22 May 2019

Where did my Settings menu go? New UCI on Dynamics 365

How to Access Settings menu in the new UCI App?

With recent updates to UCI, you might have wondered, how can you access the developer items like Customization, Process etc.
Well,

Its very simple. Just click on the Settings gear on the top right. And then select “Advanced Settings” and there you go.!!




Thursday 14 December 2017

How to Import N:N relationship records in MS Dynamics CRM/365








Recently, I came across a scenario where I was expected to import a set of records in CRM which has related records,and  the relationship being a N:N.
I was just given an Excel file and there is no option to import related records out of the box(at least when this post was written).
So after spending a couple of hours, I found an easy way to do this.

Step 1: Import the records of the related entity as usual(using OOTB import feature). Now we have to figure out a way to just associate the records of these two entities.
For this example, lets assume I want to import records of Account and Product which are related with  a N:N.

Step 2: Import Accounts separately. Import Products separately via OOTB import feature.

Step 3: Now, download XRMToolBox.

Step 4: Make use of the N:N data


Step 5: Connect to the desired organization. Click on Load Metadata button.


Step 6:Select the Account Entity under First Entity option and the relationship name(N:N) . Select which attribute you want the app to find out for a particular account. For eg, Account Number or Account name etc.

Step 7: Select the corresponding relationship name.

Step 8: Select the Product Entity against Second Entity and the field name for identifying a Product record. Say Product ID.

Step 9: Now, select the csv file with only two columns. First Column should contain Account Id and the second column should contain contact id.
Ensure you dont add headers to this csv file.

Click on Import  and you can see the status of each line in the log.