October 17, 2011

Differentiating between NULL and NO CHANGE in CRM 2011 plugins

Unfortunately a NULL value of a field in the plugin target can have 2 meanings:

  1. Value has been changed to NULL (new value equals NULL)
  2. Value has not changed (new value equals old/current value)

I’m referring to values of properties when using the strongly typed approach like:

Microsoft.Xrm.Sdk.Entity target = ExecutionContext.InputParameters["Target"] as Entity;
Account account = target.ToEntity<Account>();
string name = account.name;

Thus it could be illustrated something like this:

Plugin target “New” value
NULL ???
VALUE VALUE

Where the plugin target is the value of the attribute taken from the target entity in the plugin and the "new" value is the value that will be saved to the DB.

Shortly speaking we don’t know the “new” value in 50% of cases.

Adding a pre entity image will lower this to 75%.

Pre Entity Image Plugin target “New” value
NULL NULL NULL
NULL VALUE VALUE
VALUE NULL ???
VALUE_1 VALUE_2 VALUE_2

So if the plugin target has any value then that is off course the “new” value.If it’s NULL and the pre image value is also NULL we are sure that the new value is NULL.

The only way to get to 100% I could think of is to get back to the not type safe, property bag approach, because if we just use:
entity.Attributes[“name”]
instead of
account.name
we actually get the information because the value is either NULL if it was set to NULL or it’s not there at all (throwing a value not found exception).

This gives:

Pre Entity Image Plugin target Property bag “New” value
NULL NULL Doesn’t matter NULL
NULL VALUE Doesn’t matter VALUE
VALUE NULL Has attribute NULL
VALUE NULL Doesn’t have attr. VALUE
VALUE_1 VALUE_2 Doesn’t matter VALUE 2

I summed this up in this little piece of code:

private object TrueValue(object preValue, object targetValue, Entity target, string attributeName)
{
    if (preValue == null || targetValue != null)
    {
        return targetValue;
    }
    else
    {              
        if(target.Attributes.ContainsKey(attributeName.ToLower()))
        {
            return null;
        }
        else
        {
            return preValue;
        }               
    }
}

Which could be used like this:

Account preImage = this.ExecutionContext.PreEntityImages["PreImage"].ToEntity<Account>();
string newName = (string) TrueValue(preImage.name, account.name, target, “name”);

It could be simplified so that it doesn’t use the typed account property at all (it’s basically the same as the property bag).

The scenario can get even more complicated when using field level security. In those cases a NULL value can have 3 meaning:

  1. Value has been changed to NULL
  2. Value has not change
  3. User (in whose context the plugin is executed) has no READ privilege to the field

The simplest solution seems to run plugins, which touch secured field, in the context of an administrative user (impersonate).

edit 2012-10-25

Created a new version of the TrueValue method which is type safe.

private object TrueValue(object preValue, object targetValue, Entity target, Expression<Func<object>> property)
{
string propertyName = "";
try
{
propertyName = ((dynamic)property.Body).Member.Name;
}
catch
{
propertyName = ((dynamic)property.Body).Operand.Member.Name;
}

if (preValue == null || targetValue != null)
{
return targetValue;
}
else
{
if (target.Attributes.ContainsKey(propertyName.ToLower()))
{
return null;
}
else
{
return preValue;
}
}
}




You can use it like this:

Account targetAccount = target.ToEntity<Account>();

Account preImage = this.ExecutionContext.PreEntityImages["PreImage"].ToEntity<Account>();

string name = (string)TrueValue(preImage.Name, targetAccount.Name, target, () => targetAccount.Name);