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.