Category Archives: Tutorial

Win or Lose opportunity from code

Today, I needed to close an opportunity from a plug-in without having an early bound context. The following code sample shows a function to generate an OpportunityClose Entity:

[csharp]

public static Entity CreateOpportunityClose(Guid opportunityId, string subject)
{
var opportunityClose = new Entity("opportunityclose");
opportunityClose.Attributes.Add("opportunityid", new EntityReference("opportunity", opportunityId));
opportunityClose.Attributes.Add("subject", subject);
return opportunityClose;
}

[/csharp]

Using this function, we can generate either a WinOpportunityRequest or a LoseOpportunityRequest.

Won:

[csharp]
var opportunityWinRequest = new WinOpportunityRequest
{
OpportunityClose = CreateOpportunityClose(opportunity.Id, "Reason for this win"),
    Status = new OptionSetValue(3)  // Status won
};
[/csharp]

Lost:

[csharp]
var opportunityLoseRequest = new LoseOpportunityRequest
{
    OpportunityClose = CreateOpportunityClose(opportunity.Id, "Reason for this loss."),
    Status = new OptionSetValue(4)  // cancelled (or your own status)
};
[/csharp]

Finally, you will have to execute the request using the IOrganizationService.Execute Method.

Programatically Updating the Secure Configuration of a Plug-in Step

When writing a plug-in for CRM 2011 you have the ability to use two strings passed to the constructor of your plug-in, the Unsecure Configuration string and the Secure Configuration string. These configuration strings are stored in the message processing step that are registered to your plug-in. You could use these configuration strings to store data that is not to be hard-coded into your plug-in.

The differences between the Unsecure Configuration and the Secure Configuration are:

  1. The Unsecure Configuration information could be read by any user in CRM whereas the Secure Configuration information could be read only by CRM Administrators.
  2. When exporting a solution, the Unsecure Configuration information on an activated step that is included in that solution will be exported, whereas the Secure Configuration information will not be included in that solution.[1]

The difference described in (2) is logical: if it would be included in the exported solution, anyone getting their hands on the solution file could extract the Secure Configuration from the solution file. However, when deploying your solution in a DTAP street, this is a pain since you’re missing your Secure Configuration on your plug-in steps after deployment of the solution.

If you do not have a lot of plug-in steps with a Secure Configuration, you could manually update your Secure Configuration using the Plug-in Registration Tool provided in the CRM 2011 SDK. I was working on a project that had 240+ registered steps which all shared a single Secure Configuration. Since we deployed our solution to our Test environment on a daily base, manually updating the Secure Configuration was not a viable option.

Described here is a simple console application that reads a Secure Configuration text from a text file and update the required plug-in steps with this text. You could easily transform this logic into a WPF or Windows Forms application if you need something fancier!

First, create a new Console Application:New projectSecond, add to following references to your project:

  1. microsoft.xrm.client
  2. microsoft.xrm.sdk

Third, add a CRM Connection String to your App.config

In order to connect to CRM, we use a connection string in the configuration file. Open your App.config, and add the

[xml]
<connectionStrings>
<add name="MyCRMConnection" connectionString="Authentication Type=Integrated; Server=http://192.168.1.12:5555/MyOrganization"/>
</connectionStrings>
[/xml]

section including the connection string to your CRM Server:

connectionstringFor more information on the connection string to connect to CRM, see MSDN article: Simplified Connection to Microsoft Dynamics CRM

Fourth, read the Secure Configuration from a file

In order to enter the Secure Configuration, we add a text file to our project named secureconfiguration.txt:

configuration fileMake sure to set the Copy to Output Directory to Copy always, so the file will be deployed on building your project.

Now open Program.cs, find the Main function:

[csharp]
/// <summary>
/// The program’s main function.
/// </summary>
/// <param name="args">The program arguments</param>
public static void Main(string[] args)
{
[/csharp]

then add:

[csharp]
Console.WriteLine("Starting update of Secure Configurations");
var secureConfig = string.Empty;
using (var fileStream = File.OpenRead("secureconfiguration.txt"))
{
using (var streamReader = new StreamReader(fileStream))
{
secureConfig = streamReader.ReadToEnd();
Console.WriteLine("Configuration loaded:\r\n{0}\r\n", secureConfig);
}
}
[/csharp]

Fifth, connect to CRM

Next, we need to connect to CRM using the connection string:

[csharp]

// connect to CRM
var connection = new CrmConnection("MyCRMConnection");
Console.WriteLine("Connecting to server {0}", connection.ServiceUri);
using (var serviceContext = new CrmOrganizationServiceContext(connection))
{

[/csharp]

Sixth, filter which plug-ins to add a secure configuration to

You need a way to filter CRM’s sdkmessageprocessingsteps because CRM has a lot of it’s own and you possibly have built other plug-ins. Say now, we have a Plug-In Type with the following properties:

  • Name: My.Name.Space.PluginName
  • FriendlyName: MyPlugIn
  • Assembly name: MyPlugInAssembly

and all of it’s registered steps need a Secure Configuration set to the value of the contents of our secureconfiguration.txt file.

In order to filter the plug-in steps we retrieve, we only look for plug-in steps that are registered to the plug-in defined above. We retrieve the id of this Plug-In Type using the following code (note that you can use either the name or the friendlyname property):

[csharp]
// Define plug-in type properties to retrieve correct plug-in type:
var pluginTypeName = "My.Name.Space.PluginName";
var assemblyname = "MyPlugInAssembly";

// Create the QueryExpression object to retrieve plug-in type
var query = new QueryExpression();
query.EntityName = "plugintype";
query.Criteria.AddCondition("name", ConditionOperator.Equal, pluginTypeName);
query.Criteria.AddCondition("assemblyname", ConditionOperator.Equal, assemblyname);
var retrievedPluginTypes = serviceContext.RetrieveMultiple(query);
if (retrievedPluginTypes.Entities.Count != 1)
{
Console.WriteLine("Error. Plug-in type was not found! Assembly: [{0}], Plug-in Type Name [{1}]", assemblyname, pluginTypeName);
Console.ReadKey();
return;
}

var pluginTypeId = (Guid)retrievedPluginTypes.Entities[0].Attributes["plugintypeid"];
Console.WriteLine("Retrieved plugin with Id = {0}", pluginTypeId);

[/csharp]

Seventh, find the plug-in steps associated with the previously found Plug-in Type

The following query will retrieve all the Plug-In Steps registered to the Plug-in Type.

[csharp]

// Create the QueryExpression object to retrieve your steps.
query = new QueryExpression();

// Set the properties of the QueryExpression object.
query.EntityName = "sdkmessageprocessingstep";
query.ColumnSet = new ColumnSet(new[] { "sdkmessageprocessingstepid", "sdkmessageprocessingstepsecureconfigid", "plugintypeid" });
query.Criteria.AddCondition(new ConditionExpression("plugintypeid", ConditionOperator.Equal, pluginTypeId));
var retrievedSteps = serviceContext.RetrieveMultiple(query);

[/csharp]

Last but not least, we iterate over all Plug-In Steps

Iterating over all Plug-In Steps, we either set the existing Secure Configuration to a new value or create a new Secure Configuration entity if one did not exist yet.

[csharp]
// Update all individual steps
foreach (var step in retrievedSteps.Entities)
{
// Test if plugin already has a Secure Configuration:
if (!step.Attributes.Contains("sdkmessageprocessingstepsecureconfigid"))
{
Console.WriteLine("Plugin has no Secure Configuration yet, so create one.");

// create new secure configuration record
var processingStepSecureConfiguration = new Entity("sdkmessageprocessingstepsecureconfig");
processingStepSecureConfiguration.Attributes.Add("secureconfig", secureConfig);
processingStepSecureConfiguration.Id = serviceContext.Create(processingStepSecureConfiguration);

// now attach secure configuration record to processing step
step.Attributes["sdkmessageprocessingstepsecureconfigid"] = processingStepSecureConfiguration.ToEntityReference();
serviceContext.Update(step);
}
else
{
Console.WriteLine("Plug-in step already has a Secure Configuration entity, so retrieve that entity and update the configuration.");

// Retrieve and update secure configuration record
var secureConfigurationReference = step.Attributes["sdkmessageprocessingstepsecureconfigid"] as EntityReference;
var secureConfiguration = serviceContext.Retrieve(secureConfigurationReference.LogicalName, secureConfigurationReference.Id, new ColumnSet(new[] { "sdkmessageprocessingstepsecureconfigid", "secureconfig" }));
secureConfiguration.Attributes["secureconfig"] = secureConfig;
serviceContext.Update(secureConfiguration);
}
}

[/csharp]

And we will close our console application with a friendly message 🙂

[csharp]
Console.WriteLine("… Secure Configuration succesfully uploaded to the steps.");
Console.WriteLine(string.Empty);
Console.WriteLine("All done. Press a key to continue.");
Console.ReadKey();
[/csharp]

And that’s it!

One final note: the secure configuration string is not passed to plug-ins running in the CRM Outlook Client!

Here is the complete code for Program.cs:

[csharp]
// ———————————————————————-
// <author>Tomas Schulkes</author>
// ————————————————————————
namespace PluginSecureConfigUpdater
{
using System;
using System.IO;
using Microsoft.Xrm.Client;
using Microsoft.Xrm.Sdk;
using Microsoft.Xrm.Sdk.Query;

/// <summary>
/// Main program
/// </summary>
public class Program
{
/// <summary>
/// The program’s main function.
/// </summary>
/// <param name="args">The program arguments</param>
public static void Main(string[] args)
{
Console.WriteLine("Starting update of Secure Configurations");
var secureConfig = string.Empty;
using (var fileStream = File.OpenRead("secureconfiguration.txt"))
{
using (var streamReader = new StreamReader(fileStream))
{
secureConfig = streamReader.ReadToEnd();
Console.WriteLine("Configuration loaded:\r\n{0}\r\n", secureConfig);
}
}

// connect to CRM
var connection = new CrmConnection("MyCRMConnection");
Console.WriteLine("Connecting to server {0}", connection.ServiceUri);
using (var serviceContext = new CrmOrganizationServiceContext(connection))
{
Console.WriteLine("Retrieving message steps…");

// Define plug-in type properties to retrieve correct plug-in type:
var pluginTypeName = "My.Name.Space.PluginName";
var assemblyname = "MyPlugInAssembly";

// Create the QueryExpression object to retrieve plug-in type
var query = new QueryExpression();
query.EntityName = "plugintype";
query.Criteria.AddCondition("name", ConditionOperator.Equal, pluginTypeName);
query.Criteria.AddCondition("assemblyname", ConditionOperator.Equal, assemblyname);
var retrievedPluginTypes = serviceContext.RetrieveMultiple(query);
if (retrievedPluginTypes.Entities.Count != 1)
{
Console.WriteLine("Error. Plug-in type was not found! Assembly: [{0}], Plug-in Type Name [{1}]", assemblyname, pluginTypeName);
Console.ReadKey();
return;
}

var pluginTypeId = (Guid)retrievedPluginTypes.Entities[0].Attributes["plugintypeid"];
Console.WriteLine("Retrieved plugin with Id = {0}", pluginTypeId);

// Create the QueryExpression object to retrieve your steps.
query = new QueryExpression();

// Set the properties of the QueryExpression object.
query.EntityName = "sdkmessageprocessingstep";
query.ColumnSet = new ColumnSet(new[] { "sdkmessageprocessingstepid", "sdkmessageprocessingstepsecureconfigid", "plugintypeid" });
query.Criteria.AddCondition(new ConditionExpression("plugintypeid", ConditionOperator.Equal, pluginTypeId));
var retrievedSteps = serviceContext.RetrieveMultiple(query);

// Update all individual steps
foreach (var step in retrievedSteps.Entities)
{
// Test if plugin already has a Secure Configuration:
if (!step.Attributes.Contains("sdkmessageprocessingstepsecureconfigid"))
{
Console.WriteLine("Plugin has no Secure Configuration yet, so create one.");

// create new secure configuration record
var processingStepSecureConfiguration = new Entity("sdkmessageprocessingstepsecureconfig");
processingStepSecureConfiguration.Attributes.Add("secureconfig", secureConfig);
processingStepSecureConfiguration.Id = serviceContext.Create(processingStepSecureConfiguration);

// now attach secure configuration record to processing step
step.Attributes["sdkmessageprocessingstepsecureconfigid"] = processingStepSecureConfiguration.ToEntityReference();
serviceContext.Update(step);
}
else
{
Console.WriteLine("Plug-in step already has a Secure Configuration entity, so retrieve that entity and update the configuration.");

// Retrieve and update secure configuration record
var secureConfigurationReference = step.Attributes["sdkmessageprocessingstepsecureconfigid"] as EntityReference;
var secureConfiguration = serviceContext.Retrieve(secureConfigurationReference.LogicalName, secureConfigurationReference.Id, new ColumnSet(new[] { "sdkmessageprocessingstepsecureconfigid", "secureconfig" }));
secureConfiguration.Attributes["secureconfig"] = secureConfig;
serviceContext.Update(secureConfiguration);
}
}

Console.WriteLine("… Secure Configuration succesfully uploaded to the steps.");
Console.WriteLine(string.Empty);
Console.WriteLine("All done. Press a key to continue.");
Console.ReadKey();
}
}
}
}
[/csharp]

[1] – Difference between Secure / Unsecure Configuration of Plugin Registration tool in CRM 2011