December 2, 2015

India and Canada Data Center Discovery URLs

In the last months Microsoft announced the opening of new data centers for Dynamics CRM Online in India and Canada.
These data centers are not available yet, but Microsoft defined the Discovery Web Service URLs. The addresses are:
https://disco.crm8.dynamics.com/XRMServices/2011/Discovery.svc (India)
https://disco.crm3.dynamics.com/XRMServices/2011/Discovery.svc (Canada)

The Indian data center is identified by the crm8 host, the Canadian one by crm3. This will affect also the organization url, for example if Contoso Ltd. has two branches, one in India and the other one in Canada, their urls will be:
https://contosoindia.crm8.dynamics.com
https://contosocanada.crm3.dynamics.com

The relative MSDN page has NOT been updated to include this change, the next table is a recap:

HostLocation
crmNorth America
crm4EMEA
crm5APAC
crm2South America
crm9North America 2 (CRM Online for Government)
crm7Japan
crm6Australia
crm8India
crm3Canada

July 21, 2015

Visual Studio 2015 Shared Projects and CRM Plugins Development

Visual Studio 2015 is now available and inside the new features and improvements there is one that I like very much: Shared Projects. A Shared Project is intended to share easily the code between different platforms.
For C# projects this was already possible using (portable) class libraries and adding the assembly to the main project references.

Which is the big advantage of Shared Projects for a Dynamics CRM developer?
With Shared Projects the code is NOT compiled to a separate assembly but directly inside the main assembly of your project.

This is can be very useful for Plugin and Custom Workflow Activity development, because now we can create a common library with CRM methods to be used inside our plugins WITHOUT the need to use ILMerge for creating a single assembly in order to be registered inside Dynamics CRM.

As example I created a small shared project containing a simple method to check the current CRM Version:
using Microsoft.Crm.Sdk.Messages;
using Microsoft.Xrm.Sdk;
namespace CRM.Common
{
    public static class Utils
    {
        public enum CRMVersion
        {
            Unknown = 0, CRM2011 = 2011, CRM2013 = 2013, CRM2015 = 2015
        }

        public static CRMVersion GetCRMVersion(IOrganizationService service)
        {
            RetrieveVersionRequest versionRequest = new RetrieveVersionRequest();
            RetrieveVersionResponse versionResponse = (RetrieveVersionResponse)service.Execute(versionRequest);
            string version = versionResponse.Version;
            if (version.StartsWith("5")) { return CRMVersion.CRM2011; }
            if (version.StartsWith("6")) { return CRMVersion.CRM2013; }
            if (version.StartsWith("7")) { return CRMVersion.CRM2015; }
            return CRMVersion.Unknown;
        }
    }
}
After I created a normal Class Library project for my plugin and I added the Shared Project to the solution and a reference inside the Class Library:

The plugin code is very simple, it throws an exception indicating the CRM version:
using Microsoft.Xrm.Sdk;
using System;
using static CRM.Common.Utils;

namespace CRM.MyPlugin
{
    public class MyPlugin : IPlugin
    {
        public void Execute(IServiceProvider serviceProvider)
        {
            IPluginExecutionContext context = (IPluginExecutionContext)serviceProvider.GetService(typeof(IPluginExecutionContext));
            IOrganizationServiceFactory serviceFactory = (IOrganizationServiceFactory)serviceProvider.GetService(typeof(IOrganizationServiceFactory));
            IOrganizationService service = serviceFactory.CreateOrganizationService(context.UserId);

            CRMVersion version = GetCRMVersion(service);
            throw new InvalidPluginExecutionException(version.ToString());
        }
    }
}
If we compile the solution only a single assembly is generated and if we analyze it using ILSpy we can confirm that the Shared Project is compiled inside the main assembly:

And the plugin works without issues:


Shared Projects can be very useful for Dynamics CRM development because will definitely improve the quality and the reuse of the code.

Shared Projects are also available for Visual Studio 2013 as separate addon, you can download from here: Shared Project Reference Manager

July 14, 2015

New Dynamics CRM for phones iOS App and getFormFactor method

Yesterday Microsoft released the new iOS App for Dynamics CRM (iTunes link). The app only works with CRM versions 7.1 and higher, this means currently can be used only with CRM Online instances that received the Update 1.

The new App supports JavaScript so I wanted to test a little script with the method getFormFactor.

getFormFactor (MSDN link) is a new method introduced with CRM Online 2015 Update 1 returning a numeric value based on the current device. The possible values are:

ValueForm Factor
0Unknown
1Desktop
2Tablet
3Phone

The script I wrote is:
function accountOnLoad() {
    var factor = Xrm.Page.context.client.getFormFactor();
    var message = "The Form Factor is: " + factor;
    Xrm.Utility.alertDialog(message);
}
And the result is:

Note: the script is not called when a new record is created.

June 18, 2015

Hide fields dynamically based on Field Level Security privileges

When we set up Field Level Security for a field we can assign three different privileges (Read, Update, Write) to the users.
It is also possible to check the privilege using a supported JavaScript method: getUserPrivilege
This method returns an object with three boolean properties: canRead, canUpdate, canCreate.

We can use these properties in combination with the forEach method of attributes and controls collections in order to dynamically hide the fields.

The following code will hide dynamically all the fields protected by Field Level Security that the user can't read:
function HideSecuredFields() {
    // fetch all attributes on the form
    Xrm.Page.data.entity.attributes.forEach(
        function(attribute, aIndex) {
            // get the FLS privileges
            var privileges = attribute.getUserPrivilege();
            // check if the user can't read the field
            if (privileges.canRead == false) {
                // fetch all the controls related to the attribute
                attribute.controls.forEach(
                    function (control, cIndex) {
                       // hide the controls
                       control.setVisible(false);
                    }
                );
            }
        }
    );
}

May 11, 2015

Dynamics CRM Theme Generator

The Dynamics CRM 2015 Online Update 1 introduced a new feature called "Themes" to customize some aspects of the Dynamics CRM User Interface.
Because it's necessary every time to preview the changes in order to see how CRM will look like, I created a theme generator with a real time preview.
It's also possible to download the theme in order to be imported inside CRM.

The link is crmthemes.azurewebsites.net

April 27, 2015

GetAttributeValue demystified

Note: the content of this post is similar to this post from Dave Berry but I wanted to approach the same argument with some example code and tips.

Dynamics CRM allows to create fields with the following data types:

Except for "Single Line of Text" and "Multiple Lines of Text" (both use string), each one uses a different underline data type in .NET, some of these data types are nullable, some are not.

What means nullable and why we need to care about this?
Let's start with an example: In our CRM we have two records, the first record has all the fields filled with a value, in the second one all the fields are empty. When we use GetAttributeValue and there is a value, the method (fairly) returns the value. But what happens with our empty record? The response is "depends".

GetAttributeValue uses Generics, so we can choose to get a nullable type or not:
bool boolean = entity.GetAttributeValue<bool>("new_boolean");
bool? booleanNullable = entity.GetAttributeValue<bool?>("new_boolean");
In this case, if the value is null (for a Boolean/Two Options field means that no value is set) the first variable will contains false, the second will contains null.
The next table is a summary:

CRM Type .NET Type can hold null? default value
Single Line of Text string Yes
Option Set OptionSetValue Yes
Two Options bool No false
Image byte[] Yes
Whole Number int No 0
Floating Point Number double No 0.0
Decimal Number decimal No 0
Currency Money Yes
Multiple Lines of Text string Yes
Date and Time DateTime No DateTime.MinValue
Lookup EntityReference Yes
For the types that can hold null, we use GetAttributeValue and after check if it's null or not:
EntityReference lookupRef = entity.GetAttributeValue<EntityReference>("new_lookupid");
if (lookupRef == null) {
   // no value set
} else {
   // we have a value
}
For the types that can't hold null we need to ask ourselves: "The default value is enough for the requirement?"

If we are in a loop and we need to do a sum of an int field, the default value (0) is ok, so we can just do
int totalSum = 0;
foreach (Entity row in RowCollection.Entities) {
   int number = entity.GetAttributeValue<int>("new_wholenumber");
   totalSum+=number;
}
but if we are doing a multiplication we need to skip the null values, so we use int?
int totalMulty = 0;
foreach (Entity row in RowCollection.Entities) {
   int? number = entity.GetAttributeValue<int?>("new_wholenumber");
   if (number != null) {
      totalMulty*=number;
   }
}
otherwise with a null value our totalMulty variable will be 0.

DateTime is a particular case, it can't hold null but the default value (MinValue = 01/01/0001) can't be a valid CRM value (as happens with bool and numeric fields) so we can do the following check:
DateTime dateTime = entity.GetAttributeValue<DateTime>("new_datetime");
if (dateTime == DateTime.MinValue) {
   // no value set
} else {
   // we have a value
}
Practically when we don't use the nullable form, Dynamics CRM is doing the following:
double floating = entity.GetAttributeValue<double>("new_floating");
// equals to
double floating = entity.GetAttributeValue<double?>("new_floating").GetValueOrDefault();
The combination of the nullable form and the GetValueOrDefault can be useful in some scenarios. Let's say that we need to do a data migration to an external system, but if the source decimal Quantity is null, the target decimal Quantity must be -1.
decimal quantity = entity.GetAttributeValue<decimal?>("new_quantity").GetValueOrDefault(-1);
In this way we deal automatically the null values and they are ready for the target system.

Now you are a Dynamics CRM True Survivor!

April 2, 2015

Simplified Connection with complicated passwords

CRM 2011 introduced a very easy way to connect to Dynamics CRM instances: the Simplified Connection (MSDN: https://msdn.microsoft.com/en-us/library/gg695810.aspx).
Basically it's necessary to build a connection string instead of dealing with the specific deployment type (OnPremise, IFD or Online).

The downside of using a Simplified Connection is its weakness management of passwords containing special characters like double quotes, single quotes, ampersands.

Considering the MSDN example for a CRM Online connection:
Url=https://contoso.crm.dynamics.com; Username=jsmith@contoso.com; Password=passcode;
If the password is ;abc123 (note the semicolon) an exception will be thrown with the following message:
Format of the initialization string does not conform to specification starting at index 102. The solution for this problem is to include the password inside single quotes, the following connection string will work:
Url=https://contoso.crm.dynamics.com; Username=jsmith@contoso.com; Password=';abc123';
Assuming the connection string is builded dynamically the following code can be used:
string connectionString =
    String.Format("Url={0}; Username={1}; Password='{2}';", url, username, password);
What if our complicated password contains single quotes as well? Let's consider for example the following password: ;a''bc'123
In this case the previous exception (Format of the initialization string) will be thrown again. This issue can be solved "escaping" the single quotes using a Replace:
string connectionString =
    String.Format("Url={0}; Username={1}; Password='{2}';",
    url, username, password.Replace("'","''"));
Please note that the escape must be done also if your connection string is stored inside your app/web.config:
<add key="CRM"
value="Url=https://contoso.crm.dynamics.com; Username=jsmith@contoso.com; Password=';a''bc'123';"/>
But in this case our replace method will not work, because it will replace also the single quotes delimiting the password. In this scenario I suggest to put inside the app/web.config a placeholder instead of the delimiting single quotes that will be replaced after (for example #XYZ#):
<add key="CRM"
value="Url=https://contoso.crm.dynamics.com; Username=jsmith@contoso.com; Password=#XYZ#;a''bc'123#XYZ#;"/>
Then after the connection string is loaded we do the escape and the replace:
string connectionString = ConfigurationManager.ConnectionStrings["CRM"].ConnectionString;
// escape the single quotes inside the password
connectionString = connectionString.Replace("'","''");
// replace the placeholder with single quotes
connectionString = connectionString.Replace("#XYZ#","'");
Of course this will not work if the password contains the placeholder as well, so it's better to choose a long placeholder.

When the password is stored inside the app/web.config it's necessary to deal with another problem, the case that our password contains XML special characters (mostly double quotes) because this file is an XML.

If it's necessary to encode the password the following .NET method can be used:
string xmlPassword = System.Security.SecurityElement.Escape(password);
The result for the password ;a''b"c'123 (note the double quote between b and c that will create problems if not encoded) will be ;a&apos;&apos;bc&apos;123, a valid string to be written inside the app/web.config.