Refresh Security Token for Microsoft Dynamics CRM Connection

In my previous blog post I used singleton pattern to keep the connection open to Dynamics CRM organization service. The only caveat of this method is that you need to monitor your token expiry depending on your implementation method and renew it before expiry.

From MSDN best practices:

Monitor your WCF security token (Token) and refresh it before it expires so that you do not lose the token and have to start over with authentication. To check the token, create a custom class that inherits from the OrganizationServiceProxy or DiscoveryServiceProxy class and that implements the business logic to check the token. Or wrap the proxy classes in a new class. Another technique is to explicitly check the token before each call to the web service. Example code that demonstrates these techniques can be found in the ManagedTokenDiscoveryServiceProxy, ManagedTokenOrganizationServiceProxy, and AutoRefreshSecurityToken classes in the Helper code: ServerConnection class topic.

See the code on GitHub

This piece of code demonstrate how to do monitor the token and refresh it before it expires:

using System;
using System.ServiceModel;
using System.ServiceModel.Description;

using Microsoft.Xrm.Sdk;
using Microsoft.Xrm.Sdk.Client;

namespace CRMConnectionHelper
{
    public sealed class ManagedTokenOrganizationServiceProxy : OrganizationServiceProxy
    {
        private AutoRefreshSecurityToken<OrganizationServiceProxy, IOrganizationService> _proxyManager;

        public ManagedTokenOrganizationServiceProxy(Uri serviceUri, ClientCredentials userCredentials)
            : base(serviceUri, null, userCredentials, null)
        {
            this._proxyManager = new AutoRefreshSecurityToken<OrganizationServiceProxy, IOrganizationService>(this);
        }

        public ManagedTokenOrganizationServiceProxy(IServiceManagement<IOrganizationService> serviceManagement,
            SecurityTokenResponse securityTokenRes)
            : base(serviceManagement, securityTokenRes)
        {
            this._proxyManager = new AutoRefreshSecurityToken<OrganizationServiceProxy, IOrganizationService>(this);
        }

        public ManagedTokenOrganizationServiceProxy(IServiceManagement<IOrganizationService> serviceManagement,
            ClientCredentials userCredentials)
            : base(serviceManagement, userCredentials)
        {
            this._proxyManager = new AutoRefreshSecurityToken<OrganizationServiceProxy, IOrganizationService>(this);
        }

        protected override void AuthenticateCore()
        {
            this._proxyManager.PrepareCredentials();
            base.AuthenticateCore();
        }

        protected override void ValidateAuthentication()
        {
            this._proxyManager.RenewTokenIfRequired();
            base.ValidateAuthentication();
        }
    }

    ///
<summary>
    /// Class that wraps acquiring the security token for a service
    /// </summary>

    public sealed class AutoRefreshSecurityToken<TProxy, TService>
        where TProxy : ServiceProxy<TService>
        where TService : class
    {
        private TProxy _proxy;

        ///
<summary>
        /// Instantiates an instance of the proxy class
        /// </summary>

        /// <param name="proxy">Proxy that will be used to authenticate the user</param>
        public AutoRefreshSecurityToken(TProxy proxy)
        {
            if (null == proxy)
            {
                throw new ArgumentNullException("proxy");
            }

            this._proxy = proxy;
        }

        ///
<summary>
        /// Prepares authentication before authenticated
        /// </summary>

        public void PrepareCredentials()
        {
            if (null == this._proxy.ClientCredentials)
            {
                return;
            }

            switch (this._proxy.ServiceConfiguration.AuthenticationType)
            {
                case AuthenticationProviderType.ActiveDirectory:
                    this._proxy.ClientCredentials.UserName.UserName = null;
                    this._proxy.ClientCredentials.UserName.Password = null;
                    break;
                case AuthenticationProviderType.Federation:
                case AuthenticationProviderType.LiveId:
                    this._proxy.ClientCredentials.Windows.ClientCredential = null;
                    break;
                default:
                    return;
            }
        }

        ///
<summary>
        /// Renews the token (if it is near expiration or has expired)
        /// </summary>

        public void RenewTokenIfRequired()
        {
            if (null != this._proxy.SecurityTokenResponse &&
            DateTime.UtcNow.AddMinutes(15) >= this._proxy.SecurityTokenResponse.Response.Lifetime.Expires)
            {
                try
                {
                    this._proxy.Authenticate();
                }
                catch (CommunicationException)
                {
                    if (null == this._proxy.SecurityTokenResponse ||
                        DateTime.UtcNow >= this._proxy.SecurityTokenResponse.Response.Lifetime.Expires)
                    {
                        throw;
                    }

                    // Ignore the exception 
                }
            }
        }
    }
}

now create an instance of ManagedTokenOrganizationServiceProxy instead of OrganizationServiceProxy :

//get the OrganizationService
//OrganizationServiceProxy _serviceproxy = new OrganizationServiceProxy(new Uri(config.XrmOrgServiceProxy), null, clientcred, null);
 ManagedTokenOrganizationServiceProxy _serviceproxy = new ManagedTokenOrganizationServiceProxy(new Uri(config.XrmOrgServiceProxy), clientcred);
Advertisements

Improving Dynamics CRM connection performance with Singleton pattern

Calling Dynamics CRM services is slow process.The authentication process adds the most overhead in establishing a connection to CRM. The call needs to go through multiple steps in order to be authenticated and authorized to access the data in Dynamics CRM.

If you have an application that needs to perform CRUD operation outside of CRM you can leave the connection open to Dynamics CRM with Singleton pattern.

Singleton pattern:

public class XrmConnectionProvider
    {
        private static IOrganizationService instance;
        private static object _lockObject = new object();

		private static IConfigurationService _configurationService;
        
		private XrmConnectionProvider() { }
		
        public static IOrganizationService CRMService
        {
            get
            {
                try
                {
                    lock (_lockObject)
                    {
                        if (instance == null)
                        {
							var container = IoC.Initialize();
							_configurationService = container.GetInstance<IConfigurationService>();


                            instance = Connect();
                        }
                        return instance;
                    }
                }
                catch (Exception ex)
                {
					throw new Exception("Unable to connect to CRM", ex);
                }

            }
        }
        private static IOrganizationService Connect()
        {
		     //user name and password stored in a config file
			var config = _configurationService.Get<XrmClientConfiguration>();

			Uri dInfo = new Uri(config.XrmUri);
			ClientCredentials clientcred = new ClientCredentials();
			clientcred.UserName.UserName = config.XrmClientCredUserName;
			clientcred.UserName.Password = config.XrmClientCredPassword;


			#region on-premise/online

			DiscoveryServiceProxy dsp = new DiscoveryServiceProxy(dInfo, null, clientcred, null);
            dsp.Authenticate();
            RetrieveOrganizationsRequest rosreq = new Microsoft.Xrm.Sdk.Discovery.RetrieveOrganizationsRequest();
            RetrieveOrganizationsResponse r = (RetrieveOrganizationsResponse)dsp.Execute(rosreq);

            //get the OrganizationService
            OrganizationServiceProxy _serviceproxy = new OrganizationServiceProxy(new Uri(config.XrmOrgServiceProxy), null, clientcred, null);
            //In order to use the generated types when dealing with the organization service, you have to add the ProxyTypesBehavior to the endpoint Behaviors collection. This instructs the OrganizationServiceProxy to look in the assembly for classes with certain attributes to use. The generated classes are already attributed with these attributes. Simply, this makes all interactions with the organization service to be done using the generated typed classes for each entity instead of the generic Entity class we used earlier.
            _serviceproxy.ServiceConfiguration.CurrentServiceEndpoint.EndpointBehaviors.Add(new ProxyTypesBehavior());
            //Do not forget to include _serviceproxy.EnableProxyTypes();. Without this line,you will be unable to use early binding.
            _serviceproxy.EnableProxyTypes();
            IOrganizationService service = (IOrganizationService)_serviceproxy;

            #endregion
            return service;
        }
    }

Now in your code you can call below class and it will only generate one instance instead of calling it for each request :

 var CRMService= XrmConnectionProvider.Connect();

Here is the result of URL based test on Visual studio online after applying above approach :

The test ran for duration of 120 seconds with 25 users load hitting my Web API application (hosted on azure) retrieving Dynamics CRM contacts.

Before:

url-based-test-dynamics-crm-before

After:

url-based-test-dynamics-crm-after

 

The only caveat to this method is that you need to monitor your token expiry depending on your implementation method and renew it before expiry.
You can read about it on this blog post.
But before that you need to increase the life time of token if you’ve configured your CRM for IFD.  By default the ADFS token last for 60 minutes. By using Windows PowerShell, you can change the TokenLifetime property for the relying party objects that you created from 60 minutes to a longer period, such as 480 minutes (8 hours).

From MSDN best practices:

Monitor your WCF security token (Token) and refresh it before it expires so that you do not lose the token and have to start over with authentication. To check the token, create a custom class that inherits from the OrganizationServiceProxy or DiscoveryServiceProxy class and that implements the business logic to check the token. Or wrap the proxy classes in a new class. Another technique is to explicitly check the token before each call to the web service. Example code that demonstrates these techniques can be found in the ManagedTokenDiscoveryServiceProxy, ManagedTokenOrganizationServiceProxy, and AutoRefreshSecurityToken classes in the Helper code: ServerConnection class topic.

Dynamics CRM processes to attach with remote debugger

For debugging plugins and custom workflow activities, attach to these Dynamics CRM processes in visual studio :

  • Online: W3WP.exe
  • Offline: Microsoft.Crm.Application.Hoster.exe
  • Asynchronous registered plug-ins (post operation): CrmAsyncService.exe
  • Custom workflow assemblies: CrmAsyncService.exe
  • Sandbox (isolation mode): Microsoft.Crm.Sandbox.WorkerProcess.exe

(https://msdn.microsoft.com/en-us/library/gg328574.aspx)

Adding a Dynamics CRM Status Reason with custom values

The default status reasons for Dynamics CRM custom entities are 1 for active and 2 for inactive.

dynamics-crm-status-reason

It’s not possible to add Status Reason (statuscode) with values less than 551,870,000 through MS Dynamics CRM user interface and added new values are read only and incremental. If the default status reason is somehow deleted or you want to create a status reason with specific values, you can add it using CRM API’s.

Here is an example of adding an active status reason in CRM with value of 1:

(in my case the default active status reason was deleted)

         var response= ((InsertStatusValueResponse)ServiceContext.Execute
             (
                new InsertStatusValueRequest
                 {
                     AttributeLogicalName = "statuscode",
                     EntityLogicalName = YourEntity.EntityLogicalName,
                     Label = new Microsoft.Xrm.Sdk.Label("Active", 1033),
                     StateCode = 0, //Status: 0 active & 1 inactive 
                     Value = 1 
                 }
             )).NewOptionValue;

Add DLL to GAC for Microsoft Dynamics CRM SSIS packages

I recently developed a SSIS package for Microsoft Dynamics CRM and after deploying it on the production SQL server I got this Error:

 Data Flow Task:Error: System.IO.FileNotFoundException: Could not load file or assembly 'Microsoft.Xrm.Sdk, Version=6.0.0.0,
 Culture=neutral, PublicKeyToken=31bf3856ad364e35' or one of its dependencies. The system cannot find the file specified.
 File name: 'Microsoft.Xrm.Sdk, Version=6.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35'
 at Microsoft.SqlServer.Dts.Pipeline.ScriptComponentHost.HandleUserException(Exception e)
 at Microsoft.SqlServer.Dts.Pipeline.ScriptComponentHost.PreExecute()
 at Microsoft.SqlServer.Dts.Pipeline.ManagedComponentHost.HostPreExecute(IDTSManagedComponentWrapper100 wrapper)

Since multiple CRM SDK  assemblies has been referenced in my SSIS project, the package execution failed with file not found error. In order to fix the issue,  those DLLs should be added to GAC in production server and here are the steps  to do it:

  1. My production environment is Windows server 2012, SQL server 2012, .net framework 4.0.
  2. In order to register DLLs I used Gacutil tool. The tool is part of Visual Studio tool set and in order to have it in production: Copy these two Gacutil files from dev environment
    • C:\Program Files (x86)\Microsoft SDKs\Windows\v7.0A\Bin\NETFX 4.0 Tools\gacutil.exe
    • C:\Program Files (x86)\Microsoft SDKs\Windows\v7.0A\Bin\NETFX 4.0 Tools\gacutil.exe.config
  3. Paste two files to “C:\Windows\Microsoft.NET\Framework\v4.0.30319” folder in production (.net 4.0):
  4. Run the command prompt as administrator and execute below command for each one of the CRM DLLs. for instance:
    • “C:\Windows\Microsoft.NET\Framework\v4.0.30319\gacutil.exe” /i “C:\Microsoft.Xrm.Sdk.dll”

now the package should run successfully on production server.

Working with Microsoft Dynamics CRM Party List

Party lists are system data types and there is no supported way to modify them.

Unsupported : In order to modify party lists you need to access them in the DOM by getting their id attribute.

for instance if you want to change the filter on the “from” party list in activities, use below code on form load:

//change the default filter to account entity and disable the others parties

function FilterFromPartyList() {
  document.getElementById("from_i").setAttribute("defaulttype", "1");
  // document.getElementById("from_i").setAttribute("defaulttype", "1,2"); //+contact
 document.getElementById("from_i").setAttribute("lookuptypes", "1");
 document.getElementById("from_i").setAttribute("lookuptypeIcons", "/_imgs/ico_1 6_1.gif");
 document.getElementById("from_i").disableViewPicker = 1;
}

Here is the list of the attributes that you can access through DOM:


<img width="20" height="18" title="Select a value." class="ms-crm-InlineLookupEdit" id="from_i" alt="Select a value." src="/_imgs/search_normal.gif" isDisplayOnly="false" ForceSubmit="false" isEnableInlineLookupForEditForms="1" lookupstyle="single" lookuptypes="1" lookuptypeIcons="/_imgs/ico_16_1.gif" lookuptypenames="account:1:Account,contact:2:Contact,lead:4:Lead,systemuser:8:User" data-fdeid="PrimaryEntity" resolveemailaddress="0" attrName="from" attrPriv="7" createpermissiondictionary="account:true,contact:true,lead:true,systemuser:true" crmattributeid="ca991199-24b8-4a1f-9bd9-55fddb6dcdcd" isDeDupLookup="0" defaulttype="1" autoresolve="1" showproperty="1" allowUnresolvedPartiesOnEmailSend="0" isInline="true" inlineViewIds="undefined" allowFilterOff="0" disableMru="1" disableQuickFind="0" disableViewPicker="0" DefaultViewId="{A3AF4AB8-861D-4CFA-92A5-E6281FED7FAB}" lookupbrowse="0" lookupDialogMultipleSelect="0">

 

Retrieve Multiple Dynamics CRM Records with XrmServiceToolkit

In order to install XrmServiceToolkit check this blog post.

In this library, I found RetrieveMultiple function really helpful and I use it for different of purposes like to create a custom numbering system in CRM records, for instance since you can retrieve multiple record, you can loop through all your record and get all the fields in those.

here is the signature of the function :

     retrieveMultipleRecords = function (type, options, successCallback, errorCallback, onComplete, async)

One important thing is that successCallback only returns one page of records (50) at the time so you should loop through the results and push the records into an array outside of the function.

Here are Some Sample retrieveMultipleRecords calls:

1. This sample retrieves all accounts with all fields in the account form and push them in accountArray :

      var accountArray = new Array();

        XrmServiceToolkit.Rest.RetrieveMultiple(
                     "AccountSet",
                     "",// if you leave it empty, it retrieves all fields
                     function (results) {
                         if (results.length >= 1)
                             for (var i = 0; i < results.length; i++) {
                                 accountArray.push(results[i]);
                             }
                     },
                     function (error) {
                         alert(error.message);
                     },
                     function onComplete() {
                         //alert(" records should have been retrieved.");
                     },
                     false
                 );

     alert(accountArray.length); //this should show the number of account records in CRM

2. This sample retrieves only AccountID and AcountNumber when “new_AccountingApproved”(custom field) check box is checked:

   var accountArray = new Array();

        XrmServiceToolkit.Rest.RetrieveMultiple(
                     "AccountSet",
                     "$select=AccountId, AccountNumber &$filter=new_AccountingApproved eq true",
                     function (results) {
                         if (results.length >= 1)
                           //accountArray = results;  //don't do this! instead use push to array. for instance if you have 58 accounts in CRM, accountArray will only get 8 of them, because successCallback will run twice.
                             for (var i = 0; i < results.length; i++) {
                                 accountArray.push(results[i]);
                             }
                     },
                     function (error) {
                         alert(error.message);
                     },
                     function onComplete() {
                         //alert(" records should have been retrieved.");
                     },
                     false
                 );

In case you are interested ,Here is RetrieveMultipleRecords function definition from Source code of the project:

var retrieveMultipleRecords = function (type, options, successCallback, errorCallback, onComplete, async) {
        ///<summary>
        /// Sends synchronous/asynchronous request to retrieve records.
        ///</summary>
        ///<param name="type" type="String">
        /// The Schema Name of the Entity type record to retrieve.
        /// For an Account record, use "Account"
        ///</param>
        stringParameterCheck(type, "XrmServiceToolkit.REST.retrieveMultipleRecords requires the type parameter is a string.");
        ///<param name="options" type="String">
        /// A String representing the OData System Query Options to control the data returned
        ///</param>
        if (options != null)
            stringParameterCheck(options, "XrmServiceToolkit.REST.retrieveMultipleRecords requires the options parameter is a string.");
        ///<param name="successCallback" type="Function">
        /// The function that will be passed through and be called for each page of records returned.
        /// Each page is 50 records. If you expect that more than one page of records will be returned,
        /// this function should loop through the results and push the records into an array outside of the function.
        /// Use the OnComplete event handler to know when all the records have been processed.
        /// </param>
        callbackParameterCheck(successCallback, "XrmServiceToolkit.REST.retrieveMultipleRecords requires the successCallback parameter is a function.");
        ///<param name="errorCallback" type="Function">
        /// The function that will be passed through and be called by a failed response.
        /// This function must accept an Error object as a parameter.
        /// </param>
        callbackParameterCheck(errorCallback, "XrmServiceToolkit.REST.retrieveMultipleRecords requires the errorCallback parameter is a function.");
        ///<param name="OnComplete" type="Function">
        /// The function that will be called when all the requested records have been returned.
        /// No parameters are passed to this function.
        /// </param>
        callbackParameterCheck(onComplete, "XrmServiceToolkit.REST.retrieveMultipleRecords requires the OnComplete parameter is a function.");
        ///<param name="async" type="Boolean">
        /// A Boolean representing if the method should run asynchronously or synchronously
        /// true means asynchronously. false means synchronously
        /// </param>
        booleanParameterCheck(async, "XrmServiceToolkit.REST.retrieveMultipleRecords requires the async parameter is a boolean.");