November 7, 2014

Custom workflow activity not visible in plugin registration tool

When trying to register a plugin assembly containing custom workflow activities to CRM Online using the Plugin Registration Tool from the 2013 SDK, the custom workflow activities were not visible. Only plugins.

Tried changing the assembly major version, and the key which signs the assembly - no luck.

The solution was to use the Plugin Registration Tool from the pre-release 2015 SDK found here - http://www.microsoft.com/en-us/download/details.aspx?id=44567.


September 25, 2014

Wrong SaveMode on Activation

Latest CRM 2013 on premise. execObj.getEventArgs().getSaveMode() when clicking Activate should be 6, but is 1 - the same as on Save.

http://msdn.microsoft.com/en-us/library/gg509060.aspx#BKMK_GetSaveMode

August 28, 2014

CRM plugins. The proper way of updating values of records in context.

In this post I will try to describe how to properly, inside a plugin, change attribute values of the record that triggered the create or update operation. From time to time I stumble upon code that threats the context record (the one on which a create or update operation triggered a plugin) as any other record, which is wrong.

How it should not be done:

  1. A plugin step is registered on the create event of entity E.
  2. Record A of entity E is created.
  3. The registered create plugin step fires.
  4. (…) Some business logic (…)
  5. Record A is updated by calling the organization service .Update() method.

Or even worse:

  1. A plugin step is registered on the update vent of entity E.
  2. Record A of entity E is updated.
  3. The registered update plugin step fires.
  4. (…) Some business logic (…)
  5. Record A is updated by calling the organization service .Update() method.
  6. The registered update plugin step fires again (since an update was done).
  7. Inside the plugin the .Depth property of the execution context is checked to avoid infinite loops.

What is wrong in both these scenarios is that an Update request is used to update the context record. This causes:

  1. Unnecessary load on the CRM application and database.
  2. Infinite loops in plugins.
  3. Possible transactional  errors and performance issues, which could lead to plugins failing.

The above approach might be used by analogy with SQL databases. But the CRM service is not a SQL database, and requires a different approach.

The proper way of doing this is registering the plugin step (create or update) in the pre event inside transaction phase (20) and modifying the attributes directly in the target entity. All changes done to entity attributes in the “20” phase will be stored, without any unnecessary round trips and service calls.

image

This is a simple example showing a plugin that sets new_attribute3 to a string concatenation of new_attribute1 and new_attribute2. It is of course simplified, does not check validate the values, check for nulls, etc. but it proves the point.

If this code is registered to execute on the pre operation (20) phase, the value of new_attribute3 will be updated, without any other operations. This is because when in phase 20 it modifies the record before CRM does the core database operation (30). So anything we change here, will be what gets later pushed to SQL.

March 16, 2014

Dynamics CRM - Delete does not trigger Disassociate; Need pre validation plugin to retrieve existing N-N relationships.

Whoever worked with multi-multi (N-N) standard relationships in CRM knows that you can trigger plugins on the Associate and Disassociate messages. They fire when you add / remove a N-N relationship between two records.

You would expect that when you delete one of the records from the N-N relationship this would also trigger a Disassociate message, after all the association is also removed. It does not.
Because of that if you want to program something that triggers whenever a N-N relationship between two records is removed is is not enough to trigger on the Disassociate message. You also need to trigger your logic on the Delete (and probably SetStateDynamicEntity, for inactivation) messages.

So far so good, but how to retrieve all the N-N related records from the other entity? This can be done by querying the "hidden" N-N cross entity, which can be found under "Relationship Entity Name" when you open the properties of any N-N relationship.

Using LINQ the query would look something like this:

// data - instance of the service context
from nn in data.nn_relationship_entity
where nn.relationship1id = FILTER_ID
select nn.relationship2.Value

An important thing to note is that if you run this in the Pre-Operation stage of the Delete message it will return 0 rows! The correct place (one of the very few examples) is the Pre-Validation phase. If run there it will correctly return the ID's of all related records, on which you can then run you disassociation logic.

Hope this helps someone...