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:
The primary attribute for the Plug-in Setting entity will be configured as follows:
We then add a single attribute that will actually hold our settings information:
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:
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,