Microsoft Dynamics 365 Blog

CRM MVP Mitch Milam returns as a guest blogger with part two of this post. You can read more from Mitch at his blog.

In part one of this series we discussed the built-in mechanism provided by CRM to store plug-in application settings. Based on several comments on the article, I’ve decided to enhance the process by moving the storage of our settings from the plug-in configuration step to a custom CRM entity.

Creating a Custom Entity for Settings Storage

The first step in this process is to create a custom CRM entity which will store our settings. The New Entity form looks like this:

clip_image002

The primary attribute for the Plug-in Setting entity will be configured as follows:

clip_image004

We then add a single attribute that will actually hold our settings information:

clip_image006

Modify the main form to allow entry of the Plug-in Name and Settings information then publish the new entity and we’re ready for business. Here is how we populate a plug-in settings record:

clip_image008

Modified Plug-In Configuration Class

The PluginConfiguration class created in the previous article has been modified slightly to retrieve our settings from the CRM database instead of using the normal plug-in configuration methods.

Note: The constants at the beginning of the class will need to be modified to match the entity and attributes you created on your system.

   1: using System;
   2: using System.Collections.Generic;
   3: using System.Text;
   4: using System.Xml;
   5: using Microsoft.Crm.Sdk;
   6: using Microsoft.Crm.Sdk.Query;
   7: using Microsoft.Crm.SdkTypeProxy;
   8:  
   9: namespace CRMAccelerators
  10: {
  11:     class PluginConfiguration
  12:     {
  13:         private const string PLUGIN_ENTITY = "m3_pluginsetting";
  14:         private const string PLUGIN_NAME_ATTRIBUTE = "m3_pluginname";
  15:         private const string PLUGIN_SETTING_ATTRIBUTE = "m3_settings";
  16:  
  17:         private static XmlDocument _settingsDoc = new XmlDocument();
  18:  
  19:         public static void RetrieveSettings(ICrmService crmService, string settingsName)
  20:         {
  21:             QueryExpression query = new QueryExpression();
  22:  
  23:             query.EntityName = PLUGIN_ENTITY;
  24:  
  25:             query.ColumnSet = new ColumnSet(new string[] { PLUGIN_SETTING_ATTRIBUTE });
  26:  
  27:             query.Criteria = new FilterExpression();
  28:             query.Criteria.FilterOperator = LogicalOperator.And;
  29:  
  30:             ConditionExpression condition1 = new ConditionExpression();
  31:             condition1.AttributeName = "statecode";
  32:             condition1.Operator = ConditionOperator.Equal;
  33:             condition1.Values = new object[] { 0 };
  34:  
  35:             ConditionExpression condition2 = new ConditionExpression();
  36:             condition2.AttributeName = PLUGIN_NAME_ATTRIBUTE;
  37:             condition2.Operator = ConditionOperator.Equal;
  38:             condition2.Values = new object[] { settingsName };
  39:  
  40:             query.Criteria.Conditions.AddRange(new ConditionExpression[] { condition1, condition2 });
  41:  
  42:             OrderExpression order1 = new OrderExpression();
  43:             order1.AttributeName = PLUGIN_NAME_ATTRIBUTE;
  44:             order1.OrderType = OrderType.Ascending;
  45:  
  46:             query.Orders.Add(order1);
  47:  
  48:             RetrieveMultipleRequest request = new RetrieveMultipleRequest();
  49:             request.ReturnDynamicEntities = true;
  50:             request.Query = query;
  51:  
  52:             RetrieveMultipleResponse retrieved = (RetrieveMultipleResponse)crmService.Execute(request);
  53:  
  54:             DynamicEntity entity = (DynamicEntity)retrieved.BusinessEntityCollection.BusinessEntities[0];
  55:  
  56:             _settingsDoc.LoadXml(GetStringProperty(entity, PLUGIN_SETTING_ATTRIBUTE));
  57:         }
  58:  
  59:         public static Guid GetConfigDataGuid(string label)
  60:         {
  61:             string tempString = GetValueNode(label);
  62:  
  63:             if (tempString != string.Empty)
  64:             {
  65:                 return new Guid(tempString);
  66:             }
  67:             return Guid.Empty;
  68:         }
  69:  
  70:         public static bool GetConfigDataBool(string label)
  71:         {
  72:             bool retVar;
  73:  
  74:             if (bool.TryParse(GetValueNode(label), out retVar))
  75:             {
  76:                 return retVar;
  77:             }
  78:             else
  79:             {
  80:                 return false;
  81:             }
  82:         }
  83:  
  84:         public static int GetConfigDataInt(string label)
  85:         {
  86:             int retVar;
  87:  
  88:             if (int.TryParse(GetValueNode(label), out retVar))
  89:             {
  90:                 return retVar;
  91:             }
  92:             else
  93:             {
  94:                 return -1;
  95:             }
  96:         }
  97:  
  98:         public static string GetConfigDataString(string label)
  99:         {
 100:             return GetValueNode(label);
 101:         }
 102:  
 103:         private static string GetValueNode(string key)
 104:         {
 105:             XmlNode node = _settingsDoc.SelectSingleNode(String.Format("Settings/setting[@name='{0}']", key));
 106:  
 107:             if (node != null)
 108:             {
 109:                 return node.SelectSingleNode("value").InnerText;
 110:             }
 111:  
 112:             return string.Empty;
 113:         }
 114:  
 115:         public static string GetStringProperty(DynamicEntity parent, string prop)
 116:         {
 117:             string retVar = string.Empty;
 118:  
 119:             if (AttributeExists(parent, prop))
 120:             {
 121:                 retVar = parent.Properties[prop].ToString();
 122:             }
 123:  
 124:             return retVar;
 125:         }
 126:  
 127:         public static bool AttributeExists(DynamicEntity entity, string attr)
 128:         {
 129:             return entity.Properties.Contains(attr);
 130:         }
 131:     }
 132: }

Putting PluginConfiguration to Work

The only change made to the usage of the PluginConfiguration class I the requirement that we call RetrieveSettings before attempting to access any properties found within the settings attribute:

PluginConfiguration.RetrieveSettings(CrmService, "CoolPlugin");

string TaskPrefix = PluginConfiguration.GetConfigDataString("TaskPrefix");

Note: Since we need a connection to CRM to retrieve settings from the database, you will need to locate the code that retrieves settings after the code that has initiated a connection to the CRM web service.

Conclusion

By moving the settings from the Plug-in step configuration to the CRM database we’ve reduced the maintenance level required to store our settings in a central location. There will be a slight performance hit since we have to retrieve a database record, but I don’t think it will be huge.

I’ll place the source code for the modified pluginconfiguration class on my blog so you may download it and add it to your next plug-in project.

Cheers,

Mitch Milam

We're always looking for feedback and would like to hear from you. Please head to the Dynamics 365 Community to start a discussion, ask questions, and tell us what you think!