Monday, September 26, 2011

Problems with plugins on connection entity in CRM 2011

If you have registered a plugin on the connection entity, you might have experience some odd behaviour, such as the plugin getting called twice or the plugin to run under the SYSTEM user always. These are probably side effects of how connections are designed. Let me explain what happens behind the scenes when you create a connection so the problem will be more evident. Assume I create a connection between an opportunity and a user and I have a plugin that triggers on “Create” and “Update” of connection. The first thing to note is that when you “connect” two records you are in fact creating two connection records (one in each direction) so this happens sequentially inside the same transaction:
  1. The connection from the opportunity to the user is created (your plugin triggers here as expected). Temporarily the “relatedconnectionid” attribute is set to the id of the connection getting created.
  2. A reciprocal (corresponding) connection is created from the user to the opportunity which uses the corresponding connection role if available (your plugin triggers again because of the create, but the primary entity here is a different connection record)
  3. Once the second connection is created then the system updates the first connection record to set the RelatedConnectionId to the connection created in step 2 (your plugin triggers again because of the update)
In the previous example, my plugin ends up triggering 3 times, but I would like it to trigger only once since I am just connecting a user with an opportunity. In order to solve your problem, you need to first restrict your “update step” of the plugin to trigger only when selected attributes change in the connection, so you can make use of the Filtering Attributes from the plugin registration tool and you must as a minimum ensure that the RelatedConnectionId, modifiedon, modifiedby, modifiedonbehalfof and connectionid attributes are not checked so your plugin will not trigger in step 3 above. As a best practice, only select the attributes you really care about that must trigger the plugin when updated. This will prevent your plugin from triggering on step 3 above.

Now depending on your business logic you might want to avoid the plugin from triggering in step2 (or maybe not). If you do, you can use this trick inside your plugin to make sure that the plugin executes only for step1:

Entity creatingConnection = (Entity)context.InputParameters["Target"];
EntityReference relatedConnection = creatingConnection["relatedconnectionid"] as EntityReference;
if (relatedConnection != null && creatingConnection.Id != relatedConnection.Id)
{
    // Creating reciprocal connection
    // Return here if the plugin should not execute in step2.
}

The other tricky aspect of plugins with connections is that sometimes your plugin gets called in the context of the SYSTEM user (even if you specify Calling User or another user in the Plugin Registration Tool). This happens at least every time you have a plugin registered on “Delete” of connection. The only workaround that you have, is that from your plugin, when you create the CRM service you impersonate back the initiating user:

IOrganizationServiceFactory serviceFactory = (IOrganizationServiceFactory)serviceProvider.GetService(typeof(IOrganizationServiceFactory));
IOrganizationService service = serviceFactory.CreateOrganizationService(context.InitiatingUserId);

I hope you find this helpful!

3 comments:

  1. Cannot you test IsMaster attribute to distinguish between master and reciprocal?

    ReplyDelete