Thursday, June 18, 2020

Retrieve data from CRM using Azure clientId/clientSecret

using Microsoft.Xrm.Sdk;
using Microsoft.Xrm.Tooling.Connector;
using System;
using System.Configuration;
using Microsoft.Xrm.Sdk.Query;
using Microsoft.Xrm.Sdk.Organization;
using System.ServiceModel;
using System.Net;
using Microsoft.Crm.Sdk.Messages;
using System.Collections.Generic;
using System.Data;
using System.Runtime.InteropServices;
using System.Linq;
using Excel = Microsoft.Office.Interop.Excel;
using Microsoft.Xrm.Sdk.Messages;
using Microsoft.SharePoint;
using System.IO;
using Microsoft.IdentityModel.Clients.ActiveDirectory;
using System.Threading.Tasks;
using System.Net.Http;
using System.Text;
using System.Net.Http.Headers;

namespace CustomCode
{
    public class AzureRetrieveCall
    {
        public static void Main(string[] args)
        {
            //you will get some An unhandled exceptions of type 'System.AggregateException' occurred in mscorlib.dll
            //{ "An error occurred while sending the request."}
            //Hresult -2146233088
            //if you wont specify SecurityProtocol
            ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
            var contacts = CrmRequest(HttpMethod.Get, "https://ORGNAME.api.crm.dynamics.com/api/data/v9.1/contacts")
               .Result.Content.ReadAsStringAsync();
            // Similarly you can make POST, PATCH & DELETE requests  
        }
        public static async Task<HttpResponseMessage> CrmRequest(HttpMethod httpMethod, string requestUri, string body = null)
        {
            // Acquiring Access Token  
            var accessToken = await AccessTokenGenerator();

            var client = new HttpClient();
            var message = new HttpRequestMessage(httpMethod, requestUri);

            // OData related headers  
            message.Headers.Add("OData-MaxVersion", "4.0");
            message.Headers.Add("OData-Version", "4.0");
            message.Headers.Add("Prefer", "odata.include-annotations=\"*\"");

            // Passing AccessToken in Authentication header  
            message.Headers.Add("Authorization", $"Bearer {accessToken}");

            // Adding body content in HTTP request   
            if (body != null)
                message.Content = new StringContent(body, UnicodeEncoding.UTF8, "application/json");

            return await client.SendAsync(message);
        }
        public static async Task<string> AccessTokenGenerator()
        {
            string clientId = ""; // Your Azure AD Application ID  
            string clientSecret = ""; // Client secret generated in your App  
            string authority = "https://login.microsoftonline.com/APPTENANTID"; // Azure AD App Tenant ID  
            string resourceUrl = "https://ORGNAME.crm.dynamics.com"; // Your Dynamics 365 Organization URL  

            var credentials = new ClientCredential(clientId, clientSecret);
            var authContext = new Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationContext(authority);
            var result = await authContext.AcquireTokenAsync(resourceUrl, credentials);
            return result.AccessToken;
        }

    }
}

Tuesday, June 9, 2020

Invalid Argument. : System.ArgumentOutOfRangeException: Specified argument was out of the range of valid values.

Recently I came across this issue while importing solutions from Dev to Prod (Importing UCI changes from Dev to Prod)

The solution failed with same error: Specified argument was out of the range of valid values. Parameter name: Default picklist value has to be one of the option values.

I have thoroughly went through all my optionssets and found the below useful information

Error : Invalid Argument. : System.ArgumentOutOfRangeException: Specified argument was out of the range of valid values. Parameter name: Picklist option with value (864630000) that has the parent OptionSet with (guid) id does not exist. Default picklist value has to be one of the option values.

To find the offending option set I took the Option Value {864630000} and looked for that value within the customizations.xml file

the value should be under <attribute> tag on xml where the type is picklist(<Type>picklist</Type>), under <attribute> tag you will find <AppDefaultValue> tag which stored the default picklist value, check if the default value is part of the optionset value or not. If not add a value with 864630000 under the mentioned option set --> Save --> re-import the solution

Ex: in my scenario I have searched for  <AppDefaultValue>864630000</AppDefaultValue> from this grab the Picklist Attribute, check if that attribute has 864630000 value

Another way is you can change the default value to other existing value in customizations.xml or change the value in CRM  to unassigned value --> Save --> Publish.

it will make easier if you know the entity that trigger the error, you just need to check on that entity scope <Entity></Entity>..

Hope this will help you in finding the wrong picklist value using AppDefaultValue..