Wednesday, January 27, 2016

Abstracting CRM Providers Using Interfaces







I have created a custom module which can switch to CRM as needed. At present I am using CRM as my Source of Record for custom module. I would like to make sure that custom module, could able to connect to any source of records example sales force, share point portal or any custom application. 

That thought lead me to apply factory pattern for CRM provider


Consider the following sample classes, which allows you to obtain a specific connection object based on the value of a custom enumeration or configuration.

Data Provider class

namespace DataLayer
{
   public abstract class DataProvider
   {
       // singleton reference to the instantiated object
       static DataProvider _provider;
       // constructor
       static DataProvider()
       {
          
           CreateProvider();
       }

       // dynamically create provider
       static void CreateProvider()
       {
           _provider = (DataLayer.DataProvider)Activator.CreateInstance(Type.GetType("<CRMProvider type Name> Ex: DataLayer.CRMProvider can get from config"));
       }
      
       #region Abstract methods
       You can define all your abstract methods here
Ex:   public abstract MasterRecord TestAddMasterRecord(MasterRecord MasterRecord);
       #endregion
   }
}


CRM Provider

namespace DataLayer
{

   
    public class CRMProvider : DataProvider
    {
        const string ProviderType = "CRM";
        readonly OrganizationService _crmService;


        public CRMProvider()
        {
            if( _crmService ==null)
            {
            _crmService = Getservice();
            }
        }

        public OrganizationService Getservice()
        {
            #region Create CRM Connection
            CrmConnectionManagement OConnectionManagement = new CrmConnectionManagement();
            return OConnectionManagement.GetService();
            #endregion
        }
       #region Abstract methods
       Implements Methods of Data Provider
  public override MasterRecord TestAddMasterRecord(MasterRecord MasterRecord)
        {
Implement Method
        }
       #endregion

    }
}


You can have any number of Provider which will talk to your module as source of system.

Configuration

<configuration>
<appSettings>
<!-- This key value maps to one of our enum values. -->
<add key="provider" value=" CRMProvider type Name "/>
</appSettings>
</configuration>

 How to Utilize the Provider?

CRMProvider provider = new CRMProvider();

Provider.TestAddMasterRecord(MasterRecord);



About Connection Management Class 

you can utilize existing class available in CRM SDK 




namespace DataLayer
{
    class CrmConnectionManagement
    {
        #region Private Methods

        /// <summary>
        /// Gets web service connection information from the app.config file.
        /// If there is more than one available, the user is prompted to select
        /// the desired connection configuration by name.
        /// </summary>
        /// <returns>A string containing web service connection configuration information.</returns>
        private static String GetServiceConfiguration()
        {
            // Get available connection strings from app.config.
            int count = ConfigurationManager.ConnectionStrings.Count;

            // Create a filter list of connection strings so that we have a list of valid
            // connection strings for Microsoft Dynamics CRM only.
            List<KeyValuePair<String, String>> filteredConnectionStrings =
                new List<KeyValuePair<String, String>>();

            for (int a = 0; a < count; a++)
            {
                if (isValidConnectionString(ConfigurationManager.ConnectionStrings[a].ConnectionString))
                    filteredConnectionStrings.Add
                        (new KeyValuePair<string, string>
                            (ConfigurationManager.ConnectionStrings[a].Name,
                            ConfigurationManager.ConnectionStrings[a].ConnectionString));
            }

            // No valid connections strings found. Write out and error message.
            if (filteredConnectionStrings.Count == 0)
            {
                throw new Exception("An app.config file containing at least one valid Microsoft Dynamics CRM " +
                   "connection string configuration must exist in the run-time folder.");

            }

            // If one valid connection string is found, use that.
            if (filteredConnectionStrings.Count <= 1)
            {
                return filteredConnectionStrings[0].Value;
            }
            return null;


        }


        /// <summary>
        /// Verifies if a connection string is valid for Microsoft Dynamics CRM.
        /// </summary>
        /// <returns>True for a valid string, otherwise False.</returns>
        private static Boolean isValidConnectionString(String connectionString)
        {
            // At a minimum, a connection string must contain one of these arguments.
            if (connectionString.Contains("Url=") ||
                connectionString.Contains("Server=") ||
                connectionString.Contains("ServiceUri="))
                return true;

            return false;
        }

        #endregion Private Methods

        #region public Methods

        public OrganizationService GetService()
        {
            String connectionString = GetServiceConfiguration();
            Microsoft.Xrm.Client.CrmConnection connection = CrmConnection.Parse(connectionString);
            return new OrganizationService(connection);
        }
        #endregion

    }


}