Showing posts with label Plug-ins. Show all posts
Showing posts with label Plug-ins. Show all posts

Friday, October 30, 2009

Debugging CRM Plug-ins, Stored Procedures & Custom Workflow Activities

by Danny Varghese 02.24.09

Whether it's developing CRM plug-ins, custom workflow activities, or writing stored procedures against the CRM database, the most useful tool I've used is the Microsoft Visual Studio Debugger. The debugger allows developers to step through the code for the above mentioned scenarios and has saved me hours!

Microsoft has phenomenal documentation on how to setup remote debugging:

http://msdn.microsoft.com/en-us/library/bt727f1t.aspx

The biggest issues I've had trying to setup the debugger has always been with permissions. I would recommend paying especially close attention to this section. The next biggest issue I had was trying to attach the debugger to a running process: http://msdn.microsoft.com/en-us/library/c6wf8e4z.aspx

Once you've setup Visual Studio debugger, you can attach the following processes for the following CRM components:

  1. For plug-ins, attach the debugger to the w3wp.exe process.
  2. For custom workflow activities, attach the debugger to MSCRMAsyncService.exe
  3. For stored procedures, attach the debugger to sqlserver.exe

Once the debugger has been attached, you must set a breakpoint in the code (either the .NET or T-SQL code). After the breakpoint is set, to test, do the following:

  1. For plug-ins, login to CRM and execute actions that will trigger the plug-in, such as create/update/assign a record.
  2. For custom workflow activities, login to CRM and perform actions that will trigger the workflow. With this, being that it's an asynchronous service, you'll have to wait until that service runs, and then Visual Studio will let you step through the code.
  3. For the stored procedure, just execute the stored procedure in Visual Studio and it will go right to the breakpoint.

I hope this post will help CRM developers save time and effort. I use the debugger every time I develop now to test and it's saved me tremendous amount of maintenance time and effort after the code has been deployed. Happy debugging!

CRM 4.0 Plug-in Registration Tool Walkthrough

by Danny Varghese 02.21.09

The CRM 4.0 Plug-in registration tool is Microsoft's tool to register any plug-in developed for CRM 4.0 and callouts that were developed for CRM 3.0, but need to be upgraded to 4.0. In this article, I'd like to take a step by step walkthrough of the tool including any interesting facts or issues I've run into during this process. Those will highlighted in the "NOTE" section.

To download the tool, please visit: http://code.msdn.microsoft.com/crmplugin for the latest version. The tool also comes packaged when you download the CRM 4.0 SDK, which you can do from: http://www.microsoft.com/DOWNLOADS/details.aspx?familyid=82E632A7-FAF9-41E0-8EC1-A2662AAE9DFB&displaylang=en.

Building The Plug-in Registration Tool

Either way, the download will contain Visual Studio project files that will need to be built. Mitch Kett in his blog has outlined how to do this: http://www.crowehorwath.com/cs/blogs/crm/archive/2008/03/19/incorporating-custom-entities-into-a-plugin-revised.aspx.

**NOTE: The CRM SDK provides great information on what each service provides for the user:

The CRM Web service provides strongly typed access to all entities in Microsoft Dynamics CRM, including custom entities and attributes. This Web service also allows execution of all supported operations, including those with built-in business logic as well as specialized operations. It provides a valid Web Services Description Language (WSDL) that is dynamically generated on the server to include the latest customizations and provides a single endpoint for your code.

The Discovery Web service is a mechanism to find the correct CrmService endpoint for your organization or to obtain a CRM ticket for Internet-facing deployment (IFD) or for Microsoft Dynamics CRM Online.

The Metadata Web service provides methods to read and write the metadata for an organization. This includes the definitions for entities, attributes, and relationships.**

**NOTE: If you don't place the assembly files mentioned in step (8) of Mitch's instructions, when you go to run the plug-in registration tool, you might get an error: "...Could Not Find Dependency…"**

Using The Plug-in Registration Tool

  1. You can launch the tool by either running the executable built from the project mentioned in the steps above, or if you've configured Visual Studio with a shortcut, you can launch it from that menu.

  1. Once the screen is loaded, click on Create New Connection. Enter a simple label for the connection, the server in which CRM is installed on, port, domain and user name.

**NOTE: There are security restrictions on the user that can register plug-ins. According to the SDK, the user account registering the plug-ins must exist in the Deployment Administrators group or must be a System Administrator. If the user is not part of at least one of those groups, you will get an error along the lines of "…Not have enough privilege to complete Create operation for an SDK entity…"**

**NOTE: If you don't enter a username and password, it will use the windows default credentials you used to login.**

  1. Click on Connect. Once connected, you will see a new screen with various menu options. Let's dive deeper into each of these options.

  1. Register
    1. Register New Assembly - you can register a new assembly that contains the plug-in execution code.
    2. Register New Step - allows you to configure the event that triggers the execution of the code. For example, this is where you can configure to fire a specific code after/before a record is created, updated, etc. This is equivalent to specifying a "PostUpdate," "PostCreate," in an xml configuration file for CRM 3.0 callouts.
    3. Register New Image - you can register an xml image of an entity. This can be used to find out what the values of a record is before and/or after the record is created, updated, etc. You can also specify which attributes of that record you want to pass into the execution context.
  1. Import/Export
    1. Export Solution Xml - allows a user to export an xml file that contains the assembly names, steps and/or images registered previously. This allows for configuration of the plug-ins to be setup once and then migrated to other environments.
    2. Import Solution Xml - allows user to import the xml file mentioned above in (3bi).

NOTE: According to a document walking through the registration tool published by Ajith Gande the "Import Export has some limitations and design assumptions

  • If a plug-in is impersonated on user MyDomain\crmUser and imported in an organization with different domain, it tries to map the user bases on the domain user name (crmuser)
  • Plug-in assembly DLL’s are not exported in the Xml. When Import is in progress, you need to copy the assemblies to the same location as the .xml file
  • Import overwrites the assemblies, plug-in, steps, images in the target organization if any other entity exists with same Guid, else it creates them
  • It is recommended to import assemblies, plug-in, and a step all at once to avoid a lot of overwrites. So delete any existing assemblies with same name on target org. But the tool supports updating or adding a step to the existing plug-in using the import."

This was referenced from:

http://code.msdn.microsoft.com/Project/Download/FileDownload.aspx?ProjectName=crmplugin&DownloadId=320**

  1. View
    1. Display By Assembly - this is the default view, showing all registered plug-ins by assembly name.
    2. Display By Entity - shows all registered plug-ins filtered by the entities they're registered for.
    3. Display By Message - organizes the plug-ins registered by messages or events such as Create, Update, etc.
  1. Unregister, Refresh & Search (all self-explanatory)
  1. Click on Register --> Register New Assembly --> Complete the steps below and press Register Selected Plugins --> Click on Close

  1. Step 1- Allows you to browse to a location and select the assembly you want to upload.
  2. Step 2 - This is not shown on the screenshot, but you now have to click Load Assembly
  3. Step 3 - Select where the assembly should be stored for execution
    1. Database - Storing the files on the database allows users to update the assembly file through this wizard as many times as they want without having to perform an "iisreset," as you had to in CRM 3.0. Advantage #2 is now the server\bin\assembly folder will not become cluttered if you have numerous plug-ins. Finally, this makes migration from different environments much easier because there are less files to move.

**NOTE: If you want to debug the assembly files, you will need to place the symbols file (.pdb) in <crm installation directory>\Server\bin\assembly folder. **

  1. Disk - This option allows you to store the assembly file as you did in CRM 3.0 under the <crm installation directory>\Server\bin\assembly directory. For debugging purposes, the symbols file (.pdb) must also be in this directory. If you store files here, an "iisreset" will be needed anytime any changes are made to the assembly.
  2. GAC - (Global Assembly Cache), for more information on this, please see my other post: http://www.crowehorwath.com/cs/blogs/crm/archive/2008/03/05/gac-it-developing-portable-code-for-microsoft-crm.aspx
  1. Once you've registered your plug-in, click on Register --> fill in the steps outline below --> Register New Step

  1. Message - In CRM 3.0, you had to configure the callout.xml file to specify if the plug-in was to be triggered via and update, create, assign, etc. Now you can configure this at this step.
  2. Primary Entity - The entity that will trigger the plug-in
  3. Secondary Entity - You should enter this only when the plug-in should be triggered for an event requiring two entities like the "SetRelated" message.
  4. Filtering Attributes - This allows you to filter which attributes of the entity will allow the triggering of the plug-in. For example if on the account you want the plug-in to fire only when the name gets updated, but not the account type, then you in your filter you can remove the account type. By default, all attributes are selected.
  5. Plugin - Select the assembly, and class you wish to associate to this step.
  6. Run In Users Context - By default it's set to calling user, which means the plug-in will be fired with the current users credentials. This is to control security so a user w/o the proper permissions won't have a plug-in fire to create/update/etc. any records. You can specify other users to run the context in. The list will pre-populate with all account names from the CRM installation.
  7. Execution order - Sometimes asynchronous operations on dependant on others asynchronous operations to complete. The execution order allows you to specify when this plug-in is supposed to fire.
  8. Event Sate - Specifies if the plug-in should be fired before or after an event. In CRM 3.0, this is similar to configuring a "PostUpdate," "PostCreate," "PreUpdate," etc.
  9. Execution Mode - A new feature which allows the plug-in to execute asynchronously or synchronously.
  10. Step Deployment - If you want the plug-in to be deployed (not registered) on the server and/or offline (Outlook client).
  11. Triggering Pipeline - For example, if you have an account with activities and you re-assign that account. If the plug-in was registered an activity, then it does not get fired because the activity is getting updated as part of another operation. To trigger the plug-in in that case you have to set the pipeline to "Child Pipeline"

**NOTE: According to the SDK, If you want your plug-in to run regardless of whether a Web service call or an (internal) platform call initiated the pipeline, register your plug-in in a child pipeline. However, do not use the IPluginExecutionContext.CreateCrmService or IPluginExecutionContext.CreateMetatdataService methods if you are writing code for a plug-in in that is used in a child pipeline. In a child pipeline, you must instantiate the CrmService or MetadataService manually.**

  1. Unsecure Configuration - if you don't want to hardcode any values and the plug-in needs certain configuration values (that may change based on deployment environments) you can specify the values here. For example, the CRM server url.
  2. Secure Configuration - same concept as above, but you could place more secure configurations like user credentials.
  1. (Optional) Click on Register New Image. I say this step is optional because you may not need this step at all times. This is equivalent to the pre and post image entity xml's in CRM 3.0. You can register an xml image of what the entity attributes were before and after an event. For example, if you've registered a post-update event on an account and you want to check what the value of a field was before the update, you could register a pre-update image. Furthermore, on this image, you can select which attributes you want the xml to contain. This allows users to help performance (all be it, just a tiny bit) but cutting down unnecessary fields.

**NOTE: You can not register a pre-create image since there were no attributes for a specific record because it hasn't been created yet. You will get an error if you try this.**

This concludes my in-depth look at the plug-in registration tool. All the notes I've added have been experiences I've had registering plug-ins. I hope this was informative and helps save you some time developing! Happy coding

Friday, February 27, 2009

Creating a Birthday Contact List

Today we welcome our guest blogger CRM MVP Darren Liu from the Crowe Horwath company.

Have you ever been asked by someone to get a list of contacts having birthdays during a certain time period from CRM? If so what have you done to perform this task? Within the application, birthdays are tracked on Contact records as a single date (including year). This causes problems when searching for birthdays in a certain time period as the birth date is evaluated including the year. To illustrate, consider the following example:

· John Dole, 10/1/1980

· Adam Smith, 9/1/1970

· Mark Francis, 10/10/1960

Within CRM, searching for date is done by range. There is no easy way to identify from the above contacts all those having birthday in October as any range you choose will include the year. Wildcard functions on date fields are not a workable solution.

There are several solutions to this problem including JavaScript to parse birthday on the onChange event, a custom report or a plug-in. The desired functionality is to be able to search by birth month, birth day, and/or birth year, allowing the user to quickly identify all birthdays in a certain time period.

In this blog, I will show you how to use a pre plug-in to parse the birthday field into day, month and year. This way, the users will able to perform searches using Advanced Find. I have chosen the plug-in approach because it will help me parse the birthday field not only when the users update the birthday on the contact form but also when updating the birthday through the CRM web service for data imports and data integration.

Implement the pre plug-in

1. Create New Attributes

Create three new attribute on the Contact entity form in CRM. After creating the new attributes, publish the Contact customization.

Display Name

Schema Name

Type

Searchable

Values

Birth Month

new_birthmonth

Picklist

Yes

Jan = 1, Feb = 2, Mar = 3, Apr = 4, May = 5, Jun = 6, Jul = 7, Aug = 8, Sept = 9, Oct = 10, Nov = 11, Dec = 12

Birth Day

new_birthday

Int

Yes

Min Value = 1

Max Value = 31

Birth Year

new_birthyear

Int

Yes

Min Value = 1900

Max Value = 9999

clip_image002

2. Create pre plug-in using Visual Studio

Create a plug-in project name Crm.Plugin, copy and paste the following code to your Plug-in project.

using System;


using System.Collections.Generic;


using System.Text;


using Microsoft.Crm.Sdk;


using Microsoft.Crm.SdkTypeProxy;


 


namespace Crm.Plugin


{


  public class MonthDayYearContactPlugin : IPlugin


  {


    public void Execute(IPluginExecutionContext context)


    {


    DynamicEntity entity = null;


 


    if (context.InputParameters.Properties.Contains(ParameterName.Target) &&


        context.InputParameters.Properties[ParameterName.Target] is DynamicEntity)


    {


        entity = (DynamicEntity)context.InputParameters[ParameterName.Target];


        if (entity.Name != EntityName.contact.ToString()) { return; }


    }


    else


    {


        return;


    }


 


    try


    {


        if (entity.Properties.Contains("birthdate"))


        {


            CrmDateTime _birthdate = (CrmDateTime)entity["birthdate"];


            if (_birthdate.IsNull)


            {


                entity["new_birthday"] = CrmNumber.Null;


                entity["new_birthmonth"] = Picklist.Null;


                entity["new_birthyear"] = CrmNumber.Null;


            }


            else


            {


                DateTime birthdayValue = _birthdate.UserTime; 


                entity["new_birthday"] = new CrmNumber(birthdayValue.Day);


                entity["new_birthmonth"] = new Picklist(birthdayValue.Month);


                entity["new_birthyear"] = new CrmNumber(birthdayValue.Year);


            }


        }


    }


    catch (Exception ex)


    {


        throw new InvalidPluginExecutionException("An error occurred in the Month, Day, Year Plug-in for Contact.", ex);


    }


    }


  }


}


 




3. Register the plug-in The last step is to register the plug-in. To register the plug-in, you may use the Plug-in Registration tool from the MSDN Code Gallery. After the assembly is uploaded, you need to associate the following steps to the plug-in:





 
























Message: Create


Primary Entity: contact


Filtering Attributes: birthdate


Eventing Pipeline Stage of Execution: Pre Stage


Execution Mode: Synchronous



Triggering Pipeline: Parent Pipeline



Message: Update


Primary Entity: contact


Filtering Attribute: birthdate


Eventing Pipeline Stage of Execution: Pre Stage


Execution Mode: Synchronous



Triggering Pipeline: Parent Pipeline



Message: Create


Primary Entity: contact


Filtering Attributes: birthdate


Eventing Pipeline Stage of Execution: Pre Stage


Execution Mode: Synchronous



Triggering Pipeline: Child Pipeline



Message: Update


Primary Entity: contact


Filtering Attribute: birthdate


Eventing Pipeline Stage of Execution: Pre Stage


Execution Mode: Synchronous



Triggering Pipeline: Child Pipeline





Summary








That’s all there is to it! The users will now be able to use Advanced Find to quickly identify their contacts birthday in a certain time period from now on. For the existing contacts previously stored in CRM, you will need to write a one-time SQL script to update the birthday fields in the MSCRM database in order for CRM to return the correct data back to the users. Hopefully this will help you on your next CRM project.



clip_image004



clip_image006



Cheers,



Darren Liu



Published Friday, February 27, 2009 9:26 AM by crmblog

Monday, December 29, 2008

Adding Notes to a Workflow E-mail

Posted by Jim Steger on December 23, 2008

In a recent post on the Microsoft Dynamics CRM Team blog, I discuss how you can easily overcome some of the native limitations of the CRM workflow Send E-mail action with some simple workflow assembly classes. Recently, I have been implementing the new eService Accelerator for our own corporate web site and came across a need for another CRM workflow e-mail utility...the ability to include all the notes of a record into the body of a workflow e-mail.

To accomplish this, I created a new activity class within my workflow utilities project and used the following code:

using System;
using System.Workflow.ComponentModel;
using System.Workflow.Activities;
using System.Web.Services.Protocols;
using Microsoft.Crm.Sdk;
using Microsoft.Crm.SdkTypeProxy;
using Microsoft.Crm.Sdk.Query;
using Microsoft.Crm.Workflow;
using System.Collections;
using System.Text;

namespace SonomaPartners.Crm.Workflow.Utilities
{
[CrmWorkflowActivity("Format Notes", "Utilities")]
public partial class FormatNotes : SequenceActivity
{
protected override ActivityExecutionStatus Execute(ActivityExecutionContext executionContext)
{
IContextService contextService = (IContextService)executionContext.GetService(typeof(IContextService));
IWorkflowContext ctx = contextService.Context;
ICrmService service = ctx.CreateCrmService();

this.FormattedNotes = BuildNotesString(service, ctx.PrimaryEntityId);

return base.Execute(executionContext);
}

public static DependencyProperty FormattedNotesProperty = DependencyProperty.Register("FormattedNotes", typeof(string), typeof(FormatNotes));
[CrmOutput("Formatted Notes")]
public string FormattedNotes
{
get { return (string)base.GetValue(FormattedNotesProperty); }
set { base.SetValue(FormattedNotesProperty, value); }
}


private string BuildNotesString(ICrmService service, Guid id)
{
StringBuilder results = new StringBuilder();
results.Append("Notes:<br>");

BusinessEntityCollection notes = new BusinessEntityCollection();
notes = RetrieveNotesForId(service, id);

foreach (annotation note in notes.BusinessEntities)
{
results.Append(string.Format("{0}<br>", note.subject));
results.Append(string.Format("{0}<br><br>", note.notetext));
}

return results.ToString();
}

private BusinessEntityCollection RetrieveNotesForId(ICrmService service, Guid id)
{
QueryByAttribute query = new QueryByAttribute();
query.EntityName = "annotation";
query.ColumnSet = new ColumnSet(new String[] { "subject", "notetext", "createdby", "createdon" });

query.Attributes = new String[] { "objectid" };
query.Values = new Object[] { id };
query.Orders = new ArrayList();
query.Orders.Add(new OrderExpression("createdon", OrderType.Descending));

try
{
RetrieveMultipleRequest request = new RetrieveMultipleRequest();
request.Query = query;

RetrieveMultipleResponse returnedNotes = (RetrieveMultipleResponse)service.Execute(request);
return returnedNotes.BusinessEntityCollection;
}
catch (SoapException ex)
{
throw new Exception(ex.Detail.InnerText);
}
}
}
}


Now, your e-mail includes any notes in the body of the message as shown here:



Feel free to download the Visual Studio 2008 workflow utilities assembly solution file.



Btw, I also wanted to thank Steve Kaplan and Jim Glass for allowing me to contribute on the Microsoft Dynamics CRM Team blog.





Posted by Jim Steger on December 23, 2008 | Permalink

Friday, December 19, 2008

Creating a customized Plug-in Template for Microsoft Dynamics CRM 4.0

Published Tuesday, December 09, 2008 8:34 AM by crmblog

Today we welcome MVP Bill Ryan from CustomerEffective as our guest blogger.

In its basic form, CRM 4.0 Plugin development is straightforward, however it has many monotonous elements. This walkthrough on writing Plugins illustrates many of the repetitive tasks that plugin development entails.  Among them are:

  • Creation of a new Class Library Project
  • Renaming of the default class (class1.cs or class1.vb to Plugin name)
  • Adding references to the Microsoft.Crm.Sdk & Microsoft.Crm.SdkTypeProxy libraries
  • Aliasing those libraries
  • Making the class inherit from the IPlugin interface
  • Adding the methods for that interface (or letting VS do it for you)

Most of these items are pretty simple to do, but it is monotonous and you have to remember to do it each time.  However most non-trivial plugins require some sort of configuration information.  Because plugins are dynamic link libraries hosted by CRM (and typically stored in the database), you can't use Settings.config,app.config or web.config files like you would in most other projects.  If you need this functionality, you need to roll your own solution. 

Fortunately, when you register each step for your Plugin, the PluginRegistration tool gives you two sections, one for an unsecured configuration and one for Secure Configuration.  But unless you write code to do something with this information, simply storing it will provide no benefit. (Mitch Milam has an excellent introduction to this).  To take advantage of configuration information, you need to add a new constructor to your plugin and then write code to read the settings.

You can just copy and paste his code if you'd like, but in all likelihood you'll probably want to write something specifically suited to your needs. Normally this would be something you'd just create in a library and attach as a reference to your project. However because of the plugin registration process, you'll typically want to work with as few dll's as possible (assemblies which reside in the Global Assembly Cache are an exception to this because you don't need to do anything special to use them).

Anyway, I found myself copying and pasting the same  class over and over into each plugin I wrote and it was something that got old quickly. Additionally, when ever developers come on board who aren't familiar with Plugin development, they will typically get stuck making the same mistakes you made when you first started writing plugins, something which can cost a lot of time and frustration.

Read more for Bill's solution...

Cheers,

MVP Bill Ryan

Wednesday, November 26, 2008

Storing Configuration Data for Microsoft Dynamics CRM Plug-ins

Microsoft Dynamics CRM Team Blog wrote this:


CRM MVP Mitch Milam returns as a guest blogger. You can read more from Mitch at his blog.

One of the benefits to the plug-in architecture of CRM 4.0 is the ability to store plug-ins in the CRM database so they may be used by multiple CRM servers. This introduces a slight complication regarding the storage of configuration information. Because the plug-in assembly doesn’t reside on the disk the normal method of using a .config file located with the assembly no longer works.

Luckily, the plug-in architecture solves this issue by allowing the developer to supply configuration information for each step executed by the plug-in.

Plug-in Configuration Architecture

As noted in the CRM SDK article, Writing the Plug-in Constructor, when creating your plug-in, you may define a constructor that passes two parameters to your plug-in: unsecure configuration and secure configuration:

   1: public class SamplePlugin : IPlugin
   2: {
   3:   public SamplePlugin(string unsecureConfig, string secureConfig)
   4:   {
   5:   }
   6: }

Both parameters are strings and may contain any configuration data, in any format, that you wish. For the purposes of this discussion, we will only be concerned with the unsecure configuration parameter.

Creating a Configuration Structure

Since most of us are familiar with the XML configuration provided by the standard Properties.Settings structure, I thought it would be a great idea to retain as much of that experience as possible so we can move code from a stand-alone test application to a plug-in with little difficulty.

Using an XML fragment that closely resembles the Settings section found in the .config file of a .Net assembly, we can create a similarly functional system for storing configuration data. Consider the following XML:

   1: <Settings>
   2:     <setting name="RetryCount">
   3:         <value>5</value>
   4:     </setting>
   5:     <setting name="TaskPrefix">
   6:         <value>This task was created on {0}.</value>
   7:     </setting>
   8:     <setting name="FirstRun">
   9:         <value>false</value>
  10:     </setting>
  11: </Settings>

As you can see, we have three settings which contain values that we would normally find in our .config file and which are used to configure our assembly. Using the Plug-in Registration Tool, we can add this information to the Unsecure Configuration field when registering a new step, as show by the figure below:

MitchCode

Plug-in Configuration Class

I created a simple class to extract values from an XML document for simple data types such as Guids, strings, Booleans, and integers, given the structure we discussed above:

   1: class PluginConfiguration
   2: {
   3:     private static string GetValueNode(XmlDocument doc, string key)
   4:     {
   5:         XmlNode node = doc.SelectSingleNode(String.Format("Settings/setting[@name='{0}']", key));
   6:  
   7:         if (node != null)
   8:         {
   9:             return node.SelectSingleNode("value").InnerText;
  10:         }
  11:         return string.Empty;
  12:     }
  13:  
  14:     public static Guid GetConfigDataGuid(XmlDocument doc, string label)
  15:     {
  16:         string tempString = GetValueNode(doc, label);
  17:  
  18:         if (tempString != string.Empty)
  19:         {
  20:             return new Guid(tempString);
  21:         }
  22:         return Guid.Empty;
  23:     }
  24:  
  25:     public static bool GetConfigDataBool(XmlDocument doc, string label)
  26:     {
  27:         bool retVar;
  28:  
  29:         if (bool.TryParse(GetValueNode(doc, label), out retVar))
  30:         {
  31:             return retVar;
  32:         }
  33:         else
  34:         {
  35:             return false;
  36:         }
  37:     }
  38:  
  39:     public static int GetConfigDataInt(XmlDocument doc, string label)
  40:     {
  41:         int retVar;
  42:  
  43:         if (int.TryParse(GetValueNode(doc, label), out retVar))
  44:         {
  45:             return retVar;
  46:         }
  47:         else
  48:         {
  49:             return -1;
  50:         }
  51:     }
  52:  
  53:     public static string GetConfigDataString(XmlDocument doc, string label)
  54:     {
  55:         return GetValueNode(doc, label);
  56:     }
  57: }

Putting PluginConfiguration to Work

Once we have our PluginConfiguration class added to our project, we need to modify the plug-in constructor to extract the values from our configuration string:

   1: public SamplePlugin(string unsecureConfig, string secureConfig)
   2: {
   3:     XmlDocument doc = new XmlDocument();
   4:     doc.LoadXml(unsecureConfig);
   5:  
   6:     string TaskPrefix = PluginConfiguration.GetConfigDataString(doc, "TaskPrefix");
   7:     bool FirstRun = PluginConfiguration.GetConfigDataBool(doc, "FirstRun");
   8:     int RetryCount = PluginConfiguration.GetConfigDataInt(doc, "RetryCount");
   9: }

There is no automatic determination of data types so you will need to know which method to use to extract a specific value from the configuration data.

Conclusion

Today we’ve covered how to store configuration information used by a CRM plug-in within the CRM database itself. One of the items you may wish to remember is that each step executed by a plug-in has its own configuration information. If you are using the same configuration data for multiple steps, you will need to set the configuration values to be the same between steps or just insert configuration data that is necessary for a particular step to complete successfully.

Mitch Milam