CrmSvcUtil Extensions for CRM 2015 released

I’ve released CRM 2015 versions for the CrmSvcUtil extensions:

CrmSvcUtil Entity Generator – Download from Codeplex

CrmSvcUtil Attribute Constant Generator – Download from Codeplex

CrmSvcUtil Option Set Enum Generator – Download from Codeplex

Advertisements

CrmSvcUtil Entity Generator Extension

Following from my other CrmSvcUtil extensions CrmSvcUtil Attribute Constant Generator Extension and CrmSvcUtil Option Set Enum Generator Extension, I’ve also implemented a (light-weight) extension to the standard entity generator.

Currently, the list of customisations that this extension adds is:

  1. Replaces the standard <summary> tag for all attributes with the new summary in the form: [ISNULL(Attribute.Label, Attribute.LogicalName)]: [Attribute.Description].
  2. Add a System.ComponentModel.Description attribute to each attribute showing the label of the attribute.

The purpose of these extensions is to give visiblity of the label of an attribute – specifically to make our lives easier when coding against attributes whose labels no longer match their logical name.

Download the CrmSvcUtil extension at codeplex: CrmSvcUtil Generate Entities.

To use the extension:

  1. Copy GenerateEntities.dll into the directory containing CrmSvcUtil.exe
  2. Run CrmSvcUtil with the following parameters:
CrmSvcUtil.exe ^
/codecustomization:"GenerateEntities.CodeCustomizationService, GenerateEntities" ^
/url:http://servername/orgname/XRMServices/2011/Organization.svc ^
/out:Attributes.cs ^

CrmSvcUtil Option Set Enum Generator Extension

Following from my recent post, I thought I’d release the option set enumeration generator I have been using as well.

I am aware that several existing CrmSvcUtil extensions as well as the unextended CrmSvcUtil generate option set enumerations but this one has several key advantages:

  1. Enumerations are generated as members of the entity classes, not global options sets. This allows copies of global option sets per attribute that uses the global option set.
  2. Duplicate enumeration options are handled by appending the value of the item, not an arbitrary counter. Appending a counter can actually cause bugs that are incredibly difficult to track down if another duplicate instance is added in between two existing duplicates – effectively changing the key/value pair combination without updating dependent code.
  3. Enumerations are generated and consistently named for Option Set, Boolean, State and Status attribute types. Enumerations are referred to in the form “[Entity Schema Name].[Attribute Logical Name]Values” in all cases.

Download the CrmSvcUtil extension at codeplex: CrmSvcUtil Generate Option Set Enums.

To use the extension:

  1. Copy GenerateOptionSetEnums.dll into the directory containing CrmSvcUtil.exe
  2. Run CrmSvcUtil with the following parameters:
CrmSvcUtil.exe ^
/codecustomization:"GenerateOptionSetEnums.CodeCustomizationService, GenerateOptionSetEnums" ^
/codewriterfilter:"GenerateOptionSetEnums.FilteringService, GenerateOptionSetEnums" ^
/url:http://servername/orgname/XRMServices/2011/Organization.svc ^
/out:Enumerations.cs ^

The baseTypes parameter is optional and can be used to override the base types for entity classes which is needed if you have previously generated the wrapper classes with an extension (eg. Microsoft.Xrm.Client.CodeGeneration.CodeCustomization).

To use the generated option set enumerations:

Contact contact = new Contact();
contact.Attributes[Contact.firstnameAttribute] = "Michael";
contact.Attributes[Contact.lastnameAttribute] = "Palmer";
contact.EducationCode = new OptionSetValue((int)Contact.gendercodeValues.Male);

CrmSvcUtil Attribute Constant Generator Extension

A recent question on the CRM MSDN forum made me realise that as far as I can see, no-one has yet created a CrmSvcUtil extension to generate attribute constants. So I did.

Download the CrmSvcUtil extension at codeplex: CrmSvcUtil Generate Attribute Constants.

To use the extension:

  1. Copy GenerateAttributeConstants.dll into the directory containing CrmSvcUtil.exe
  2. Run CrmSvcUtil with the following parameters:
CrmSvcUtil.exe ^
/codecustomization:&quot;GenerateAttributeConstants.CodeCustomizationService, GenerateAttributeConstants&quot; ^
/codewriterfilter:&quot;GenerateAttributeConstants.FilteringService, GenerateAttributeConstants&quot; ^
/url:http://servername/orgname/XRMServices/2011/Organization.svc ^
/out:Attributes.cs

The baseTypes parameter is optional and can be used to override the base types for entity classes which is needed if you have previously generated the wrapper classes with an extension (eg. Microsoft.Xrm.Client.CodeGeneration.CodeCustomization).

To use the generated attribute constants:

Contact contact = new Contact();
contact.Attributes[Contact.firstnameAttribute] = &quot;Michael&quot;;
contact.Attributes[Contact.lastnameAttribute] = &quot;Palmer&quot;;

CRM2011 Field Data Type Converter

Download: CRM 2011 Field Data Type Converter (1.0.4926.18530)

When trying to import a solution into CRM 2011, if you try import a field with the same name as an existing field but with a different data type, you will receive the following error.

Attribute <Attribute Name> is a <Existing Data Type>, but a <Import Data Type> type was specified.

Usually this happens when attempting to convert a field to a different type. In an environment where there is no data that cannot be lost, this is usually not an issue as one can simply

  1. Delete the existing attribute
  2. Import the solution again

In a production environment or an environment where data cannot be lost, this is not so simple as step 1 above will result in all data in the field being lost.

Enter the CRM2011 Field Data Converter…

The CRM2011 Field Data Converter does the following:

  1. Accepts a CRM Url and credentials, an entity and field, a destination type and various type specific parameters.
  2. Checks whether the supplied entity and field exist and what the field’s existing type is.
  3. Checks whether the source field can be converted to the destination type (see Conversions).
  4. Backs up the existing data.
  5. Deletes the source field.
  6. Creates a new field with the same name and new data type using the supplied type specific parameters.
  7. Populates the data back into the field (converted to the new type – see Conversions).
  8. Outputs a file listing the failures (conversion failures, save failures)

The CRM2011 Field Data Converter DOES NOT do the following:

  • Recover from catastrophic failures: As the data is backed in “in memory”, if a server, network or SQL instance goes down after storing the data and deleting the field, the DATA WILL BE LOST. BACK UP YOUR CRM DATABASE BEFORE RUNNING THIS TOOL.
  • Delete dependencies on the source field: If the source field cannot be deleted manually within CRM, it cannot be deleted by this tool. All dependencies on the field (forms, views, etc) must be manually removed before the tool is run and put back afterwards.

Conversions
  Destination
Boolean DateTime Decimal Double Integer Memo Money String
Source Boolean   * * * * * * *
DateTime   * *
Decimal * *   * * * * *
Double * * *   * * * *
Integer * * * *   * * *
Memo * # * * *   * *
Money * * * * * *   *
String * # * * * * *  
Key
* Convert.To<Type>(sourceValue, CultureInfo.CurrentCulture)
# DateTime.ParseExact(sourceValue, %/datetimepattern%, CultureInfo.CurrentCulture)

CRM2011 Plugin InputParameter and OutputParameter Helper

Download: CRM 2011 Plugin Parameter Wrapper Generator (1.0.1.412)

Every time I am asked to write a plugin using one of the less common plugin messages, I silently wish that I didn’t have to trawl through the SDK and then access the InputParameters and OutputParameters collection through a DataCollection<string, object> collection. I usually end up deploying the plugin several times due to casting parameters to the wrong type or accessing parameters that just don’t exist. I know that there are several sources I could use to avoid this but lets face it – intelli-sense made us lazy.

So, the other day I’d had enough and created a tool that generates a helper file for plugin entity messages that gives me strongly typed access to all parameters in each plugin’s InputParameters and OutputParameters collection in C# or VB.Net.

The generated helper file includes a namespace for each plugin message and a class for each of the InputParameters and OutputParameters collection. Every parameter in the collection has a Contains<ParameterName> and a Get<ParameterName> method that both expect the Microsoft.Xrm.Sdk.IPluginExecutionContext passed to them. The Contains<ParameterName> method returns a boolean value indicating whether the parameter exists and the Get<ParameterName> method gets the strongly typed value of the parameter.

AccountPreCreate.cs (below) shows the use of the Xrm.Core.Helpers.Plugins.Create helper class. Because there is a namespace per message type, I can include the relevant namespace in the using statements in my plugin class and then simply call the Contains and Get methods on the InputParameters and OutputParameters classes.

namespace Client.Plugins
{
    using System;
    using Microsoft.Xrm.Sdk;
    using Xrm.Core.Helpers.Plugins.Create;

    /// <summary>
    /// AccountPreCreate Plugin.
    /// </summary>
    public class AccountPreCreate : Plugin
    {
        /// <summary>
        /// Initializes a new instance of the <see cref="AccountPreCreate"/> class.
        /// </summary>
        public AccountPreCreate() : base(typeof(AccountPreCreate))
        {
            this.RegisteredEvents.Add(new Tuple<int, string, string, Action<LocalPluginContext>>(20, "Create", "account", new Action<LocalPluginContext>(this.ExecuteAccountPreCreate)));
        }

        /// <summary>
        /// Executes the plug-in.
        /// </summary>
        /// <param name="localContext">The <see cref="LocalPluginContext"/></param>
        protected void ExecuteAccountPreCreate(LocalPluginContext localContext)
        {
            IPluginExecutionContext context = localContext.PluginExecutionContext;
            if (InputParameters.ContainsTarget(context))
            {
                Entity target = InputParameters.GetTarget(context);
                //Do something with the target
            }
        }
    }
}

In order to generate the helper classes, I needed to get the list of plugin parameters for all messages. The SDK was not very helpful there and while I was able to find a complete list for CRM4 here, no such thing seems to exist for CRM2011. Eventually, I decided to generate my own list using the following SQL statements in the CRM database and used them in the generator to generate the helper classes.

SELECT DISTINCT REPLACE(mreq.Name, '{Entity.PrimaryEntityName}', ''), mrf.Name, mrf.ClrParser, ISNULL(mreq.PrimaryObjectTypeCode, 0)
FROM SdkMessageRequestField mrf
INNER JOIN SdkMessageRequest mreq ON mrf.SdkMessageRequestId = mreq.SdkMessageRequestId
WHERE NOT mrf.ClrParser LIKE '%Microsoft.Crm.Sdk%'
AND NOT mrf.ClrParser LIKE '%Microsoft.Crm.Sdk.Reserved%'
ORDER BY 1, 2, 4 DESC
SELECT DISTINCT REPLACE(mreq.Name, '{Entity.PrimaryEntityName}', ''), mrf.Value, mrf.ClrFormatter, ISNULL(mreq.PrimaryObjectTypeCode, 0)
FROM SdkMessageResponseField mrf
INNER JOIN SdkMessageResponse mres ON mrf.SdkMessageResponseId = mres.SdkMessageResponseId
INNER JOIN SdkMessageRequest mreq ON mres.SdkMessageRequestId = mreq.SdkMessageRequestId
WHERE NOT mrf.ClrFormatter LIKE '%Microsoft.Crm.Sdk%'
AND NOT mrf.ClrFormatter LIKE '%Microsoft.Crm.Sdk.Reserved%'
ORDER BY 1, 2, 4 DESC

Where the statements above select more than one instance of a message (one with a primary object type code and the other without), the tools assume that the one with the primary object type code is the correct one.

I have obviously not tested every single plugin message input/output parameter so please let me know if there is a message/parameter missing, an extra message/parameter or a parameter with the incorrect data type etc.